From 76c60972692d7ad0492110b5dc5f6b65e75682e0 Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 13 Jun 2023 14:16:30 +0200 Subject: [PATCH 001/142] had to comment out pytorch-cuda otherwise getting version error environment.yml --- environment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/environment.yml b/environment.yml index 77ae5ea0..d47b7e2f 100644 --- a/environment.yml +++ b/environment.yml @@ -13,7 +13,7 @@ dependencies: - gin-config=0.5.0 - ignite=0.4.11 - pytorch=2.0.1 - - pytorch-cuda=11.8 + #- pytorch-cuda=11.8 - lightgbm=3.3.5 - numpy=1.24.3 - pandas=2.0.0 From c7657c3e5061fd220dbf3dc2da5f706840282049 Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 13 Jun 2023 14:19:47 +0200 Subject: [PATCH 002/142] added how to enviroment variable PYTORCH_ENABLE_MPS_FALLBACK=1 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 84a7e41b..6f6fffd2 100644 --- a/README.md +++ b/README.md @@ -182,6 +182,7 @@ icu-benchmarks train \ > For a list of available flags, run `icu-benchmarks train -h`. > Run with `PYTORCH_ENABLE_MPS_FALLBACK=1` on Macs with Metal Performance Shaders. +> Can set conda enviroment variable by running `conda env config vars set PYTORCH_ENABLE_MPS_FALLBACK=1` [//]: # (> Please note that, for Windows based systems, paths need to be formatted differently, e.g: ` r"\..\data\mortality_seq\hirid"`.) > For Windows based systems, the next line character (\\) needs to be replaced by (^) (Command Prompt) or (`) (Powershell) From cf8696ef04d06c86c064b40521a15c49e401564b Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 13 Jun 2023 14:24:40 +0200 Subject: [PATCH 003/142] type casted float64 to float32 to make it work with MPS --- icu_benchmarks/data/loader.py | 2 +- icu_benchmarks/models/wrappers.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 9b831aa7..d0e040e4 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -151,7 +151,7 @@ def get_data_and_labels(self) -> Tuple[np.array, np.array]: def to_tensor(self): data, labels = self.get_data_and_labels() - return from_numpy(data), from_numpy(labels) + return from_numpy(data).to(float32), from_numpy(labels).to(float32) @gin.configurable("ImputationDataset") diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 5fc94a12..867e9874 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -135,7 +135,7 @@ def finalize_step(self, step_prefix=""): try: self.log_dict( { - f"{step_prefix}/{name}": metric.compute() + f"{step_prefix}/{name}": (np.float32(metric.compute()) if isinstance(metric.compute(), np.float64 ) else metric.compute() ) for name, metric in self.metrics[step_prefix].items() if "_Curve" not in name }, @@ -358,9 +358,9 @@ def test_step(self, dataset, _): self.set_metrics(test_label) test_pred = self.predict(test_rep) - self.log("test/loss", self.loss(test_label, test_pred), sync_dist=True) + self.log("test/loss", np.float32(self.loss(test_label, test_pred)), sync_dist=True) logging.debug(f"Test loss: {self.loss(test_label, test_pred)}") - self.log_metrics(test_label, test_pred, "test") + self.log_metrics(np.float32(test_label), np.float32(test_pred), "test") def predict(self, features): if self.run_mode == RunMode.regression: @@ -373,7 +373,7 @@ def log_metrics(self, label, pred, metric_type): self.log_dict( { - f"{metric_type}/{name}": metric(self.label_transform(label), self.output_transform(pred)) + f"{metric_type}/{name}": np.float32(metric(self.label_transform(label), self.output_transform(pred))) for name, metric in self.metrics.items() # Filter out metrics that return a tuple (e.g. precision_recall_curve) if not isinstance(metric(self.label_transform(label), self.output_transform(pred)), tuple) From c08ed2ca108ded995fab682cd96eee9d3c003713 Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 20 Jun 2023 16:13:58 +0200 Subject: [PATCH 004/142] added components of tft --- icu_benchmarks/models/layers.py | 445 +++++++++++++++++++++++++++++++- 1 file changed, 444 insertions(+), 1 deletion(-) diff --git a/icu_benchmarks/models/layers.py b/icu_benchmarks/models/layers.py index c08623bd..969e3ac0 100644 --- a/icu_benchmarks/models/layers.py +++ b/icu_benchmarks/models/layers.py @@ -1,10 +1,14 @@ +import os import gin import math import torch import torch.nn as nn import torch.nn.functional as F from torch.nn.utils import weight_norm - +from torch import Tensor +from torch.nn.parameter import UninitializedParameter +from typing import Dict, Tuple, Optional, List +from torch.nn import LayerNorm @gin.configurable("masking") def parallel_recomb(q_t, kv_t, att_type="all", local_context=3, bin_size=None): @@ -314,3 +318,442 @@ def forward(self, x): out = self.net(x) res = x if self.downsample is None else self.downsample(x) return self.relu(out + res) + +class MaybeLayerNorm(nn.Module): + ''' + Implements layer normalization or identity function depending on output_size + ''' + def __init__(self, output_size, hidden_size, eps): + super().__init__() + if output_size and output_size == 1: + self.ln = nn.Identity() + else: + self.ln = LayerNorm(output_size if output_size else hidden_size, eps=eps) + + def forward(self, x): + return self.ln(x) + + +class GLU(nn.Module): + ''' + Gated Linear Unit consists of a linear layer followed by a GLU where input is split in half along dim to form a and b + GLU(a,b)=a ⊗ σ(b)where σ is signmoid activation and ⊗ is element-wise product + ''' + def __init__(self, hidden_size, output_size): + super().__init__() + self.lin = nn.Linear(hidden_size, output_size * 2) + + def forward(self, x: Tensor) -> Tensor: + x = self.lin(x) + x = F.glu(x) + return x + +class GRN(nn.Module): + ''' + Gated Residual Network consists of a maybe normalization layer -->linear --> ELU -->linear-->GLU + in addition to a residual connection + ''' + def __init__(self, + input_size, + hidden_size, + output_size=None, + context_hidden_size=None, + dropout=0.0,): + super().__init__() + self.layer_norm = MaybeLayerNorm(output_size, hidden_size, eps=1e-3) + self.lin_a = nn.Linear(input_size, hidden_size) + if context_hidden_size is not None: + self.lin_c = nn.Linear(context_hidden_size, hidden_size, bias=False) + else: + self.lin_c = nn.Identity() + self.lin_i = nn.Linear(hidden_size, hidden_size) + self.glu = GLU(hidden_size, output_size if output_size else hidden_size) + self.dropout = nn.Dropout(dropout) + self.out_proj = nn.Linear(input_size, output_size) if output_size else None + + def forward(self, a: Tensor, c: Optional[Tensor] = None): + x = self.lin_a(a) + if c is not None: + x = x + self.lin_c(c).unsqueeze(1) + x = F.elu(x) + x = self.lin_i(x) + x = self.dropout(x) + x = self.glu(x) + y = a if self.out_proj is None else self.out_proj(a) + x = x + y + return self.layer_norm(x) + + +# @torch.jit.script #Currently broken with autocast +def fused_pointwise_linear_v1(x, a, b): + out = torch.mul(x.unsqueeze(-1), a) + out = out + b + return out + +@torch.jit.script +def fused_pointwise_linear_v2(x, a, b): + out = x.unsqueeze(3) * a + out = out + b + return out + + +class TFTEmbedding(nn.Module): + def __init__(self,static_categorical_inp_lens,temporal_known_categorical_inp_lens, + temporal_observed_categorical_inp_lens,static_continuous_inp_size,temporal_known_continuous_inp_size, + temporal_observed_continuous_inp_size,temporal_target_size,hidden_size, + initialize_cont_params=False): + # initialize_cont_params=False prevents form initializing parameters inside this class + # so they can be lazily initialized in LazyEmbedding module + super().__init__() + self.s_cat_inp_lens = static_categorical_inp_lens + self.t_cat_k_inp_lens = temporal_known_categorical_inp_lens + self.t_cat_o_inp_lens = temporal_observed_categorical_inp_lens + self.s_cont_inp_size = static_continuous_inp_size + self.t_cont_k_inp_size = temporal_known_continuous_inp_size + self.t_cont_o_inp_size = temporal_observed_continuous_inp_size + self.t_tgt_size = temporal_target_size + + self.hidden_size = hidden_size + + # There are 7 types of input: + # 1. Static categorical + # 2. Static continuous + # 3. Temporal known a priori categorical + # 4. Temporal known a priori continuous + # 5. Temporal observed categorical + # 6. Temporal observed continuous + # 7. Temporal observed targets (time series obseved so far) + + self.s_cat_embed = nn.ModuleList([ + nn.Embedding(n, self.hidden_size) for n in self.s_cat_inp_lens]) if self.s_cat_inp_lens else None + self.t_cat_k_embed = nn.ModuleList([ + nn.Embedding(n, self.hidden_size) for n in self.t_cat_k_inp_lens]) if self.t_cat_k_inp_lens else None + self.t_cat_o_embed = nn.ModuleList([ + nn.Embedding(n, self.hidden_size) for n in self.t_cat_o_inp_lens]) if self.t_cat_o_inp_lens else None + + if initialize_cont_params: + self.s_cont_embedding_vectors = nn.Parameter(torch.Tensor(self.s_cont_inp_size, self.hidden_size)) if self.s_cont_inp_size else None + self.t_cont_k_embedding_vectors = nn.Parameter(torch.Tensor(self.t_cont_k_inp_size, self.hidden_size)) if self.t_cont_k_inp_size else None + self.t_cont_o_embedding_vectors = nn.Parameter(torch.Tensor(self.t_cont_o_inp_size, self.hidden_size)) if self.t_cont_o_inp_size else None + self.t_tgt_embedding_vectors = nn.Parameter(torch.Tensor(self.t_tgt_size, self.hidden_size)) + + self.s_cont_embedding_bias = nn.Parameter(torch.zeros(self.s_cont_inp_size, self.hidden_size)) if self.s_cont_inp_size else None + self.t_cont_k_embedding_bias = nn.Parameter(torch.zeros(self.t_cont_k_inp_size, self.hidden_size)) if self.t_cont_k_inp_size else None + self.t_cont_o_embedding_bias = nn.Parameter(torch.zeros(self.t_cont_o_inp_size, self.hidden_size)) if self.t_cont_o_inp_size else None + self.t_tgt_embedding_bias = nn.Parameter(torch.zeros(self.t_tgt_size, self.hidden_size)) + + self.reset_parameters() + + + def reset_parameters(self): + '''' + embeddings are initilitized using xavier's method and biases are initlitized with zeros + ''' + if self.s_cont_embedding_vectors is not None: + torch.nn.init.xavier_normal_(self.s_cont_embedding_vectors) + torch.nn.init.zeros_(self.s_cont_embedding_bias) + if self.t_cont_k_embedding_vectors is not None: + torch.nn.init.xavier_normal_(self.t_cont_k_embedding_vectors) + torch.nn.init.zeros_(self.t_cont_k_embedding_bias) + if self.t_cont_o_embedding_vectors is not None: + torch.nn.init.xavier_normal_(self.t_cont_o_embedding_vectors) + torch.nn.init.zeros_(self.t_cont_o_embedding_bias) + if self.t_tgt_embedding_vectors is not None: + torch.nn.init.xavier_normal_(self.t_tgt_embedding_vectors) + torch.nn.init.zeros_(self.t_tgt_embedding_bias) + if self.s_cat_embed is not None: + for module in self.s_cat_embed: + module.reset_parameters() + if self.t_cat_k_embed is not None: + for module in self.t_cat_k_embed: + module.reset_parameters() + if self.t_cat_o_embed is not None: + for module in self.t_cat_o_embed: + module.reset_parameters() + + + def _apply_embedding(self, + cat: Optional[Tensor], + cont: Optional[Tensor], + cat_emb: Optional[nn.ModuleList], + cont_emb: Tensor, + cont_bias: Tensor, + ) -> Tuple[Optional[Tensor], Optional[Tensor]]: + e_cat = torch.stack([embed(cat[...,i]) for i, embed in enumerate(cat_emb)], dim=-2) if cat is not None else None + if cont is not None: + #the line below is equivalent to following einsums + #e_cont = torch.einsum('btf,fh->bthf', cont, cont_emb) + #e_cont = torch.einsum('bf,fh->bhf', cont, cont_emb) + + e_cont = fused_pointwise_linear_v1(cont, cont_emb, cont_bias) + else: + e_cont = None + + if e_cat is not None and e_cont is not None: + return torch.cat([e_cat, e_cont], dim=-2) + elif e_cat is not None: + return e_cat + elif e_cont is not None: + return e_cont + else: + return None + + def forward(self, x: Dict[str, Tensor]): + # temporal/static categorical/continuous known/observed input + s_cat_inp = x.get('s_cat', None) + s_cont_inp = x.get('s_cont', None) + t_cat_k_inp = x.get('k_cat', None) + t_cont_k_inp = x.get('k_cont', None) + t_cat_o_inp = x.get('o_cat', None) + t_cont_o_inp = x.get('o_cont', None) + t_tgt_obs = x['target'] # Has to be present + + # Static inputs are expected to be equal for all timesteps + # For memory efficiency there is no assert statement + s_cat_inp = s_cat_inp[:,0,:] if s_cat_inp is not None else None + s_cont_inp = s_cont_inp[:,0,:] if s_cont_inp is not None else None + + s_inp = self._apply_embedding(s_cat_inp, + s_cont_inp, + self.s_cat_embed, + self.s_cont_embedding_vectors, + self.s_cont_embedding_bias) + t_known_inp = self._apply_embedding(t_cat_k_inp, + t_cont_k_inp, + self.t_cat_k_embed, + self.t_cont_k_embedding_vectors, + self.t_cont_k_embedding_bias) + t_observed_inp = self._apply_embedding(t_cat_o_inp, + t_cont_o_inp, + self.t_cat_o_embed, + self.t_cont_o_embedding_vectors, + self.t_cont_o_embedding_bias) + + # Temporal observed targets + t_observed_tgt = fused_pointwise_linear_v2(t_tgt_obs, self.t_tgt_embedding_vectors, self.t_tgt_embedding_bias) + + return s_inp, t_known_inp, t_observed_inp, t_observed_tgt + +class LazyEmbedding(nn.modules.lazy.LazyModuleMixin, TFTEmbedding): + cls_to_become = TFTEmbedding + + def __init__(self, static_categorical_inp_lens,temporal_known_categorical_inp_lens, + temporal_observed_categorical_inp_lens,static_continuous_inp_size,temporal_known_continuous_inp_size, + temporal_observed_continuous_inp_size,temporal_target_size,hidden_size): + super().__init__(static_categorical_inp_lens,temporal_known_categorical_inp_lens, + temporal_observed_categorical_inp_lens,static_continuous_inp_size,temporal_known_continuous_inp_size, + temporal_observed_continuous_inp_size,temporal_target_size,hidden_size, initialize_cont_params=False) + + if static_continuous_inp_size: + self.s_cont_embedding_vectors = UninitializedParameter() + self.s_cont_embedding_bias = UninitializedParameter() + else: + self.s_cont_embedding_vectors = None + self.s_cont_embedding_bias = None + + if temporal_known_continuous_inp_size: + self.t_cont_k_embedding_vectors = UninitializedParameter() + self.t_cont_k_embedding_bias = UninitializedParameter() + else: + self.t_cont_k_embedding_vectors = None + self.t_cont_k_embedding_bias = None + + if temporal_observed_continuous_inp_size: + self.t_cont_o_embedding_vectors = UninitializedParameter() + self.t_cont_o_embedding_bias = UninitializedParameter() + else: + self.t_cont_o_embedding_vectors = None + self.t_cont_o_embedding_bias = None + + self.t_tgt_embedding_vectors = UninitializedParameter() + self.t_tgt_embedding_bias = UninitializedParameter() + + def initialize_parameters(self, x): + if self.has_uninitialized_params(): + s_cont_inp = x.get('s_cont', None) + t_cont_k_inp = x.get('k_cont', None) + t_cont_o_inp = x.get('o_cont', None) + t_tgt_obs = x['target'] # Has to be present + + if s_cont_inp is not None: + self.s_cont_embedding_vectors.materialize((s_cont_inp.shape[-1], self.hidden_size)) + self.s_cont_embedding_bias.materialize((s_cont_inp.shape[-1], self.hidden_size)) + + if t_cont_k_inp is not None: + self.t_cont_k_embedding_vectors.materialize((t_cont_k_inp.shape[-1], self.hidden_size)) + self.t_cont_k_embedding_bias.materialize((t_cont_k_inp.shape[-1], self.hidden_size)) + + if t_cont_o_inp is not None: + self.t_cont_o_embedding_vectors.materialize((t_cont_o_inp.shape[-1], self.hidden_size)) + self.t_cont_o_embedding_bias.materialize((t_cont_o_inp.shape[-1], self.hidden_size)) + + self.t_tgt_embedding_vectors.materialize((t_tgt_obs.shape[-1], self.hidden_size)) + self.t_tgt_embedding_bias.materialize((t_tgt_obs.shape[-1], self.hidden_size)) + + self.reset_parameters() + +class VariableSelectionNetwork(nn.Module): + ''' + Learns to select important netowrks consists of GRNs with one GRN for variable weights + and the others for input embedding + ''' + def __init__(self, hidden_size,dropout, num_inputs): + super().__init__() + self.joint_grn = GRN(hidden_size*num_inputs, hidden_size, output_size=num_inputs, context_hidden_size=hidden_size) + self.var_grns = nn.ModuleList([GRN(hidden_size, hidden_size, dropout=dropout) for _ in range(num_inputs)]) + + def forward(self, x: Tensor, context: Optional[Tensor] = None): + Xi = torch.flatten(x, start_dim=-2) + grn_outputs = self.joint_grn(Xi, c=context) + sparse_weights = F.softmax(grn_outputs, dim=-1) + transformed_embed_list = [m(x[...,i,:]) for i, m in enumerate(self.var_grns)] + transformed_embed = torch.stack(transformed_embed_list, dim=-1) + #the line below performs batched matrix vector multiplication + #for temporal features it's bthf,btf->bth + #for static features it's bhf,bf->bh + variable_ctx = torch.matmul(transformed_embed, sparse_weights.unsqueeze(-1)).squeeze(-1) + + return variable_ctx, sparse_weights + +class StaticCovariateEncoder(nn.Module): + ''' + Network to produce 4 contexts vectors to enrich static variables + Vriable selection Network --> GRNs + ''' + def __init__(self, num_static_vars,hidden_size,dropout): + super().__init__() + self.vsn = VariableSelectionNetwork(hidden_size,dropout, num_static_vars) + self.context_grns = nn.ModuleList([GRN(hidden_size, hidden_size, dropout=dropout) for _ in range(4)]) + + def forward(self, x: Tensor) -> Tuple[Tensor, Tensor, Tensor, Tensor]: + variable_ctx, sparse_weights = self.vsn(x) + + # Context vectors: + # variable selection context + # enrichment context + # state_c context + # state_h context + cs, ce, ch, cc = [m(variable_ctx) for m in self.context_grns] + + return cs, ce, ch, cc + + +class InterpretableMultiHeadAttention(nn.Module): + ''' + Multi-head attention different as it outputs the attention_probability and it combines the attention weights instead of + concating them different from the one implemented already in YAIB + ''' + def __init__(self,n_head,hidden_size,attn_dropout,dropout,example_length,d_head): + super().__init__() + self.n_head = n_head + assert hidden_size % n_head == 0 + self.d_head = hidden_size // n_head + self.qkv_linears = nn.Linear(hidden_size, (2 * n_head + 1) * d_head, bias=False) + self.out_proj = nn.Linear(self.d_head, hidden_size, bias=False) + self.attn_dropout = nn.Dropout(attn_dropout) + self.out_dropout = nn.Dropout(dropout) + self.scale = self.d_head**-0.5 + self.register_buffer("_mask", torch.triu(torch.full((example_length, example_length), float('-inf')), 1).unsqueeze(0)) + + def forward(self, x: Tensor) -> Tuple[Tensor, Tensor]: + bs, t, h_size = x.shape + qkv = self.qkv_linears(x) + q, k, v = qkv.split((self.n_head * self.d_head, self.n_head * self.d_head, self.d_head), dim=-1) + q = q.view(bs, t, self.n_head, self.d_head) + k = k.view(bs, t, self.n_head, self.d_head) + v = v.view(bs, t, self.d_head) + + # attn_score = torch.einsum('bind,bjnd->bnij', q, k) + attn_score = torch.matmul(q.permute((0, 2, 1, 3)), k.permute((0, 2, 3, 1))) + attn_score.mul_(self.scale) + + attn_score = attn_score + self._mask + + attn_prob = F.softmax(attn_score, dim=3) + attn_prob = self.attn_dropout(attn_prob) + + # attn_vec = torch.einsum('bnij,bjd->bnid', attn_prob, v) + attn_vec = torch.matmul(attn_prob, v.unsqueeze(1)) + m_attn_vec = torch.mean(attn_vec, dim=1) + out = self.out_proj(m_attn_vec) + out = self.out_dropout(out) + + return out, attn_prob + +class TFTBack(nn.Module): + ''' + Big part of TFT architecture consists of static enrichment followed by mutli-head self-attention then position wise feed forward + followed by a gate and a dense layer + GRNs-->multi-head attention-->GRNs-->GLU-->Linear-->output + ''' + def __init__(self, encoder_length,num_historic_vars,hidden_size,dropout,num_future_vars, + n_head,attn_dropout,example_length,d_head,quantiles): + super().__init__() + + self.encoder_length = encoder_length + self.history_vsn = VariableSelectionNetwork(hidden_size,dropout, num_historic_vars) + self.history_encoder = nn.LSTM(hidden_size, hidden_size, batch_first=True) + self.future_vsn = VariableSelectionNetwork(hidden_size,dropout,num_future_vars) + self.future_encoder = nn.LSTM(hidden_size, hidden_size, batch_first=True) + + + self.input_gate = GLU(hidden_size, hidden_size) + self.input_gate_ln = LayerNorm(hidden_size, eps=1e-3) + + self.enrichment_grn = GRN(hidden_size, + hidden_size, + context_hidden_size=hidden_size, + dropout=dropout) + self.attention = InterpretableMultiHeadAttention(n_head,hidden_size,attn_dropout,dropout,example_length,d_head) + self.attention_gate = GLU(hidden_size, hidden_size) + self.attention_ln = LayerNorm(hidden_size, eps=1e-3) + + self.positionwise_grn = GRN(hidden_size, + hidden_size, + dropout=dropout) + + self.decoder_gate = GLU(hidden_size, hidden_size) + self.decoder_ln = LayerNorm(hidden_size, eps=1e-3) + + self.quantile_proj = nn.Linear(hidden_size, len(quantiles)) + + def forward(self, historical_inputs, cs, ch, cc, ce, future_inputs): + historical_features, _ = self.history_vsn(historical_inputs, cs) + history, state = self.history_encoder(historical_features, (ch, cc)) + future_features, _ = self.future_vsn(future_inputs, cs) + future, _ = self.future_encoder(future_features, state) + torch.cuda.synchronize() + + # skip connection + input_embedding = torch.cat([historical_features, future_features], dim=1) + temporal_features = torch.cat([history, future], dim=1) + temporal_features = self.input_gate(temporal_features) + temporal_features = temporal_features + input_embedding + temporal_features = self.input_gate_ln(temporal_features) + + # Static enrichment + enriched = self.enrichment_grn(temporal_features, c=ce) + + # Temporal self attention + x, _ = self.attention(enriched) + + # Don't compute hictorical quantiles + x = x[:, self.encoder_length:, :] + temporal_features = temporal_features[:, self.encoder_length:, :] + enriched = enriched[:, self.encoder_length:, :] + + x = self.attention_gate(x) + x = x + enriched + x = self.attention_ln(x) + + # Position-wise feed-forward + x = self.positionwise_grn(x) + + # Final skip connection + x = self.decoder_gate(x) + x = x + temporal_features + x = self.decoder_ln(x) + + out = self.quantile_proj(x) + + return out \ No newline at end of file From 0a5caff5d02e5ffb022b55c1d745be7dc71219cd Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 20 Jun 2023 16:14:16 +0200 Subject: [PATCH 005/142] added tft --- icu_benchmarks/models/dl_models.py | 40 +++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 0fb1b0d2..39eac551 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -3,7 +3,8 @@ import numpy as np import torch.nn as nn from icu_benchmarks.contants import RunMode -from icu_benchmarks.models.layers import TransformerBlock, LocalBlock, TemporalBlock, PositionalEncoding +from icu_benchmarks.models.layers import TransformerBlock, LocalBlock, TemporalBlock, PositionalEncoding,LazyEmbedding,StaticCovariateEncoder,TFTBack + from icu_benchmarks.models.wrappers import DLPredictionWrapper @@ -280,3 +281,40 @@ def forward(self, x): o = o.permute(0, 2, 1) # Permute to channel last pred = self.logit(o) return pred +@gin.configurable +class TemporalFusionTransformer(DLPredictionWrapper): + """ + Implementation of https://arxiv.org/abs/1912.09363 + """ + def __init__(self, encoder_length,static_categorical_inp_lens,temporal_known_categorical_inp_lens, + temporal_observed_categorical_inp_lens,static_continuous_inp_size,temporal_known_continuous_inp_size, + temporal_observed_continuous_inp_size,temporal_target_size,hidden_size,num_static_vars,dropout,num_historic_vars,num_future_vars, + n_head,attn_dropout,example_length,d_head,quantiles): + super().__init__() + + + + self.encoder_length = encoder_length #this determines from how distant past we want to use data from + + self.embedding = LazyEmbedding(static_categorical_inp_lens,temporal_known_categorical_inp_lens, + temporal_observed_categorical_inp_lens,static_continuous_inp_size,temporal_known_continuous_inp_size, + temporal_observed_continuous_inp_size,temporal_target_size,hidden_size) + self.static_encoder = StaticCovariateEncoder(num_static_vars,hidden_size,dropout) + self.TFTpart2 = torch.jit.script(TFTBack(encoder_length,num_historic_vars,hidden_size,dropout,num_future_vars, + n_head,attn_dropout,example_length,d_head,quantiles)) + + def forward(self, x: Dict[str, Tensor]) -> Tensor: + s_inp, t_known_inp, t_observed_inp, t_observed_tgt = self.embedding(x) + + # Static context + cs, ce, ch, cc = self.static_encoder(s_inp) + ch, cc = ch.unsqueeze(0), cc.unsqueeze(0) #lstm initial states + + # Temporal input + _historical_inputs = [t_known_inp[:,:self.encoder_length,:], t_observed_tgt[:,:self.encoder_length,:]] + if t_observed_inp is not None: + _historical_inputs.insert(0,t_observed_inp[:,:self.encoder_length,:]) + + historical_inputs = torch.cat(_historical_inputs, dim=-2) + future_inputs = t_known_inp[:, self.encoder_length:] + return self.TFTpart2(historical_inputs, cs, ch, cc, ce, future_inputs) \ No newline at end of file From 52d0daa3fb9b6d67cdc159c3f2250e481bb65637 Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Thu, 22 Jun 2023 13:55:47 +0200 Subject: [PATCH 006/142] setting variables for tft and tuning --- .../TemporalFusionTransformer.gin | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 configs/prediction_models/TemporalFusionTransformer.gin diff --git a/configs/prediction_models/TemporalFusionTransformer.gin b/configs/prediction_models/TemporalFusionTransformer.gin new file mode 100644 index 00000000..d430a443 --- /dev/null +++ b/configs/prediction_models/TemporalFusionTransformer.gin @@ -0,0 +1,26 @@ +# Settings for Transformer model. + +# Common settings for DL models +include "configs/prediction_models/common/DLCommon.gin" + +# Optimizer params +train_common.model = @TemporalFusionTransformer + +optimizer/hyperparameter.class_to_tune = @Adam +optimizer/hyperparameter.weight_decay = 1e-6 +optimizer/hyperparameter.lr = (1e-5, 3e-4) + +# Encoder params +model/hyperparameter.class_to_tune = @TemporalFusionTransformer +model/hyperparameter.encoder_length = (1,14,"log-uniform", 2) +model/hyperparameter.hidden = (32, 256, "log-uniform", 2) +model/hyperparameter.num_classes = %NUM_CLASSES +model/hyperparameter.hidden = (32, 256, "log-uniform", 2) +model/hyperparameter.depth = (1, 3) +model/hyperparameter.dropout = (0.0, 0.4) +model/hyperparameter.dropout_att = (0.0, 0.4) +model/hyperparameter.n_heads = (1, 8, "log-uniform", 2) +model/hyperparameter.quantiles=[0.1, 0.5, 0.9] +model/hyperparameter.example_length=(1,14,"log-uniform", 2) + + From 8e812a0010db7987120da6d1cb7cf364799b7704 Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Thu, 22 Jun 2023 13:56:27 +0200 Subject: [PATCH 007/142] modifications to fit in the tft and comments --- icu_benchmarks/models/dl_models.py | 36 ++++--- icu_benchmarks/models/layers.py | 149 +++++++++++++++-------------- 2 files changed, 98 insertions(+), 87 deletions(-) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 39eac551..4646502e 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -4,8 +4,9 @@ import torch.nn as nn from icu_benchmarks.contants import RunMode from icu_benchmarks.models.layers import TransformerBlock, LocalBlock, TemporalBlock, PositionalEncoding,LazyEmbedding,StaticCovariateEncoder,TFTBack - +from typing import Dict from icu_benchmarks.models.wrappers import DLPredictionWrapper +from torch import Tensor,cat @gin.configurable @@ -286,22 +287,31 @@ class TemporalFusionTransformer(DLPredictionWrapper): """ Implementation of https://arxiv.org/abs/1912.09363 """ - def __init__(self, encoder_length,static_categorical_inp_lens,temporal_known_categorical_inp_lens, - temporal_observed_categorical_inp_lens,static_continuous_inp_size,temporal_known_continuous_inp_size, - temporal_observed_continuous_inp_size,temporal_target_size,hidden_size,num_static_vars,dropout,num_historic_vars,num_future_vars, - n_head,attn_dropout,example_length,d_head,quantiles): - super().__init__() + _supported_run_modes = [RunMode.classification, RunMode.regression] + def __init__(self, encoder_length,hidden,num_static_vars,dropout,num_historic_vars,num_future_vars, + n_head,dropout_att,example_length,quantiles,static_categorical_inp_size=1,temporal_known_categorical_inp_size=4, + temporal_observed_categorical_inp_size=0,static_continuous_inp_size=3,temporal_known_continuous_inp_size=0, + temporal_observed_continuous_inp_size=48,temporal_target_size=1): + super().__init__() + #dervied embeddings size + num_static_vars=static_categorical_inp_size+static_continuous_inp_size + num_future_vars=temporal_known_categorical_inp_size+temporal_known_continuous_inp_size + num_historic_vars=sum([num_future_vars, + temporal_observed_continuous_inp_size, + temporal_target_size, + temporal_observed_categorical_inp_size, + ]) self.encoder_length = encoder_length #this determines from how distant past we want to use data from - self.embedding = LazyEmbedding(static_categorical_inp_lens,temporal_known_categorical_inp_lens, - temporal_observed_categorical_inp_lens,static_continuous_inp_size,temporal_known_continuous_inp_size, - temporal_observed_continuous_inp_size,temporal_target_size,hidden_size) - self.static_encoder = StaticCovariateEncoder(num_static_vars,hidden_size,dropout) - self.TFTpart2 = torch.jit.script(TFTBack(encoder_length,num_historic_vars,hidden_size,dropout,num_future_vars, - n_head,attn_dropout,example_length,d_head,quantiles)) + self.embedding = LazyEmbedding(static_categorical_inp_size,temporal_known_categorical_inp_size, + temporal_observed_categorical_inp_size,static_continuous_inp_size,temporal_known_continuous_inp_size, + temporal_observed_continuous_inp_size,temporal_target_size,hidden) + self.static_encoder = StaticCovariateEncoder(num_static_vars,hidden,dropout) + self.TFTpart2 = torch.jit.script(TFTBack(encoder_length,num_historic_vars,hidden,dropout,num_future_vars, + n_head,dropout_att,example_length,quantiles)) def forward(self, x: Dict[str, Tensor]) -> Tensor: s_inp, t_known_inp, t_observed_inp, t_observed_tgt = self.embedding(x) @@ -315,6 +325,6 @@ def forward(self, x: Dict[str, Tensor]) -> Tensor: if t_observed_inp is not None: _historical_inputs.insert(0,t_observed_inp[:,:self.encoder_length,:]) - historical_inputs = torch.cat(_historical_inputs, dim=-2) + historical_inputs = cat(_historical_inputs, dim=-2) future_inputs = t_known_inp[:, self.encoder_length:] return self.TFTpart2(historical_inputs, cs, ch, cc, ce, future_inputs) \ No newline at end of file diff --git a/icu_benchmarks/models/layers.py b/icu_benchmarks/models/layers.py index 969e3ac0..89201db5 100644 --- a/icu_benchmarks/models/layers.py +++ b/icu_benchmarks/models/layers.py @@ -323,12 +323,12 @@ class MaybeLayerNorm(nn.Module): ''' Implements layer normalization or identity function depending on output_size ''' - def __init__(self, output_size, hidden_size, eps): + def __init__(self, output_size, hidden, eps): super().__init__() if output_size and output_size == 1: self.ln = nn.Identity() else: - self.ln = LayerNorm(output_size if output_size else hidden_size, eps=eps) + self.ln = LayerNorm(output_size if output_size else hidden, eps=eps) def forward(self, x): return self.ln(x) @@ -339,9 +339,9 @@ class GLU(nn.Module): Gated Linear Unit consists of a linear layer followed by a GLU where input is split in half along dim to form a and b GLU(a,b)=a ⊗ σ(b)where σ is signmoid activation and ⊗ is element-wise product ''' - def __init__(self, hidden_size, output_size): + def __init__(self, hidden, output_size): super().__init__() - self.lin = nn.Linear(hidden_size, output_size * 2) + self.lin = nn.Linear(hidden, output_size * 2) def forward(self, x: Tensor) -> Tensor: x = self.lin(x) @@ -355,19 +355,19 @@ class GRN(nn.Module): ''' def __init__(self, input_size, - hidden_size, + hidden, output_size=None, context_hidden_size=None, dropout=0.0,): super().__init__() - self.layer_norm = MaybeLayerNorm(output_size, hidden_size, eps=1e-3) - self.lin_a = nn.Linear(input_size, hidden_size) + self.layer_norm = MaybeLayerNorm(output_size, hidden, eps=1e-3) + self.lin_a = nn.Linear(input_size, hidden) if context_hidden_size is not None: - self.lin_c = nn.Linear(context_hidden_size, hidden_size, bias=False) + self.lin_c = nn.Linear(context_hidden_size, hidden, bias=False) else: self.lin_c = nn.Identity() - self.lin_i = nn.Linear(hidden_size, hidden_size) - self.glu = GLU(hidden_size, output_size if output_size else hidden_size) + self.lin_i = nn.Linear(hidden, hidden) + self.glu = GLU(hidden, output_size if output_size else hidden) self.dropout = nn.Dropout(dropout) self.out_proj = nn.Linear(input_size, output_size) if output_size else None @@ -398,22 +398,23 @@ def fused_pointwise_linear_v2(x, a, b): class TFTEmbedding(nn.Module): - def __init__(self,static_categorical_inp_lens,temporal_known_categorical_inp_lens, - temporal_observed_categorical_inp_lens,static_continuous_inp_size,temporal_known_continuous_inp_size, - temporal_observed_continuous_inp_size,temporal_target_size,hidden_size, + def __init__(self,static_categorical_inp_size,temporal_known_categorical_inp_size, + temporal_observed_categorical_inp_size,static_continuous_inp_size,temporal_known_continuous_inp_size, + temporal_observed_continuous_inp_size,temporal_target_size,hidden, initialize_cont_params=False): # initialize_cont_params=False prevents form initializing parameters inside this class # so they can be lazily initialized in LazyEmbedding module super().__init__() - self.s_cat_inp_lens = static_categorical_inp_lens - self.t_cat_k_inp_lens = temporal_known_categorical_inp_lens - self.t_cat_o_inp_lens = temporal_observed_categorical_inp_lens + #these are basically number of varaibales that falls under each category + self.s_cat_inp_size = static_categorical_inp_size + self.t_cat_k_inp_size = temporal_known_categorical_inp_size + self.t_cat_o_inp_size = temporal_observed_categorical_inp_size self.s_cont_inp_size = static_continuous_inp_size self.t_cont_k_inp_size = temporal_known_continuous_inp_size self.t_cont_o_inp_size = temporal_observed_continuous_inp_size self.t_tgt_size = temporal_target_size - self.hidden_size = hidden_size + self.hidden = hidden # There are 7 types of input: # 1. Static categorical @@ -425,22 +426,22 @@ def __init__(self,static_categorical_inp_lens,temporal_known_categorical_inp_len # 7. Temporal observed targets (time series obseved so far) self.s_cat_embed = nn.ModuleList([ - nn.Embedding(n, self.hidden_size) for n in self.s_cat_inp_lens]) if self.s_cat_inp_lens else None + nn.Embedding(n, self.hidden) for n in self.s_cat_inp_size]) if self.s_cat_inp_size else None self.t_cat_k_embed = nn.ModuleList([ - nn.Embedding(n, self.hidden_size) for n in self.t_cat_k_inp_lens]) if self.t_cat_k_inp_lens else None + nn.Embedding(n, self.hidden) for n in self.t_cat_k_inp_size]) if self.t_cat_k_inp_size else None self.t_cat_o_embed = nn.ModuleList([ - nn.Embedding(n, self.hidden_size) for n in self.t_cat_o_inp_lens]) if self.t_cat_o_inp_lens else None + nn.Embedding(n, self.hidden) for n in self.t_cat_o_inp_size]) if self.t_cat_o_inp_size else None if initialize_cont_params: - self.s_cont_embedding_vectors = nn.Parameter(torch.Tensor(self.s_cont_inp_size, self.hidden_size)) if self.s_cont_inp_size else None - self.t_cont_k_embedding_vectors = nn.Parameter(torch.Tensor(self.t_cont_k_inp_size, self.hidden_size)) if self.t_cont_k_inp_size else None - self.t_cont_o_embedding_vectors = nn.Parameter(torch.Tensor(self.t_cont_o_inp_size, self.hidden_size)) if self.t_cont_o_inp_size else None - self.t_tgt_embedding_vectors = nn.Parameter(torch.Tensor(self.t_tgt_size, self.hidden_size)) + self.s_cont_embedding_vectors = nn.Parameter(torch.Tensor(self.s_cont_inp_size, self.hidden)) if self.s_cont_inp_size else None + self.t_cont_k_embedding_vectors = nn.Parameter(torch.Tensor(self.t_cont_k_inp_size, self.hidden)) if self.t_cont_k_inp_size else None + self.t_cont_o_embedding_vectors = nn.Parameter(torch.Tensor(self.t_cont_o_inp_size, self.hidden)) if self.t_cont_o_inp_size else None + self.t_tgt_embedding_vectors = nn.Parameter(torch.Tensor(self.t_tgt_size, self.hidden)) - self.s_cont_embedding_bias = nn.Parameter(torch.zeros(self.s_cont_inp_size, self.hidden_size)) if self.s_cont_inp_size else None - self.t_cont_k_embedding_bias = nn.Parameter(torch.zeros(self.t_cont_k_inp_size, self.hidden_size)) if self.t_cont_k_inp_size else None - self.t_cont_o_embedding_bias = nn.Parameter(torch.zeros(self.t_cont_o_inp_size, self.hidden_size)) if self.t_cont_o_inp_size else None - self.t_tgt_embedding_bias = nn.Parameter(torch.zeros(self.t_tgt_size, self.hidden_size)) + self.s_cont_embedding_bias = nn.Parameter(torch.zeros(self.s_cont_inp_size, self.hidden)) if self.s_cont_inp_size else None + self.t_cont_k_embedding_bias = nn.Parameter(torch.zeros(self.t_cont_k_inp_size, self.hidden)) if self.t_cont_k_inp_size else None + self.t_cont_o_embedding_bias = nn.Parameter(torch.zeros(self.t_cont_o_inp_size, self.hidden)) if self.t_cont_o_inp_size else None + self.t_tgt_embedding_bias = nn.Parameter(torch.zeros(self.t_tgt_size, self.hidden)) self.reset_parameters() @@ -537,12 +538,12 @@ def forward(self, x: Dict[str, Tensor]): class LazyEmbedding(nn.modules.lazy.LazyModuleMixin, TFTEmbedding): cls_to_become = TFTEmbedding - def __init__(self, static_categorical_inp_lens,temporal_known_categorical_inp_lens, - temporal_observed_categorical_inp_lens,static_continuous_inp_size,temporal_known_continuous_inp_size, - temporal_observed_continuous_inp_size,temporal_target_size,hidden_size): - super().__init__(static_categorical_inp_lens,temporal_known_categorical_inp_lens, - temporal_observed_categorical_inp_lens,static_continuous_inp_size,temporal_known_continuous_inp_size, - temporal_observed_continuous_inp_size,temporal_target_size,hidden_size, initialize_cont_params=False) + def __init__(self, static_categorical_inp_size,temporal_known_categorical_inp_size, + temporal_observed_categorical_inp_size,static_continuous_inp_size,temporal_known_continuous_inp_size, + temporal_observed_continuous_inp_size,temporal_target_size,hidden): + super().__init__(static_categorical_inp_size,temporal_known_categorical_inp_size, + temporal_observed_categorical_inp_size,static_continuous_inp_size,temporal_known_continuous_inp_size, + temporal_observed_continuous_inp_size,temporal_target_size,hidden, initialize_cont_params=False) if static_continuous_inp_size: self.s_cont_embedding_vectors = UninitializedParameter() @@ -576,19 +577,19 @@ def initialize_parameters(self, x): t_tgt_obs = x['target'] # Has to be present if s_cont_inp is not None: - self.s_cont_embedding_vectors.materialize((s_cont_inp.shape[-1], self.hidden_size)) - self.s_cont_embedding_bias.materialize((s_cont_inp.shape[-1], self.hidden_size)) + self.s_cont_embedding_vectors.materialize((s_cont_inp.shape[-1], self.hidden)) + self.s_cont_embedding_bias.materialize((s_cont_inp.shape[-1], self.hidden)) if t_cont_k_inp is not None: - self.t_cont_k_embedding_vectors.materialize((t_cont_k_inp.shape[-1], self.hidden_size)) - self.t_cont_k_embedding_bias.materialize((t_cont_k_inp.shape[-1], self.hidden_size)) + self.t_cont_k_embedding_vectors.materialize((t_cont_k_inp.shape[-1], self.hidden)) + self.t_cont_k_embedding_bias.materialize((t_cont_k_inp.shape[-1], self.hidden)) if t_cont_o_inp is not None: - self.t_cont_o_embedding_vectors.materialize((t_cont_o_inp.shape[-1], self.hidden_size)) - self.t_cont_o_embedding_bias.materialize((t_cont_o_inp.shape[-1], self.hidden_size)) + self.t_cont_o_embedding_vectors.materialize((t_cont_o_inp.shape[-1], self.hidden)) + self.t_cont_o_embedding_bias.materialize((t_cont_o_inp.shape[-1], self.hidden)) - self.t_tgt_embedding_vectors.materialize((t_tgt_obs.shape[-1], self.hidden_size)) - self.t_tgt_embedding_bias.materialize((t_tgt_obs.shape[-1], self.hidden_size)) + self.t_tgt_embedding_vectors.materialize((t_tgt_obs.shape[-1], self.hidden)) + self.t_tgt_embedding_bias.materialize((t_tgt_obs.shape[-1], self.hidden)) self.reset_parameters() @@ -597,10 +598,10 @@ class VariableSelectionNetwork(nn.Module): Learns to select important netowrks consists of GRNs with one GRN for variable weights and the others for input embedding ''' - def __init__(self, hidden_size,dropout, num_inputs): + def __init__(self, hidden,dropout, num_inputs): super().__init__() - self.joint_grn = GRN(hidden_size*num_inputs, hidden_size, output_size=num_inputs, context_hidden_size=hidden_size) - self.var_grns = nn.ModuleList([GRN(hidden_size, hidden_size, dropout=dropout) for _ in range(num_inputs)]) + self.joint_grn = GRN(hidden*num_inputs, hidden, output_size=num_inputs, context_hidden=hidden) + self.var_grns = nn.ModuleList([GRN(hidden, hidden, dropout=dropout) for _ in range(num_inputs)]) def forward(self, x: Tensor, context: Optional[Tensor] = None): Xi = torch.flatten(x, start_dim=-2) @@ -620,10 +621,10 @@ class StaticCovariateEncoder(nn.Module): Network to produce 4 contexts vectors to enrich static variables Vriable selection Network --> GRNs ''' - def __init__(self, num_static_vars,hidden_size,dropout): + def __init__(self, num_static_vars,hidden,dropout): super().__init__() - self.vsn = VariableSelectionNetwork(hidden_size,dropout, num_static_vars) - self.context_grns = nn.ModuleList([GRN(hidden_size, hidden_size, dropout=dropout) for _ in range(4)]) + self.vsn = VariableSelectionNetwork(hidden,dropout, num_static_vars) + self.context_grns = nn.ModuleList([GRN(hidden, hidden, dropout=dropout) for _ in range(4)]) def forward(self, x: Tensor) -> Tuple[Tensor, Tensor, Tensor, Tensor]: variable_ctx, sparse_weights = self.vsn(x) @@ -643,14 +644,14 @@ class InterpretableMultiHeadAttention(nn.Module): Multi-head attention different as it outputs the attention_probability and it combines the attention weights instead of concating them different from the one implemented already in YAIB ''' - def __init__(self,n_head,hidden_size,attn_dropout,dropout,example_length,d_head): + def __init__(self,n_head,hidden,dropout_att,dropout,example_length): super().__init__() self.n_head = n_head - assert hidden_size % n_head == 0 - self.d_head = hidden_size // n_head - self.qkv_linears = nn.Linear(hidden_size, (2 * n_head + 1) * d_head, bias=False) - self.out_proj = nn.Linear(self.d_head, hidden_size, bias=False) - self.attn_dropout = nn.Dropout(attn_dropout) + assert hidden % n_head == 0 + self.d_head = hidden // n_head + self.qkv_linears = nn.Linear(hidden, (2 * n_head + 1) * self.d_head, bias=False) + self.out_proj = nn.Linear(self.d_head, hidden, bias=False) + self.dropout_att = nn.Dropout(dropout_att) self.out_dropout = nn.Dropout(dropout) self.scale = self.d_head**-0.5 self.register_buffer("_mask", torch.triu(torch.full((example_length, example_length), float('-inf')), 1).unsqueeze(0)) @@ -670,7 +671,7 @@ def forward(self, x: Tensor) -> Tuple[Tensor, Tensor]: attn_score = attn_score + self._mask attn_prob = F.softmax(attn_score, dim=3) - attn_prob = self.attn_dropout(attn_prob) + attn_prob = self.dropout_att(attn_prob) # attn_vec = torch.einsum('bnij,bjd->bnid', attn_prob, v) attn_vec = torch.matmul(attn_prob, v.unsqueeze(1)) @@ -686,36 +687,36 @@ class TFTBack(nn.Module): followed by a gate and a dense layer GRNs-->multi-head attention-->GRNs-->GLU-->Linear-->output ''' - def __init__(self, encoder_length,num_historic_vars,hidden_size,dropout,num_future_vars, - n_head,attn_dropout,example_length,d_head,quantiles): + def __init__(self, encoder_length,num_historic_vars,hidden,dropout,num_future_vars, + n_head,dropout_att,example_length,quantiles): super().__init__() self.encoder_length = encoder_length - self.history_vsn = VariableSelectionNetwork(hidden_size,dropout, num_historic_vars) - self.history_encoder = nn.LSTM(hidden_size, hidden_size, batch_first=True) - self.future_vsn = VariableSelectionNetwork(hidden_size,dropout,num_future_vars) - self.future_encoder = nn.LSTM(hidden_size, hidden_size, batch_first=True) + self.history_vsn = VariableSelectionNetwork(hidden,dropout, num_historic_vars) + self.history_encoder = nn.LSTM(hidden, hidden, batch_first=True) + self.future_vsn = VariableSelectionNetwork(hidden,dropout,num_future_vars) + self.future_encoder = nn.LSTM(hidden, hidden, batch_first=True) - self.input_gate = GLU(hidden_size, hidden_size) - self.input_gate_ln = LayerNorm(hidden_size, eps=1e-3) + self.input_gate = GLU(hidden, hidden) + self.input_gate_ln = LayerNorm(hidden, eps=1e-3) - self.enrichment_grn = GRN(hidden_size, - hidden_size, - context_hidden_size=hidden_size, + self.enrichment_grn = GRN(hidden, + hidden, + context_hidden_size=hidden, dropout=dropout) - self.attention = InterpretableMultiHeadAttention(n_head,hidden_size,attn_dropout,dropout,example_length,d_head) - self.attention_gate = GLU(hidden_size, hidden_size) - self.attention_ln = LayerNorm(hidden_size, eps=1e-3) + self.attention = InterpretableMultiHeadAttention(n_head,hidden,dropout_att,dropout,example_length) + self.attention_gate = GLU(hidden, hidden) + self.attention_ln = LayerNorm(hidden, eps=1e-3) - self.positionwise_grn = GRN(hidden_size, - hidden_size, + self.positionwise_grn = GRN(hidden, + hidden, dropout=dropout) - self.decoder_gate = GLU(hidden_size, hidden_size) - self.decoder_ln = LayerNorm(hidden_size, eps=1e-3) + self.decoder_gate = GLU(hidden, hidden) + self.decoder_ln = LayerNorm(hidden, eps=1e-3) - self.quantile_proj = nn.Linear(hidden_size, len(quantiles)) + self.quantile_proj = nn.Linear(hidden, len(quantiles)) def forward(self, historical_inputs, cs, ch, cc, ce, future_inputs): historical_features, _ = self.history_vsn(historical_inputs, cs) From 92404d530ece301866dc83c1d6a475289cc1ad1a Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Thu, 22 Jun 2023 14:03:27 +0200 Subject: [PATCH 008/142] cleaned gin file --- configs/prediction_models/TemporalFusionTransformer.gin | 2 -- 1 file changed, 2 deletions(-) diff --git a/configs/prediction_models/TemporalFusionTransformer.gin b/configs/prediction_models/TemporalFusionTransformer.gin index d430a443..25be512d 100644 --- a/configs/prediction_models/TemporalFusionTransformer.gin +++ b/configs/prediction_models/TemporalFusionTransformer.gin @@ -15,8 +15,6 @@ model/hyperparameter.class_to_tune = @TemporalFusionTransformer model/hyperparameter.encoder_length = (1,14,"log-uniform", 2) model/hyperparameter.hidden = (32, 256, "log-uniform", 2) model/hyperparameter.num_classes = %NUM_CLASSES -model/hyperparameter.hidden = (32, 256, "log-uniform", 2) -model/hyperparameter.depth = (1, 3) model/hyperparameter.dropout = (0.0, 0.4) model/hyperparameter.dropout_att = (0.0, 0.4) model/hyperparameter.n_heads = (1, 8, "log-uniform", 2) From bed9b87bc9029c64745262d3a20b4ff6fcb1ab7c Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Thu, 22 Jun 2023 14:03:53 +0200 Subject: [PATCH 009/142] changed output to logits to match num of classes --- icu_benchmarks/models/dl_models.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 4646502e..be1f53bd 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -290,7 +290,7 @@ class TemporalFusionTransformer(DLPredictionWrapper): _supported_run_modes = [RunMode.classification, RunMode.regression] - def __init__(self, encoder_length,hidden,num_static_vars,dropout,num_historic_vars,num_future_vars, + def __init__(self,num_classes, encoder_length,hidden,num_static_vars,dropout,num_historic_vars,num_future_vars, n_head,dropout_att,example_length,quantiles,static_categorical_inp_size=1,temporal_known_categorical_inp_size=4, temporal_observed_categorical_inp_size=0,static_continuous_inp_size=3,temporal_known_continuous_inp_size=0, temporal_observed_continuous_inp_size=48,temporal_target_size=1): @@ -312,6 +312,7 @@ def __init__(self, encoder_length,hidden,num_static_vars,dropout,num_historic_va self.static_encoder = StaticCovariateEncoder(num_static_vars,hidden,dropout) self.TFTpart2 = torch.jit.script(TFTBack(encoder_length,num_historic_vars,hidden,dropout,num_future_vars, n_head,dropout_att,example_length,quantiles)) + self.logit = nn.Linear(len(quantiles), num_classes) def forward(self, x: Dict[str, Tensor]) -> Tensor: s_inp, t_known_inp, t_observed_inp, t_observed_tgt = self.embedding(x) @@ -327,4 +328,6 @@ def forward(self, x: Dict[str, Tensor]) -> Tensor: historical_inputs = cat(_historical_inputs, dim=-2) future_inputs = t_known_inp[:, self.encoder_length:] - return self.TFTpart2(historical_inputs, cs, ch, cc, ce, future_inputs) \ No newline at end of file + o=self.TFTpart2(historical_inputs, cs, ch, cc, ce, future_inputs) + pred = self.logit(o) + return pred \ No newline at end of file From 9bda7693c66b5202c14572fc8702963a7a9919e2 Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Thu, 22 Jun 2023 14:38:44 +0200 Subject: [PATCH 010/142] added jit import --- icu_benchmarks/models/dl_models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index be1f53bd..38c94a01 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -6,7 +6,7 @@ from icu_benchmarks.models.layers import TransformerBlock, LocalBlock, TemporalBlock, PositionalEncoding,LazyEmbedding,StaticCovariateEncoder,TFTBack from typing import Dict from icu_benchmarks.models.wrappers import DLPredictionWrapper -from torch import Tensor,cat +from torch import Tensor,cat,jit @gin.configurable @@ -310,7 +310,7 @@ def __init__(self,num_classes, encoder_length,hidden,num_static_vars,dropout,num temporal_observed_categorical_inp_size,static_continuous_inp_size,temporal_known_continuous_inp_size, temporal_observed_continuous_inp_size,temporal_target_size,hidden) self.static_encoder = StaticCovariateEncoder(num_static_vars,hidden,dropout) - self.TFTpart2 = torch.jit.script(TFTBack(encoder_length,num_historic_vars,hidden,dropout,num_future_vars, + self.TFTpart2 = jit.script(TFTBack(encoder_length,num_historic_vars,hidden,dropout,num_future_vars, n_head,dropout_att,example_length,quantiles)) self.logit = nn.Linear(len(quantiles), num_classes) From 960320b2921680d6cbc6191480d59a26dab299a5 Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Thu, 22 Jun 2023 15:27:39 +0200 Subject: [PATCH 011/142] added quantile loss --- icu_benchmarks/models/dl_models.py | 4 ++-- icu_benchmarks/models/layers.py | 12 +++++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 38c94a01..f78081d4 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -3,7 +3,7 @@ import numpy as np import torch.nn as nn from icu_benchmarks.contants import RunMode -from icu_benchmarks.models.layers import TransformerBlock, LocalBlock, TemporalBlock, PositionalEncoding,LazyEmbedding,StaticCovariateEncoder,TFTBack +from icu_benchmarks.models.layers import TransformerBlock, LocalBlock, TemporalBlock, PositionalEncoding,LazyEmbedding,StaticCovariateEncoder,TFTBack,QuantileLoss from typing import Dict from icu_benchmarks.models.wrappers import DLPredictionWrapper from torch import Tensor,cat,jit @@ -294,7 +294,7 @@ def __init__(self,num_classes, encoder_length,hidden,num_static_vars,dropout,num n_head,dropout_att,example_length,quantiles,static_categorical_inp_size=1,temporal_known_categorical_inp_size=4, temporal_observed_categorical_inp_size=0,static_continuous_inp_size=3,temporal_known_continuous_inp_size=0, temporal_observed_continuous_inp_size=48,temporal_target_size=1): - super().__init__() + super().__init__(loss=QuantileLoss()) #dervied embeddings size num_static_vars=static_categorical_inp_size+static_continuous_inp_size diff --git a/icu_benchmarks/models/layers.py b/icu_benchmarks/models/layers.py index 89201db5..7fa910bf 100644 --- a/icu_benchmarks/models/layers.py +++ b/icu_benchmarks/models/layers.py @@ -757,4 +757,14 @@ def forward(self, historical_inputs, cs, ch, cc, ce, future_inputs): out = self.quantile_proj(x) - return out \ No newline at end of file + return out +class QuantileLoss(nn.Module): + def __init__(self, config): + super().__init__() + self.register_buffer('q', torch.tensor(config.quantiles)) + + def forward(self, predictions, targets): + diff = predictions - targets + ql = (1-self.q)*F.relu(diff) + self.q*F.relu(-diff) + losses = ql.view(-1, ql.shape[-1]).mean(0) + return losses \ No newline at end of file From 3cb823dab9fc3542eaffb1fa13d62fa9cbf12a3a Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Thu, 22 Jun 2023 16:42:20 +0200 Subject: [PATCH 012/142] added new loader to work with tft but still needs work --- icu_benchmarks/data/loader.py | 84 +++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 3 deletions(-) diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index d0e040e4..cb6df099 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -2,15 +2,15 @@ from pandas import DataFrame import gin import numpy as np -from torch import Tensor, cat, from_numpy, float32 +from torch import Tensor, cat, from_numpy, float32,stack,empty from torch.utils.data import Dataset import logging from typing import Dict, Tuple - +from collections import OrderedDic from icu_benchmarks.imputation.amputations import ampute_data from .constants import DataSegment as Segment from .constants import DataSplit as Split - +FEAT_NAMES = ['s_cat' , 's_cont' , 'k_cat' , 'k_cont' , 'o_cat' , 'o_cont' , 'target', 'id'] class CommonDataset(Dataset): """Common dataset: subclass of Torch Dataset that represents the data to learn on. @@ -31,6 +31,7 @@ def __init__( self.vars = vars self.grouping_df = data[split][grouping_segment].set_index(self.vars["GROUP"]) self.features_df = ( + #drops time coulmn and sets index to stay_id data[split][Segment.features].set_index(self.vars["GROUP"]).drop(labels=self.vars["SEQUENCE"], axis=1) ) @@ -153,6 +154,82 @@ def to_tensor(self): data, labels = self.get_data_and_labels() return from_numpy(data).to(float32), from_numpy(labels).to(float32) +@gin.configurable("PredictionDatasetTFT") +class PredictionDatasetTFT(PredictionDataset): + """Subclass of prediction dataset for TFT as we need to define if variables are cont,static,known or observed. + + Args: + ram_cache (bool, optional): Whether the complete dataset should be stored in ram. Defaults to True. + """ + + def __init__(self, *args, ram_cache: bool = True, **kwargs): + super().__init__(*args,ram_cache = True, grouping_segment=Segment.outcome, **kwargs) + + + def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: + """Function to sample from the data split of choice. Used for TFT. + + Args: + idx: A specific row index to sample. + + Returns: + A sample from the data, consisting of data, labels and padding mask. + """ + if self._cached_dataset is not None: + return self._cached_dataset[idx] + + pad_value = 0.0 + stay_id = self.outcome_df.index.unique()[idx] # [self.vars["GROUP"]] + + + # We need to be sure that tensors are returned in the correct order to be processed correclty by tft + tensors = tuple([] for _ in range(8)) + for var in self.features_df.columns: + if var == 'sex' : + tensors[0].append(self.features_df.loc[stay_id:stay_id][var].to_numpy()) + elif var == 'age' or var== 'height' or var== 'weight': + tensors[1].append(self.features_df.loc[stay_id:stay_id][var].to_numpy()) + else : + tensors[5].append(self.features_df.loc[stay_id:stay_id][var].to_numpy()) + + + tensors[6].append(self.outcome_df.loc[stay_id:stay_id][self.vars["LABEL"]].to_numpy(dtype=float)) + tensors[7].append(stay_id.to_numpy()) + tensors = [stack(x, dim=-1) if x else empty(0) for x in tensors] + + window = self.features_df.loc[stay_id:stay_id].to_numpy() + labels = self.outcome_df.loc[stay_id:stay_id][self.vars["LABEL"]].to_numpy(dtype=float) + + if len(tensors[6]) == 1: + # only one label per stay, align with window + tensors[6] = np.concatenate([np.empty(window.shape[0] - 1) * np.nan, tensors[6]], axis=0) + + length_diff = self.maxlen - window.shape[0] + + pad_mask = np.ones(window.shape[0]) + + # Padding the array to fulfill size requirement + if length_diff > 0: + # window shorter than the longest window in dataset, pad to same length + + window = np.concatenate([window, np.ones((length_diff, window.shape[1])) * pad_value], axis=0) + tensors[6] = np.concatenate([tensors[6], np.ones(length_diff) * pad_value], axis=0) + pad_mask = np.concatenate([pad_mask, np.zeros(length_diff)], axis=0) + + not_labeled = np.argwhere(np.isnan(tensors[6])) + if len(not_labeled) > 0: + tensors[6][not_labeled] = -1 + pad_mask[not_labeled] = 0 + + pad_mask = pad_mask.astype(bool) + tensors[6] = tensors[6].astype(np.float32) + data = window.astype(np.float32) + + return OrderedDict(zip(FEAT_NAMES, tensors)) + + + + @gin.configurable("ImputationDataset") class ImputationDataset(CommonDataset): @@ -285,3 +362,4 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: window = self.dyn_df.loc[stay_id:stay_id, :] return from_numpy(window.values).to(float32) + From f823e29fa4fcbb88ceb57f07868399411a972a53 Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 27 Jun 2023 13:54:22 +0200 Subject: [PATCH 013/142] added dataloader for tft with correct order --- icu_benchmarks/data/loader.py | 34 ++++++++++++++++++++++++---------- icu_benchmarks/models/train.py | 5 +++-- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index cb6df099..e380a7cc 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -10,6 +10,7 @@ from icu_benchmarks.imputation.amputations import ampute_data from .constants import DataSegment as Segment from .constants import DataSplit as Split +from collections import OrderedDict FEAT_NAMES = ['s_cat' , 's_cont' , 'k_cat' , 'k_cont' , 'o_cat' , 'o_cont' , 'target', 'id'] class CommonDataset(Dataset): @@ -197,22 +198,28 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: tensors[7].append(stay_id.to_numpy()) tensors = [stack(x, dim=-1) if x else empty(0) for x in tensors] - window = self.features_df.loc[stay_id:stay_id].to_numpy() - labels = self.outcome_df.loc[stay_id:stay_id][self.vars["LABEL"]].to_numpy(dtype=float) + # window = self.features_df.loc[stay_id:stay_id].to_numpy() + # labels = self.outcome_df.loc[stay_id:stay_id][self.vars["LABEL"]].to_numpy(dtype=float) if len(tensors[6]) == 1: # only one label per stay, align with window - tensors[6] = np.concatenate([np.empty(window.shape[0] - 1) * np.nan, tensors[6]], axis=0) + tensors[6] = np.concatenate([np.empty(tensors[0].shape[0] - 1) * np.nan, tensors[6]], axis=0) - length_diff = self.maxlen - window.shape[0] + length_diff = self.maxlen - tensors[0].shape[0] - pad_mask = np.ones(window.shape[0]) + pad_mask = np.ones(tensors[0].shape[0]) # Padding the array to fulfill size requirement if length_diff > 0: # window shorter than the longest window in dataset, pad to same length - - window = np.concatenate([window, np.ones((length_diff, window.shape[1])) * pad_value], axis=0) + tensors[0]= np.concatenate([tensors[0], np.ones((length_diff, tensors[0].shape[1])) * pad_value], axis=0) + tensors[1]= np.concatenate([tensors[1], np.ones((length_diff, tensors[1].shape[1])) * pad_value], axis=0) + tensors[2]= np.concatenate([tensors[2], np.ones((length_diff, tensors[2].shape[1])) * pad_value], axis=0) + tensors[3]= np.concatenate([tensors[3], np.ones((length_diff, tensors[3].shape[1])) * pad_value], axis=0) + tensors[4]= np.concatenate([tensors[4], np.ones((length_diff, tensors[4].shape[1])) * pad_value], axis=0) + tensors[5]= np.concatenate([tensors[5], np.ones((length_diff, tensors[5].shape[1])) * pad_value], axis=0) + tensors[7]= np.concatenate([tensors[7], np.ones((length_diff, tensors[7].shape[1])) * pad_value], axis=0) + # window = np.concatenate([window, np.ones((length_diff, window.shape[1])) * pad_value], axis=0) tensors[6] = np.concatenate([tensors[6], np.ones(length_diff) * pad_value], axis=0) pad_mask = np.concatenate([pad_mask, np.zeros(length_diff)], axis=0) @@ -223,9 +230,16 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: pad_mask = pad_mask.astype(bool) tensors[6] = tensors[6].astype(np.float32) - data = window.astype(np.float32) - - return OrderedDict(zip(FEAT_NAMES, tensors)) + # data = window.astype(np.float32) + tensors[0]= tensors[0].astype(np.float32) + tensors[1]= tensors[1].astype(np.float32) + tensors[2]=tensors[2].astype(np.float32) + tensors[3]= tensors[3].astype(np.float32) + tensors[4]= tensors[4].astype(np.float32) + tensors[5]=tensors[5].astype(np.float32) + tensors[7]= tensors[7].astype(np.float32) + + return OrderedDict(zip(FEAT_NAMES, tensors)),from_numpy(pad_mask) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 1c2a91cd..ba57fa6b 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -9,7 +9,7 @@ from pytorch_lightning import Trainer from pytorch_lightning.callbacks import EarlyStopping, ModelCheckpoint, TQDMProgressBar from pathlib import Path -from icu_benchmarks.data.loader import PredictionDataset, ImputationDataset +from icu_benchmarks.data.loader import PredictionDataset, ImputationDataset,PredictionDatasetTFT from icu_benchmarks.models.utils import save_config_file, JSONMetricsLogger from icu_benchmarks.contants import RunMode from icu_benchmarks.data.constants import DataSplit as Split @@ -72,7 +72,8 @@ def train_common( """ logging.info(f"Training model: {model.__name__}.") - dataset_class = ImputationDataset if mode == RunMode.imputation else PredictionDataset + dataset_class = ImputationDataset if mode == RunMode.imputation else model == PredictionDatasetTFT if model =='TemporalFusionTransformer' else PredictionDataset + logging.info(f"Logging to directory: {log_dir}.") save_config_file(log_dir) # We save the operative config before and also after training From 742905fac7bf0cda830a7f478dd0523672bcd97a Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 27 Jun 2023 13:54:54 +0200 Subject: [PATCH 014/142] tft --- icu_benchmarks/models/dl_models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index f78081d4..c76a6fda 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -315,6 +315,7 @@ def __init__(self,num_classes, encoder_length,hidden,num_static_vars,dropout,num self.logit = nn.Linear(len(quantiles), num_classes) def forward(self, x: Dict[str, Tensor]) -> Tensor: + s_inp, t_known_inp, t_observed_inp, t_observed_tgt = self.embedding(x) # Static context From bb7ecc05379978f7d7f919012ac4baaa1aa06396 Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 27 Jun 2023 14:06:44 +0200 Subject: [PATCH 015/142] added quantile loss --- configs/tasks/RegressionTFT.gin | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 configs/tasks/RegressionTFT.gin diff --git a/configs/tasks/RegressionTFT.gin b/configs/tasks/RegressionTFT.gin new file mode 100644 index 00000000..b31408fb --- /dev/null +++ b/configs/tasks/RegressionTFT.gin @@ -0,0 +1,33 @@ +# COMMON IMPORTS +include "configs/tasks/common/Imports.gin" + +# DATASET CONFIGURATION +include "configs/tasks/common/PredictionTaskVariables.gin" + +# CROSS-VALIDATION +include "configs/tasks/common/CrossValidation.gin" + +# MODE SETTINGS +Run.mode = "Regression" +NUM_CLASSES = 1 +HORIZON = 24 +train_common.weight = "balanced" +train_common.ram_cache = True + +# LOSS FUNCTION +DLPredictionWrapper.loss = @QuantileLoss + + +# SELECTING PREPROCESSOR +preprocess.preprocessor = @base_regression_preprocessor +preprocess.vars = %vars +preprocess.use_static = True + +# SPECIFYING REGRESSION OUTCOME SCALING +base_regression_preprocessor.outcome_min = 0 +base_regression_preprocessor.outcome_max = 15 + +# SELECTING DATASET +PredictionDataset.vars = %vars +PredictionDataset.ram_cache = True + From c4085ef1be4cf7bc8260a462050327e214f4003d Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 27 Jun 2023 14:14:33 +0200 Subject: [PATCH 016/142] added gin configurable for quantile loss --- icu_benchmarks/models/layers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/icu_benchmarks/models/layers.py b/icu_benchmarks/models/layers.py index 7fa910bf..de8afaf5 100644 --- a/icu_benchmarks/models/layers.py +++ b/icu_benchmarks/models/layers.py @@ -758,6 +758,7 @@ def forward(self, historical_inputs, cs, ch, cc, ce, future_inputs): out = self.quantile_proj(x) return out +@gin.configurable class QuantileLoss(nn.Module): def __init__(self, config): super().__init__() From fa7e4b94ba3504e9f7ada59ad2a83c1a0897f547 Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 27 Jun 2023 14:14:49 +0200 Subject: [PATCH 017/142] remove gin config for quantile loss --- icu_benchmarks/models/wrappers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 867e9874..70dfbdc9 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -17,14 +17,14 @@ from icu_benchmarks.models.utils import create_optimizer, create_scheduler from joblib import dump from pytorch_lightning import LightningModule - +import layers from icu_benchmarks.models.constants import MLMetrics, DLMetrics from icu_benchmarks.contants import RunMode gin.config.external_configurable(torch.nn.functional.nll_loss, module="torch.nn.functional") gin.config.external_configurable(torch.nn.functional.cross_entropy, module="torch.nn.functional") +#gin.config.external_configurable(layers.QuantileLoss, module="layers") gin.config.external_configurable(torch.nn.functional.mse_loss, module="torch.nn.functional") - gin.config.external_configurable(sklearn.metrics.mean_squared_error, module="sklearn.metrics") gin.config.external_configurable(sklearn.metrics.log_loss, module="sklearn.metrics") From da9725bbec1614d475d5dccd235e2fdc57504fc1 Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 27 Jun 2023 14:18:09 +0200 Subject: [PATCH 018/142] added quantile loss --- icu_benchmarks/models/wrappers.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 70dfbdc9..df19c0b9 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -28,7 +28,17 @@ gin.config.external_configurable(sklearn.metrics.mean_squared_error, module="sklearn.metrics") gin.config.external_configurable(sklearn.metrics.log_loss, module="sklearn.metrics") +@gin.configurable +class QuantileLoss(nn.Module): + def __init__(self, config): + super().__init__() + self.register_buffer('q', torch.tensor(config.quantiles)) + def forward(self, predictions, targets): + diff = predictions - targets + ql = (1-self.q)*F.relu(diff) + self.q*F.relu(-diff) + losses = ql.view(-1, ql.shape[-1]).mean(0) + return losses @gin.configurable("BaseModule") class BaseModule(LightningModule): needs_training = False From 7c3d5111a5af1b5af7300e380df1354a1aa3dfae Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 27 Jun 2023 14:18:19 +0200 Subject: [PATCH 019/142] removed quantile loss --- icu_benchmarks/models/layers.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/icu_benchmarks/models/layers.py b/icu_benchmarks/models/layers.py index de8afaf5..8a8a95d3 100644 --- a/icu_benchmarks/models/layers.py +++ b/icu_benchmarks/models/layers.py @@ -758,14 +758,3 @@ def forward(self, historical_inputs, cs, ch, cc, ce, future_inputs): out = self.quantile_proj(x) return out -@gin.configurable -class QuantileLoss(nn.Module): - def __init__(self, config): - super().__init__() - self.register_buffer('q', torch.tensor(config.quantiles)) - - def forward(self, predictions, targets): - diff = predictions - targets - ql = (1-self.q)*F.relu(diff) + self.q*F.relu(-diff) - losses = ql.view(-1, ql.shape[-1]).mean(0) - return losses \ No newline at end of file From d410d53e6b048a33a93b8b30c8cb70be8945a80d Mon Sep 17 00:00:00 2001 From: youssefmecky96 <47695820+youssefmecky96@users.noreply.github.com> Date: Tue, 27 Jun 2023 14:26:21 +0200 Subject: [PATCH 020/142] Update ci.yml To see reason for failure --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 048e2275..77fffa73 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,6 +30,7 @@ jobs: auto-activate-base: false - name: Lint with flake8 run: | + set -x # stop the build if there are Python syntax errors or undefined names flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics # the GitHub editor is 127 chars wide @@ -40,4 +41,4 @@ jobs: # - name: Setup package # run: pip install -e . # - name: Test command line tool - # run: python -m icu_benchmarks.run --help \ No newline at end of file + # run: python -m icu_benchmarks.run --help From 38f9dee97a104b94175e327cea51e1042882cead Mon Sep 17 00:00:00 2001 From: youssefmecky96 <47695820+youssefmecky96@users.noreply.github.com> Date: Tue, 27 Jun 2023 14:39:15 +0200 Subject: [PATCH 021/142] Update ci.yml Removed changes --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 77fffa73..6c857c4b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,6 @@ jobs: auto-activate-base: false - name: Lint with flake8 run: | - set -x # stop the build if there are Python syntax errors or undefined names flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics # the GitHub editor is 127 chars wide From bd0a9a5614a5553ca052434d58291f751ea5aea1 Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 27 Jun 2023 16:40:12 +0200 Subject: [PATCH 022/142] fixing bugs to get tft to work --- .../TemporalFusionTransformer.gin | 8 +++-- configs/prediction_models/common/DLCommon.gin | 1 + configs/tasks/common/Imports.gin | 3 +- environment.yml | 4 +-- experiments/demo_benchmark_classification.yml | 23 +++++++------ icu_benchmarks/data/loader.py | 3 +- icu_benchmarks/models/dl_models.py | 34 ++++++++++++------- icu_benchmarks/models/layers.py | 27 +++++++++++---- icu_benchmarks/models/train.py | 2 +- icu_benchmarks/models/wrappers.py | 16 +++------ 10 files changed, 69 insertions(+), 52 deletions(-) diff --git a/configs/prediction_models/TemporalFusionTransformer.gin b/configs/prediction_models/TemporalFusionTransformer.gin index 25be512d..1d8ec015 100644 --- a/configs/prediction_models/TemporalFusionTransformer.gin +++ b/configs/prediction_models/TemporalFusionTransformer.gin @@ -1,4 +1,4 @@ -# Settings for Transformer model. +# Settings for TFT model. # Common settings for DL models include "configs/prediction_models/common/DLCommon.gin" @@ -17,8 +17,10 @@ model/hyperparameter.hidden = (32, 256, "log-uniform", 2) model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.dropout = (0.0, 0.4) model/hyperparameter.dropout_att = (0.0, 0.4) -model/hyperparameter.n_heads = (1, 8, "log-uniform", 2) -model/hyperparameter.quantiles=[0.1, 0.5, 0.9] +model/hyperparameter.n_heads = 1 + model/hyperparameter.example_length=(1,14,"log-uniform", 2) + + diff --git a/configs/prediction_models/common/DLCommon.gin b/configs/prediction_models/common/DLCommon.gin index c220e6ab..4b761726 100644 --- a/configs/prediction_models/common/DLCommon.gin +++ b/configs/prediction_models/common/DLCommon.gin @@ -5,6 +5,7 @@ import gin.torch.external_configurables import icu_benchmarks.models.wrappers import icu_benchmarks.models.dl_models import icu_benchmarks.models.utils +import icu_benchmarks.models.layers # Do not generate features from dynamic data base_classification_preprocessor.generate_features = False diff --git a/configs/tasks/common/Imports.gin b/configs/tasks/common/Imports.gin index ea448476..faf3c8d6 100644 --- a/configs/tasks/common/Imports.gin +++ b/configs/tasks/common/Imports.gin @@ -3,4 +3,5 @@ import icu_benchmarks.data.split_process_data import icu_benchmarks.data.loader import icu_benchmarks.models.wrappers import icu_benchmarks.models.dl_models -import icu_benchmarks.models.ml_models \ No newline at end of file +import icu_benchmarks.models.ml_models +import icu_benchmarks.models.layers \ No newline at end of file diff --git a/environment.yml b/environment.yml index d47b7e2f..a1b3f061 100644 --- a/environment.yml +++ b/environment.yml @@ -13,7 +13,7 @@ dependencies: - gin-config=0.5.0 - ignite=0.4.11 - pytorch=2.0.1 - #- pytorch-cuda=11.8 + # - pytorch-cuda=11.8 - lightgbm=3.3.5 - numpy=1.24.3 - pandas=2.0.0 @@ -28,7 +28,7 @@ dependencies: - einops=0.6.1 - hydra-core=1.3 - pip: - - recipies==0.1.1 + - recipies==0.1.2 # Fixed version because of NumPy incompatibility and stale development status. - scikit-optimize-fix==0.9.1 - hydra-submitit-launcher==1.2.0 diff --git a/experiments/demo_benchmark_classification.yml b/experiments/demo_benchmark_classification.yml index 32516df2..15802f9b 100644 --- a/experiments/demo_benchmark_classification.yml +++ b/experiments/demo_benchmark_classification.yml @@ -18,19 +18,20 @@ parameters: data_dir: values: - demo_data/mortality24/eicu_demo - - demo_data/mortality24/mimic_demo - - demo_data/aki/eicu_demo - - demo_data/aki/mimic_demo - - demo_data/sepsis/eicu_demo - - demo_data/sepsis/mimic_demo + #- demo_data/mortality24/mimic_demo + # - demo_data/aki/eicu_demo + # - demo_data/aki/mimic_demo + # - demo_data/sepsis/eicu_demo + # - demo_data/sepsis/mimic_demo model: values: - - LogisticRegression - - LGBMClassifier - - GRU - - LSTM - - TCN - - Transformer + # - LogisticRegression + # - LGBMClassifier + # - GRU + # - LSTM + # - TCN + # - Transformer + - TemporalFusionTransformer seed: values: - 1111 diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index e380a7cc..5c67942c 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -6,11 +6,10 @@ from torch.utils.data import Dataset import logging from typing import Dict, Tuple -from collections import OrderedDic from icu_benchmarks.imputation.amputations import ampute_data from .constants import DataSegment as Segment from .constants import DataSplit as Split -from collections import OrderedDict +from collections import OrderedDict FEAT_NAMES = ['s_cat' , 's_cont' , 'k_cat' , 'k_cont' , 'o_cat' , 'o_cont' , 'target', 'id'] class CommonDataset(Dataset): diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index c76a6fda..2240e2e1 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -3,7 +3,7 @@ import numpy as np import torch.nn as nn from icu_benchmarks.contants import RunMode -from icu_benchmarks.models.layers import TransformerBlock, LocalBlock, TemporalBlock, PositionalEncoding,LazyEmbedding,StaticCovariateEncoder,TFTBack,QuantileLoss +from icu_benchmarks.models.layers import TransformerBlock, LocalBlock, TemporalBlock, PositionalEncoding,LazyEmbedding,StaticCovariateEncoder,TFTBack from typing import Dict from icu_benchmarks.models.wrappers import DLPredictionWrapper from torch import Tensor,cat,jit @@ -282,6 +282,7 @@ def forward(self, x): o = o.permute(0, 2, 1) # Permute to channel last pred = self.logit(o) return pred + @gin.configurable class TemporalFusionTransformer(DLPredictionWrapper): """ @@ -290,28 +291,35 @@ class TemporalFusionTransformer(DLPredictionWrapper): _supported_run_modes = [RunMode.classification, RunMode.regression] - def __init__(self,num_classes, encoder_length,hidden,num_static_vars,dropout,num_historic_vars,num_future_vars, - n_head,dropout_att,example_length,quantiles,static_categorical_inp_size=1,temporal_known_categorical_inp_size=4, - temporal_observed_categorical_inp_size=0,static_continuous_inp_size=3,temporal_known_continuous_inp_size=0, - temporal_observed_continuous_inp_size=48,temporal_target_size=1): - super().__init__(loss=QuantileLoss()) - - #dervied embeddings size - num_static_vars=static_categorical_inp_size+static_continuous_inp_size - num_future_vars=temporal_known_categorical_inp_size+temporal_known_continuous_inp_size + def __init__(self,num_classes, encoder_length,hidden,dropout, + n_heads,dropout_att,example_length,*args,quantiles=[0.1, 0.5, 0.9],static_categorical_inp_size=[4],temporal_known_categorical_inp_size=[], + temporal_observed_categorical_inp_size=[],static_continuous_inp_size=3,temporal_known_continuous_inp_size=0, + temporal_observed_continuous_inp_size=48,temporal_target_size=1,**kwargs): + #derived variables + num_static_vars=len(static_categorical_inp_size)+static_continuous_inp_size + num_future_vars=len(temporal_known_categorical_inp_size)+temporal_known_continuous_inp_size num_historic_vars=sum([num_future_vars, temporal_observed_continuous_inp_size, temporal_target_size, - temporal_observed_categorical_inp_size, + len(temporal_observed_categorical_inp_size), ]) + + super().__init__(num_classes=num_classes, encoder_length=encoder_length,hidden=hidden, + n_heads=n_heads,dropout_att=dropout_att,example_length=example_length,quantiles=quantiles, + num_static_vars=num_static_vars,num_future_vars=num_future_vars,num_historic_vars=num_historic_vars,*args,static_categorical_inp_size=1,temporal_known_categorical_inp_size=4, + temporal_observed_categorical_inp_size=0,static_continuous_inp_size=3,temporal_known_continuous_inp_size=0, + temporal_observed_continuous_inp_size=48,temporal_target_size=1,**kwargs) + + + self.encoder_length = encoder_length #this determines from how distant past we want to use data from self.embedding = LazyEmbedding(static_categorical_inp_size,temporal_known_categorical_inp_size, temporal_observed_categorical_inp_size,static_continuous_inp_size,temporal_known_continuous_inp_size, temporal_observed_continuous_inp_size,temporal_target_size,hidden) self.static_encoder = StaticCovariateEncoder(num_static_vars,hidden,dropout) - self.TFTpart2 = jit.script(TFTBack(encoder_length,num_historic_vars,hidden,dropout,num_future_vars, - n_head,dropout_att,example_length,quantiles)) + self.TFTpart2 = TFTBack(encoder_length,num_historic_vars,hidden,dropout,num_future_vars, + n_heads,dropout_att,example_length,quantiles) self.logit = nn.Linear(len(quantiles), num_classes) def forward(self, x: Dict[str, Tensor]) -> Tensor: diff --git a/icu_benchmarks/models/layers.py b/icu_benchmarks/models/layers.py index 8a8a95d3..c4616679 100644 --- a/icu_benchmarks/models/layers.py +++ b/icu_benchmarks/models/layers.py @@ -1,4 +1,3 @@ -import os import gin import math import torch @@ -7,9 +6,10 @@ from torch.nn.utils import weight_norm from torch import Tensor from torch.nn.parameter import UninitializedParameter -from typing import Dict, Tuple, Optional, List +from typing import Dict, Tuple, Optional from torch.nn import LayerNorm + @gin.configurable("masking") def parallel_recomb(q_t, kv_t, att_type="all", local_context=3, bin_size=None): """Return mask of attention matrix (ts_q, ts_kv)""" @@ -357,13 +357,13 @@ def __init__(self, input_size, hidden, output_size=None, - context_hidden_size=None, + context_hidden=None, dropout=0.0,): super().__init__() self.layer_norm = MaybeLayerNorm(output_size, hidden, eps=1e-3) self.lin_a = nn.Linear(input_size, hidden) - if context_hidden_size is not None: - self.lin_c = nn.Linear(context_hidden_size, hidden, bias=False) + if context_hidden is not None: + self.lin_c = nn.Linear(context_hidden, hidden, bias=False) else: self.lin_c = nn.Identity() self.lin_i = nn.Linear(hidden, hidden) @@ -570,6 +570,7 @@ def __init__(self, static_categorical_inp_size,temporal_known_categorical_inp_si self.t_tgt_embedding_bias = UninitializedParameter() def initialize_parameters(self, x): + if self.has_uninitialized_params(): s_cont_inp = x.get('s_cont', None) t_cont_k_inp = x.get('k_cont', None) @@ -703,7 +704,7 @@ def __init__(self, encoder_length,num_historic_vars,hidden,dropout,num_future_va self.enrichment_grn = GRN(hidden, hidden, - context_hidden_size=hidden, + context_hidden=hidden, dropout=dropout) self.attention = InterpretableMultiHeadAttention(n_head,hidden,dropout_att,dropout,example_length) self.attention_gate = GLU(hidden, hidden) @@ -715,7 +716,8 @@ def __init__(self, encoder_length,num_historic_vars,hidden,dropout,num_future_va self.decoder_gate = GLU(hidden, hidden) self.decoder_ln = LayerNorm(hidden, eps=1e-3) - + + self.quantile_proj = nn.Linear(hidden, len(quantiles)) def forward(self, historical_inputs, cs, ch, cc, ce, future_inputs): @@ -758,3 +760,14 @@ def forward(self, historical_inputs, cs, ch, cc, ce, future_inputs): out = self.quantile_proj(x) return out +@gin.configurable +class QuantileLoss(torch.nn.Module): + def __init__(self, quantiles): + super().__init__() + self.register_buffer('q', torch.tensor(quantiles)) + + def forward(self, predictions, targets): + diff = predictions - targets + ql = (1-self.q)*F.relu(diff) + self.q*F.relu(-diff) + losses = ql.view(-1, ql.shape[-1]).mean(0) + return losses \ No newline at end of file diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index ba57fa6b..61b9bf5e 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -72,7 +72,7 @@ def train_common( """ logging.info(f"Training model: {model.__name__}.") - dataset_class = ImputationDataset if mode == RunMode.imputation else model == PredictionDatasetTFT if model =='TemporalFusionTransformer' else PredictionDataset + dataset_class = ImputationDataset if mode == RunMode.imputation else (PredictionDatasetTFT if model.__name__ =='TemporalFusionTransformer' else PredictionDataset ) logging.info(f"Logging to directory: {log_dir}.") diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index df19c0b9..fc1bd77e 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -8,6 +8,7 @@ from torch.nn import MSELoss, CrossEntropyLoss from torch.nn.modules.loss import _Loss from torch.optim import Optimizer + import inspect import gin import numpy as np @@ -17,28 +18,16 @@ from icu_benchmarks.models.utils import create_optimizer, create_scheduler from joblib import dump from pytorch_lightning import LightningModule -import layers from icu_benchmarks.models.constants import MLMetrics, DLMetrics from icu_benchmarks.contants import RunMode gin.config.external_configurable(torch.nn.functional.nll_loss, module="torch.nn.functional") gin.config.external_configurable(torch.nn.functional.cross_entropy, module="torch.nn.functional") -#gin.config.external_configurable(layers.QuantileLoss, module="layers") gin.config.external_configurable(torch.nn.functional.mse_loss, module="torch.nn.functional") gin.config.external_configurable(sklearn.metrics.mean_squared_error, module="sklearn.metrics") gin.config.external_configurable(sklearn.metrics.log_loss, module="sklearn.metrics") -@gin.configurable -class QuantileLoss(nn.Module): - def __init__(self, config): - super().__init__() - self.register_buffer('q', torch.tensor(config.quantiles)) - def forward(self, predictions, targets): - diff = predictions - targets - ql = (1-self.q)*F.relu(diff) + self.q*F.relu(-diff) - losses = ql.view(-1, ql.shape[-1]).mean(0) - return losses @gin.configurable("BaseModule") class BaseModule(LightningModule): needs_training = False @@ -108,6 +97,7 @@ class DLWrapper(BaseModule, ABC): def __init__( self, + loss=CrossEntropyLoss(), optimizer=torch.optim.Adam, run_mode: RunMode = RunMode.classification, @@ -120,8 +110,10 @@ def __init__( epochs: int = 100, input_size: torch.Tensor = None, initialization_method: str = "normal", + **kwargs, ): + """Interface for Deep Learning models.""" super().__init__() self.save_hyperparameters(ignore=["loss", "optimizer"]) From f6d1272f4525f3e39c777670eb2a2b4acabde058 Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 27 Jun 2023 17:45:38 +0200 Subject: [PATCH 023/142] fixing bugs to run tft for classification --- icu_benchmarks/data/loader.py | 53 ++++++++++++++++------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 5c67942c..d0814800 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -2,7 +2,7 @@ from pandas import DataFrame import gin import numpy as np -from torch import Tensor, cat, from_numpy, float32,stack,empty +from torch import Tensor, cat, from_numpy, float32,empty,stack from torch.utils.data import Dataset import logging from typing import Dict, Tuple @@ -103,7 +103,8 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: labels = np.concatenate([np.empty(window.shape[0] - 1) * np.nan, labels], axis=0) length_diff = self.maxlen - window.shape[0] - + + pad_mask = np.ones(window.shape[0]) # Padding the array to fulfill size requirement @@ -163,7 +164,7 @@ class PredictionDatasetTFT(PredictionDataset): """ def __init__(self, *args, ram_cache: bool = True, **kwargs): - super().__init__(*args,ram_cache = True, grouping_segment=Segment.outcome, **kwargs) + super().__init__(*args,ram_cache = True, **kwargs) def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: @@ -183,7 +184,7 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: # We need to be sure that tensors are returned in the correct order to be processed correclty by tft - tensors = tuple([] for _ in range(8)) + tensors = [[] for _ in range(8)] for var in self.features_df.columns: if var == 'sex' : tensors[0].append(self.features_df.loc[stay_id:stay_id][var].to_numpy()) @@ -192,33 +193,34 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: else : tensors[5].append(self.features_df.loc[stay_id:stay_id][var].to_numpy()) - - tensors[6].append(self.outcome_df.loc[stay_id:stay_id][self.vars["LABEL"]].to_numpy(dtype=float)) - tensors[7].append(stay_id.to_numpy()) - tensors = [stack(x, dim=-1) if x else empty(0) for x in tensors] - + + tensors[6].extend(self.outcome_df.loc[stay_id:stay_id][self.vars["LABEL"]].to_numpy(dtype=float)) + tensors[7].append(np.asarray([stay_id])) + + window_shape0=np.shape(tensors[0])[1] # window = self.features_df.loc[stay_id:stay_id].to_numpy() # labels = self.outcome_df.loc[stay_id:stay_id][self.vars["LABEL"]].to_numpy(dtype=float) - + if len(tensors[6]) == 1: # only one label per stay, align with window - tensors[6] = np.concatenate([np.empty(tensors[0].shape[0] - 1) * np.nan, tensors[6]], axis=0) + tensors[6] = np.concatenate([np.empty(window_shape0 - 1) * np.nan, tensors[6]], axis=0) - length_diff = self.maxlen - tensors[0].shape[0] + length_diff = self.maxlen - window_shape0 - pad_mask = np.ones(tensors[0].shape[0]) + pad_mask = np.ones(window_shape0) # Padding the array to fulfill size requirement if length_diff > 0: # window shorter than the longest window in dataset, pad to same length - tensors[0]= np.concatenate([tensors[0], np.ones((length_diff, tensors[0].shape[1])) * pad_value], axis=0) - tensors[1]= np.concatenate([tensors[1], np.ones((length_diff, tensors[1].shape[1])) * pad_value], axis=0) - tensors[2]= np.concatenate([tensors[2], np.ones((length_diff, tensors[2].shape[1])) * pad_value], axis=0) - tensors[3]= np.concatenate([tensors[3], np.ones((length_diff, tensors[3].shape[1])) * pad_value], axis=0) - tensors[4]= np.concatenate([tensors[4], np.ones((length_diff, tensors[4].shape[1])) * pad_value], axis=0) - tensors[5]= np.concatenate([tensors[5], np.ones((length_diff, tensors[5].shape[1])) * pad_value], axis=0) - tensors[7]= np.concatenate([tensors[7], np.ones((length_diff, tensors[7].shape[1])) * pad_value], axis=0) + tensors[0]= np.concatenate([tensors[0], np.ones((length_diff, np.shape(tensors[0])[0])) * pad_value], axis=0) + tensors[1]= np.concatenate([tensors[1], np.ones((length_diff, np.shape(tensors[1])[0])) * pad_value], axis=0) + tensors[2]= np.concatenate([tensors[2], np.ones((length_diff, np.shape(tensors[2])[0])) * pad_value], axis=0) + tensors[3]= np.concatenate([tensors[3], np.ones((length_diff, np.shape(tensors[3])[0])) * pad_value], axis=0) + tensors[4]= np.concatenate([tensors[4], np.ones((length_diff, np.shape(tensors[4])[0])) * pad_value], axis=0) + tensors[5]= np.concatenate([tensors[5], np.ones((length_diff, np.shape(tensors[5])[0])) * pad_value], axis=0) + tensors[7]= np.concatenate([tensors[7], np.ones((length_diff, np.shape(tensors[7])[0])) * pad_value], axis=0) # window = np.concatenate([window, np.ones((length_diff, window.shape[1])) * pad_value], axis=0) + tensors[6] = np.concatenate([tensors[6], np.ones(length_diff) * pad_value], axis=0) pad_mask = np.concatenate([pad_mask, np.zeros(length_diff)], axis=0) @@ -228,16 +230,9 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: pad_mask[not_labeled] = 0 pad_mask = pad_mask.astype(bool) - tensors[6] = tensors[6].astype(np.float32) # data = window.astype(np.float32) - tensors[0]= tensors[0].astype(np.float32) - tensors[1]= tensors[1].astype(np.float32) - tensors[2]=tensors[2].astype(np.float32) - tensors[3]= tensors[3].astype(np.float32) - tensors[4]= tensors[4].astype(np.float32) - tensors[5]=tensors[5].astype(np.float32) - tensors[7]= tensors[7].astype(np.float32) - + tensors = [from_numpy(np.array(tensor)).to(float32) for tensor in tensors] + tensors = [stack((x,), dim=-1) if x.numel() else empty(0) for x in tensors] return OrderedDict(zip(FEAT_NAMES, tensors)),from_numpy(pad_mask) From 0c44d2ffbdbddaa98a1e2e76447ad994f5cfbcc5 Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 27 Jun 2023 17:48:06 +0200 Subject: [PATCH 024/142] fixing bugs to run tft for classification --- icu_benchmarks/models/train.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 61b9bf5e..30deb6a4 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -102,7 +102,7 @@ def train_common( pin_memory=True, drop_last=True, ) - + print(type(next(iter(train_loader))[0])) data_shape = next(iter(train_loader))[0].shape model = model(optimizer=optimizer, input_size=data_shape, epochs=epochs, run_mode=mode) From 9963d7bc3c0495c07d7ba0615d61a5bf18bdd17a Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 27 Jun 2023 23:03:04 +0200 Subject: [PATCH 025/142] fixing bugs to run tft --- icu_benchmarks/data/loader.py | 11 +++++------ icu_benchmarks/models/dl_models.py | 2 +- icu_benchmarks/models/layers.py | 13 +++++++------ icu_benchmarks/models/train.py | 13 ++++++++----- icu_benchmarks/models/wrappers.py | 9 ++++++--- 5 files changed, 27 insertions(+), 21 deletions(-) diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index d0814800..b2b7ad72 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -200,13 +200,11 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: window_shape0=np.shape(tensors[0])[1] # window = self.features_df.loc[stay_id:stay_id].to_numpy() # labels = self.outcome_df.loc[stay_id:stay_id][self.vars["LABEL"]].to_numpy(dtype=float) - if len(tensors[6]) == 1: # only one label per stay, align with window tensors[6] = np.concatenate([np.empty(window_shape0 - 1) * np.nan, tensors[6]], axis=0) length_diff = self.maxlen - window_shape0 - pad_mask = np.ones(window_shape0) # Padding the array to fulfill size requirement @@ -214,11 +212,11 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: # window shorter than the longest window in dataset, pad to same length tensors[0]= np.concatenate([tensors[0], np.ones((length_diff, np.shape(tensors[0])[0])) * pad_value], axis=0) tensors[1]= np.concatenate([tensors[1], np.ones((length_diff, np.shape(tensors[1])[0])) * pad_value], axis=0) - tensors[2]= np.concatenate([tensors[2], np.ones((length_diff, np.shape(tensors[2])[0])) * pad_value], axis=0) - tensors[3]= np.concatenate([tensors[3], np.ones((length_diff, np.shape(tensors[3])[0])) * pad_value], axis=0) - tensors[4]= np.concatenate([tensors[4], np.ones((length_diff, np.shape(tensors[4])[0])) * pad_value], axis=0) + # tensors[2]= np.concatenate([tensors[2], np.ones((length_diff, np.shape(tensors[2])[0])) * pad_value], axis=0) + # tensors[3]= np.concatenate([tensors[3], np.ones((length_diff, np.shape(tensors[3])[0])) * pad_value], axis=0) + # tensors[4]= np.concatenate([tensors[4], np.ones((length_diff, np.shape(tensors[4])[0])) * pad_value], axis=0) tensors[5]= np.concatenate([tensors[5], np.ones((length_diff, np.shape(tensors[5])[0])) * pad_value], axis=0) - tensors[7]= np.concatenate([tensors[7], np.ones((length_diff, np.shape(tensors[7])[0])) * pad_value], axis=0) + tensors[7]= np.concatenate([tensors[7], np.ones((length_diff, np.shape(tensors[7])[0])) * stay_id], axis=0) # window = np.concatenate([window, np.ones((length_diff, window.shape[1])) * pad_value], axis=0) tensors[6] = np.concatenate([tensors[6], np.ones(length_diff) * pad_value], axis=0) @@ -233,6 +231,7 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: # data = window.astype(np.float32) tensors = [from_numpy(np.array(tensor)).to(float32) for tensor in tensors] tensors = [stack((x,), dim=-1) if x.numel() else empty(0) for x in tensors] + return OrderedDict(zip(FEAT_NAMES, tensors)),from_numpy(pad_mask) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 2240e2e1..e8572ca6 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -292,7 +292,7 @@ class TemporalFusionTransformer(DLPredictionWrapper): _supported_run_modes = [RunMode.classification, RunMode.regression] def __init__(self,num_classes, encoder_length,hidden,dropout, - n_heads,dropout_att,example_length,*args,quantiles=[0.1, 0.5, 0.9],static_categorical_inp_size=[4],temporal_known_categorical_inp_size=[], + n_heads,dropout_att,example_length,*args,quantiles=[0.1, 0.5, 0.9],static_categorical_inp_size=[2],temporal_known_categorical_inp_size=[], temporal_observed_categorical_inp_size=[],static_continuous_inp_size=3,temporal_known_continuous_inp_size=0, temporal_observed_continuous_inp_size=48,temporal_target_size=1,**kwargs): #derived variables diff --git a/icu_benchmarks/models/layers.py b/icu_benchmarks/models/layers.py index c4616679..e6f2e585 100644 --- a/icu_benchmarks/models/layers.py +++ b/icu_benchmarks/models/layers.py @@ -431,6 +431,7 @@ def __init__(self,static_categorical_inp_size,temporal_known_categorical_inp_siz nn.Embedding(n, self.hidden) for n in self.t_cat_k_inp_size]) if self.t_cat_k_inp_size else None self.t_cat_o_embed = nn.ModuleList([ nn.Embedding(n, self.hidden) for n in self.t_cat_o_inp_size]) if self.t_cat_o_inp_size else None + if initialize_cont_params: self.s_cont_embedding_vectors = nn.Parameter(torch.Tensor(self.s_cont_inp_size, self.hidden)) if self.s_cont_inp_size else None @@ -480,7 +481,8 @@ def _apply_embedding(self, cont_emb: Tensor, cont_bias: Tensor, ) -> Tuple[Optional[Tensor], Optional[Tensor]]: - e_cat = torch.stack([embed(cat[...,i]) for i, embed in enumerate(cat_emb)], dim=-2) if cat is not None else None + e_cat = torch.stack([embed(cat[...,i].int()) for i, embed in enumerate(cat_emb)], dim=-2) if (cat is not None) and (cat_emb is not None) else None + if cont is not None: #the line below is equivalent to following einsums #e_cont = torch.einsum('btf,fh->bthf', cont, cont_emb) @@ -508,12 +510,10 @@ def forward(self, x: Dict[str, Tensor]): t_cat_o_inp = x.get('o_cat', None) t_cont_o_inp = x.get('o_cont', None) t_tgt_obs = x['target'] # Has to be present - # Static inputs are expected to be equal for all timesteps # For memory efficiency there is no assert statement s_cat_inp = s_cat_inp[:,0,:] if s_cat_inp is not None else None s_cont_inp = s_cont_inp[:,0,:] if s_cont_inp is not None else None - s_inp = self._apply_embedding(s_cat_inp, s_cont_inp, self.s_cat_embed, @@ -577,15 +577,16 @@ def initialize_parameters(self, x): t_cont_o_inp = x.get('o_cont', None) t_tgt_obs = x['target'] # Has to be present - if s_cont_inp is not None: + + if (s_cont_inp is not None) and (s_cont_inp.size()[1]>0): self.s_cont_embedding_vectors.materialize((s_cont_inp.shape[-1], self.hidden)) self.s_cont_embedding_bias.materialize((s_cont_inp.shape[-1], self.hidden)) - if t_cont_k_inp is not None: + if (t_cont_k_inp is not None) and (t_cont_k_inp.size()[1]>0): self.t_cont_k_embedding_vectors.materialize((t_cont_k_inp.shape[-1], self.hidden)) self.t_cont_k_embedding_bias.materialize((t_cont_k_inp.shape[-1], self.hidden)) - if t_cont_o_inp is not None: + if (t_cont_o_inp) is not None and (t_cont_o_inp.size()[1]>0): self.t_cont_o_embedding_vectors.materialize((t_cont_o_inp.shape[-1], self.hidden)) self.t_cont_o_embedding_bias.materialize((t_cont_o_inp.shape[-1], self.hidden)) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 30deb6a4..33730134 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -13,7 +13,7 @@ from icu_benchmarks.models.utils import save_config_file, JSONMetricsLogger from icu_benchmarks.contants import RunMode from icu_benchmarks.data.constants import DataSplit as Split - +from collections import OrderedDict cpu_core_count = len(os.sched_getaffinity(0)) if hasattr(os, "sched_getaffinity") else os.cpu_count() @@ -102,10 +102,13 @@ def train_common( pin_memory=True, drop_last=True, ) - print(type(next(iter(train_loader))[0])) - data_shape = next(iter(train_loader))[0].shape - - model = model(optimizer=optimizer, input_size=data_shape, epochs=epochs, run_mode=mode) + if(isinstance(next(iter(train_loader))[0] ,OrderedDict)): + model = model(optimizer=optimizer, epochs=epochs, run_mode=mode) + + else: + data_shape = next(iter(train_loader))[0].shape + + model = model(optimizer=optimizer, input_size=data_shape, epochs=epochs, run_mode=mode) model.set_weight(weight, train_dataset) if load_weights: if source_dir.exists(): diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index fc1bd77e..d7ae32c6 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -8,7 +8,7 @@ from torch.nn import MSELoss, CrossEntropyLoss from torch.nn.modules.loss import _Loss from torch.optim import Optimizer - +from collections import OrderedDict import inspect import gin import numpy as np @@ -230,8 +230,11 @@ def softmax_multi_output_transform(output): def step_fn(self, element, step_prefix=""): """Perform a step in the training loop.""" - - if len(element) == 2: + if(isinstance(element[0] ,OrderedDict)): + data,mask=element[0], element[1].to(self.device) + for key, value in data.items(): + data[key] = value.float().to(self.device) + elif len(element) == 2: data, labels = element[0], element[1].to(self.device) if isinstance(data, list): for i in range(len(data)): From 4a462d2d84a6057bb22c077fc1b06f3a9554f0f2 Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Thu, 29 Jun 2023 17:13:05 +0200 Subject: [PATCH 026/142] fixed return shape of data --- .../TemporalFusionTransformer.gin | 12 ++--- icu_benchmarks/data/loader.py | 6 +-- icu_benchmarks/models/dl_models.py | 7 ++- icu_benchmarks/models/layers.py | 46 +++++++++++++------ icu_benchmarks/models/train.py | 2 +- icu_benchmarks/models/wrappers.py | 14 +++++- 6 files changed, 57 insertions(+), 30 deletions(-) diff --git a/configs/prediction_models/TemporalFusionTransformer.gin b/configs/prediction_models/TemporalFusionTransformer.gin index 1d8ec015..48a0fb15 100644 --- a/configs/prediction_models/TemporalFusionTransformer.gin +++ b/configs/prediction_models/TemporalFusionTransformer.gin @@ -12,14 +12,14 @@ optimizer/hyperparameter.lr = (1e-5, 3e-4) # Encoder params model/hyperparameter.class_to_tune = @TemporalFusionTransformer -model/hyperparameter.encoder_length = (1,14,"log-uniform", 2) -model/hyperparameter.hidden = (32, 256, "log-uniform", 2) +model/hyperparameter.encoder_length = 24 +model/hyperparameter.hidden = 128 model/hyperparameter.num_classes = %NUM_CLASSES -model/hyperparameter.dropout = (0.0, 0.4) -model/hyperparameter.dropout_att = (0.0, 0.4) -model/hyperparameter.n_heads = 1 +model/hyperparameter.dropout = 0.1 +model/hyperparameter.dropout_att = 0.0 +model/hyperparameter.n_heads = 4 -model/hyperparameter.example_length=(1,14,"log-uniform", 2) +model/hyperparameter.example_length=48 diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index b2b7ad72..bc8022aa 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -226,11 +226,11 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: if len(not_labeled) > 0: tensors[6][not_labeled] = -1 pad_mask[not_labeled] = 0 - + tensors[6]=[tensors[6]] pad_mask = pad_mask.astype(bool) # data = window.astype(np.float32) - tensors = [from_numpy(np.array(tensor)).to(float32) for tensor in tensors] - tensors = [stack((x,), dim=-1) if x.numel() else empty(0) for x in tensors] + tensors = (from_numpy(np.array(tensor)).to(float32) for tensor in tensors) + tensors = [stack((x,), dim=-1) if x.numel() > 0 else empty(0) for x in tensors] return OrderedDict(zip(FEAT_NAMES, tensors)),from_numpy(pad_mask) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index e8572ca6..f61fb40b 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -306,7 +306,7 @@ def __init__(self,num_classes, encoder_length,hidden,dropout, super().__init__(num_classes=num_classes, encoder_length=encoder_length,hidden=hidden, n_heads=n_heads,dropout_att=dropout_att,example_length=example_length,quantiles=quantiles, - num_static_vars=num_static_vars,num_future_vars=num_future_vars,num_historic_vars=num_historic_vars,*args,static_categorical_inp_size=1,temporal_known_categorical_inp_size=4, + num_static_vars=num_static_vars,num_future_vars=num_future_vars,num_historic_vars=num_historic_vars,*args,static_categorical_inp_size=1,temporal_known_categorical_inp_size=0, temporal_observed_categorical_inp_size=0,static_continuous_inp_size=3,temporal_known_continuous_inp_size=0, temporal_observed_continuous_inp_size=48,temporal_target_size=1,**kwargs) @@ -317,19 +317,18 @@ def __init__(self,num_classes, encoder_length,hidden,dropout, self.embedding = LazyEmbedding(static_categorical_inp_size,temporal_known_categorical_inp_size, temporal_observed_categorical_inp_size,static_continuous_inp_size,temporal_known_continuous_inp_size, temporal_observed_continuous_inp_size,temporal_target_size,hidden) + self.static_encoder = StaticCovariateEncoder(num_static_vars,hidden,dropout) self.TFTpart2 = TFTBack(encoder_length,num_historic_vars,hidden,dropout,num_future_vars, n_heads,dropout_att,example_length,quantiles) self.logit = nn.Linear(len(quantiles), num_classes) def forward(self, x: Dict[str, Tensor]) -> Tensor: - + # print(x.get('s_cont', None)) s_inp, t_known_inp, t_observed_inp, t_observed_tgt = self.embedding(x) - # Static context cs, ce, ch, cc = self.static_encoder(s_inp) ch, cc = ch.unsqueeze(0), cc.unsqueeze(0) #lstm initial states - # Temporal input _historical_inputs = [t_known_inp[:,:self.encoder_length,:], t_observed_tgt[:,:self.encoder_length,:]] if t_observed_inp is not None: diff --git a/icu_benchmarks/models/layers.py b/icu_benchmarks/models/layers.py index e6f2e585..ef4f9e74 100644 --- a/icu_benchmarks/models/layers.py +++ b/icu_benchmarks/models/layers.py @@ -360,8 +360,12 @@ def __init__(self, context_hidden=None, dropout=0.0,): super().__init__() + self.layer_norm = MaybeLayerNorm(output_size, hidden, eps=1e-3) + self.lin_a = nn.Linear(input_size, hidden) + + if context_hidden is not None: self.lin_c = nn.Linear(context_hidden, hidden, bias=False) else: @@ -372,7 +376,9 @@ def __init__(self, self.out_proj = nn.Linear(input_size, output_size) if output_size else None def forward(self, a: Tensor, c: Optional[Tensor] = None): + x = self.lin_a(a) + if c is not None: x = x + self.lin_c(c).unsqueeze(1) x = F.elu(x) @@ -381,16 +387,18 @@ def forward(self, a: Tensor, c: Optional[Tensor] = None): x = self.glu(x) y = a if self.out_proj is None else self.out_proj(a) x = x + y + return self.layer_norm(x) # @torch.jit.script #Currently broken with autocast def fused_pointwise_linear_v1(x, a, b): + out = torch.mul(x.unsqueeze(-1), a) out = out + b return out -@torch.jit.script +#@torch.jit.script def fused_pointwise_linear_v2(x, a, b): out = x.unsqueeze(3) * a out = out + b @@ -438,7 +446,6 @@ def __init__(self,static_categorical_inp_size,temporal_known_categorical_inp_siz self.t_cont_k_embedding_vectors = nn.Parameter(torch.Tensor(self.t_cont_k_inp_size, self.hidden)) if self.t_cont_k_inp_size else None self.t_cont_o_embedding_vectors = nn.Parameter(torch.Tensor(self.t_cont_o_inp_size, self.hidden)) if self.t_cont_o_inp_size else None self.t_tgt_embedding_vectors = nn.Parameter(torch.Tensor(self.t_tgt_size, self.hidden)) - self.s_cont_embedding_bias = nn.Parameter(torch.zeros(self.s_cont_inp_size, self.hidden)) if self.s_cont_inp_size else None self.t_cont_k_embedding_bias = nn.Parameter(torch.zeros(self.t_cont_k_inp_size, self.hidden)) if self.t_cont_k_inp_size else None self.t_cont_o_embedding_bias = nn.Parameter(torch.zeros(self.t_cont_o_inp_size, self.hidden)) if self.t_cont_o_inp_size else None @@ -446,7 +453,6 @@ def __init__(self,static_categorical_inp_size,temporal_known_categorical_inp_siz self.reset_parameters() - def reset_parameters(self): '''' embeddings are initilitized using xavier's method and biases are initlitized with zeros @@ -481,18 +487,20 @@ def _apply_embedding(self, cont_emb: Tensor, cont_bias: Tensor, ) -> Tuple[Optional[Tensor], Optional[Tensor]]: - e_cat = torch.stack([embed(cat[...,i].int()) for i, embed in enumerate(cat_emb)], dim=-2) if (cat is not None) and (cat_emb is not None) else None - if cont is not None: + e_cat = torch.stack([embed(cat[...,i].int()) for i, embed in enumerate(cat_emb)], dim=-2) if (cat is not None) and (cat.size()[1]>0) else None + if (cont is not None ) and (cont.size()[1]>0): #the line below is equivalent to following einsums #e_cont = torch.einsum('btf,fh->bthf', cont, cont_emb) #e_cont = torch.einsum('bf,fh->bhf', cont, cont_emb) - - e_cont = fused_pointwise_linear_v1(cont, cont_emb, cont_bias) + e_cont = torch.mul(cont.unsqueeze(-1), cont_emb) + e_cont = e_cont + cont_bias + # e_cont = fused_pointwise_linear_v1(cont, cont_emb, cont_bias) else: e_cont = None - + if e_cat is not None and e_cont is not None: + return torch.cat([e_cat, e_cont], dim=-2) elif e_cat is not None: return e_cat @@ -512,13 +520,21 @@ def forward(self, x: Dict[str, Tensor]): t_tgt_obs = x['target'] # Has to be present # Static inputs are expected to be equal for all timesteps # For memory efficiency there is no assert statement + + + s_cat_inp = s_cat_inp[:,0,:] if s_cat_inp is not None else None s_cont_inp = s_cont_inp[:,0,:] if s_cont_inp is not None else None + + + s_inp = self._apply_embedding(s_cat_inp, s_cont_inp, self.s_cat_embed, self.s_cont_embedding_vectors, self.s_cont_embedding_bias) + + t_known_inp = self._apply_embedding(t_cat_k_inp, t_cont_k_inp, self.t_cat_k_embed, @@ -531,7 +547,8 @@ def forward(self, x: Dict[str, Tensor]): self.t_cont_o_embedding_bias) # Temporal observed targets - t_observed_tgt = fused_pointwise_linear_v2(t_tgt_obs, self.t_tgt_embedding_vectors, self.t_tgt_embedding_bias) + t_observed_tgt = torch.matmul(t_tgt_obs.unsqueeze(3).unsqueeze(4), self.t_tgt_embedding_vectors.unsqueeze(1)).squeeze(3) + t_observed_tgt = t_observed_tgt + self.t_tgt_embedding_bias return s_inp, t_known_inp, t_observed_inp, t_observed_tgt @@ -544,14 +561,12 @@ def __init__(self, static_categorical_inp_size,temporal_known_categorical_inp_si super().__init__(static_categorical_inp_size,temporal_known_categorical_inp_size, temporal_observed_categorical_inp_size,static_continuous_inp_size,temporal_known_continuous_inp_size, temporal_observed_continuous_inp_size,temporal_target_size,hidden, initialize_cont_params=False) - if static_continuous_inp_size: self.s_cont_embedding_vectors = UninitializedParameter() self.s_cont_embedding_bias = UninitializedParameter() else: self.s_cont_embedding_vectors = None self.s_cont_embedding_bias = None - if temporal_known_continuous_inp_size: self.t_cont_k_embedding_vectors = UninitializedParameter() self.t_cont_k_embedding_bias = UninitializedParameter() @@ -565,7 +580,6 @@ def __init__(self, static_categorical_inp_size,temporal_known_categorical_inp_si else: self.t_cont_o_embedding_vectors = None self.t_cont_o_embedding_bias = None - self.t_tgt_embedding_vectors = UninitializedParameter() self.t_tgt_embedding_bias = UninitializedParameter() @@ -576,8 +590,7 @@ def initialize_parameters(self, x): t_cont_k_inp = x.get('k_cont', None) t_cont_o_inp = x.get('o_cont', None) t_tgt_obs = x['target'] # Has to be present - - + print(s_cont_inp.shape) if (s_cont_inp is not None) and (s_cont_inp.size()[1]>0): self.s_cont_embedding_vectors.materialize((s_cont_inp.shape[-1], self.hidden)) self.s_cont_embedding_bias.materialize((s_cont_inp.shape[-1], self.hidden)) @@ -602,6 +615,7 @@ class VariableSelectionNetwork(nn.Module): ''' def __init__(self, hidden,dropout, num_inputs): super().__init__() + self.joint_grn = GRN(hidden*num_inputs, hidden, output_size=num_inputs, context_hidden=hidden) self.var_grns = nn.ModuleList([GRN(hidden, hidden, dropout=dropout) for _ in range(num_inputs)]) @@ -614,6 +628,7 @@ def forward(self, x: Tensor, context: Optional[Tensor] = None): #the line below performs batched matrix vector multiplication #for temporal features it's bthf,btf->bth #for static features it's bhf,bf->bh + variable_ctx = torch.matmul(transformed_embed, sparse_weights.unsqueeze(-1)).squeeze(-1) return variable_ctx, sparse_weights @@ -629,8 +644,9 @@ def __init__(self, num_static_vars,hidden,dropout): self.context_grns = nn.ModuleList([GRN(hidden, hidden, dropout=dropout) for _ in range(4)]) def forward(self, x: Tensor) -> Tuple[Tensor, Tensor, Tensor, Tensor]: + variable_ctx, sparse_weights = self.vsn(x) - + # Context vectors: # variable selection context # enrichment context diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 33730134..9a7284a3 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -135,7 +135,7 @@ def train_common( max_epochs=epochs if model.needs_training else 1, callbacks=callbacks, precision=precision, - accelerator="auto" if not cpu else "cpu", + accelerator="cpu", devices=max(torch.cuda.device_count(), 1), deterministic=reproducible, benchmark=not reproducible, diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index d7ae32c6..ee7cc8f1 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -232,8 +232,20 @@ def step_fn(self, element, step_prefix=""): """Perform a step in the training loop.""" if(isinstance(element[0] ,OrderedDict)): data,mask=element[0], element[1].to(self.device) + for key, value in data.items(): - data[key] = value.float().to(self.device) + + value = value.float().to(self.device) + + + + if value.shape[-1] == 1: + value = value.squeeze(-1) + if value.dim() == 3: + value = value.permute(0, 2, 1) + + data[key] = value + elif len(element) == 2: data, labels = element[0], element[1].to(self.device) if isinstance(data, list): From 1114690ecbd71926a10248324c2abc79d51895c1 Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Thu, 29 Jun 2023 17:15:09 +0200 Subject: [PATCH 027/142] fixed return shape of data --- icu_benchmarks/models/train.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 9a7284a3..33730134 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -135,7 +135,7 @@ def train_common( max_epochs=epochs if model.needs_training else 1, callbacks=callbacks, precision=precision, - accelerator="cpu", + accelerator="auto" if not cpu else "cpu", devices=max(torch.cuda.device_count(), 1), deterministic=reproducible, benchmark=not reproducible, From 2a89d216e81cf9ce64f76978169a0803d7c3e2ee Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 4 Jul 2023 12:35:37 +0200 Subject: [PATCH 028/142] bug fixes and checks --- .../TemporalFusionTransformer.gin | 9 +++--- icu_benchmarks/data/constants.py | 3 ++ icu_benchmarks/data/loader.py | 10 ++++--- icu_benchmarks/models/dl_models.py | 30 ++++++++++++++----- icu_benchmarks/models/layers.py | 19 ++++++------ icu_benchmarks/models/wrappers.py | 5 ++-- 6 files changed, 48 insertions(+), 28 deletions(-) diff --git a/configs/prediction_models/TemporalFusionTransformer.gin b/configs/prediction_models/TemporalFusionTransformer.gin index 48a0fb15..b2a71b4c 100644 --- a/configs/prediction_models/TemporalFusionTransformer.gin +++ b/configs/prediction_models/TemporalFusionTransformer.gin @@ -12,14 +12,13 @@ optimizer/hyperparameter.lr = (1e-5, 3e-4) # Encoder params model/hyperparameter.class_to_tune = @TemporalFusionTransformer -model/hyperparameter.encoder_length = 24 -model/hyperparameter.hidden = 128 +model/hyperparameter.encoder_length = 20 +model/hyperparameter.hidden = 64 model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.dropout = 0.1 model/hyperparameter.dropout_att = 0.0 -model/hyperparameter.n_heads = 4 - -model/hyperparameter.example_length=48 +model/hyperparameter.n_heads = 1 +model/hyperparameter.example_length=21 diff --git a/icu_benchmarks/data/constants.py b/icu_benchmarks/data/constants.py index 031f35ca..c7f6c635 100644 --- a/icu_benchmarks/data/constants.py +++ b/icu_benchmarks/data/constants.py @@ -15,3 +15,6 @@ class VarType: group = "GROUP" sequence = "SEQUENCE" label = "LABEL" + +class FeatType: + FEAT_NAMES = ['s_cat' , 's_cont' , 'k_cat' , 'k_cont' , 'o_cat' , 'o_cont' , 'target', 'id'] diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index bc8022aa..7289e312 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -9,8 +9,8 @@ from icu_benchmarks.imputation.amputations import ampute_data from .constants import DataSegment as Segment from .constants import DataSplit as Split +from .constants import FeatType as Features from collections import OrderedDict -FEAT_NAMES = ['s_cat' , 's_cont' , 'k_cat' , 'k_cont' , 'o_cat' , 'o_cont' , 'target', 'id'] class CommonDataset(Dataset): """Common dataset: subclass of Torch Dataset that represents the data to learn on. @@ -147,6 +147,7 @@ def get_data_and_labels(self) -> Tuple[np.array, np.array]: if len(labels) == self.num_stays: # order of groups could be random, we make sure not to change it rep = rep.groupby(level=self.vars["GROUP"], sort=False).last() + # print('after') rep = rep.to_numpy().astype(float) return rep, labels @@ -180,11 +181,12 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: return self._cached_dataset[idx] pad_value = 0.0 - stay_id = self.outcome_df.index.unique()[idx] # [self.vars["GROUP"]] + stay_id = self.outcome_df.index.unique()[idx] # We need to be sure that tensors are returned in the correct order to be processed correclty by tft tensors = [[] for _ in range(8)] + for var in self.features_df.columns: if var == 'sex' : tensors[0].append(self.features_df.loc[stay_id:stay_id][var].to_numpy()) @@ -206,7 +208,7 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: length_diff = self.maxlen - window_shape0 pad_mask = np.ones(window_shape0) - + print(np.shape(tensors[0])) # Padding the array to fulfill size requirement if length_diff > 0: # window shorter than the longest window in dataset, pad to same length @@ -232,7 +234,7 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: tensors = (from_numpy(np.array(tensor)).to(float32) for tensor in tensors) tensors = [stack((x,), dim=-1) if x.numel() > 0 else empty(0) for x in tensors] - return OrderedDict(zip(FEAT_NAMES, tensors)),from_numpy(pad_mask) + return OrderedDict(zip(Features.FEAT_NAMES, tensors)),from_numpy(pad_mask) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index f61fb40b..a34ad0a4 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -294,7 +294,7 @@ class TemporalFusionTransformer(DLPredictionWrapper): def __init__(self,num_classes, encoder_length,hidden,dropout, n_heads,dropout_att,example_length,*args,quantiles=[0.1, 0.5, 0.9],static_categorical_inp_size=[2],temporal_known_categorical_inp_size=[], temporal_observed_categorical_inp_size=[],static_continuous_inp_size=3,temporal_known_continuous_inp_size=0, - temporal_observed_continuous_inp_size=48,temporal_target_size=1,**kwargs): + temporal_observed_continuous_inp_size=96,temporal_target_size=1,**kwargs): #derived variables num_static_vars=len(static_categorical_inp_size)+static_continuous_inp_size num_future_vars=len(temporal_known_categorical_inp_size)+temporal_known_continuous_inp_size @@ -303,7 +303,7 @@ def __init__(self,num_classes, encoder_length,hidden,dropout, temporal_target_size, len(temporal_observed_categorical_inp_size), ]) - + super().__init__(num_classes=num_classes, encoder_length=encoder_length,hidden=hidden, n_heads=n_heads,dropout_att=dropout_att,example_length=example_length,quantiles=quantiles, num_static_vars=num_static_vars,num_future_vars=num_future_vars,num_historic_vars=num_historic_vars,*args,static_categorical_inp_size=1,temporal_known_categorical_inp_size=0, @@ -324,18 +324,32 @@ def __init__(self,num_classes, encoder_length,hidden,dropout, self.logit = nn.Linear(len(quantiles), num_classes) def forward(self, x: Dict[str, Tensor]) -> Tensor: - # print(x.get('s_cont', None)) + + s_inp, t_known_inp, t_observed_inp, t_observed_tgt = self.embedding(x) # Static context cs, ce, ch, cc = self.static_encoder(s_inp) ch, cc = ch.unsqueeze(0), cc.unsqueeze(0) #lstm initial states # Temporal input - _historical_inputs = [t_known_inp[:,:self.encoder_length,:], t_observed_tgt[:,:self.encoder_length,:]] + + _historical_inputs = [] + + # Check for t_observed_inp if t_observed_inp is not None: - _historical_inputs.insert(0,t_observed_inp[:,:self.encoder_length,:]) - + _historical_inputs.append(t_observed_inp[:, :self.encoder_length, :]) + + # Check for t_known_inp + if t_known_inp is not None: + _historical_inputs.append(t_known_inp[:, :self.encoder_length, :]) + # Check for t_observed_tgt + if t_observed_tgt is not None: + _historical_inputs.append(t_observed_tgt[:, :self.encoder_length, :]) historical_inputs = cat(_historical_inputs, dim=-2) - future_inputs = t_known_inp[:, self.encoder_length:] - o=self.TFTpart2(historical_inputs, cs, ch, cc, ce, future_inputs) + future_inputs= Tensor() + if t_known_inp is not None: + future_inputs = t_known_inp[:, self.encoder_length:] + + + o=self.TFTpart2(historical_inputs, cs, ch, cc, ce, future_inputs.to(historical_inputs.device)) pred = self.logit(o) return pred \ No newline at end of file diff --git a/icu_benchmarks/models/layers.py b/icu_benchmarks/models/layers.py index ef4f9e74..642136d2 100644 --- a/icu_benchmarks/models/layers.py +++ b/icu_benchmarks/models/layers.py @@ -590,7 +590,6 @@ def initialize_parameters(self, x): t_cont_k_inp = x.get('k_cont', None) t_cont_o_inp = x.get('o_cont', None) t_tgt_obs = x['target'] # Has to be present - print(s_cont_inp.shape) if (s_cont_inp is not None) and (s_cont_inp.size()[1]>0): self.s_cont_embedding_vectors.materialize((s_cont_inp.shape[-1], self.hidden)) self.s_cont_embedding_bias.materialize((s_cont_inp.shape[-1], self.hidden)) @@ -615,22 +614,24 @@ class VariableSelectionNetwork(nn.Module): ''' def __init__(self, hidden,dropout, num_inputs): super().__init__() - + self.hidden=hidden self.joint_grn = GRN(hidden*num_inputs, hidden, output_size=num_inputs, context_hidden=hidden) self.var_grns = nn.ModuleList([GRN(hidden, hidden, dropout=dropout) for _ in range(num_inputs)]) def forward(self, x: Tensor, context: Optional[Tensor] = None): + if x.numel() == 0: # Check if x is an empty tensor + batch_size = context.size(0) if context is not None else 1 + variable_ctx = torch.zeros(batch_size,1, self.hidden,device=x.device) + sparse_weights = torch.ones(batch_size,1, self.hidden,device=x.device) + return variable_ctx, sparse_weights + Xi = torch.flatten(x, start_dim=-2) grn_outputs = self.joint_grn(Xi, c=context) sparse_weights = F.softmax(grn_outputs, dim=-1) transformed_embed_list = [m(x[...,i,:]) for i, m in enumerate(self.var_grns)] transformed_embed = torch.stack(transformed_embed_list, dim=-1) - #the line below performs batched matrix vector multiplication - #for temporal features it's bthf,btf->bth - #for static features it's bhf,bf->bh - variable_ctx = torch.matmul(transformed_embed, sparse_weights.unsqueeze(-1)).squeeze(-1) - + return variable_ctx, sparse_weights class StaticCovariateEncoder(nn.Module): @@ -685,7 +686,6 @@ def forward(self, x: Tensor) -> Tuple[Tensor, Tensor]: # attn_score = torch.einsum('bind,bjnd->bnij', q, k) attn_score = torch.matmul(q.permute((0, 2, 1, 3)), k.permute((0, 2, 3, 1))) attn_score.mul_(self.scale) - attn_score = attn_score + self._mask attn_prob = F.softmax(attn_score, dim=3) @@ -741,8 +741,9 @@ def forward(self, historical_inputs, cs, ch, cc, ce, future_inputs): historical_features, _ = self.history_vsn(historical_inputs, cs) history, state = self.history_encoder(historical_features, (ch, cc)) future_features, _ = self.future_vsn(future_inputs, cs) + future, _ = self.future_encoder(future_features, state) - torch.cuda.synchronize() + # skip connection input_embedding = torch.cat([historical_features, future_features], dim=1) diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index ee7cc8f1..759ec7f4 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -234,7 +234,6 @@ def step_fn(self, element, step_prefix=""): data,mask=element[0], element[1].to(self.device) for key, value in data.items(): - value = value.float().to(self.device) @@ -243,7 +242,8 @@ def step_fn(self, element, step_prefix=""): value = value.squeeze(-1) if value.dim() == 3: value = value.permute(0, 2, 1) - + if(key=='target'): + labels=value.squeeze() data[key] = value elif len(element) == 2: @@ -270,6 +270,7 @@ def step_fn(self, element, step_prefix=""): else: aux_loss = 0 prediction = torch.masked_select(out, mask.unsqueeze(-1)).reshape(-1, out.shape[-1]).to(self.device) + target = torch.masked_select(labels, mask).to(self.device) if prediction.shape[-1] > 1 and self.run_mode == RunMode.classification: # Classification task From b757c66bd7a93cae2cf184a8d8ce7cb451cf8b84 Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 4 Jul 2023 13:06:40 +0200 Subject: [PATCH 029/142] fixed concat to be along the correct axis --- icu_benchmarks/data/loader.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 7289e312..9e848fc6 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -205,25 +205,26 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: if len(tensors[6]) == 1: # only one label per stay, align with window tensors[6] = np.concatenate([np.empty(window_shape0 - 1) * np.nan, tensors[6]], axis=0) - + length_diff = self.maxlen - window_shape0 - pad_mask = np.ones(window_shape0) print(np.shape(tensors[0])) + print(np.shape(np.ones((length_diff, np.shape(tensors[0])[0])))) + pad_mask = np.ones(window_shape0) # Padding the array to fulfill size requirement if length_diff > 0: # window shorter than the longest window in dataset, pad to same length - tensors[0]= np.concatenate([tensors[0], np.ones((length_diff, np.shape(tensors[0])[0])) * pad_value], axis=0) - tensors[1]= np.concatenate([tensors[1], np.ones((length_diff, np.shape(tensors[1])[0])) * pad_value], axis=0) + tensors[0]= np.concatenate([tensors[0], np.ones(( np.shape(tensors[0])[0],length_diff)) * pad_value], axis=1) + tensors[1]= np.concatenate([tensors[1], np.ones(( np.shape(tensors[1])[0],length_diff)) * pad_value], axis=1) # tensors[2]= np.concatenate([tensors[2], np.ones((length_diff, np.shape(tensors[2])[0])) * pad_value], axis=0) # tensors[3]= np.concatenate([tensors[3], np.ones((length_diff, np.shape(tensors[3])[0])) * pad_value], axis=0) # tensors[4]= np.concatenate([tensors[4], np.ones((length_diff, np.shape(tensors[4])[0])) * pad_value], axis=0) - tensors[5]= np.concatenate([tensors[5], np.ones((length_diff, np.shape(tensors[5])[0])) * pad_value], axis=0) - tensors[7]= np.concatenate([tensors[7], np.ones((length_diff, np.shape(tensors[7])[0])) * stay_id], axis=0) + tensors[5]= np.concatenate([tensors[5], np.ones(( np.shape(tensors[5])[0],length_diff)) * pad_value], axis=1) + tensors[7]= np.concatenate([tensors[7], np.ones(( np.shape(tensors[7])[0],length_diff)) * stay_id], axis=1) # window = np.concatenate([window, np.ones((length_diff, window.shape[1])) * pad_value], axis=0) tensors[6] = np.concatenate([tensors[6], np.ones(length_diff) * pad_value], axis=0) pad_mask = np.concatenate([pad_mask, np.zeros(length_diff)], axis=0) - + print(np.shape(tensors[0])) not_labeled = np.argwhere(np.isnan(tensors[6])) if len(not_labeled) > 0: tensors[6][not_labeled] = -1 From 273b9aea3a76360ffd2ce037545a10b0c40848db Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 4 Jul 2023 14:25:11 +0200 Subject: [PATCH 030/142] regression is working now with quantile loss --- .../TemporalFusionTransformer.gin | 2 +- configs/tasks/RegressionTFT.gin | 2 +- configs/tasks/common/Imports.gin | 2 +- experiments/demo_benchmark_classification.yml | 10 ++++----- icu_benchmarks/data/loader.py | 21 +++++++++---------- icu_benchmarks/models/metrics.py | 18 ++++++++++++++-- icu_benchmarks/models/train.py | 3 ++- icu_benchmarks/models/wrappers.py | 2 +- 8 files changed, 37 insertions(+), 23 deletions(-) diff --git a/configs/prediction_models/TemporalFusionTransformer.gin b/configs/prediction_models/TemporalFusionTransformer.gin index b2a71b4c..74dde441 100644 --- a/configs/prediction_models/TemporalFusionTransformer.gin +++ b/configs/prediction_models/TemporalFusionTransformer.gin @@ -13,7 +13,7 @@ optimizer/hyperparameter.lr = (1e-5, 3e-4) # Encoder params model/hyperparameter.class_to_tune = @TemporalFusionTransformer model/hyperparameter.encoder_length = 20 -model/hyperparameter.hidden = 64 +model/hyperparameter.hidden = 12 model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.dropout = 0.1 model/hyperparameter.dropout_att = 0.0 diff --git a/configs/tasks/RegressionTFT.gin b/configs/tasks/RegressionTFT.gin index b31408fb..a1ff4d55 100644 --- a/configs/tasks/RegressionTFT.gin +++ b/configs/tasks/RegressionTFT.gin @@ -16,7 +16,7 @@ train_common.ram_cache = True # LOSS FUNCTION DLPredictionWrapper.loss = @QuantileLoss - +QuantileLoss.quantiles=[0.1, 0.5, 0.9] # SELECTING PREPROCESSOR preprocess.preprocessor = @base_regression_preprocessor diff --git a/configs/tasks/common/Imports.gin b/configs/tasks/common/Imports.gin index faf3c8d6..b3da4a80 100644 --- a/configs/tasks/common/Imports.gin +++ b/configs/tasks/common/Imports.gin @@ -4,4 +4,4 @@ import icu_benchmarks.data.loader import icu_benchmarks.models.wrappers import icu_benchmarks.models.dl_models import icu_benchmarks.models.ml_models -import icu_benchmarks.models.layers \ No newline at end of file +import icu_benchmarks.models.metrics \ No newline at end of file diff --git a/experiments/demo_benchmark_classification.yml b/experiments/demo_benchmark_classification.yml index 15802f9b..150bac03 100644 --- a/experiments/demo_benchmark_classification.yml +++ b/experiments/demo_benchmark_classification.yml @@ -18,11 +18,11 @@ parameters: data_dir: values: - demo_data/mortality24/eicu_demo - #- demo_data/mortality24/mimic_demo - # - demo_data/aki/eicu_demo - # - demo_data/aki/mimic_demo - # - demo_data/sepsis/eicu_demo - # - demo_data/sepsis/mimic_demo + - demo_data/mortality24/mimic_demo + - demo_data/aki/eicu_demo + - demo_data/aki/mimic_demo + - demo_data/sepsis/eicu_demo + - demo_data/sepsis/mimic_demo model: values: # - LogisticRegression diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 9e848fc6..c576d857 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -147,7 +147,6 @@ def get_data_and_labels(self) -> Tuple[np.array, np.array]: if len(labels) == self.num_stays: # order of groups could be random, we make sure not to change it rep = rep.groupby(level=self.vars["GROUP"], sort=False).last() - # print('after') rep = rep.to_numpy().astype(float) return rep, labels @@ -198,7 +197,6 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: tensors[6].extend(self.outcome_df.loc[stay_id:stay_id][self.vars["LABEL"]].to_numpy(dtype=float)) tensors[7].append(np.asarray([stay_id])) - window_shape0=np.shape(tensors[0])[1] # window = self.features_df.loc[stay_id:stay_id].to_numpy() # labels = self.outcome_df.loc[stay_id:stay_id][self.vars["LABEL"]].to_numpy(dtype=float) @@ -207,24 +205,24 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: tensors[6] = np.concatenate([np.empty(window_shape0 - 1) * np.nan, tensors[6]], axis=0) length_diff = self.maxlen - window_shape0 - print(np.shape(tensors[0])) - print(np.shape(np.ones((length_diff, np.shape(tensors[0])[0])))) + length_diff_id=self.maxlen - np.shape(tensors[7])[1] pad_mask = np.ones(window_shape0) # Padding the array to fulfill size requirement + if length_diff > 0: # window shorter than the longest window in dataset, pad to same length - tensors[0]= np.concatenate([tensors[0], np.ones(( np.shape(tensors[0])[0],length_diff)) * pad_value], axis=1) - tensors[1]= np.concatenate([tensors[1], np.ones(( np.shape(tensors[1])[0],length_diff)) * pad_value], axis=1) + tensors[0]= np.concatenate([tensors[0], np.ones(( np.shape(tensors[0])[0],self.maxlen - np.shape(tensors[0])[1])) * pad_value], axis=1) + tensors[1]= np.concatenate([tensors[1], np.ones(( np.shape(tensors[1])[0],self.maxlen - np.shape(tensors[1])[1])) * pad_value], axis=1) # tensors[2]= np.concatenate([tensors[2], np.ones((length_diff, np.shape(tensors[2])[0])) * pad_value], axis=0) # tensors[3]= np.concatenate([tensors[3], np.ones((length_diff, np.shape(tensors[3])[0])) * pad_value], axis=0) # tensors[4]= np.concatenate([tensors[4], np.ones((length_diff, np.shape(tensors[4])[0])) * pad_value], axis=0) - tensors[5]= np.concatenate([tensors[5], np.ones(( np.shape(tensors[5])[0],length_diff)) * pad_value], axis=1) - tensors[7]= np.concatenate([tensors[7], np.ones(( np.shape(tensors[7])[0],length_diff)) * stay_id], axis=1) + tensors[5]= np.concatenate([tensors[5], np.ones(( np.shape(tensors[5])[0],self.maxlen - np.shape(tensors[5])[1])) * pad_value], axis=1) + # window = np.concatenate([window, np.ones((length_diff, window.shape[1])) * pad_value], axis=0) - tensors[6] = np.concatenate([tensors[6], np.ones(length_diff) * pad_value], axis=0) + tensors[6] = np.concatenate([tensors[6], np.ones(self.maxlen - np.shape(tensors[6])[0]) * pad_value], axis=0) pad_mask = np.concatenate([pad_mask, np.zeros(length_diff)], axis=0) - print(np.shape(tensors[0])) + tensors[7]= np.concatenate([tensors[7], np.ones(( np.shape(tensors[7])[0],self.maxlen - np.shape(tensors[7])[1])) * stay_id], axis=1)#should be done regardless of length_diff not_labeled = np.argwhere(np.isnan(tensors[6])) if len(not_labeled) > 0: tensors[6][not_labeled] = -1 @@ -232,9 +230,10 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: tensors[6]=[tensors[6]] pad_mask = pad_mask.astype(bool) # data = window.astype(np.float32) + + tensors = (from_numpy(np.array(tensor)).to(float32) for tensor in tensors) tensors = [stack((x,), dim=-1) if x.numel() > 0 else empty(0) for x in tensors] - return OrderedDict(zip(Features.FEAT_NAMES, tensors)),from_numpy(pad_mask) diff --git a/icu_benchmarks/models/metrics.py b/icu_benchmarks/models/metrics.py index 280a0653..108f65da 100644 --- a/icu_benchmarks/models/metrics.py +++ b/icu_benchmarks/models/metrics.py @@ -5,13 +5,27 @@ from sklearn.metrics import balanced_accuracy_score, mean_absolute_error from sklearn.calibration import calibration_curve from scipy.spatial.distance import jensenshannon - +import gin +import torch.nn.functional as F """" This file contains metrics that are not available in ignite.metrics. Specifically, it adds transformation capabilities to some metrics. """ - +class QuantileLossClass(torch.nn.Module): + def __init__(self, quantiles): + super().__init__() + self.register_buffer('q', torch.tensor(quantiles)) + + def forward(self, predictions, targets): + diff = predictions - targets + ql = (1-self.q)*F.relu(diff) + self.q*F.relu(-diff) + losses = ql.view(-1, ql.shape[-1]).mean(0) + return losses +@gin.configurable +def Quantileloss(self,predictions, targets,quantiles=[0.1,0.5,0.9]): + loss=QuantileLossClass(quantiles=quantiles) + return loss(predictions,targets) def accuracy(output, target, topk=(1,)): """Computes the accuracy over the k top predictions for the specified values of k""" with torch.no_grad(): diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 33730134..c5def311 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -102,6 +102,7 @@ def train_common( pin_memory=True, drop_last=True, ) + if(isinstance(next(iter(train_loader))[0] ,OrderedDict)): model = model(optimizer=optimizer, epochs=epochs, run_mode=mode) @@ -135,7 +136,7 @@ def train_common( max_epochs=epochs if model.needs_training else 1, callbacks=callbacks, precision=precision, - accelerator="auto" if not cpu else "cpu", + accelerator= "cpu", devices=max(torch.cuda.device_count(), 1), deterministic=reproducible, benchmark=not reproducible, diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 759ec7f4..5872acb3 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -20,7 +20,6 @@ from pytorch_lightning import LightningModule from icu_benchmarks.models.constants import MLMetrics, DLMetrics from icu_benchmarks.contants import RunMode - gin.config.external_configurable(torch.nn.functional.nll_loss, module="torch.nn.functional") gin.config.external_configurable(torch.nn.functional.cross_entropy, module="torch.nn.functional") gin.config.external_configurable(torch.nn.functional.mse_loss, module="torch.nn.functional") @@ -278,6 +277,7 @@ def step_fn(self, element, step_prefix=""): # torch.long because NLL elif self.run_mode == RunMode.regression: # Regression task + loss = self.loss(prediction[:, 0], target.float()) + aux_loss else: raise ValueError(f"Run mode {self.run_mode} not supported.") From 5b97aacef67efe35089036edbb693e9eca05c476 Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 4 Jul 2023 15:56:27 +0200 Subject: [PATCH 031/142] changed gin file --- .../prediction_models/TemporalFusionTransformer.gin | 12 ++++++------ experiments/demo_benchmark_classification.yml | 2 +- icu_benchmarks/models/layers.py | 11 ----------- icu_benchmarks/models/train.py | 2 +- icu_benchmarks/models/utils.py | 5 +++++ 5 files changed, 13 insertions(+), 19 deletions(-) diff --git a/configs/prediction_models/TemporalFusionTransformer.gin b/configs/prediction_models/TemporalFusionTransformer.gin index 74dde441..870213ab 100644 --- a/configs/prediction_models/TemporalFusionTransformer.gin +++ b/configs/prediction_models/TemporalFusionTransformer.gin @@ -12,13 +12,13 @@ optimizer/hyperparameter.lr = (1e-5, 3e-4) # Encoder params model/hyperparameter.class_to_tune = @TemporalFusionTransformer -model/hyperparameter.encoder_length = 20 -model/hyperparameter.hidden = 12 +model/hyperparameter.encoder_length = 24 +model/hyperparameter.hidden = (32, 256, "log-uniform", 2) model/hyperparameter.num_classes = %NUM_CLASSES -model/hyperparameter.dropout = 0.1 -model/hyperparameter.dropout_att = 0.0 -model/hyperparameter.n_heads = 1 -model/hyperparameter.example_length=21 +model/hyperparameter.dropout = (0.0, 0.4) +model/hyperparameter.dropout_att = (0.0, 0.4) +model/hyperparameter.n_heads =heads_divisible_by_hidden(model/hyperparameter.hidden) +model/hyperparameter.example_length=25 diff --git a/experiments/demo_benchmark_classification.yml b/experiments/demo_benchmark_classification.yml index 150bac03..58b14a45 100644 --- a/experiments/demo_benchmark_classification.yml +++ b/experiments/demo_benchmark_classification.yml @@ -11,7 +11,7 @@ command: - --tune - --wandb-sweep - -gc - - -lc + - -lc method: grid name: yaib_demo_classification_benchmark parameters: diff --git a/icu_benchmarks/models/layers.py b/icu_benchmarks/models/layers.py index 642136d2..016cb366 100644 --- a/icu_benchmarks/models/layers.py +++ b/icu_benchmarks/models/layers.py @@ -778,14 +778,3 @@ def forward(self, historical_inputs, cs, ch, cc, ce, future_inputs): out = self.quantile_proj(x) return out -@gin.configurable -class QuantileLoss(torch.nn.Module): - def __init__(self, quantiles): - super().__init__() - self.register_buffer('q', torch.tensor(quantiles)) - - def forward(self, predictions, targets): - diff = predictions - targets - ql = (1-self.q)*F.relu(diff) + self.q*F.relu(-diff) - losses = ql.view(-1, ql.shape[-1]).mean(0) - return losses \ No newline at end of file diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index c5def311..f358483f 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -136,7 +136,7 @@ def train_common( max_epochs=epochs if model.needs_training else 1, callbacks=callbacks, precision=precision, - accelerator= "cpu", + accelerator="auto" if not cpu else "cpu", devices=max(torch.cuda.device_count(), 1), deterministic=reproducible, benchmark=not reproducible, diff --git a/icu_benchmarks/models/utils.py b/icu_benchmarks/models/utils.py index 53bfcae2..ddc97028 100644 --- a/icu_benchmarks/models/utils.py +++ b/icu_benchmarks/models/utils.py @@ -188,3 +188,8 @@ def version(self): @rank_zero_only def log_hyperparams(self, params): pass +@gin.configurable +def heads_divisible_by_hidden(hidden): + heads_range = range(1, 9) # Range for heads from 1 to 8 + divisible_heads = (heads for heads in heads_range if hidden % heads == 0) + return divisible_heads \ No newline at end of file From f767499f90e6a76cb9fe6e12e359552ff0398ab8 Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 11 Jul 2023 14:57:07 +0200 Subject: [PATCH 032/142] fixing bugs to get tftpytorch to work --- .../TemporalFusionTransformer.gin | 25 ----- configs/prediction_models/common/DLCommon.gin | 1 + experiments/demo_benchmark_regression.yml | 16 ++-- icu_benchmarks/data/loader.py | 91 ++++++++++++++++--- icu_benchmarks/models/dl_models.py | 29 +++++- icu_benchmarks/models/train.py | 61 +++++++------ 6 files changed, 149 insertions(+), 74 deletions(-) delete mode 100644 configs/prediction_models/TemporalFusionTransformer.gin diff --git a/configs/prediction_models/TemporalFusionTransformer.gin b/configs/prediction_models/TemporalFusionTransformer.gin deleted file mode 100644 index 870213ab..00000000 --- a/configs/prediction_models/TemporalFusionTransformer.gin +++ /dev/null @@ -1,25 +0,0 @@ -# Settings for TFT model. - -# Common settings for DL models -include "configs/prediction_models/common/DLCommon.gin" - -# Optimizer params -train_common.model = @TemporalFusionTransformer - -optimizer/hyperparameter.class_to_tune = @Adam -optimizer/hyperparameter.weight_decay = 1e-6 -optimizer/hyperparameter.lr = (1e-5, 3e-4) - -# Encoder params -model/hyperparameter.class_to_tune = @TemporalFusionTransformer -model/hyperparameter.encoder_length = 24 -model/hyperparameter.hidden = (32, 256, "log-uniform", 2) -model/hyperparameter.num_classes = %NUM_CLASSES -model/hyperparameter.dropout = (0.0, 0.4) -model/hyperparameter.dropout_att = (0.0, 0.4) -model/hyperparameter.n_heads =heads_divisible_by_hidden(model/hyperparameter.hidden) -model/hyperparameter.example_length=25 - - - - diff --git a/configs/prediction_models/common/DLCommon.gin b/configs/prediction_models/common/DLCommon.gin index 4b761726..99251ea2 100644 --- a/configs/prediction_models/common/DLCommon.gin +++ b/configs/prediction_models/common/DLCommon.gin @@ -6,6 +6,7 @@ import icu_benchmarks.models.wrappers import icu_benchmarks.models.dl_models import icu_benchmarks.models.utils import icu_benchmarks.models.layers +import icu_benchmarks.data.loader # Do not generate features from dynamic data base_classification_preprocessor.generate_features = False diff --git a/experiments/demo_benchmark_regression.yml b/experiments/demo_benchmark_regression.yml index 3b678371..047f0c88 100644 --- a/experiments/demo_benchmark_regression.yml +++ b/experiments/demo_benchmark_regression.yml @@ -19,16 +19,16 @@ parameters: values: - demo_data/los/eicu_demo - demo_data/los/mimic_demo - - demo_data/kf/eicu_demo - - demo_data/kf/mimic_demo + # - demo_data/kf/eicu_demo + # - demo_data/kf/mimic_demo model: values: - - ElasticNet - - LGBMRegressor - - GRU - - LSTM - - TCN - - Transformer + # - ElasticNet + # - LGBMRegressor + # - GRU + # - LSTM + # - TCN + - TemporalFusionTransformer seed: values: - 1111 diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index c576d857..2f13814b 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -1,5 +1,6 @@ from typing import List from pandas import DataFrame +import pandas as pd import gin import numpy as np from torch import Tensor, cat, from_numpy, float32,empty,stack @@ -11,7 +12,7 @@ from .constants import DataSplit as Split from .constants import FeatType as Features from collections import OrderedDict - +from pytorch_forecasting import TimeSeriesDataSet class CommonDataset(Dataset): """Common dataset: subclass of Torch Dataset that represents the data to learn on. @@ -92,7 +93,7 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: return self._cached_dataset[idx] pad_value = 0.0 - stay_id = self.outcome_df.index.unique()[idx] # [self.vars["GROUP"]] + stay_id = self.outcome_df.index.unique()[idx] # slice to make sure to always return a DF window = self.features_df.loc[stay_id:stay_id].to_numpy() @@ -185,7 +186,6 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: # We need to be sure that tensors are returned in the correct order to be processed correclty by tft tensors = [[] for _ in range(8)] - for var in self.features_df.columns: if var == 'sex' : tensors[0].append(self.features_df.loc[stay_id:stay_id][var].to_numpy()) @@ -198,8 +198,7 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: tensors[6].extend(self.outcome_df.loc[stay_id:stay_id][self.vars["LABEL"]].to_numpy(dtype=float)) tensors[7].append(np.asarray([stay_id])) window_shape0=np.shape(tensors[0])[1] - # window = self.features_df.loc[stay_id:stay_id].to_numpy() - # labels = self.outcome_df.loc[stay_id:stay_id][self.vars["LABEL"]].to_numpy(dtype=float) + if len(tensors[6]) == 1: # only one label per stay, align with window tensors[6] = np.concatenate([np.empty(window_shape0 - 1) * np.nan, tensors[6]], axis=0) @@ -213,12 +212,10 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: # window shorter than the longest window in dataset, pad to same length tensors[0]= np.concatenate([tensors[0], np.ones(( np.shape(tensors[0])[0],self.maxlen - np.shape(tensors[0])[1])) * pad_value], axis=1) tensors[1]= np.concatenate([tensors[1], np.ones(( np.shape(tensors[1])[0],self.maxlen - np.shape(tensors[1])[1])) * pad_value], axis=1) - # tensors[2]= np.concatenate([tensors[2], np.ones((length_diff, np.shape(tensors[2])[0])) * pad_value], axis=0) - # tensors[3]= np.concatenate([tensors[3], np.ones((length_diff, np.shape(tensors[3])[0])) * pad_value], axis=0) - # tensors[4]= np.concatenate([tensors[4], np.ones((length_diff, np.shape(tensors[4])[0])) * pad_value], axis=0) + tensors[5]= np.concatenate([tensors[5], np.ones(( np.shape(tensors[5])[0],self.maxlen - np.shape(tensors[5])[1])) * pad_value], axis=1) - # window = np.concatenate([window, np.ones((length_diff, window.shape[1])) * pad_value], axis=0) + tensors[6] = np.concatenate([tensors[6], np.ones(self.maxlen - np.shape(tensors[6])[0]) * pad_value], axis=0) pad_mask = np.concatenate([pad_mask, np.zeros(length_diff)], axis=0) @@ -229,7 +226,7 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: pad_mask[not_labeled] = 0 tensors[6]=[tensors[6]] pad_mask = pad_mask.astype(bool) - # data = window.astype(np.float32) + tensors = (from_numpy(np.array(tensor)).to(float32) for tensor in tensors) @@ -317,7 +314,7 @@ class ImputationPredictionDataset(Dataset): select_columns (List[str], optional): the columns to serve as input for the imputation model. Defaults to None. ram_cache (bool, optional): wether the dataset should be stored in ram. Defaults to True. """ - + def __init__( self, data: DataFrame, @@ -372,3 +369,75 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: return from_numpy(window.values).to(float32) +@gin.configurable("PredictionDatasetTFTpytorch") +class PredictionDatasetTFTpytorch(TimeSeriesDataSet): + def __init__(self, + data: dict, + split: str , + max_prediction_length: int, + max_encoder_length: int, + *args, + ram_cache: bool = True, + **kwargs): + data[split]["FEATURES"]["time_idx"]=((data[split]["FEATURES"]["time"]/ pd.Timedelta(seconds=3600))).astype(int) + data=data.get(split) + labels = data["OUTCOME"] + features=data["FEATURES"] + self.data=pd.merge(labels, features, on=['stay_id', 'time']) + self.split = split + self.args=args + self.ram_cache=ram_cache + self.kwargs=kwargs + + super().__init__(data=self.data, + time_idx="time_idx", + target="label", + group_ids=["stay_id"], + min_encoder_length=max_encoder_length // 2, + max_encoder_length=max_encoder_length, + min_prediction_length=1, + max_prediction_length=max_prediction_length, + static_categoricals=[], + static_reals=["height", "weight","age","sex"], + time_varying_known_categoricals=[], + time_varying_known_reals=[], + time_varying_unknown_categoricals=[], + time_varying_unknown_reals=[ + "alb", "alp", "alt", "ast", "be", "bicar", "bili", "bili_dir", "bnd", "bun", "ca", "cai", "ck", "ckmb", "cl", + "crea", "crp", "dbp", "fgn", "fio2", "glu", "hgb", "hr", "inr_pt", "k", "lact", "lymph", "map", "mch", "mchc", "mcv", + "methb", "mg", "na", "neut", "o2sat", "pco2", "ph", "phos", "plt", "po2", "ptt", "resp", "sbp", "temp", "tnt", "urine", + "wbc",'MissingIndicator_1', 'MissingIndicator_2', + 'MissingIndicator_3', 'MissingIndicator_4', 'MissingIndicator_5', + 'MissingIndicator_6', 'MissingIndicator_7', 'MissingIndicator_8', + 'MissingIndicator_9', 'MissingIndicator_10', 'MissingIndicator_11', + 'MissingIndicator_12', 'MissingIndicator_13', 'MissingIndicator_14', + 'MissingIndicator_15', 'MissingIndicator_16', 'MissingIndicator_17', + 'MissingIndicator_18', 'MissingIndicator_19', 'MissingIndicator_20', + 'MissingIndicator_21', 'MissingIndicator_22', 'MissingIndicator_23', + 'MissingIndicator_24', 'MissingIndicator_25', 'MissingIndicator_26', + 'MissingIndicator_27', 'MissingIndicator_28', 'MissingIndicator_29', + 'MissingIndicator_30', 'MissingIndicator_31', 'MissingIndicator_32', + 'MissingIndicator_33', 'MissingIndicator_34', 'MissingIndicator_35', + 'MissingIndicator_36', 'MissingIndicator_37', 'MissingIndicator_38', + 'MissingIndicator_39', 'MissingIndicator_40', 'MissingIndicator_41', + 'MissingIndicator_42', 'MissingIndicator_43', 'MissingIndicator_44', + 'MissingIndicator_45', 'MissingIndicator_46', 'MissingIndicator_47', + 'MissingIndicator_48' + ], + add_relative_time_idx=True, + add_target_scales=True, + add_encoder_length=True,) + def get_balance(self) -> list: + """Return the weight balance for the split of interest. + + Returns: + Weights for each label. + """ + + counts = self.data["target"][0].unique(return_counts=True) + + + return list((1/counts[1]) * counts[1].sum() / counts[0].shape[0]) + + + \ No newline at end of file diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index a34ad0a4..e5df2793 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -7,7 +7,9 @@ from typing import Dict from icu_benchmarks.models.wrappers import DLPredictionWrapper from torch import Tensor,cat,jit +from pytorch_forecasting import TemporalFusionTransformer +from pytorch_forecasting.metrics import QuantileLoss @gin.configurable class RNNet(DLPredictionWrapper): @@ -282,9 +284,9 @@ def forward(self, x): o = o.permute(0, 2, 1) # Permute to channel last pred = self.logit(o) return pred - + @gin.configurable -class TemporalFusionTransformer(DLPredictionWrapper): +class TFT(DLPredictionWrapper): """ Implementation of https://arxiv.org/abs/1912.09363 """ @@ -352,4 +354,25 @@ def forward(self, x: Dict[str, Tensor]) -> Tensor: o=self.TFTpart2(historical_inputs, cs, ch, cc, ce, future_inputs.to(historical_inputs.device)) pred = self.logit(o) - return pred \ No newline at end of file + return pred + +@gin.configurable +class TFTpytorch(DLPredictionWrapper): + """ + Implementation of https://arxiv.org/abs/1912.09363 + """ + supported_run_modes = [RunMode.classification, RunMode.regression] + def __init__(self,dataset,hidden,dropout, + n_heads,dropout_att,lr,optimizer,*args,**kwargs): + self.device=device + TemporalFusionTransformer.from_dataset( + dataset, + learning_rate=lr, + hidden_size=hidden, + attention_head_size=n_heads, + dropout=dropout, + hidden_continuous_size=hidden, + loss=QuantileLoss(), + optimizer=optimizer, + +) \ No newline at end of file diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index f358483f..21334a67 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -8,8 +8,9 @@ from pytorch_lightning.loggers import TensorBoardLogger from pytorch_lightning import Trainer from pytorch_lightning.callbacks import EarlyStopping, ModelCheckpoint, TQDMProgressBar +from pytorch_forecasting import TimeSeriesDataSet from pathlib import Path -from icu_benchmarks.data.loader import PredictionDataset, ImputationDataset,PredictionDatasetTFT +from icu_benchmarks.data.loader import PredictionDataset, ImputationDataset,PredictionDatasetTFT,PredictionDatasetTFTpytorch from icu_benchmarks.models.utils import save_config_file, JSONMetricsLogger from icu_benchmarks.contants import RunMode from icu_benchmarks.data.constants import DataSplit as Split @@ -72,12 +73,13 @@ def train_common( """ logging.info(f"Training model: {model.__name__}.") - dataset_class = ImputationDataset if mode == RunMode.imputation else (PredictionDatasetTFT if model.__name__ =='TemporalFusionTransformer' else PredictionDataset ) - + dataset_class = (ImputationDataset if mode == RunMode.imputation + else (PredictionDatasetTFT if model.__name__ == 'TFT' + else (PredictionDatasetTFTpytorch if model.__name__ == 'TFTpytorch' else PredictionDataset))) logging.info(f"Logging to directory: {log_dir}.") save_config_file(log_dir) # We save the operative config before and also after training - + train_dataset = dataset_class(data, split=Split.train, ram_cache=ram_cache) val_dataset = dataset_class(data, split=Split.val, ram_cache=ram_cache) train_dataset, val_dataset = assure_minimum_length(train_dataset), assure_minimum_length(val_dataset) @@ -85,31 +87,36 @@ def train_common( logging.debug(f"Training on {len(train_dataset)} samples and validating on {len(val_dataset)} samples.") logging.info(f"Using {num_workers} workers for data loading.") - - train_loader = DataLoader( - train_dataset, - batch_size=batch_size, - shuffle=True, - num_workers=num_workers, - pin_memory=True, - drop_last=True, - ) - val_loader = DataLoader( - val_dataset, - batch_size=batch_size, - shuffle=False, - num_workers=num_workers, - pin_memory=True, - drop_last=True, - ) - - if(isinstance(next(iter(train_loader))[0] ,OrderedDict)): - model = model(optimizer=optimizer, epochs=epochs, run_mode=mode) - + if(model.__name__ =='TFTpytorch'): + + train_loader = train_dataset.to_dataloader(train=True, batch_size=batch_size, num_workers=num_workers,pin_memory=True,drop_last=True) + val_loader = val_dataset.to_dataloader(train=False, batch_size=batch_size ,num_workers=num_workers,pin_memory=True,drop_last=True) + model=model(dataset=train_dataset,optimizer=optimizer, epochs=epochs, run_mode=mode) else: - data_shape = next(iter(train_loader))[0].shape + train_loader = DataLoader( + train_dataset, + batch_size=batch_size, + shuffle=True, + num_workers=num_workers, + pin_memory=True, + drop_last=True, + ) + val_loader = DataLoader( + val_dataset, + batch_size=batch_size, + shuffle=False, + num_workers=num_workers, + pin_memory=True, + drop_last=True, + ) - model = model(optimizer=optimizer, input_size=data_shape, epochs=epochs, run_mode=mode) + if(isinstance(next(iter(train_loader))[0] ,OrderedDict)): + model = model(optimizer=optimizer, epochs=epochs, run_mode=mode) + + else: + data_shape = next(iter(train_loader))[0].shape + + model = model(optimizer=optimizer, input_size=data_shape, epochs=epochs, run_mode=mode) model.set_weight(weight, train_dataset) if load_weights: if source_dir.exists(): From e72927af4a60bca9f6588f958ce7f8a87c879222 Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 11 Jul 2023 14:57:54 +0200 Subject: [PATCH 033/142] new gin files --- configs/prediction_models/TFT.gin | 25 ++++++++++++++++++++++++ configs/prediction_models/TFTpytorch.gin | 22 +++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 configs/prediction_models/TFT.gin create mode 100644 configs/prediction_models/TFTpytorch.gin diff --git a/configs/prediction_models/TFT.gin b/configs/prediction_models/TFT.gin new file mode 100644 index 00000000..d9bbe607 --- /dev/null +++ b/configs/prediction_models/TFT.gin @@ -0,0 +1,25 @@ +# Settings for TFT model. + +# Common settings for DL models +include "configs/prediction_models/common/DLCommon.gin" + +# Optimizer params +train_common.model = @TFT + +optimizer/hyperparameter.class_to_tune = @Adam +optimizer/hyperparameter.weight_decay = 1e-6 +optimizer/hyperparameter.lr = (1e-5, 3e-4) + +# Encoder params +model/hyperparameter.class_to_tune = @TFT +model/hyperparameter.encoder_length = 24 +model/hyperparameter.hidden = (32, 256, "log-uniform", 2) +model/hyperparameter.num_classes = %NUM_CLASSES +model/hyperparameter.dropout = (0.0, 0.4) +model/hyperparameter.dropout_att = (0.0, 0.4) +model/hyperparameter.n_heads =1 +model/hyperparameter.example_length=25 + + + + diff --git a/configs/prediction_models/TFTpytorch.gin b/configs/prediction_models/TFTpytorch.gin new file mode 100644 index 00000000..77649f97 --- /dev/null +++ b/configs/prediction_models/TFTpytorch.gin @@ -0,0 +1,22 @@ +# Settings for TFT model. + +# Common settings for DL models +include "configs/prediction_models/common/DLCommon.gin" + +# Optimizer params +train_common.model = @TFTpytorch + +optimizer/hyperparameter.class_to_tune = @Adam +optimizer/hyperparameter.weight_decay = 1e-6 +#optimizer/hyperparameter.lr = (1e-5, 3e-4) + +# Encoder params +model/hyperparameter.class_to_tune = @TFTpytorch +model/hyperparameter.hidden = (32, 256, "log-uniform", 2) +model/hyperparameter.num_classes = %NUM_CLASSES +model/hyperparameter.dropout = (0.0, 0.4) +model/hyperparameter.dropout_att = (0.0, 0.4) +model/hyperparameter.n_heads =1 +model/hyperparameter.lr = (1e-5, 3e-4) +PredictionDatasetTFTpytorch.max_encoder_length = 24 +PredictionDatasetTFTpytorch.max_prediction_length = 48 From 760a25dc54ca9ad982b9629d68cbad9517045405 Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 11 Jul 2023 15:19:51 +0200 Subject: [PATCH 034/142] created own set_weights for tft pytorch --- icu_benchmarks/models/dl_models.py | 17 +++++++++++++---- icu_benchmarks/models/wrappers.py | 1 - 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index e5df2793..ebc78ec4 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -6,7 +6,7 @@ from icu_benchmarks.models.layers import TransformerBlock, LocalBlock, TemporalBlock, PositionalEncoding,LazyEmbedding,StaticCovariateEncoder,TFTBack from typing import Dict from icu_benchmarks.models.wrappers import DLPredictionWrapper -from torch import Tensor,cat,jit +from torch import Tensor,cat,jit,FloatTensor from pytorch_forecasting import TemporalFusionTransformer from pytorch_forecasting.metrics import QuantileLoss @@ -363,8 +363,9 @@ class TFTpytorch(DLPredictionWrapper): """ supported_run_modes = [RunMode.classification, RunMode.regression] def __init__(self,dataset,hidden,dropout, - n_heads,dropout_att,lr,optimizer,*args,**kwargs): - self.device=device + n_heads,dropout_att,lr,optimizer,*args,device='cpu',**kwargs): + + TemporalFusionTransformer.from_dataset( dataset, learning_rate=lr, @@ -375,4 +376,12 @@ def __init__(self,dataset,hidden,dropout, loss=QuantileLoss(), optimizer=optimizer, -) \ No newline at end of file +) + + def set_weight(self, weight, dataset): + """Set the weight for the loss function.""" + if isinstance(weight, list): + weight = FloatTensor(weight) + elif weight == "balanced": + weight = FloatTensor(dataset.get_balance()) + self.loss_weights = weight \ No newline at end of file diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 5872acb3..bf9452b1 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -187,7 +187,6 @@ class DLPredictionWrapper(DLWrapper): def set_weight(self, weight, dataset): """Set the weight for the loss function.""" - if isinstance(weight, list): weight = torch.FloatTensor(weight).to(self.device) elif weight == "balanced": From a356df61115357a176938d35d52d0e439c23800f Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 11 Jul 2023 15:24:23 +0200 Subject: [PATCH 035/142] overrode get_features_names for tftpytorch dataloader --- icu_benchmarks/data/loader.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 2f13814b..16be3676 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -388,7 +388,7 @@ def __init__(self, self.args=args self.ram_cache=ram_cache self.kwargs=kwargs - + self.column_names=features.columns super().__init__(data=self.data, time_idx="time_idx", target="label", @@ -438,6 +438,9 @@ def get_balance(self) -> list: return list((1/counts[1]) * counts[1].sum() / counts[0].shape[0]) + def get_feature_names(self): + + return self.column_names \ No newline at end of file From 2e125928dce146855bb6b2ef5eb88ebca9fbcf05 Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 18 Jul 2023 13:24:11 +0200 Subject: [PATCH 036/142] fixed datatypes for old tft and working on new tft --- configs/prediction_models/TFT.gin | 2 +- configs/prediction_models/TFTpytorch.gin | 6 +- icu_benchmarks/cross_validation.py | 1 + icu_benchmarks/data/loader.py | 6 +- icu_benchmarks/models/dl_models.py | 93 +++++++++++++++++++----- icu_benchmarks/models/layers.py | 3 +- icu_benchmarks/models/train.py | 45 +++++++----- icu_benchmarks/models/wrappers.py | 21 ++++-- 8 files changed, 127 insertions(+), 50 deletions(-) diff --git a/configs/prediction_models/TFT.gin b/configs/prediction_models/TFT.gin index d9bbe607..d03b8c21 100644 --- a/configs/prediction_models/TFT.gin +++ b/configs/prediction_models/TFT.gin @@ -13,7 +13,7 @@ optimizer/hyperparameter.lr = (1e-5, 3e-4) # Encoder params model/hyperparameter.class_to_tune = @TFT model/hyperparameter.encoder_length = 24 -model/hyperparameter.hidden = (32, 256, "log-uniform", 2) +model/hyperparameter.hidden = 12 model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.dropout = (0.0, 0.4) model/hyperparameter.dropout_att = (0.0, 0.4) diff --git a/configs/prediction_models/TFTpytorch.gin b/configs/prediction_models/TFTpytorch.gin index 77649f97..4a618903 100644 --- a/configs/prediction_models/TFTpytorch.gin +++ b/configs/prediction_models/TFTpytorch.gin @@ -18,5 +18,7 @@ model/hyperparameter.dropout = (0.0, 0.4) model/hyperparameter.dropout_att = (0.0, 0.4) model/hyperparameter.n_heads =1 model/hyperparameter.lr = (1e-5, 3e-4) -PredictionDatasetTFTpytorch.max_encoder_length = 24 -PredictionDatasetTFTpytorch.max_prediction_length = 48 +PredictionDatasetTFTpytorch.max_encoder_length = 6 +PredictionDatasetTFTpytorch.max_prediction_length = 12 + + diff --git a/icu_benchmarks/cross_validation.py b/icu_benchmarks/cross_validation.py index 89a98864..43e40bf1 100644 --- a/icu_benchmarks/cross_validation.py +++ b/icu_benchmarks/cross_validation.py @@ -101,6 +101,7 @@ def execute_repeated_cv( verbose=verbose, use_wandb=wandb, ) + train_time = datetime.now() - start_time log_full_line( diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 16be3676..82b34c08 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -191,6 +191,8 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: tensors[0].append(self.features_df.loc[stay_id:stay_id][var].to_numpy()) elif var == 'age' or var== 'height' or var== 'weight': tensors[1].append(self.features_df.loc[stay_id:stay_id][var].to_numpy()) + elif "MissingIndicator" in var: + tensors[4].append(self.features_df.loc[stay_id:stay_id][var].to_numpy()) else : tensors[5].append(self.features_df.loc[stay_id:stay_id][var].to_numpy()) @@ -212,7 +214,7 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: # window shorter than the longest window in dataset, pad to same length tensors[0]= np.concatenate([tensors[0], np.ones(( np.shape(tensors[0])[0],self.maxlen - np.shape(tensors[0])[1])) * pad_value], axis=1) tensors[1]= np.concatenate([tensors[1], np.ones(( np.shape(tensors[1])[0],self.maxlen - np.shape(tensors[1])[1])) * pad_value], axis=1) - + tensors[4]= np.concatenate([tensors[4], np.ones(( np.shape(tensors[4])[0],self.maxlen - np.shape(tensors[4])[1])) * pad_value], axis=1) tensors[5]= np.concatenate([tensors[5], np.ones(( np.shape(tensors[5])[0],self.maxlen - np.shape(tensors[5])[1])) * pad_value], axis=1) @@ -395,7 +397,7 @@ def __init__(self, group_ids=["stay_id"], min_encoder_length=max_encoder_length // 2, max_encoder_length=max_encoder_length, - min_prediction_length=1, + min_prediction_length=2, max_prediction_length=max_prediction_length, static_categoricals=[], static_reals=["height", "weight","age","sex"], diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index ebc78ec4..500b6015 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -7,7 +7,7 @@ from typing import Dict from icu_benchmarks.models.wrappers import DLPredictionWrapper from torch import Tensor,cat,jit,FloatTensor -from pytorch_forecasting import TemporalFusionTransformer +from pytorch_forecasting import TemporalFusionTransformer,TimeSeriesDataSet from pytorch_forecasting.metrics import QuantileLoss @@ -295,8 +295,8 @@ class TFT(DLPredictionWrapper): _supported_run_modes = [RunMode.classification, RunMode.regression] def __init__(self,num_classes, encoder_length,hidden,dropout, n_heads,dropout_att,example_length,*args,quantiles=[0.1, 0.5, 0.9],static_categorical_inp_size=[2],temporal_known_categorical_inp_size=[], - temporal_observed_categorical_inp_size=[],static_continuous_inp_size=3,temporal_known_continuous_inp_size=0, - temporal_observed_continuous_inp_size=96,temporal_target_size=1,**kwargs): + temporal_observed_categorical_inp_size=[48],static_continuous_inp_size=3,temporal_known_continuous_inp_size=0, + temporal_observed_continuous_inp_size=48,temporal_target_size=1,**kwargs): #derived variables num_static_vars=len(static_categorical_inp_size)+static_continuous_inp_size num_future_vars=len(temporal_known_categorical_inp_size)+temporal_known_continuous_inp_size @@ -309,7 +309,7 @@ def __init__(self,num_classes, encoder_length,hidden,dropout, super().__init__(num_classes=num_classes, encoder_length=encoder_length,hidden=hidden, n_heads=n_heads,dropout_att=dropout_att,example_length=example_length,quantiles=quantiles, num_static_vars=num_static_vars,num_future_vars=num_future_vars,num_historic_vars=num_historic_vars,*args,static_categorical_inp_size=1,temporal_known_categorical_inp_size=0, - temporal_observed_categorical_inp_size=0,static_continuous_inp_size=3,temporal_known_continuous_inp_size=0, + temporal_observed_categorical_inp_size=48,static_continuous_inp_size=3,temporal_known_continuous_inp_size=0, temporal_observed_continuous_inp_size=48,temporal_target_size=1,**kwargs) @@ -355,33 +355,86 @@ def forward(self, x: Dict[str, Tensor]) -> Tensor: o=self.TFTpart2(historical_inputs, cs, ch, cc, ce, future_inputs.to(historical_inputs.device)) pred = self.logit(o) return pred - + @gin.configurable -class TFTpytorch(DLPredictionWrapper): - """ +class TFTpytorch(TemporalFusionTransformer,DLPredictionWrapper): + """ Implementation of https://arxiv.org/abs/1912.09363 """ supported_run_modes = [RunMode.classification, RunMode.regression] def __init__(self,dataset,hidden,dropout, - n_heads,dropout_att,lr,optimizer,*args,device='cpu',**kwargs): + n_heads,dropout_att,lr,optimizer,num_classes,*args,**kwargs): + DLPredictionWrapper.__init__(self,lr=lr,optimizer=optimizer,*args,**kwargs) - TemporalFusionTransformer.from_dataset( - dataset, - learning_rate=lr, - hidden_size=hidden, - attention_head_size=n_heads, - dropout=dropout, - hidden_continuous_size=hidden, - loss=QuantileLoss(), - optimizer=optimizer, + TemporalFusionTransformer.__init__(self) + + self.model=TemporalFusionTransformer.from_dataset(dataset=dataset,hidden_size=hidden,dropout=dropout, + attention_head_size=n_heads,learning_rate=lr,optimizer=optimizer,loss=QuantileLoss(),hidden_continuous_size=hidden) + self.hparams.update(self.model.hparams) + + #for key in model.hparams.keys(): + # self.hparams[key]=model.hparams[key] + # del self.hparams["input_shape"] + # del self.hparams["input_size"] + #print(self.hparams) + # print(self.m) + self.logit = nn.Linear(7, num_classes) + def set_weight(self, weight, dataset): + """ + Set the weight for the loss function + """ + if isinstance(weight, list): + weight = FloatTensor(weight) + elif weight == "balanced": + weight = FloatTensor(dataset.get_balance()) + self.loss_weights = weight + + def test_step(self, batch, batch_idx): + x, y = batch + log, out = self.step(x, y, batch_idx) + print("finally here") + log.update(self.create_log(x, y, out, batch_idx)) + self.testing_step_outputs.append(log) + self.log(f"test/loss", log, on_step=False, on_epoch=True, sync_dist=True) + + return log + + +""" +@gin.configurable +class TFTpytorch(TemporalFusionTransformer,DLPredictionWrapper): + + Implementation of https://arxiv.org/abs/1912.09363 + + supported_run_modes = [RunMode.classification, RunMode.regression] + def __init__(self,dataset,hidden,dropout, + n_heads,dropout_att,lr,optimizer,num_classes,*args,**kwargs): + + DLPredictionWrapper.__init__(self,lr=lr,optimizer=optimizer,*args,**kwargs) + self.model=TemporalFusionTransformer.from_dataset(dataset) -) + def set_weight(self, weight, dataset): - """Set the weight for the loss function.""" + + Set the weight for the loss function + if isinstance(weight, list): weight = FloatTensor(weight) elif weight == "balanced": weight = FloatTensor(dataset.get_balance()) - self.loss_weights = weight \ No newline at end of file + self.loss_weights = weight + + def test_step(self, batch, batch_idx): + x, y = batch + log, out = self.model.step(x, y, batch_idx) + log.update(self.model.create_log(x, y, out, batch_idx)) + self.testing_step_outputs.append(log) + self.model.log(f"test/loss", log, on_step=False, on_epoch=True, sync_dist=True) + + return log + def forward(self, x: Dict[str,Tensor]) -> Dict[str, Tensor]: + return self.model.forward(x) + +""" \ No newline at end of file diff --git a/icu_benchmarks/models/layers.py b/icu_benchmarks/models/layers.py index 016cb366..79ee0e61 100644 --- a/icu_benchmarks/models/layers.py +++ b/icu_benchmarks/models/layers.py @@ -540,6 +540,7 @@ def forward(self, x: Dict[str, Tensor]): self.t_cat_k_embed, self.t_cont_k_embedding_vectors, self.t_cont_k_embedding_bias) + t_observed_inp = self._apply_embedding(t_cat_o_inp, t_cont_o_inp, self.t_cat_o_embed, @@ -756,7 +757,7 @@ def forward(self, historical_inputs, cs, ch, cc, ce, future_inputs): enriched = self.enrichment_grn(temporal_features, c=ce) # Temporal self attention - x, _ = self.attention(enriched) + x, attn_prob= self.attention(enriched) # Don't compute hictorical quantiles x = x[:, self.encoder_length:, :] diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 21334a67..fdd753a9 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -8,7 +8,7 @@ from pytorch_lightning.loggers import TensorBoardLogger from pytorch_lightning import Trainer from pytorch_lightning.callbacks import EarlyStopping, ModelCheckpoint, TQDMProgressBar -from pytorch_forecasting import TimeSeriesDataSet +from pytorch_forecasting import TimeSeriesDataSet,TemporalFusionTransformer from pathlib import Path from icu_benchmarks.data.loader import PredictionDataset, ImputationDataset,PredictionDatasetTFT,PredictionDatasetTFTpytorch from icu_benchmarks.models.utils import save_config_file, JSONMetricsLogger @@ -43,7 +43,7 @@ def train_common( test_on: str = Split.test, use_wandb: bool = False, cpu: bool = False, - verbose=False, + verbose=True, ram_cache=False, num_workers: int = min(cpu_core_count, torch.cuda.device_count() * 4 * int(torch.cuda.is_available()), 32), ): @@ -84,14 +84,20 @@ def train_common( val_dataset = dataset_class(data, split=Split.val, ram_cache=ram_cache) train_dataset, val_dataset = assure_minimum_length(train_dataset), assure_minimum_length(val_dataset) batch_size = min(batch_size, len(train_dataset), len(val_dataset)) - + test_dataset = dataset_class(data, split=test_on) logging.debug(f"Training on {len(train_dataset)} samples and validating on {len(val_dataset)} samples.") logging.info(f"Using {num_workers} workers for data loading.") if(model.__name__ =='TFTpytorch'): train_loader = train_dataset.to_dataloader(train=True, batch_size=batch_size, num_workers=num_workers,pin_memory=True,drop_last=True) val_loader = val_dataset.to_dataloader(train=False, batch_size=batch_size ,num_workers=num_workers,pin_memory=True,drop_last=True) - model=model(dataset=train_dataset,optimizer=optimizer, epochs=epochs, run_mode=mode) + test_loader = test_dataset.to_dataloader(train=False, batch_size=min(batch_size * 4, len(test_dataset)), num_workers=num_workers,pin_memory=True,drop_last=True,shuffle=False) + model=model(train_dataset,optimizer=optimizer, epochs=epochs, run_mode=mode) + model=model.model + + + + else: train_loader = DataLoader( train_dataset, @@ -143,7 +149,7 @@ def train_common( max_epochs=epochs if model.needs_training else 1, callbacks=callbacks, precision=precision, - accelerator="auto" if not cpu else "cpu", + accelerator= "cpu", devices=max(torch.cuda.device_count(), 1), deterministic=reproducible, benchmark=not reproducible, @@ -151,6 +157,8 @@ def train_common( logger=loggers, num_sanity_val_steps=0, ) + # print(trainer.test(model, dataloaders=test_loader, verbose=verbose)) + # test_loss = trainer.test(model.model, dataloaders=test_loader, verbose=verbose)[0]["RMSE"] if model.needs_fit: logging.info("Fitting model to data.") @@ -160,25 +168,28 @@ def train_common( if model.needs_training: logging.info("Training model.") + + trainer.fit(model, train_dataloaders=train_loader, val_dataloaders=val_loader) logging.info("Training complete.") - test_dataset = dataset_class(data, split=test_on) test_dataset = assure_minimum_length(test_dataset) - test_loader = ( - DataLoader( - test_dataset, - batch_size=min(batch_size * 4, len(test_dataset)), - shuffle=False, - num_workers=num_workers, - pin_memory=True, - drop_last=True, - ) + if(type(model).__name__ =='TFTpytorch'): + test_loader = test_dataset.to_dataloader(train=False, batch_size=min(batch_size * 4, len(test_dataset)), num_workers=num_workers,pin_memory=True,drop_last=True,shuffle=False) + else: + + test_loader = ( + DataLoader( + test_dataset, + batch_size=min(batch_size * 4, len(test_dataset)), + shuffle=False, + num_workers=num_workers, + pin_memory=True, + drop_last=True, + ) if model.needs_training else DataLoader([test_dataset.to_tensor()], batch_size=1) ) - - model.set_weight("balanced", train_dataset) test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0]["test/loss"] save_config_file(log_dir) return test_loss diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index bf9452b1..eb2c5db2 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -243,15 +243,19 @@ def step_fn(self, element, step_prefix=""): if(key=='target'): labels=value.squeeze() data[key] = value - + + elif len(element) == 2: - data, labels = element[0], element[1].to(self.device) - if isinstance(data, list): - for i in range(len(data)): - data[i] = data[i].float().to(self.device) + if(isinstance(element[1] ,tuple)): + data, labels = element[0], element[1] else: - data = data.float().to(self.device) - mask = torch.ones_like(labels).bool() + data, labels = element[0], element[1].to(self.device) + if isinstance(data, list): + for i in range(len(data)): + data[i] = data[i].float().to(self.device) + else: + data = data.float().to(self.device) + mask = torch.ones_like(labels).bool() elif len(element) == 3: data, labels, mask = element[0], element[1].to(self.device), element[2].to(self.device) @@ -262,7 +266,10 @@ def step_fn(self, element, step_prefix=""): data = data.float().to(self.device) else: raise Exception("Loader should return either (data, label) or (data, label, mask)") + out = self(data) + + if len(out) == 2 and isinstance(out, tuple): out, aux_loss = out else: From c0dd05ff70632764b74cd5d92c0cf8acc0af91e0 Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 18 Jul 2023 13:24:11 +0200 Subject: [PATCH 037/142] fixed datatypes for old tft and working on new tft --- configs/prediction_models/TFT.gin | 2 +- configs/prediction_models/TFTpytorch.gin | 6 +- icu_benchmarks/cross_validation.py | 1 + icu_benchmarks/data/loader.py | 6 +- icu_benchmarks/models/dl_models.py | 93 +++++++++++++++++++----- icu_benchmarks/models/layers.py | 3 +- icu_benchmarks/models/train.py | 45 +++++++----- icu_benchmarks/models/wrappers.py | 21 ++++-- 8 files changed, 127 insertions(+), 50 deletions(-) diff --git a/configs/prediction_models/TFT.gin b/configs/prediction_models/TFT.gin index d9bbe607..d03b8c21 100644 --- a/configs/prediction_models/TFT.gin +++ b/configs/prediction_models/TFT.gin @@ -13,7 +13,7 @@ optimizer/hyperparameter.lr = (1e-5, 3e-4) # Encoder params model/hyperparameter.class_to_tune = @TFT model/hyperparameter.encoder_length = 24 -model/hyperparameter.hidden = (32, 256, "log-uniform", 2) +model/hyperparameter.hidden = 12 model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.dropout = (0.0, 0.4) model/hyperparameter.dropout_att = (0.0, 0.4) diff --git a/configs/prediction_models/TFTpytorch.gin b/configs/prediction_models/TFTpytorch.gin index 77649f97..4a618903 100644 --- a/configs/prediction_models/TFTpytorch.gin +++ b/configs/prediction_models/TFTpytorch.gin @@ -18,5 +18,7 @@ model/hyperparameter.dropout = (0.0, 0.4) model/hyperparameter.dropout_att = (0.0, 0.4) model/hyperparameter.n_heads =1 model/hyperparameter.lr = (1e-5, 3e-4) -PredictionDatasetTFTpytorch.max_encoder_length = 24 -PredictionDatasetTFTpytorch.max_prediction_length = 48 +PredictionDatasetTFTpytorch.max_encoder_length = 6 +PredictionDatasetTFTpytorch.max_prediction_length = 12 + + diff --git a/icu_benchmarks/cross_validation.py b/icu_benchmarks/cross_validation.py index 89a98864..43e40bf1 100644 --- a/icu_benchmarks/cross_validation.py +++ b/icu_benchmarks/cross_validation.py @@ -101,6 +101,7 @@ def execute_repeated_cv( verbose=verbose, use_wandb=wandb, ) + train_time = datetime.now() - start_time log_full_line( diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 16be3676..82b34c08 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -191,6 +191,8 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: tensors[0].append(self.features_df.loc[stay_id:stay_id][var].to_numpy()) elif var == 'age' or var== 'height' or var== 'weight': tensors[1].append(self.features_df.loc[stay_id:stay_id][var].to_numpy()) + elif "MissingIndicator" in var: + tensors[4].append(self.features_df.loc[stay_id:stay_id][var].to_numpy()) else : tensors[5].append(self.features_df.loc[stay_id:stay_id][var].to_numpy()) @@ -212,7 +214,7 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: # window shorter than the longest window in dataset, pad to same length tensors[0]= np.concatenate([tensors[0], np.ones(( np.shape(tensors[0])[0],self.maxlen - np.shape(tensors[0])[1])) * pad_value], axis=1) tensors[1]= np.concatenate([tensors[1], np.ones(( np.shape(tensors[1])[0],self.maxlen - np.shape(tensors[1])[1])) * pad_value], axis=1) - + tensors[4]= np.concatenate([tensors[4], np.ones(( np.shape(tensors[4])[0],self.maxlen - np.shape(tensors[4])[1])) * pad_value], axis=1) tensors[5]= np.concatenate([tensors[5], np.ones(( np.shape(tensors[5])[0],self.maxlen - np.shape(tensors[5])[1])) * pad_value], axis=1) @@ -395,7 +397,7 @@ def __init__(self, group_ids=["stay_id"], min_encoder_length=max_encoder_length // 2, max_encoder_length=max_encoder_length, - min_prediction_length=1, + min_prediction_length=2, max_prediction_length=max_prediction_length, static_categoricals=[], static_reals=["height", "weight","age","sex"], diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index ebc78ec4..500b6015 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -7,7 +7,7 @@ from typing import Dict from icu_benchmarks.models.wrappers import DLPredictionWrapper from torch import Tensor,cat,jit,FloatTensor -from pytorch_forecasting import TemporalFusionTransformer +from pytorch_forecasting import TemporalFusionTransformer,TimeSeriesDataSet from pytorch_forecasting.metrics import QuantileLoss @@ -295,8 +295,8 @@ class TFT(DLPredictionWrapper): _supported_run_modes = [RunMode.classification, RunMode.regression] def __init__(self,num_classes, encoder_length,hidden,dropout, n_heads,dropout_att,example_length,*args,quantiles=[0.1, 0.5, 0.9],static_categorical_inp_size=[2],temporal_known_categorical_inp_size=[], - temporal_observed_categorical_inp_size=[],static_continuous_inp_size=3,temporal_known_continuous_inp_size=0, - temporal_observed_continuous_inp_size=96,temporal_target_size=1,**kwargs): + temporal_observed_categorical_inp_size=[48],static_continuous_inp_size=3,temporal_known_continuous_inp_size=0, + temporal_observed_continuous_inp_size=48,temporal_target_size=1,**kwargs): #derived variables num_static_vars=len(static_categorical_inp_size)+static_continuous_inp_size num_future_vars=len(temporal_known_categorical_inp_size)+temporal_known_continuous_inp_size @@ -309,7 +309,7 @@ def __init__(self,num_classes, encoder_length,hidden,dropout, super().__init__(num_classes=num_classes, encoder_length=encoder_length,hidden=hidden, n_heads=n_heads,dropout_att=dropout_att,example_length=example_length,quantiles=quantiles, num_static_vars=num_static_vars,num_future_vars=num_future_vars,num_historic_vars=num_historic_vars,*args,static_categorical_inp_size=1,temporal_known_categorical_inp_size=0, - temporal_observed_categorical_inp_size=0,static_continuous_inp_size=3,temporal_known_continuous_inp_size=0, + temporal_observed_categorical_inp_size=48,static_continuous_inp_size=3,temporal_known_continuous_inp_size=0, temporal_observed_continuous_inp_size=48,temporal_target_size=1,**kwargs) @@ -355,33 +355,86 @@ def forward(self, x: Dict[str, Tensor]) -> Tensor: o=self.TFTpart2(historical_inputs, cs, ch, cc, ce, future_inputs.to(historical_inputs.device)) pred = self.logit(o) return pred - + @gin.configurable -class TFTpytorch(DLPredictionWrapper): - """ +class TFTpytorch(TemporalFusionTransformer,DLPredictionWrapper): + """ Implementation of https://arxiv.org/abs/1912.09363 """ supported_run_modes = [RunMode.classification, RunMode.regression] def __init__(self,dataset,hidden,dropout, - n_heads,dropout_att,lr,optimizer,*args,device='cpu',**kwargs): + n_heads,dropout_att,lr,optimizer,num_classes,*args,**kwargs): + DLPredictionWrapper.__init__(self,lr=lr,optimizer=optimizer,*args,**kwargs) - TemporalFusionTransformer.from_dataset( - dataset, - learning_rate=lr, - hidden_size=hidden, - attention_head_size=n_heads, - dropout=dropout, - hidden_continuous_size=hidden, - loss=QuantileLoss(), - optimizer=optimizer, + TemporalFusionTransformer.__init__(self) + + self.model=TemporalFusionTransformer.from_dataset(dataset=dataset,hidden_size=hidden,dropout=dropout, + attention_head_size=n_heads,learning_rate=lr,optimizer=optimizer,loss=QuantileLoss(),hidden_continuous_size=hidden) + self.hparams.update(self.model.hparams) + + #for key in model.hparams.keys(): + # self.hparams[key]=model.hparams[key] + # del self.hparams["input_shape"] + # del self.hparams["input_size"] + #print(self.hparams) + # print(self.m) + self.logit = nn.Linear(7, num_classes) + def set_weight(self, weight, dataset): + """ + Set the weight for the loss function + """ + if isinstance(weight, list): + weight = FloatTensor(weight) + elif weight == "balanced": + weight = FloatTensor(dataset.get_balance()) + self.loss_weights = weight + + def test_step(self, batch, batch_idx): + x, y = batch + log, out = self.step(x, y, batch_idx) + print("finally here") + log.update(self.create_log(x, y, out, batch_idx)) + self.testing_step_outputs.append(log) + self.log(f"test/loss", log, on_step=False, on_epoch=True, sync_dist=True) + + return log + + +""" +@gin.configurable +class TFTpytorch(TemporalFusionTransformer,DLPredictionWrapper): + + Implementation of https://arxiv.org/abs/1912.09363 + + supported_run_modes = [RunMode.classification, RunMode.regression] + def __init__(self,dataset,hidden,dropout, + n_heads,dropout_att,lr,optimizer,num_classes,*args,**kwargs): + + DLPredictionWrapper.__init__(self,lr=lr,optimizer=optimizer,*args,**kwargs) + self.model=TemporalFusionTransformer.from_dataset(dataset) -) + def set_weight(self, weight, dataset): - """Set the weight for the loss function.""" + + Set the weight for the loss function + if isinstance(weight, list): weight = FloatTensor(weight) elif weight == "balanced": weight = FloatTensor(dataset.get_balance()) - self.loss_weights = weight \ No newline at end of file + self.loss_weights = weight + + def test_step(self, batch, batch_idx): + x, y = batch + log, out = self.model.step(x, y, batch_idx) + log.update(self.model.create_log(x, y, out, batch_idx)) + self.testing_step_outputs.append(log) + self.model.log(f"test/loss", log, on_step=False, on_epoch=True, sync_dist=True) + + return log + def forward(self, x: Dict[str,Tensor]) -> Dict[str, Tensor]: + return self.model.forward(x) + +""" \ No newline at end of file diff --git a/icu_benchmarks/models/layers.py b/icu_benchmarks/models/layers.py index 016cb366..79ee0e61 100644 --- a/icu_benchmarks/models/layers.py +++ b/icu_benchmarks/models/layers.py @@ -540,6 +540,7 @@ def forward(self, x: Dict[str, Tensor]): self.t_cat_k_embed, self.t_cont_k_embedding_vectors, self.t_cont_k_embedding_bias) + t_observed_inp = self._apply_embedding(t_cat_o_inp, t_cont_o_inp, self.t_cat_o_embed, @@ -756,7 +757,7 @@ def forward(self, historical_inputs, cs, ch, cc, ce, future_inputs): enriched = self.enrichment_grn(temporal_features, c=ce) # Temporal self attention - x, _ = self.attention(enriched) + x, attn_prob= self.attention(enriched) # Don't compute hictorical quantiles x = x[:, self.encoder_length:, :] diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 21334a67..fdd753a9 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -8,7 +8,7 @@ from pytorch_lightning.loggers import TensorBoardLogger from pytorch_lightning import Trainer from pytorch_lightning.callbacks import EarlyStopping, ModelCheckpoint, TQDMProgressBar -from pytorch_forecasting import TimeSeriesDataSet +from pytorch_forecasting import TimeSeriesDataSet,TemporalFusionTransformer from pathlib import Path from icu_benchmarks.data.loader import PredictionDataset, ImputationDataset,PredictionDatasetTFT,PredictionDatasetTFTpytorch from icu_benchmarks.models.utils import save_config_file, JSONMetricsLogger @@ -43,7 +43,7 @@ def train_common( test_on: str = Split.test, use_wandb: bool = False, cpu: bool = False, - verbose=False, + verbose=True, ram_cache=False, num_workers: int = min(cpu_core_count, torch.cuda.device_count() * 4 * int(torch.cuda.is_available()), 32), ): @@ -84,14 +84,20 @@ def train_common( val_dataset = dataset_class(data, split=Split.val, ram_cache=ram_cache) train_dataset, val_dataset = assure_minimum_length(train_dataset), assure_minimum_length(val_dataset) batch_size = min(batch_size, len(train_dataset), len(val_dataset)) - + test_dataset = dataset_class(data, split=test_on) logging.debug(f"Training on {len(train_dataset)} samples and validating on {len(val_dataset)} samples.") logging.info(f"Using {num_workers} workers for data loading.") if(model.__name__ =='TFTpytorch'): train_loader = train_dataset.to_dataloader(train=True, batch_size=batch_size, num_workers=num_workers,pin_memory=True,drop_last=True) val_loader = val_dataset.to_dataloader(train=False, batch_size=batch_size ,num_workers=num_workers,pin_memory=True,drop_last=True) - model=model(dataset=train_dataset,optimizer=optimizer, epochs=epochs, run_mode=mode) + test_loader = test_dataset.to_dataloader(train=False, batch_size=min(batch_size * 4, len(test_dataset)), num_workers=num_workers,pin_memory=True,drop_last=True,shuffle=False) + model=model(train_dataset,optimizer=optimizer, epochs=epochs, run_mode=mode) + model=model.model + + + + else: train_loader = DataLoader( train_dataset, @@ -143,7 +149,7 @@ def train_common( max_epochs=epochs if model.needs_training else 1, callbacks=callbacks, precision=precision, - accelerator="auto" if not cpu else "cpu", + accelerator= "cpu", devices=max(torch.cuda.device_count(), 1), deterministic=reproducible, benchmark=not reproducible, @@ -151,6 +157,8 @@ def train_common( logger=loggers, num_sanity_val_steps=0, ) + # print(trainer.test(model, dataloaders=test_loader, verbose=verbose)) + # test_loss = trainer.test(model.model, dataloaders=test_loader, verbose=verbose)[0]["RMSE"] if model.needs_fit: logging.info("Fitting model to data.") @@ -160,25 +168,28 @@ def train_common( if model.needs_training: logging.info("Training model.") + + trainer.fit(model, train_dataloaders=train_loader, val_dataloaders=val_loader) logging.info("Training complete.") - test_dataset = dataset_class(data, split=test_on) test_dataset = assure_minimum_length(test_dataset) - test_loader = ( - DataLoader( - test_dataset, - batch_size=min(batch_size * 4, len(test_dataset)), - shuffle=False, - num_workers=num_workers, - pin_memory=True, - drop_last=True, - ) + if(type(model).__name__ =='TFTpytorch'): + test_loader = test_dataset.to_dataloader(train=False, batch_size=min(batch_size * 4, len(test_dataset)), num_workers=num_workers,pin_memory=True,drop_last=True,shuffle=False) + else: + + test_loader = ( + DataLoader( + test_dataset, + batch_size=min(batch_size * 4, len(test_dataset)), + shuffle=False, + num_workers=num_workers, + pin_memory=True, + drop_last=True, + ) if model.needs_training else DataLoader([test_dataset.to_tensor()], batch_size=1) ) - - model.set_weight("balanced", train_dataset) test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0]["test/loss"] save_config_file(log_dir) return test_loss diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index bf9452b1..eb2c5db2 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -243,15 +243,19 @@ def step_fn(self, element, step_prefix=""): if(key=='target'): labels=value.squeeze() data[key] = value - + + elif len(element) == 2: - data, labels = element[0], element[1].to(self.device) - if isinstance(data, list): - for i in range(len(data)): - data[i] = data[i].float().to(self.device) + if(isinstance(element[1] ,tuple)): + data, labels = element[0], element[1] else: - data = data.float().to(self.device) - mask = torch.ones_like(labels).bool() + data, labels = element[0], element[1].to(self.device) + if isinstance(data, list): + for i in range(len(data)): + data[i] = data[i].float().to(self.device) + else: + data = data.float().to(self.device) + mask = torch.ones_like(labels).bool() elif len(element) == 3: data, labels, mask = element[0], element[1].to(self.device), element[2].to(self.device) @@ -262,7 +266,10 @@ def step_fn(self, element, step_prefix=""): data = data.float().to(self.device) else: raise Exception("Loader should return either (data, label) or (data, label, mask)") + out = self(data) + + if len(out) == 2 and isinstance(out, tuple): out, aux_loss = out else: From 9e52574adfa1980fe53ffa09b7a8c782629b410e Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 1 Aug 2023 12:51:17 +0200 Subject: [PATCH 038/142] latest changes --- configs/prediction_models/TFT.gin | 4 +- configs/prediction_models/TFTpytorch.gin | 6 +- experiments/demo_benchmark_classification.yml | 22 +++--- experiments/demo_benchmark_regression.yml | 12 ++-- icu_benchmarks/data/loader.py | 6 +- icu_benchmarks/models/dl_models.py | 71 +++---------------- icu_benchmarks/models/train.py | 32 +++++---- icu_benchmarks/models/wrappers.py | 10 +-- 8 files changed, 58 insertions(+), 105 deletions(-) diff --git a/configs/prediction_models/TFT.gin b/configs/prediction_models/TFT.gin index d03b8c21..3abcb239 100644 --- a/configs/prediction_models/TFT.gin +++ b/configs/prediction_models/TFT.gin @@ -13,11 +13,11 @@ optimizer/hyperparameter.lr = (1e-5, 3e-4) # Encoder params model/hyperparameter.class_to_tune = @TFT model/hyperparameter.encoder_length = 24 -model/hyperparameter.hidden = 12 +model/hyperparameter.hidden = 256 model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.dropout = (0.0, 0.4) model/hyperparameter.dropout_att = (0.0, 0.4) -model/hyperparameter.n_heads =1 +model/hyperparameter.n_heads =4 model/hyperparameter.example_length=25 diff --git a/configs/prediction_models/TFTpytorch.gin b/configs/prediction_models/TFTpytorch.gin index 4a618903..8c320809 100644 --- a/configs/prediction_models/TFTpytorch.gin +++ b/configs/prediction_models/TFTpytorch.gin @@ -12,13 +12,13 @@ optimizer/hyperparameter.weight_decay = 1e-6 # Encoder params model/hyperparameter.class_to_tune = @TFTpytorch -model/hyperparameter.hidden = (32, 256, "log-uniform", 2) +model/hyperparameter.hidden = 128 model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.dropout = (0.0, 0.4) model/hyperparameter.dropout_att = (0.0, 0.4) -model/hyperparameter.n_heads =1 +model/hyperparameter.n_heads =4 model/hyperparameter.lr = (1e-5, 3e-4) -PredictionDatasetTFTpytorch.max_encoder_length = 6 +PredictionDatasetTFTpytorch.max_encoder_length = 24 PredictionDatasetTFTpytorch.max_prediction_length = 12 diff --git a/experiments/demo_benchmark_classification.yml b/experiments/demo_benchmark_classification.yml index 58b14a45..7adac471 100644 --- a/experiments/demo_benchmark_classification.yml +++ b/experiments/demo_benchmark_classification.yml @@ -17,21 +17,21 @@ name: yaib_demo_classification_benchmark parameters: data_dir: values: - - demo_data/mortality24/eicu_demo - - demo_data/mortality24/mimic_demo + # - demo_data/mortality24/eicu_demo + # - demo_data/mortality24/mimic_demo - demo_data/aki/eicu_demo - - demo_data/aki/mimic_demo - - demo_data/sepsis/eicu_demo + # - demo_data/aki/mimic_demo + # - demo_data/sepsis/eicu_demo - demo_data/sepsis/mimic_demo model: values: - # - LogisticRegression - # - LGBMClassifier - # - GRU - # - LSTM - # - TCN - # - Transformer - - TemporalFusionTransformer + - LogisticRegression + - LGBMClassifier + - GRU + - LSTM + - TCN + - Transformer + - TFT seed: values: - 1111 diff --git a/experiments/demo_benchmark_regression.yml b/experiments/demo_benchmark_regression.yml index 047f0c88..57f380b8 100644 --- a/experiments/demo_benchmark_regression.yml +++ b/experiments/demo_benchmark_regression.yml @@ -23,12 +23,12 @@ parameters: # - demo_data/kf/mimic_demo model: values: - # - ElasticNet - # - LGBMRegressor - # - GRU - # - LSTM - # - TCN - - TemporalFusionTransformer + - ElasticNet + - LGBMRegressor + - GRU + - LSTM + - TCN + - TFT seed: values: - 1111 diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 82b34c08..a183eb41 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -379,8 +379,9 @@ def __init__(self, max_prediction_length: int, max_encoder_length: int, *args, - ram_cache: bool = True, + ram_cache: bool = False, **kwargs): + data[split]["FEATURES"]["time_idx"]=((data[split]["FEATURES"]["time"]/ pd.Timedelta(seconds=3600))).astype(int) data=data.get(split) labels = data["OUTCOME"] @@ -428,7 +429,8 @@ def __init__(self, ], add_relative_time_idx=True, add_target_scales=True, - add_encoder_length=True,) + add_encoder_length=True, + ) def get_balance(self) -> list: """Return the weight balance for the split of interest. diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 500b6015..d6e075af 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -357,7 +357,7 @@ def forward(self, x: Dict[str, Tensor]) -> Tensor: return pred @gin.configurable -class TFTpytorch(TemporalFusionTransformer,DLPredictionWrapper): +class TFTpytorch(DLPredictionWrapper): """ Implementation of https://arxiv.org/abs/1912.09363 """ @@ -365,76 +365,25 @@ class TFTpytorch(TemporalFusionTransformer,DLPredictionWrapper): def __init__(self,dataset,hidden,dropout, n_heads,dropout_att,lr,optimizer,num_classes,*args,**kwargs): - DLPredictionWrapper.__init__(self,lr=lr,optimizer=optimizer,*args,**kwargs) - - TemporalFusionTransformer.__init__(self) - + super().__init__(lr=lr,optimizer=optimizer,*args,**kwargs) self.model=TemporalFusionTransformer.from_dataset(dataset=dataset,hidden_size=hidden,dropout=dropout, attention_head_size=n_heads,learning_rate=lr,optimizer=optimizer,loss=QuantileLoss(),hidden_continuous_size=hidden) - self.hparams.update(self.model.hparams) - - #for key in model.hparams.keys(): - # self.hparams[key]=model.hparams[key] - # del self.hparams["input_shape"] - # del self.hparams["input_size"] - #print(self.hparams) - # print(self.m) + self.logit = nn.Linear(7, num_classes) - def set_weight(self, weight, dataset): - """ - Set the weight for the loss function - """ - if isinstance(weight, list): - weight = FloatTensor(weight) - elif weight == "balanced": - weight = FloatTensor(dataset.get_balance()) - self.loss_weights = weight - - def test_step(self, batch, batch_idx): - x, y = batch - log, out = self.step(x, y, batch_idx) - print("finally here") - log.update(self.create_log(x, y, out, batch_idx)) - self.testing_step_outputs.append(log) - self.log(f"test/loss", log, on_step=False, on_epoch=True, sync_dist=True) - - return log - - -""" -@gin.configurable -class TFTpytorch(TemporalFusionTransformer,DLPredictionWrapper): - - Implementation of https://arxiv.org/abs/1912.09363 - - supported_run_modes = [RunMode.classification, RunMode.regression] - def __init__(self,dataset,hidden,dropout, - n_heads,dropout_att,lr,optimizer,num_classes,*args,**kwargs): - DLPredictionWrapper.__init__(self,lr=lr,optimizer=optimizer,*args,**kwargs) - self.model=TemporalFusionTransformer.from_dataset(dataset) - - def set_weight(self, weight, dataset): - + """ Set the weight for the loss function - + """ if isinstance(weight, list): weight = FloatTensor(weight) elif weight == "balanced": weight = FloatTensor(dataset.get_balance()) self.loss_weights = weight + def forward(self,x): + out= self.model(x) + pred = self.logit(out["prediction"]) + return pred - def test_step(self, batch, batch_idx): - x, y = batch - log, out = self.model.step(x, y, batch_idx) - log.update(self.model.create_log(x, y, out, batch_idx)) - self.testing_step_outputs.append(log) - self.model.log(f"test/loss", log, on_step=False, on_epoch=True, sync_dist=True) - - return log - def forward(self, x: Dict[str,Tensor]) -> Dict[str, Tensor]: - return self.model.forward(x) - -""" \ No newline at end of file + \ No newline at end of file diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index fdd753a9..d833b5e7 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -15,6 +15,7 @@ from icu_benchmarks.contants import RunMode from icu_benchmarks.data.constants import DataSplit as Split from collections import OrderedDict +import pickle cpu_core_count = len(os.sched_getaffinity(0)) if hasattr(os, "sched_getaffinity") else os.cpu_count() @@ -70,8 +71,12 @@ def train_common( verbose: Enable detailed logging. ram_cache: Whether to cache the data in RAM. num_workers: Number of workers to use for data loading. + with open('saved_dictionary.pkl', 'wb') as f: + pickle.dump(data, f) + data["train"]["OUTCOME"].to_csv('outcome.csv', index=False) + data["train"]["FEATURES"].to_csv('features.csv', index=False) """ - + logging.info(f"Training model: {model.__name__}.") dataset_class = (ImputationDataset if mode == RunMode.imputation else (PredictionDatasetTFT if model.__name__ == 'TFT' @@ -89,12 +94,10 @@ def train_common( logging.info(f"Using {num_workers} workers for data loading.") if(model.__name__ =='TFTpytorch'): - train_loader = train_dataset.to_dataloader(train=True, batch_size=batch_size, num_workers=num_workers,pin_memory=True,drop_last=True) - val_loader = val_dataset.to_dataloader(train=False, batch_size=batch_size ,num_workers=num_workers,pin_memory=True,drop_last=True) - test_loader = test_dataset.to_dataloader(train=False, batch_size=min(batch_size * 4, len(test_dataset)), num_workers=num_workers,pin_memory=True,drop_last=True,shuffle=False) + train_loader = train_dataset.to_dataloader(train=True, batch_size=batch_size, num_workers=0,pin_memory=False,drop_last=True) + val_loader = val_dataset.to_dataloader(train=False, batch_size=batch_size ,num_workers=0,pin_memory=False,drop_last=True) + test_loader = test_dataset.to_dataloader(train=False, batch_size=min(batch_size * 4, len(test_dataset)), num_workers=0,pin_memory=False,drop_last=True,shuffle=False) model=model(train_dataset,optimizer=optimizer, epochs=epochs, run_mode=mode) - model=model.model - @@ -116,6 +119,7 @@ def train_common( drop_last=True, ) + if(isinstance(next(iter(train_loader))[0] ,OrderedDict)): model = model(optimizer=optimizer, epochs=epochs, run_mode=mode) @@ -123,6 +127,7 @@ def train_common( data_shape = next(iter(train_loader))[0].shape model = model(optimizer=optimizer, input_size=data_shape, epochs=epochs, run_mode=mode) + model.set_weight(weight, train_dataset) if load_weights: if source_dir.exists(): @@ -149,7 +154,7 @@ def train_common( max_epochs=epochs if model.needs_training else 1, callbacks=callbacks, precision=precision, - accelerator= "cpu", + accelerator= "auto" if not cpu else "cpu", devices=max(torch.cuda.device_count(), 1), deterministic=reproducible, benchmark=not reproducible, @@ -157,8 +162,7 @@ def train_common( logger=loggers, num_sanity_val_steps=0, ) - # print(trainer.test(model, dataloaders=test_loader, verbose=verbose)) - # test_loss = trainer.test(model.model, dataloaders=test_loader, verbose=verbose)[0]["RMSE"] + if model.needs_fit: logging.info("Fitting model to data.") @@ -168,14 +172,16 @@ def train_common( if model.needs_training: logging.info("Training model.") - - - trainer.fit(model, train_dataloaders=train_loader, val_dataloaders=val_loader) + if(type(model).__name__ =='TFTpytorch'): + trainer.fit(model, train_dataloaders=train_loader, val_dataloaders=val_loader) + else: + trainer.fit(model, train_dataloaders=train_loader, val_dataloaders=val_loader) logging.info("Training complete.") test_dataset = dataset_class(data, split=test_on) test_dataset = assure_minimum_length(test_dataset) if(type(model).__name__ =='TFTpytorch'): test_loader = test_dataset.to_dataloader(train=False, batch_size=min(batch_size * 4, len(test_dataset)), num_workers=num_workers,pin_memory=True,drop_last=True,shuffle=False) + test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0]["test/loss"] else: test_loader = ( @@ -190,6 +196,6 @@ def train_common( if model.needs_training else DataLoader([test_dataset.to_tensor()], batch_size=1) ) - test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0]["test/loss"] + test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0]["test/loss"] save_config_file(log_dir) return test_loss diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index eb2c5db2..d96d3e5a 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -96,7 +96,6 @@ class DLWrapper(BaseModule, ABC): def __init__( self, - loss=CrossEntropyLoss(), optimizer=torch.optim.Adam, run_mode: RunMode = RunMode.classification, @@ -109,7 +108,6 @@ def __init__( epochs: int = 100, input_size: torch.Tensor = None, initialization_method: str = "normal", - **kwargs, ): @@ -247,7 +245,8 @@ def step_fn(self, element, step_prefix=""): elif len(element) == 2: if(isinstance(element[1] ,tuple)): - data, labels = element[0], element[1] + data, labels = element[0], element[1][0] + mask = torch.ones_like(labels).bool() else: data, labels = element[0], element[1].to(self.device) if isinstance(data, list): @@ -266,10 +265,7 @@ def step_fn(self, element, step_prefix=""): data = data.float().to(self.device) else: raise Exception("Loader should return either (data, label) or (data, label, mask)") - out = self(data) - - if len(out) == 2 and isinstance(out, tuple): out, aux_loss = out else: @@ -290,7 +286,7 @@ def step_fn(self, element, step_prefix=""): transformed_output = self.output_transform((prediction, target)) for metric in self.metrics[step_prefix].values(): metric.update(transformed_output) - self.log(f"{step_prefix}/loss", loss, on_step=False, on_epoch=True, sync_dist=True) + self.log(f"{step_prefix}/loss", loss, on_step=False, on_epoch=True, sync_dist=True, prog_bar=True) return loss From 803d677b1ea6c27efd98af4c5e516db3f6fbad2d Mon Sep 17 00:00:00 2001 From: "youssef.mecky" Date: Tue, 8 Aug 2023 11:37:11 +0200 Subject: [PATCH 039/142] latest --- experiments/benchmark_classification.yml | 38 ++++++++++--------- experiments/benchmark_regression.yml | 18 +++++---- experiments/demo_benchmark_classification.yml | 11 +++--- icu_benchmarks/models/dl_models.py | 2 +- icu_benchmarks/models/train.py | 1 + icu_benchmarks/models/wrappers.py | 1 + 6 files changed, 39 insertions(+), 32 deletions(-) diff --git a/experiments/benchmark_classification.yml b/experiments/benchmark_classification.yml index 61763ffa..a9d999ce 100644 --- a/experiments/benchmark_classification.yml +++ b/experiments/benchmark_classification.yml @@ -17,26 +17,28 @@ name: yaib_classification_benchmark parameters: data_dir: values: - - ../data/mortality24/miiv - - ../data/mortality24/hirid - - ../data/mortality24/eicu - - ../data/mortality24/aumc - - ../data/aki/miiv - - ../data/aki/hirid - - ../data/aki/eicu - - ../data/aki/aumc - - ../data/sepsis/miiv - - ../data/sepsis/hirid - - ../data/sepsis/eicu - - ../data/sepsis/aumc + - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/aki/miiv + - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/aki/hirid + - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/aki/eicu + - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/aki/aumc + - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/morality24/miiv + - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/morality24/miiv + - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/morality24/miiv + - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/morality24/miiv + - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/sepsis/miiv + - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/sepsis/hirid + - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/sepsis/eicu + - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/sepsis/aumc model: values: - - LogisticRegression - - LGBMClassifier - - GRU - - LSTM - - TCN - - Transformer + # - LogisticRegression + # - LGBMClassifier + # - GRU + # - LSTM + # - TCN + # - Transformer + - TFT + seed: values: - 1111 diff --git a/experiments/benchmark_regression.yml b/experiments/benchmark_regression.yml index 8aa8d13e..ff23ca2e 100644 --- a/experiments/benchmark_regression.yml +++ b/experiments/benchmark_regression.yml @@ -17,14 +17,14 @@ name: yaib_regression_benchmark parameters: data_dir: values: - - ../data/los/miiv - - ../data/los/hirid - - ../data/los/eicu - - ../data/los/aumc - - ../data/kf/miiv - - ../data/kf/hirid - - ../data/kf/eicu - - ../data/kf/aumc + - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/los/miiv + - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/los/hirid + - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/los/eicu + - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/los/aumc + - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/kf/miiv + - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/kf/hirid + - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/kf/eicu + - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/kf/aumc model: values: - ElasticNet @@ -33,6 +33,8 @@ parameters: - LSTM - TCN - Transformer + - TFT + seed: values: - 1111 diff --git a/experiments/demo_benchmark_classification.yml b/experiments/demo_benchmark_classification.yml index 7adac471..341a40dd 100644 --- a/experiments/demo_benchmark_classification.yml +++ b/experiments/demo_benchmark_classification.yml @@ -17,11 +17,11 @@ name: yaib_demo_classification_benchmark parameters: data_dir: values: - # - demo_data/mortality24/eicu_demo - # - demo_data/mortality24/mimic_demo - - demo_data/aki/eicu_demo - # - demo_data/aki/mimic_demo - # - demo_data/sepsis/eicu_demo + - demo_data/mortality24/eicu_demo + - demo_data/mortality24/mimic_demo + #fails for some reason - demo_data/aki/eicu_demo + - demo_data/aki/mimic_demo + - demo_data/sepsis/eicu_demo - demo_data/sepsis/mimic_demo model: values: @@ -32,6 +32,7 @@ parameters: - TCN - Transformer - TFT + # - TFTpytorch seed: values: - 1111 diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index d6e075af..c3a1a7fd 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -368,7 +368,7 @@ def __init__(self,dataset,hidden,dropout, super().__init__(lr=lr,optimizer=optimizer,*args,**kwargs) self.model=TemporalFusionTransformer.from_dataset(dataset=dataset,hidden_size=hidden,dropout=dropout, attention_head_size=n_heads,learning_rate=lr,optimizer=optimizer,loss=QuantileLoss(),hidden_continuous_size=hidden) - + self.logit = nn.Linear(7, num_classes) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index d833b5e7..868ebe85 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -101,6 +101,7 @@ def train_common( + else: train_loader = DataLoader( train_dataset, diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index d96d3e5a..267da7fa 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -244,6 +244,7 @@ def step_fn(self, element, step_prefix=""): elif len(element) == 2: + if(isinstance(element[1] ,tuple)): data, labels = element[0], element[1][0] mask = torch.ones_like(labels).bool() From c2fcc74c14c834b9e69b342647d60e4784c79295 Mon Sep 17 00:00:00 2001 From: "youssef.mecky" Date: Mon, 14 Aug 2023 14:27:40 +0200 Subject: [PATCH 040/142] added range for layers and attention heads --- configs/prediction_models/TFT.gin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configs/prediction_models/TFT.gin b/configs/prediction_models/TFT.gin index 3abcb239..10428d9a 100644 --- a/configs/prediction_models/TFT.gin +++ b/configs/prediction_models/TFT.gin @@ -13,11 +13,11 @@ optimizer/hyperparameter.lr = (1e-5, 3e-4) # Encoder params model/hyperparameter.class_to_tune = @TFT model/hyperparameter.encoder_length = 24 -model/hyperparameter.hidden = 256 +model/hyperparameter.hidden = (16,64,128,256) model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.dropout = (0.0, 0.4) model/hyperparameter.dropout_att = (0.0, 0.4) -model/hyperparameter.n_heads =4 +model/hyperparameter.n_heads =(1,4) model/hyperparameter.example_length=25 From 5f18f72bfb569987216e43ff6a988dddbb3f6812 Mon Sep 17 00:00:00 2001 From: "youssef.mecky" Date: Mon, 14 Aug 2023 15:56:29 +0200 Subject: [PATCH 041/142] formatted and added comments --- configs/prediction_models/TFT.gin | 4 +- configs/tasks/Regression.gin | 2 +- experiments/benchmark_classification.yml | 24 +- experiments/benchmark_regression.yml | 16 +- icu_benchmarks/cross_validation.py | 2 +- icu_benchmarks/data/constants.py | 3 +- icu_benchmarks/data/loader.py | 252 +++++++++----- icu_benchmarks/imputation/diffwave.py | 2 +- icu_benchmarks/imputation/layers/s4layer.py | 143 ++++---- icu_benchmarks/imputation/sssds4.py | 2 +- icu_benchmarks/imputation/sssdsa.py | 2 +- icu_benchmarks/models/dl_models.py | 195 +++++++---- icu_benchmarks/models/layers.py | 368 +++++++++++--------- icu_benchmarks/models/metrics.py | 18 +- icu_benchmarks/models/train.py | 98 +++--- icu_benchmarks/models/utils.py | 5 - icu_benchmarks/models/wrappers.py | 31 +- 17 files changed, 687 insertions(+), 480 deletions(-) diff --git a/configs/prediction_models/TFT.gin b/configs/prediction_models/TFT.gin index 10428d9a..3abcb239 100644 --- a/configs/prediction_models/TFT.gin +++ b/configs/prediction_models/TFT.gin @@ -13,11 +13,11 @@ optimizer/hyperparameter.lr = (1e-5, 3e-4) # Encoder params model/hyperparameter.class_to_tune = @TFT model/hyperparameter.encoder_length = 24 -model/hyperparameter.hidden = (16,64,128,256) +model/hyperparameter.hidden = 256 model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.dropout = (0.0, 0.4) model/hyperparameter.dropout_att = (0.0, 0.4) -model/hyperparameter.n_heads =(1,4) +model/hyperparameter.n_heads =4 model/hyperparameter.example_length=25 diff --git a/configs/tasks/Regression.gin b/configs/tasks/Regression.gin index 5cf3f8d9..ed7f794a 100644 --- a/configs/tasks/Regression.gin +++ b/configs/tasks/Regression.gin @@ -8,7 +8,7 @@ include "configs/tasks/common/PredictionTaskVariables.gin" include "configs/tasks/common/CrossValidation.gin" # MODE SETTINGS -Run.mode = "Regression" +Run.mode = "RegressionTFT" NUM_CLASSES = 1 HORIZON = 24 train_common.weight = "balanced" diff --git a/experiments/benchmark_classification.yml b/experiments/benchmark_classification.yml index a9d999ce..e9a32b4c 100644 --- a/experiments/benchmark_classification.yml +++ b/experiments/benchmark_classification.yml @@ -17,18 +17,18 @@ name: yaib_classification_benchmark parameters: data_dir: values: - - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/aki/miiv - - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/aki/hirid - - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/aki/eicu - - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/aki/aumc - - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/morality24/miiv - - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/morality24/miiv - - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/morality24/miiv - - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/morality24/miiv - - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/sepsis/miiv - - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/sepsis/hirid - - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/sepsis/eicu - - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/sepsis/aumc + - data/YAIB_Preprint/aki/miiv + #- data/YAIB_Preprint/aki/hirid + #- data/YAIB_Preprint/aki/eicu + #- data/YAIB_Preprint/aki/aumc + - data/YAIB_Preprint/morality24/miiv + - data/YAIB_Preprint/morality24/miiv + - data/YAIB_Preprint/morality24/miiv + - data/YAIB_Preprint/morality24/miiv + #- data/YAIB_Preprint/sepsis/miiv + #- data/YAIB_Preprint/sepsis/hirid + #- data/YAIB_Preprint/sepsis/eicu + #- data/YAIB_Preprint/sepsis/aumc model: values: # - LogisticRegression diff --git a/experiments/benchmark_regression.yml b/experiments/benchmark_regression.yml index ff23ca2e..516a4b0f 100644 --- a/experiments/benchmark_regression.yml +++ b/experiments/benchmark_regression.yml @@ -17,14 +17,14 @@ name: yaib_regression_benchmark parameters: data_dir: values: - - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/los/miiv - - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/los/hirid - - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/los/eicu - - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/los/aumc - - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/kf/miiv - - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/kf/hirid - - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/kf/eicu - - /dhc/groups/icu-prediction/datasets/YAIB_Preprint/kf/aumc + - data/YAIB_Preprint/los/miiv + - data/YAIB_Preprint/los/hirid + - data/YAIB_Preprint/los/eicu + - data/YAIB_Preprint/los/aumc + - data/YAIB_Preprint/kf/miiv + - data/YAIB_Preprint/kf/hirid + - data/YAIB_Preprint/kf/eicu + - data/YAIB_Preprint/kf/aumc model: values: - ElasticNet diff --git a/icu_benchmarks/cross_validation.py b/icu_benchmarks/cross_validation.py index 43e40bf1..81d8c3ae 100644 --- a/icu_benchmarks/cross_validation.py +++ b/icu_benchmarks/cross_validation.py @@ -101,7 +101,7 @@ def execute_repeated_cv( verbose=verbose, use_wandb=wandb, ) - + train_time = datetime.now() - start_time log_full_line( diff --git a/icu_benchmarks/data/constants.py b/icu_benchmarks/data/constants.py index c7f6c635..4e4a9c34 100644 --- a/icu_benchmarks/data/constants.py +++ b/icu_benchmarks/data/constants.py @@ -16,5 +16,6 @@ class VarType: sequence = "SEQUENCE" label = "LABEL" + class FeatType: - FEAT_NAMES = ['s_cat' , 's_cont' , 'k_cat' , 'k_cont' , 'o_cat' , 'o_cont' , 'target', 'id'] + FEAT_NAMES = ["s_cat", "s_cont", "k_cat", "k_cont", "o_cat", "o_cont", "target", "id"] diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index a183eb41..c7e8bda2 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -3,7 +3,7 @@ import pandas as pd import gin import numpy as np -from torch import Tensor, cat, from_numpy, float32,empty,stack +from torch import Tensor, cat, from_numpy, float32, empty, stack from torch.utils.data import Dataset import logging from typing import Dict, Tuple @@ -13,6 +13,8 @@ from .constants import FeatType as Features from collections import OrderedDict from pytorch_forecasting import TimeSeriesDataSet + + class CommonDataset(Dataset): """Common dataset: subclass of Torch Dataset that represents the data to learn on. @@ -32,8 +34,10 @@ def __init__( self.vars = vars self.grouping_df = data[split][grouping_segment].set_index(self.vars["GROUP"]) self.features_df = ( - #drops time coulmn and sets index to stay_id - data[split][Segment.features].set_index(self.vars["GROUP"]).drop(labels=self.vars["SEQUENCE"], axis=1) + # drops time coulmn and sets index to stay_id + data[split][Segment.features] + .set_index(self.vars["GROUP"]) + .drop(labels=self.vars["SEQUENCE"], axis=1) ) # calculate basic info for the data @@ -93,7 +97,7 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: return self._cached_dataset[idx] pad_value = 0.0 - stay_id = self.outcome_df.index.unique()[idx] + stay_id = self.outcome_df.index.unique()[idx] # slice to make sure to always return a DF window = self.features_df.loc[stay_id:stay_id].to_numpy() @@ -104,8 +108,7 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: labels = np.concatenate([np.empty(window.shape[0] - 1) * np.nan, labels], axis=0) length_diff = self.maxlen - window.shape[0] - - + pad_mask = np.ones(window.shape[0]) # Padding the array to fulfill size requirement @@ -156,20 +159,23 @@ def to_tensor(self): data, labels = self.get_data_and_labels() return from_numpy(data).to(float32), from_numpy(labels).to(float32) + @gin.configurable("PredictionDatasetTFT") class PredictionDatasetTFT(PredictionDataset): """Subclass of prediction dataset for TFT as we need to define if variables are cont,static,known or observed. + We also need to feed the model the variables in a specific order Args: ram_cache (bool, optional): Whether the complete dataset should be stored in ram. Defaults to True. """ def __init__(self, *args, ram_cache: bool = True, **kwargs): - super().__init__(*args,ram_cache = True, **kwargs) - + super().__init__(*args, ram_cache=True, **kwargs) def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: """Function to sample from the data split of choice. Used for TFT. + The data needs to be given to the model in the following order + [static categorical,static contious,known catergorical,known continous, observed categorical, observed continous,target ,id] Args: idx: A specific row index to sample. @@ -181,62 +187,62 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: return self._cached_dataset[idx] pad_value = 0.0 - stay_id = self.outcome_df.index.unique()[idx] + stay_id = self.outcome_df.index.unique()[idx] - # We need to be sure that tensors are returned in the correct order to be processed correclty by tft tensors = [[] for _ in range(8)] for var in self.features_df.columns: - if var == 'sex' : + if var == "sex": tensors[0].append(self.features_df.loc[stay_id:stay_id][var].to_numpy()) - elif var == 'age' or var== 'height' or var== 'weight': + elif var == "age" or var == "height" or var == "weight": tensors[1].append(self.features_df.loc[stay_id:stay_id][var].to_numpy()) elif "MissingIndicator" in var: tensors[4].append(self.features_df.loc[stay_id:stay_id][var].to_numpy()) - else : + else: tensors[5].append(self.features_df.loc[stay_id:stay_id][var].to_numpy()) - - + tensors[6].extend(self.outcome_df.loc[stay_id:stay_id][self.vars["LABEL"]].to_numpy(dtype=float)) tensors[7].append(np.asarray([stay_id])) - window_shape0=np.shape(tensors[0])[1] + window_shape0 = np.shape(tensors[0])[1] if len(tensors[6]) == 1: # only one label per stay, align with window tensors[6] = np.concatenate([np.empty(window_shape0 - 1) * np.nan, tensors[6]], axis=0) - + length_diff = self.maxlen - window_shape0 - length_diff_id=self.maxlen - np.shape(tensors[7])[1] pad_mask = np.ones(window_shape0) # Padding the array to fulfill size requirement - + if length_diff > 0: # window shorter than the longest window in dataset, pad to same length - tensors[0]= np.concatenate([tensors[0], np.ones(( np.shape(tensors[0])[0],self.maxlen - np.shape(tensors[0])[1])) * pad_value], axis=1) - tensors[1]= np.concatenate([tensors[1], np.ones(( np.shape(tensors[1])[0],self.maxlen - np.shape(tensors[1])[1])) * pad_value], axis=1) - tensors[4]= np.concatenate([tensors[4], np.ones(( np.shape(tensors[4])[0],self.maxlen - np.shape(tensors[4])[1])) * pad_value], axis=1) - tensors[5]= np.concatenate([tensors[5], np.ones(( np.shape(tensors[5])[0],self.maxlen - np.shape(tensors[5])[1])) * pad_value], axis=1) - - - + tensors[0] = np.concatenate( + [tensors[0], np.ones((np.shape(tensors[0])[0], self.maxlen - np.shape(tensors[0])[1])) * pad_value], axis=1 + ) + tensors[1] = np.concatenate( + [tensors[1], np.ones((np.shape(tensors[1])[0], self.maxlen - np.shape(tensors[1])[1])) * pad_value], axis=1 + ) + tensors[4] = np.concatenate( + [tensors[4], np.ones((np.shape(tensors[4])[0], self.maxlen - np.shape(tensors[4])[1])) * pad_value], axis=1 + ) + tensors[5] = np.concatenate( + [tensors[5], np.ones((np.shape(tensors[5])[0], self.maxlen - np.shape(tensors[5])[1])) * pad_value], axis=1 + ) + tensors[6] = np.concatenate([tensors[6], np.ones(self.maxlen - np.shape(tensors[6])[0]) * pad_value], axis=0) pad_mask = np.concatenate([pad_mask, np.zeros(length_diff)], axis=0) - tensors[7]= np.concatenate([tensors[7], np.ones(( np.shape(tensors[7])[0],self.maxlen - np.shape(tensors[7])[1])) * stay_id], axis=1)#should be done regardless of length_diff + tensors[7] = np.concatenate( + [tensors[7], np.ones((np.shape(tensors[7])[0], self.maxlen - np.shape(tensors[7])[1])) * stay_id], axis=1 + ) # should be done regardless of length_diff not_labeled = np.argwhere(np.isnan(tensors[6])) if len(not_labeled) > 0: tensors[6][not_labeled] = -1 pad_mask[not_labeled] = 0 - tensors[6]=[tensors[6]] + tensors[6] = [tensors[6]] pad_mask = pad_mask.astype(bool) - - - + tensors = (from_numpy(np.array(tensor)).to(float32) for tensor in tensors) tensors = [stack((x,), dim=-1) if x.numel() > 0 else empty(0) for x in tensors] - return OrderedDict(zip(Features.FEAT_NAMES, tensors)),from_numpy(pad_mask) - - - + return OrderedDict(zip(Features.FEAT_NAMES, tensors)), from_numpy(pad_mask) @gin.configurable("ImputationDataset") @@ -316,7 +322,7 @@ class ImputationPredictionDataset(Dataset): select_columns (List[str], optional): the columns to serve as input for the imputation model. Defaults to None. ram_cache (bool, optional): wether the dataset should be stored in ram. Defaults to True. """ - + def __init__( self, data: DataFrame, @@ -371,80 +377,168 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: return from_numpy(window.values).to(float32) + @gin.configurable("PredictionDatasetTFTpytorch") class PredictionDatasetTFTpytorch(TimeSeriesDataSet): - def __init__(self, + """Subclass of timeseries dataset works with pyotrch forecasting library . + + Args: + data (DataFrame): dict of the different splits of the data + split: Either 'train','val' or 'test' + max_prediction_length: maximum number of time steps to predict, + max_encoder_length: maximum length of input sequence to give the model, + ram_cache (bool, optional): wether the dataset should be stored in ram. Defaults to True. + """ + + def __init__( + self, data: dict, - split: str , + split: str, max_prediction_length: int, max_encoder_length: int, *args, ram_cache: bool = False, - **kwargs): - - data[split]["FEATURES"]["time_idx"]=((data[split]["FEATURES"]["time"]/ pd.Timedelta(seconds=3600))).astype(int) - data=data.get(split) + **kwargs + ): + data[split]["FEATURES"]["time_idx"] = ((data[split]["FEATURES"]["time"] / pd.Timedelta(seconds=3600))).astype( + int + ) # create an incremental column indicating the time step(required by constructor) + data = data.get(split) # get split labels = data["OUTCOME"] - features=data["FEATURES"] - self.data=pd.merge(labels, features, on=['stay_id', 'time']) + features = data["FEATURES"] + self.data = pd.merge(labels, features, on=["stay_id", "time"]) # combine labels and features self.split = split - self.args=args - self.ram_cache=ram_cache - self.kwargs=kwargs - self.column_names=features.columns - super().__init__(data=self.data, + self.args = args + self.ram_cache = ram_cache + self.kwargs = kwargs + self.column_names = features.columns + super().__init__( + data=self.data, time_idx="time_idx", target="label", group_ids=["stay_id"], - min_encoder_length=max_encoder_length // 2, + min_encoder_length=max_encoder_length // 2, max_encoder_length=max_encoder_length, min_prediction_length=2, max_prediction_length=max_prediction_length, static_categoricals=[], - static_reals=["height", "weight","age","sex"], + static_reals=["height", "weight", "age", "sex"], time_varying_known_categoricals=[], time_varying_known_reals=[], time_varying_unknown_categoricals=[], time_varying_unknown_reals=[ - "alb", "alp", "alt", "ast", "be", "bicar", "bili", "bili_dir", "bnd", "bun", "ca", "cai", "ck", "ckmb", "cl", - "crea", "crp", "dbp", "fgn", "fio2", "glu", "hgb", "hr", "inr_pt", "k", "lact", "lymph", "map", "mch", "mchc", "mcv", - "methb", "mg", "na", "neut", "o2sat", "pco2", "ph", "phos", "plt", "po2", "ptt", "resp", "sbp", "temp", "tnt", "urine", - "wbc",'MissingIndicator_1', 'MissingIndicator_2', - 'MissingIndicator_3', 'MissingIndicator_4', 'MissingIndicator_5', - 'MissingIndicator_6', 'MissingIndicator_7', 'MissingIndicator_8', - 'MissingIndicator_9', 'MissingIndicator_10', 'MissingIndicator_11', - 'MissingIndicator_12', 'MissingIndicator_13', 'MissingIndicator_14', - 'MissingIndicator_15', 'MissingIndicator_16', 'MissingIndicator_17', - 'MissingIndicator_18', 'MissingIndicator_19', 'MissingIndicator_20', - 'MissingIndicator_21', 'MissingIndicator_22', 'MissingIndicator_23', - 'MissingIndicator_24', 'MissingIndicator_25', 'MissingIndicator_26', - 'MissingIndicator_27', 'MissingIndicator_28', 'MissingIndicator_29', - 'MissingIndicator_30', 'MissingIndicator_31', 'MissingIndicator_32', - 'MissingIndicator_33', 'MissingIndicator_34', 'MissingIndicator_35', - 'MissingIndicator_36', 'MissingIndicator_37', 'MissingIndicator_38', - 'MissingIndicator_39', 'MissingIndicator_40', 'MissingIndicator_41', - 'MissingIndicator_42', 'MissingIndicator_43', 'MissingIndicator_44', - 'MissingIndicator_45', 'MissingIndicator_46', 'MissingIndicator_47', - 'MissingIndicator_48' + "alb", + "alp", + "alt", + "ast", + "be", + "bicar", + "bili", + "bili_dir", + "bnd", + "bun", + "ca", + "cai", + "ck", + "ckmb", + "cl", + "crea", + "crp", + "dbp", + "fgn", + "fio2", + "glu", + "hgb", + "hr", + "inr_pt", + "k", + "lact", + "lymph", + "map", + "mch", + "mchc", + "mcv", + "methb", + "mg", + "na", + "neut", + "o2sat", + "pco2", + "ph", + "phos", + "plt", + "po2", + "ptt", + "resp", + "sbp", + "temp", + "tnt", + "urine", + "wbc", + "MissingIndicator_1", + "MissingIndicator_2", + "MissingIndicator_3", + "MissingIndicator_4", + "MissingIndicator_5", + "MissingIndicator_6", + "MissingIndicator_7", + "MissingIndicator_8", + "MissingIndicator_9", + "MissingIndicator_10", + "MissingIndicator_11", + "MissingIndicator_12", + "MissingIndicator_13", + "MissingIndicator_14", + "MissingIndicator_15", + "MissingIndicator_16", + "MissingIndicator_17", + "MissingIndicator_18", + "MissingIndicator_19", + "MissingIndicator_20", + "MissingIndicator_21", + "MissingIndicator_22", + "MissingIndicator_23", + "MissingIndicator_24", + "MissingIndicator_25", + "MissingIndicator_26", + "MissingIndicator_27", + "MissingIndicator_28", + "MissingIndicator_29", + "MissingIndicator_30", + "MissingIndicator_31", + "MissingIndicator_32", + "MissingIndicator_33", + "MissingIndicator_34", + "MissingIndicator_35", + "MissingIndicator_36", + "MissingIndicator_37", + "MissingIndicator_38", + "MissingIndicator_39", + "MissingIndicator_40", + "MissingIndicator_41", + "MissingIndicator_42", + "MissingIndicator_43", + "MissingIndicator_44", + "MissingIndicator_45", + "MissingIndicator_46", + "MissingIndicator_47", + "MissingIndicator_48", ], add_relative_time_idx=True, add_target_scales=True, add_encoder_length=True, - ) + ) + def get_balance(self) -> list: """Return the weight balance for the split of interest. Returns: Weights for each label. """ - + counts = self.data["target"][0].unique(return_counts=True) - - return list((1/counts[1]) * counts[1].sum() / counts[0].shape[0]) + return list((1 / counts[1]) * counts[1].sum() / counts[0].shape[0]) + def get_feature_names(self): - return self.column_names - - - \ No newline at end of file diff --git a/icu_benchmarks/imputation/diffwave.py b/icu_benchmarks/imputation/diffwave.py index 0945c0e6..a1f04fd8 100644 --- a/icu_benchmarks/imputation/diffwave.py +++ b/icu_benchmarks/imputation/diffwave.py @@ -301,7 +301,7 @@ def forward(self, input_data): cond = self.cond_conv(cond) h += cond - out = torch.tanh(h[:, : self.res_channels, :]) * torch.sigmoid(h[:, self.res_channels:, :]) + out = torch.tanh(h[:, : self.res_channels, :]) * torch.sigmoid(h[:, self.res_channels :, :]) res = self.res_conv(out) assert x.shape == res.shape diff --git a/icu_benchmarks/imputation/layers/s4layer.py b/icu_benchmarks/imputation/layers/s4layer.py index 0691c7ef..1441a834 100644 --- a/icu_benchmarks/imputation/layers/s4layer.py +++ b/icu_benchmarks/imputation/layers/s4layer.py @@ -92,6 +92,7 @@ def _resolve_conj(x): def _resolve_conj(x): return x.conj() + """ simple nn.Module components """ @@ -162,16 +163,16 @@ def forward(self, x): def LinearActivation( - d_input, - d_output, - bias=True, - zero_bias_init=False, - transposed=False, - initializer=None, - activation=None, - activate=False, # Apply activation as part of this module - weight_norm=False, - **kwargs, + d_input, + d_output, + bias=True, + zero_bias_init=False, + transposed=False, + initializer=None, + activation=None, + activate=False, # Apply activation as part of this module + weight_norm=False, + **kwargs, ): """Returns a linear nn.Module with control over axes order, initialization, and activation""" @@ -385,7 +386,7 @@ def rank_correction(measure, N, rank=1, dtype=torch.float): P = torch.stack([P0, P1], dim=0) # (2 N) elif measure == "lagt": assert rank >= 1 - P = 0.5 ** 0.5 * torch.ones(1, N, dtype=dtype) + P = 0.5**0.5 * torch.ones(1, N, dtype=dtype) elif measure == "fourier": P = torch.ones(N, dtype=dtype) # (N) P0 = P.clone() @@ -509,18 +510,18 @@ def _omega(self, L, dtype, device, cache=True): return omega, z def __init__( - self, - L, - w, - P, - B, - C, - log_dt, - hurwitz=False, - trainable=None, - lr=None, - tie_state=False, - length_correction=True, + self, + L, + w, + P, + B, + C, + log_dt, + hurwitz=False, + trainable=None, + lr=None, + tie_state=False, + length_correction=True, ): """ L: Maximum length; this module computes an SSM kernel of length L @@ -684,23 +685,23 @@ def forward(self, state=None, rate=1.0, L=None): k_f = r[:-1, :-1, :, :] - r[:-1, -1:, :, :] * r[-1:, :-1, :, :] / (1 + r[-1:, -1:, :, :]) elif self.rank == 2: r00 = r[: -self.rank, : -self.rank, :, :] - r01 = r[: -self.rank, -self.rank:, :, :] - r10 = r[-self.rank:, : -self.rank, :, :] - r11 = r[-self.rank:, -self.rank:, :, :] + r01 = r[: -self.rank, -self.rank :, :, :] + r10 = r[-self.rank :, : -self.rank, :, :] + r11 = r[-self.rank :, -self.rank :, :, :] det = (1 + r11[:1, :1, :, :]) * (1 + r11[1:, 1:, :, :]) - r11[:1, 1:, :, :] * r11[1:, :1, :, :] s = ( - r01[:, :1, :, :] * (1 + r11[1:, 1:, :, :]) * r10[:1, :, :, :] - + r01[:, 1:, :, :] * (1 + r11[:1, :1, :, :]) * r10[1:, :, :, :] - - r01[:, :1, :, :] * (r11[:1, 1:, :, :]) * r10[1:, :, :, :] - - r01[:, 1:, :, :] * (r11[1:, :1, :, :]) * r10[:1, :, :, :] + r01[:, :1, :, :] * (1 + r11[1:, 1:, :, :]) * r10[:1, :, :, :] + + r01[:, 1:, :, :] * (1 + r11[:1, :1, :, :]) * r10[1:, :, :, :] + - r01[:, :1, :, :] * (r11[:1, 1:, :, :]) * r10[1:, :, :, :] + - r01[:, 1:, :, :] * (r11[1:, :1, :, :]) * r10[:1, :, :, :] ) s = s / det k_f = r00 - s else: r00 = r[: -self.rank, : -self.rank, :, :] - r01 = r[: -self.rank, -self.rank:, :, :] - r10 = r[-self.rank:, : -self.rank, :, :] - r11 = r[-self.rank:, -self.rank:, :, :] + r01 = r[: -self.rank, -self.rank :, :, :] + r10 = r[-self.rank :, : -self.rank, :, :] + r11 = r[-self.rank :, -self.rank :, :, :] r11 = rearrange(r11, "a b h n -> h n a b") r11 = torch.linalg.inv(torch.eye(self.rank, device=r.device) + r11) r11 = rearrange(r11, "h n a b -> a b h n") @@ -737,7 +738,7 @@ def _setup_linear(self): dt = torch.exp(self.log_dt) D = (2.0 / dt.unsqueeze(-1) - w).reciprocal() # (H, N) R = ( - torch.eye(self.rank, dtype=w.dtype, device=w.device) + 2 * contract("r h n, h n, s h n -> h r s", Q, D, P).real + torch.eye(self.rank, dtype=w.dtype, device=w.device) + 2 * contract("r h n, h n, s h n -> h r s", Q, D, P).real ) # (H r r) Q_D = rearrange(Q * D, "r h n -> h r n") R = torch.linalg.solve(R.to(Q_D), Q_D) # (H r N) @@ -778,8 +779,8 @@ def _step_state_linear(self, u=None, state=None): def contract_fn(p, x, y): return contract("r h n, r h m, ... h m -> ... h n", _conj(p), _conj(x), _conj(y))[ - ..., : self.N - ] # inner outer product + ..., : self.N + ] # inner outer product else: assert state.size(-1) == 2 * self.N @@ -940,24 +941,24 @@ class HippoSSKernel(nn.Module): """ def __init__( - self, - H, - N=64, - L=1, - measure="legs", - rank=1, - channels=1, # 1-dim to C-dim map; can think of C as having separate "heads" - dt_min=0.001, - dt_max=0.1, - trainable=None, # Dictionary of options to train various HiPPO parameters - lr=None, # Hook to set LR of hippo parameters differently - length_correction=True, # Multiply by I-A|^L after initialization; can be turned off for initialization speed - hurwitz=False, - tie_state=False, # Tie parameters of HiPPO ODE across the H features - precision=1, # 1 (single) or 2 (double) for the kernel - resample=False, # If given inputs of different lengths, adjust the sampling rate. - # Note that L should always be provided in this case, as it assumes that L is the true underlying - # length of the continuous signal + self, + H, + N=64, + L=1, + measure="legs", + rank=1, + channels=1, # 1-dim to C-dim map; can think of C as having separate "heads" + dt_min=0.001, + dt_max=0.1, + trainable=None, # Dictionary of options to train various HiPPO parameters + lr=None, # Hook to set LR of hippo parameters differently + length_correction=True, # Multiply by I-A|^L after initialization; can be turned off for initialization speed + hurwitz=False, + tie_state=False, # Tie parameters of HiPPO ODE across the H features + precision=1, # 1 (single) or 2 (double) for the kernel + resample=False, # If given inputs of different lengths, adjust the sampling rate. + # Note that L should always be provided in this case, as it assumes that L is the true underlying + # length of the continuous signal ): super().__init__() self.N = N @@ -1007,23 +1008,23 @@ def get_torch_trans(heads=8, layers=1, channels=64): class S4(nn.Module): def __init__( - self, - d_model, - d_state=64, - l_max=1, # Maximum length of sequence. Fine if not provided: the kernel will keep doubling in length until longer - # than sequence. However, this can be marginally slower if the true length is not a power of 2 - channels=1, # maps 1-dim to C-dim - bidirectional=False, - # Arguments for FF - activation="gelu", # activation in between SS and FF - postact=None, # activation after FF - initializer=None, # initializer on FF - weight_norm=False, # weight normalization on FF - hyper_act=None, # Use a "hypernetwork" multiplication - dropout=0.0, - transposed=True, # axis ordering (B, L, D) or (B, D, L) - # SSM Kernel arguments - **kernel_args, + self, + d_model, + d_state=64, + l_max=1, # Maximum length of sequence. Fine if not provided: the kernel will keep doubling in length until longer + # than sequence. However, this can be marginally slower if the true length is not a power of 2 + channels=1, # maps 1-dim to C-dim + bidirectional=False, + # Arguments for FF + activation="gelu", # activation in between SS and FF + postact=None, # activation after FF + initializer=None, # initializer on FF + weight_norm=False, # weight normalization on FF + hyper_act=None, # Use a "hypernetwork" multiplication + dropout=0.0, + transposed=True, # axis ordering (B, L, D) or (B, D, L) + # SSM Kernel arguments + **kernel_args, ): """ d_state: the dimension of the state, also denoted by N diff --git a/icu_benchmarks/imputation/sssds4.py b/icu_benchmarks/imputation/sssds4.py index ce6e8c0d..d359fb09 100644 --- a/icu_benchmarks/imputation/sssds4.py +++ b/icu_benchmarks/imputation/sssds4.py @@ -298,7 +298,7 @@ def forward(self, input_data): h = self.S42(h.permute(2, 0, 1)).permute(1, 2, 0) - out = torch.tanh(h[:, :self.res_channels, :]) * torch.sigmoid(h[:, self.res_channels:, :]) + out = torch.tanh(h[:, : self.res_channels, :]) * torch.sigmoid(h[:, self.res_channels :, :]) res = self.res_conv(out) assert x.shape == res.shape diff --git a/icu_benchmarks/imputation/sssdsa.py b/icu_benchmarks/imputation/sssdsa.py index fb384efe..55764104 100644 --- a/icu_benchmarks/imputation/sssdsa.py +++ b/icu_benchmarks/imputation/sssdsa.py @@ -389,7 +389,7 @@ def step(self, x, state, **kwargs): if self.unet: for i in range(skipped): next_state.append(state.pop()) - u_layers = list(self.u_layers)[skipped // 3:] + u_layers = list(self.u_layers)[skipped // 3 :] else: for i in range(skipped): for _ in range(len(self.u_layers[i])): diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index c3a1a7fd..c5360da3 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -3,13 +3,22 @@ import numpy as np import torch.nn as nn from icu_benchmarks.contants import RunMode -from icu_benchmarks.models.layers import TransformerBlock, LocalBlock, TemporalBlock, PositionalEncoding,LazyEmbedding,StaticCovariateEncoder,TFTBack +from icu_benchmarks.models.layers import ( + TransformerBlock, + LocalBlock, + TemporalBlock, + PositionalEncoding, + LazyEmbedding, + StaticCovariateEncoder, + TFTBack, +) from typing import Dict from icu_benchmarks.models.wrappers import DLPredictionWrapper -from torch import Tensor,cat,jit,FloatTensor -from pytorch_forecasting import TemporalFusionTransformer,TimeSeriesDataSet +from torch import Tensor, cat, FloatTensor +from pytorch_forecasting import TemporalFusionTransformer + +from pytorch_forecasting.metrics import QuantileLoss -from pytorch_forecasting.metrics import QuantileLoss @gin.configurable class RNNet(DLPredictionWrapper): @@ -285,93 +294,150 @@ def forward(self, x): pred = self.logit(o) return pred + @gin.configurable class TFT(DLPredictionWrapper): - """ - Implementation of https://arxiv.org/abs/1912.09363 """ - + Implementation of https://arxiv.org/abs/1912.09363 from https://github.com/NVIDIA/DeepLearningExamples/tree/master/PyTorch/Forecasting/TFT + """ _supported_run_modes = [RunMode.classification, RunMode.regression] - def __init__(self,num_classes, encoder_length,hidden,dropout, - n_heads,dropout_att,example_length,*args,quantiles=[0.1, 0.5, 0.9],static_categorical_inp_size=[2],temporal_known_categorical_inp_size=[], - temporal_observed_categorical_inp_size=[48],static_continuous_inp_size=3,temporal_known_continuous_inp_size=0, - temporal_observed_continuous_inp_size=48,temporal_target_size=1,**kwargs): - #derived variables - num_static_vars=len(static_categorical_inp_size)+static_continuous_inp_size - num_future_vars=len(temporal_known_categorical_inp_size)+temporal_known_continuous_inp_size - num_historic_vars=sum([num_future_vars, - temporal_observed_continuous_inp_size, - temporal_target_size, - len(temporal_observed_categorical_inp_size), - ]) - - super().__init__(num_classes=num_classes, encoder_length=encoder_length,hidden=hidden, - n_heads=n_heads,dropout_att=dropout_att,example_length=example_length,quantiles=quantiles, - num_static_vars=num_static_vars,num_future_vars=num_future_vars,num_historic_vars=num_historic_vars,*args,static_categorical_inp_size=1,temporal_known_categorical_inp_size=0, - temporal_observed_categorical_inp_size=48,static_continuous_inp_size=3,temporal_known_continuous_inp_size=0, - temporal_observed_continuous_inp_size=48,temporal_target_size=1,**kwargs) - - - - self.encoder_length = encoder_length #this determines from how distant past we want to use data from - - self.embedding = LazyEmbedding(static_categorical_inp_size,temporal_known_categorical_inp_size, - temporal_observed_categorical_inp_size,static_continuous_inp_size,temporal_known_continuous_inp_size, - temporal_observed_continuous_inp_size,temporal_target_size,hidden) - - self.static_encoder = StaticCovariateEncoder(num_static_vars,hidden,dropout) - self.TFTpart2 = TFTBack(encoder_length,num_historic_vars,hidden,dropout,num_future_vars, - n_heads,dropout_att,example_length,quantiles) - self.logit = nn.Linear(len(quantiles), num_classes) + + def __init__( + self, + num_classes, + encoder_length, # determines interval to use for prediction + hidden, + dropout, + n_heads, + dropout_att, + example_length, # determines interval to predict + *args, + quantiles=[0.1, 0.5, 0.9], # quantiles to produce + static_categorical_inp_size=[2], # number of catergories + temporal_known_categorical_inp_size=[], + temporal_observed_categorical_inp_size=[48], # number of categorical observed variables + static_continuous_inp_size=3, # number of static coutinous variables + temporal_known_continuous_inp_size=0, + temporal_observed_continuous_inp_size=48, + temporal_target_size=1, # number of target variables + **kwargs, + ): + # derived variables + num_static_vars = len(static_categorical_inp_size) + static_continuous_inp_size + num_future_vars = len(temporal_known_categorical_inp_size) + temporal_known_continuous_inp_size + num_historic_vars = sum( + [ + num_future_vars, + temporal_observed_continuous_inp_size, + temporal_target_size, + len(temporal_observed_categorical_inp_size), + ] + ) + + super().__init__( + num_classes=num_classes, + encoder_length=encoder_length, + hidden=hidden, + n_heads=n_heads, + dropout_att=dropout_att, + example_length=example_length, + quantiles=quantiles, + num_static_vars=num_static_vars, + num_future_vars=num_future_vars, + num_historic_vars=num_historic_vars, + *args, + static_categorical_inp_size=1, + temporal_known_categorical_inp_size=0, + temporal_observed_categorical_inp_size=48, + static_continuous_inp_size=3, + temporal_known_continuous_inp_size=0, + temporal_observed_continuous_inp_size=48, + temporal_target_size=1, + **kwargs, + ) + + self.encoder_length = encoder_length # this determines from how distant past we want to use data from + + self.embedding = LazyEmbedding( + static_categorical_inp_size, + temporal_known_categorical_inp_size, + temporal_observed_categorical_inp_size, + static_continuous_inp_size, + temporal_known_continuous_inp_size, + temporal_observed_continuous_inp_size, + temporal_target_size, + hidden, + ) # embeddings for all variables + + self.static_encoder = StaticCovariateEncoder(num_static_vars, hidden, dropout) # encoding for static variables + self.TFTpart2 = TFTBack( + encoder_length, + num_historic_vars, + hidden, + dropout, + num_future_vars, + n_heads, + dropout_att, + example_length, + quantiles, + ) # The main part of the TFT + self.logit = nn.Linear( + len(quantiles), num_classes + ) # Linear layer on top to output to the number of classes and allow modification by predictionwrapper def forward(self, x: Dict[str, Tensor]) -> Tensor: - - s_inp, t_known_inp, t_observed_inp, t_observed_tgt = self.embedding(x) # Static context cs, ce, ch, cc = self.static_encoder(s_inp) - ch, cc = ch.unsqueeze(0), cc.unsqueeze(0) #lstm initial states + ch, cc = ch.unsqueeze(0), cc.unsqueeze(0) # lstm initial states # Temporal input - + _historical_inputs = [] - + # Check for t_observed_inp if t_observed_inp is not None: - _historical_inputs.append(t_observed_inp[:, :self.encoder_length, :]) + _historical_inputs.append(t_observed_inp[:, : self.encoder_length, :]) # Check for t_known_inp if t_known_inp is not None: - _historical_inputs.append(t_known_inp[:, :self.encoder_length, :]) + _historical_inputs.append(t_known_inp[:, : self.encoder_length, :]) # Check for t_observed_tgt if t_observed_tgt is not None: - _historical_inputs.append(t_observed_tgt[:, :self.encoder_length, :]) + _historical_inputs.append(t_observed_tgt[:, : self.encoder_length, :]) historical_inputs = cat(_historical_inputs, dim=-2) - future_inputs= Tensor() + future_inputs = Tensor() if t_known_inp is not None: - future_inputs = t_known_inp[:, self.encoder_length:] - - - o=self.TFTpart2(historical_inputs, cs, ch, cc, ce, future_inputs.to(historical_inputs.device)) + future_inputs = t_known_inp[:, self.encoder_length :] + + o = self.TFTpart2(historical_inputs, cs, ch, cc, ce, future_inputs.to(historical_inputs.device)) pred = self.logit(o) return pred + @gin.configurable class TFTpytorch(DLPredictionWrapper): """ - Implementation of https://arxiv.org/abs/1912.09363 + Implementation of https://arxiv.org/abs/1912.09363 from pytorch forecasting """ + supported_run_modes = [RunMode.classification, RunMode.regression] - def __init__(self,dataset,hidden,dropout, - n_heads,dropout_att,lr,optimizer,num_classes,*args,**kwargs): - - super().__init__(lr=lr,optimizer=optimizer,*args,**kwargs) - self.model=TemporalFusionTransformer.from_dataset(dataset=dataset,hidden_size=hidden,dropout=dropout, - attention_head_size=n_heads,learning_rate=lr,optimizer=optimizer,loss=QuantileLoss(),hidden_continuous_size=hidden) - + + def __init__(self, dataset, hidden, dropout, n_heads, dropout_att, lr, optimizer, num_classes, *args, **kwargs): + super().__init__(lr=lr, optimizer=optimizer, *args, **kwargs) + self.model = TemporalFusionTransformer.from_dataset( + dataset=dataset, + hidden_size=hidden, + dropout=dropout, + attention_head_size=n_heads, + learning_rate=lr, + optimizer=optimizer, + loss=QuantileLoss(), + hidden_continuous_size=hidden, + ) + self.logit = nn.Linear(7, num_classes) - - + def set_weight(self, weight, dataset): """ Set the weight for the loss function @@ -381,9 +447,8 @@ def set_weight(self, weight, dataset): elif weight == "balanced": weight = FloatTensor(dataset.get_balance()) self.loss_weights = weight - def forward(self,x): - out= self.model(x) + + def forward(self, x): + out = self.model(x) pred = self.logit(out["prediction"]) return pred - - \ No newline at end of file diff --git a/icu_benchmarks/models/layers.py b/icu_benchmarks/models/layers.py index 79ee0e61..7307bb52 100644 --- a/icu_benchmarks/models/layers.py +++ b/icu_benchmarks/models/layers.py @@ -319,26 +319,29 @@ def forward(self, x): res = x if self.downsample is None else self.downsample(x) return self.relu(out + res) + class MaybeLayerNorm(nn.Module): - ''' + """ Implements layer normalization or identity function depending on output_size - ''' + """ + def __init__(self, output_size, hidden, eps): super().__init__() if output_size and output_size == 1: self.ln = nn.Identity() else: self.ln = LayerNorm(output_size if output_size else hidden, eps=eps) - + def forward(self, x): return self.ln(x) class GLU(nn.Module): - ''' + """ Gated Linear Unit consists of a linear layer followed by a GLU where input is split in half along dim to form a and b - GLU(a,b)=a ⊗ σ(b)where σ is signmoid activation and ⊗ is element-wise product - ''' + GLU(a,b)=a ⊗ σ(b)where σ is signmoid activation and ⊗ is element-wise product + """ + def __init__(self, hidden, output_size): super().__init__() self.lin = nn.Linear(hidden, output_size * 2) @@ -348,24 +351,27 @@ def forward(self, x: Tensor) -> Tensor: x = F.glu(x) return x + class GRN(nn.Module): - ''' + """ Gated Residual Network consists of a maybe normalization layer -->linear --> ELU -->linear-->GLU - in addition to a residual connection - ''' - def __init__(self, - input_size, - hidden, - output_size=None, - context_hidden=None, - dropout=0.0,): + in addition to a residual connection + """ + + def __init__( + self, + input_size, + hidden, + output_size=None, + context_hidden=None, + dropout=0.0, + ): super().__init__() - + self.layer_norm = MaybeLayerNorm(output_size, hidden, eps=1e-3) - + self.lin_a = nn.Linear(input_size, hidden) - - + if context_hidden is not None: self.lin_c = nn.Linear(context_hidden, hidden, bias=False) else: @@ -376,9 +382,8 @@ def __init__(self, self.out_proj = nn.Linear(input_size, output_size) if output_size else None def forward(self, a: Tensor, c: Optional[Tensor] = None): - x = self.lin_a(a) - + if c is not None: x = x + self.lin_c(c).unsqueeze(1) x = F.elu(x) @@ -387,18 +392,18 @@ def forward(self, a: Tensor, c: Optional[Tensor] = None): x = self.glu(x) y = a if self.out_proj is None else self.out_proj(a) x = x + y - - return self.layer_norm(x) + + return self.layer_norm(x) # @torch.jit.script #Currently broken with autocast def fused_pointwise_linear_v1(x, a, b): - out = torch.mul(x.unsqueeze(-1), a) out = out + b return out -#@torch.jit.script + +# @torch.jit.script def fused_pointwise_linear_v2(x, a, b): out = x.unsqueeze(3) * a out = out + b @@ -406,21 +411,29 @@ def fused_pointwise_linear_v2(x, a, b): class TFTEmbedding(nn.Module): - def __init__(self,static_categorical_inp_size,temporal_known_categorical_inp_size, - temporal_observed_categorical_inp_size,static_continuous_inp_size,temporal_known_continuous_inp_size, - temporal_observed_continuous_inp_size,temporal_target_size,hidden, - initialize_cont_params=False): + def __init__( + self, + static_categorical_inp_size, + temporal_known_categorical_inp_size, + temporal_observed_categorical_inp_size, + static_continuous_inp_size, + temporal_known_continuous_inp_size, + temporal_observed_continuous_inp_size, + temporal_target_size, + hidden, + initialize_cont_params=False, + ): # initialize_cont_params=False prevents form initializing parameters inside this class # so they can be lazily initialized in LazyEmbedding module super().__init__() - #these are basically number of varaibales that falls under each category - self.s_cat_inp_size = static_categorical_inp_size - self.t_cat_k_inp_size = temporal_known_categorical_inp_size - self.t_cat_o_inp_size = temporal_observed_categorical_inp_size - self.s_cont_inp_size = static_continuous_inp_size + # these are basically number of varaibales that falls under each category + self.s_cat_inp_size = static_categorical_inp_size + self.t_cat_k_inp_size = temporal_known_categorical_inp_size + self.t_cat_o_inp_size = temporal_observed_categorical_inp_size + self.s_cont_inp_size = static_continuous_inp_size self.t_cont_k_inp_size = temporal_known_continuous_inp_size self.t_cont_o_inp_size = temporal_observed_continuous_inp_size - self.t_tgt_size = temporal_target_size + self.t_tgt_size = temporal_target_size self.hidden = hidden @@ -433,30 +446,44 @@ def __init__(self,static_categorical_inp_size,temporal_known_categorical_inp_siz # 6. Temporal observed continuous # 7. Temporal observed targets (time series obseved so far) - self.s_cat_embed = nn.ModuleList([ - nn.Embedding(n, self.hidden) for n in self.s_cat_inp_size]) if self.s_cat_inp_size else None - self.t_cat_k_embed = nn.ModuleList([ - nn.Embedding(n, self.hidden) for n in self.t_cat_k_inp_size]) if self.t_cat_k_inp_size else None - self.t_cat_o_embed = nn.ModuleList([ - nn.Embedding(n, self.hidden) for n in self.t_cat_o_inp_size]) if self.t_cat_o_inp_size else None - + self.s_cat_embed = ( + nn.ModuleList([nn.Embedding(n, self.hidden) for n in self.s_cat_inp_size]) if self.s_cat_inp_size else None + ) + self.t_cat_k_embed = ( + nn.ModuleList([nn.Embedding(n, self.hidden) for n in self.t_cat_k_inp_size]) if self.t_cat_k_inp_size else None + ) + self.t_cat_o_embed = ( + nn.ModuleList([nn.Embedding(n, self.hidden) for n in self.t_cat_o_inp_size]) if self.t_cat_o_inp_size else None + ) if initialize_cont_params: - self.s_cont_embedding_vectors = nn.Parameter(torch.Tensor(self.s_cont_inp_size, self.hidden)) if self.s_cont_inp_size else None - self.t_cont_k_embedding_vectors = nn.Parameter(torch.Tensor(self.t_cont_k_inp_size, self.hidden)) if self.t_cont_k_inp_size else None - self.t_cont_o_embedding_vectors = nn.Parameter(torch.Tensor(self.t_cont_o_inp_size, self.hidden)) if self.t_cont_o_inp_size else None + self.s_cont_embedding_vectors = ( + nn.Parameter(torch.Tensor(self.s_cont_inp_size, self.hidden)) if self.s_cont_inp_size else None + ) + self.t_cont_k_embedding_vectors = ( + nn.Parameter(torch.Tensor(self.t_cont_k_inp_size, self.hidden)) if self.t_cont_k_inp_size else None + ) + self.t_cont_o_embedding_vectors = ( + nn.Parameter(torch.Tensor(self.t_cont_o_inp_size, self.hidden)) if self.t_cont_o_inp_size else None + ) self.t_tgt_embedding_vectors = nn.Parameter(torch.Tensor(self.t_tgt_size, self.hidden)) - self.s_cont_embedding_bias = nn.Parameter(torch.zeros(self.s_cont_inp_size, self.hidden)) if self.s_cont_inp_size else None - self.t_cont_k_embedding_bias = nn.Parameter(torch.zeros(self.t_cont_k_inp_size, self.hidden)) if self.t_cont_k_inp_size else None - self.t_cont_o_embedding_bias = nn.Parameter(torch.zeros(self.t_cont_o_inp_size, self.hidden)) if self.t_cont_o_inp_size else None + self.s_cont_embedding_bias = ( + nn.Parameter(torch.zeros(self.s_cont_inp_size, self.hidden)) if self.s_cont_inp_size else None + ) + self.t_cont_k_embedding_bias = ( + nn.Parameter(torch.zeros(self.t_cont_k_inp_size, self.hidden)) if self.t_cont_k_inp_size else None + ) + self.t_cont_o_embedding_bias = ( + nn.Parameter(torch.zeros(self.t_cont_o_inp_size, self.hidden)) if self.t_cont_o_inp_size else None + ) self.t_tgt_embedding_bias = nn.Parameter(torch.zeros(self.t_tgt_size, self.hidden)) self.reset_parameters() def reset_parameters(self): - '''' + """' embeddings are initilitized using xavier's method and biases are initlitized with zeros - ''' + """ if self.s_cont_embedding_vectors is not None: torch.nn.init.xavier_normal_(self.s_cont_embedding_vectors) torch.nn.init.zeros_(self.s_cont_embedding_bias) @@ -479,28 +506,30 @@ def reset_parameters(self): for module in self.t_cat_o_embed: module.reset_parameters() - - def _apply_embedding(self, - cat: Optional[Tensor], - cont: Optional[Tensor], - cat_emb: Optional[nn.ModuleList], - cont_emb: Tensor, - cont_bias: Tensor, - ) -> Tuple[Optional[Tensor], Optional[Tensor]]: - - e_cat = torch.stack([embed(cat[...,i].int()) for i, embed in enumerate(cat_emb)], dim=-2) if (cat is not None) and (cat.size()[1]>0) else None - if (cont is not None ) and (cont.size()[1]>0): - #the line below is equivalent to following einsums - #e_cont = torch.einsum('btf,fh->bthf', cont, cont_emb) - #e_cont = torch.einsum('bf,fh->bhf', cont, cont_emb) + def _apply_embedding( + self, + cat: Optional[Tensor], + cont: Optional[Tensor], + cat_emb: Optional[nn.ModuleList], + cont_emb: Tensor, + cont_bias: Tensor, + ) -> Tuple[Optional[Tensor], Optional[Tensor]]: + e_cat = ( + torch.stack([embed(cat[..., i].int()) for i, embed in enumerate(cat_emb)], dim=-2) + if (cat is not None) and (cat.size()[1] > 0) + else None + ) + if (cont is not None) and (cont.size()[1] > 0): + # the line below is equivalent to following einsums + # e_cont = torch.einsum('btf,fh->bthf', cont, cont_emb) + # e_cont = torch.einsum('bf,fh->bhf', cont, cont_emb) e_cont = torch.mul(cont.unsqueeze(-1), cont_emb) e_cont = e_cont + cont_bias - # e_cont = fused_pointwise_linear_v1(cont, cont_emb, cont_bias) + # e_cont = fused_pointwise_linear_v1(cont, cont_emb, cont_bias) else: e_cont = None - + if e_cat is not None and e_cont is not None: - return torch.cat([e_cat, e_cont], dim=-2) elif e_cat is not None: return e_cat @@ -510,58 +539,66 @@ def _apply_embedding(self, return None def forward(self, x: Dict[str, Tensor]): - # temporal/static categorical/continuous known/observed input - s_cat_inp = x.get('s_cat', None) - s_cont_inp = x.get('s_cont', None) - t_cat_k_inp = x.get('k_cat', None) - t_cont_k_inp = x.get('k_cont', None) - t_cat_o_inp = x.get('o_cat', None) - t_cont_o_inp = x.get('o_cont', None) - t_tgt_obs = x['target'] # Has to be present + # temporal/static categorical/continuous known/observed input + s_cat_inp = x.get("s_cat", None) + s_cont_inp = x.get("s_cont", None) + t_cat_k_inp = x.get("k_cat", None) + t_cont_k_inp = x.get("k_cont", None) + t_cat_o_inp = x.get("o_cat", None) + t_cont_o_inp = x.get("o_cont", None) + t_tgt_obs = x["target"] # Has to be present # Static inputs are expected to be equal for all timesteps # For memory efficiency there is no assert statement - - - - s_cat_inp = s_cat_inp[:,0,:] if s_cat_inp is not None else None - s_cont_inp = s_cont_inp[:,0,:] if s_cont_inp is not None else None - - - - s_inp = self._apply_embedding(s_cat_inp, - s_cont_inp, - self.s_cat_embed, - self.s_cont_embedding_vectors, - self.s_cont_embedding_bias) - - - t_known_inp = self._apply_embedding(t_cat_k_inp, - t_cont_k_inp, - self.t_cat_k_embed, - self.t_cont_k_embedding_vectors, - self.t_cont_k_embedding_bias) - - t_observed_inp = self._apply_embedding(t_cat_o_inp, - t_cont_o_inp, - self.t_cat_o_embed, - self.t_cont_o_embedding_vectors, - self.t_cont_o_embedding_bias) + + s_cat_inp = s_cat_inp[:, 0, :] if s_cat_inp is not None else None + s_cont_inp = s_cont_inp[:, 0, :] if s_cont_inp is not None else None + + s_inp = self._apply_embedding( + s_cat_inp, s_cont_inp, self.s_cat_embed, self.s_cont_embedding_vectors, self.s_cont_embedding_bias + ) + + t_known_inp = self._apply_embedding( + t_cat_k_inp, t_cont_k_inp, self.t_cat_k_embed, self.t_cont_k_embedding_vectors, self.t_cont_k_embedding_bias + ) + + t_observed_inp = self._apply_embedding( + t_cat_o_inp, t_cont_o_inp, self.t_cat_o_embed, self.t_cont_o_embedding_vectors, self.t_cont_o_embedding_bias + ) # Temporal observed targets - t_observed_tgt = torch.matmul(t_tgt_obs.unsqueeze(3).unsqueeze(4), self.t_tgt_embedding_vectors.unsqueeze(1)).squeeze(3) + t_observed_tgt = torch.matmul(t_tgt_obs.unsqueeze(3).unsqueeze(4), self.t_tgt_embedding_vectors.unsqueeze(1)).squeeze( + 3 + ) t_observed_tgt = t_observed_tgt + self.t_tgt_embedding_bias return s_inp, t_known_inp, t_observed_inp, t_observed_tgt + class LazyEmbedding(nn.modules.lazy.LazyModuleMixin, TFTEmbedding): cls_to_become = TFTEmbedding - def __init__(self, static_categorical_inp_size,temporal_known_categorical_inp_size, - temporal_observed_categorical_inp_size,static_continuous_inp_size,temporal_known_continuous_inp_size, - temporal_observed_continuous_inp_size,temporal_target_size,hidden): - super().__init__(static_categorical_inp_size,temporal_known_categorical_inp_size, - temporal_observed_categorical_inp_size,static_continuous_inp_size,temporal_known_continuous_inp_size, - temporal_observed_continuous_inp_size,temporal_target_size,hidden, initialize_cont_params=False) + def __init__( + self, + static_categorical_inp_size, + temporal_known_categorical_inp_size, + temporal_observed_categorical_inp_size, + static_continuous_inp_size, + temporal_known_continuous_inp_size, + temporal_observed_continuous_inp_size, + temporal_target_size, + hidden, + ): + super().__init__( + static_categorical_inp_size, + temporal_known_categorical_inp_size, + temporal_observed_categorical_inp_size, + static_continuous_inp_size, + temporal_known_continuous_inp_size, + temporal_observed_continuous_inp_size, + temporal_target_size, + hidden, + initialize_cont_params=False, + ) if static_continuous_inp_size: self.s_cont_embedding_vectors = UninitializedParameter() self.s_cont_embedding_bias = UninitializedParameter() @@ -585,21 +622,20 @@ def __init__(self, static_categorical_inp_size,temporal_known_categorical_inp_si self.t_tgt_embedding_bias = UninitializedParameter() def initialize_parameters(self, x): - if self.has_uninitialized_params(): - s_cont_inp = x.get('s_cont', None) - t_cont_k_inp = x.get('k_cont', None) - t_cont_o_inp = x.get('o_cont', None) - t_tgt_obs = x['target'] # Has to be present - if (s_cont_inp is not None) and (s_cont_inp.size()[1]>0): + s_cont_inp = x.get("s_cont", None) + t_cont_k_inp = x.get("k_cont", None) + t_cont_o_inp = x.get("o_cont", None) + t_tgt_obs = x["target"] # Has to be present + if (s_cont_inp is not None) and (s_cont_inp.size()[1] > 0): self.s_cont_embedding_vectors.materialize((s_cont_inp.shape[-1], self.hidden)) self.s_cont_embedding_bias.materialize((s_cont_inp.shape[-1], self.hidden)) - if (t_cont_k_inp is not None) and (t_cont_k_inp.size()[1]>0): + if (t_cont_k_inp is not None) and (t_cont_k_inp.size()[1] > 0): self.t_cont_k_embedding_vectors.materialize((t_cont_k_inp.shape[-1], self.hidden)) self.t_cont_k_embedding_bias.materialize((t_cont_k_inp.shape[-1], self.hidden)) - if (t_cont_o_inp) is not None and (t_cont_o_inp.size()[1]>0): + if (t_cont_o_inp) is not None and (t_cont_o_inp.size()[1] > 0): self.t_cont_o_embedding_vectors.materialize((t_cont_o_inp.shape[-1], self.hidden)) self.t_cont_o_embedding_bias.materialize((t_cont_o_inp.shape[-1], self.hidden)) @@ -608,47 +644,50 @@ def initialize_parameters(self, x): self.reset_parameters() + class VariableSelectionNetwork(nn.Module): - ''' - Learns to select important netowrks consists of GRNs with one GRN for variable weights + """ + Learns to select important netowrks consists of GRNs with one GRN for variable weights and the others for input embedding - ''' - def __init__(self, hidden,dropout, num_inputs): + """ + + def __init__(self, hidden, dropout, num_inputs): super().__init__() - self.hidden=hidden - self.joint_grn = GRN(hidden*num_inputs, hidden, output_size=num_inputs, context_hidden=hidden) + self.hidden = hidden + self.joint_grn = GRN(hidden * num_inputs, hidden, output_size=num_inputs, context_hidden=hidden) self.var_grns = nn.ModuleList([GRN(hidden, hidden, dropout=dropout) for _ in range(num_inputs)]) def forward(self, x: Tensor, context: Optional[Tensor] = None): if x.numel() == 0: # Check if x is an empty tensor batch_size = context.size(0) if context is not None else 1 - variable_ctx = torch.zeros(batch_size,1, self.hidden,device=x.device) - sparse_weights = torch.ones(batch_size,1, self.hidden,device=x.device) + variable_ctx = torch.zeros(batch_size, 1, self.hidden, device=x.device) + sparse_weights = torch.ones(batch_size, 1, self.hidden, device=x.device) return variable_ctx, sparse_weights Xi = torch.flatten(x, start_dim=-2) grn_outputs = self.joint_grn(Xi, c=context) sparse_weights = F.softmax(grn_outputs, dim=-1) - transformed_embed_list = [m(x[...,i,:]) for i, m in enumerate(self.var_grns)] + transformed_embed_list = [m(x[..., i, :]) for i, m in enumerate(self.var_grns)] transformed_embed = torch.stack(transformed_embed_list, dim=-1) variable_ctx = torch.matmul(transformed_embed, sparse_weights.unsqueeze(-1)).squeeze(-1) - + return variable_ctx, sparse_weights + class StaticCovariateEncoder(nn.Module): - ''' - Network to produce 4 contexts vectors to enrich static variables + """ + Network to produce 4 contexts vectors to enrich static variables Vriable selection Network --> GRNs - ''' - def __init__(self, num_static_vars,hidden,dropout): + """ + + def __init__(self, num_static_vars, hidden, dropout): super().__init__() - self.vsn = VariableSelectionNetwork(hidden,dropout, num_static_vars) + self.vsn = VariableSelectionNetwork(hidden, dropout, num_static_vars) self.context_grns = nn.ModuleList([GRN(hidden, hidden, dropout=dropout) for _ in range(4)]) def forward(self, x: Tensor) -> Tuple[Tensor, Tensor, Tensor, Tensor]: - variable_ctx, sparse_weights = self.vsn(x) - + # Context vectors: # variable selection context # enrichment context @@ -660,11 +699,12 @@ def forward(self, x: Tensor) -> Tuple[Tensor, Tensor, Tensor, Tensor]: class InterpretableMultiHeadAttention(nn.Module): - ''' - Multi-head attention different as it outputs the attention_probability and it combines the attention weights instead of + """ + Multi-head attention different as it outputs the attention_probability and it combines the attention weights instead of concating them different from the one implemented already in YAIB - ''' - def __init__(self,n_head,hidden,dropout_att,dropout,example_length): + """ + + def __init__(self, n_head, hidden, dropout_att, dropout, example_length): super().__init__() self.n_head = n_head assert hidden % n_head == 0 @@ -674,7 +714,7 @@ def __init__(self,n_head,hidden,dropout_att,dropout,example_length): self.dropout_att = nn.Dropout(dropout_att) self.out_dropout = nn.Dropout(dropout) self.scale = self.d_head**-0.5 - self.register_buffer("_mask", torch.triu(torch.full((example_length, example_length), float('-inf')), 1).unsqueeze(0)) + self.register_buffer("_mask", torch.triu(torch.full((example_length, example_length), float("-inf")), 1).unsqueeze(0)) def forward(self, x: Tensor) -> Tuple[Tensor, Tensor]: bs, t, h_size = x.shape @@ -700,51 +740,55 @@ def forward(self, x: Tensor) -> Tuple[Tensor, Tensor]: return out, attn_prob + class TFTBack(nn.Module): - ''' + """ Big part of TFT architecture consists of static enrichment followed by mutli-head self-attention then position wise feed forward - followed by a gate and a dense layer + followed by a gate and a dense layer GRNs-->multi-head attention-->GRNs-->GLU-->Linear-->output - ''' - def __init__(self, encoder_length,num_historic_vars,hidden,dropout,num_future_vars, - n_head,dropout_att,example_length,quantiles): + """ + + def __init__( + self, + encoder_length, + num_historic_vars, + hidden, + dropout, + num_future_vars, + n_head, + dropout_att, + example_length, + quantiles, + ): super().__init__() self.encoder_length = encoder_length - self.history_vsn = VariableSelectionNetwork(hidden,dropout, num_historic_vars) + self.history_vsn = VariableSelectionNetwork(hidden, dropout, num_historic_vars) self.history_encoder = nn.LSTM(hidden, hidden, batch_first=True) - self.future_vsn = VariableSelectionNetwork(hidden,dropout,num_future_vars) + self.future_vsn = VariableSelectionNetwork(hidden, dropout, num_future_vars) self.future_encoder = nn.LSTM(hidden, hidden, batch_first=True) - self.input_gate = GLU(hidden, hidden) self.input_gate_ln = LayerNorm(hidden, eps=1e-3) - self.enrichment_grn = GRN(hidden, - hidden, - context_hidden=hidden, - dropout=dropout) - self.attention = InterpretableMultiHeadAttention(n_head,hidden,dropout_att,dropout,example_length) + self.enrichment_grn = GRN(hidden, hidden, context_hidden=hidden, dropout=dropout) + self.attention = InterpretableMultiHeadAttention(n_head, hidden, dropout_att, dropout, example_length) self.attention_gate = GLU(hidden, hidden) self.attention_ln = LayerNorm(hidden, eps=1e-3) - self.positionwise_grn = GRN(hidden, - hidden, - dropout=dropout) + self.positionwise_grn = GRN(hidden, hidden, dropout=dropout) self.decoder_gate = GLU(hidden, hidden) self.decoder_ln = LayerNorm(hidden, eps=1e-3) - - + self.quantile_proj = nn.Linear(hidden, len(quantiles)) - + def forward(self, historical_inputs, cs, ch, cc, ce, future_inputs): historical_features, _ = self.history_vsn(historical_inputs, cs) history, state = self.history_encoder(historical_features, (ch, cc)) future_features, _ = self.future_vsn(future_inputs, cs) future, _ = self.future_encoder(future_features, state) - # skip connection input_embedding = torch.cat([historical_features, future_features], dim=1) @@ -757,12 +801,12 @@ def forward(self, historical_inputs, cs, ch, cc, ce, future_inputs): enriched = self.enrichment_grn(temporal_features, c=ce) # Temporal self attention - x, attn_prob= self.attention(enriched) + x, attn_prob = self.attention(enriched) # Don't compute hictorical quantiles - x = x[:, self.encoder_length:, :] - temporal_features = temporal_features[:, self.encoder_length:, :] - enriched = enriched[:, self.encoder_length:, :] + x = x[:, self.encoder_length :, :] + temporal_features = temporal_features[:, self.encoder_length :, :] + enriched = enriched[:, self.encoder_length :, :] x = self.attention_gate(x) x = x + enriched diff --git a/icu_benchmarks/models/metrics.py b/icu_benchmarks/models/metrics.py index 108f65da..837bcaae 100644 --- a/icu_benchmarks/models/metrics.py +++ b/icu_benchmarks/models/metrics.py @@ -5,27 +5,33 @@ from sklearn.metrics import balanced_accuracy_score, mean_absolute_error from sklearn.calibration import calibration_curve from scipy.spatial.distance import jensenshannon -import gin +import gin import torch.nn.functional as F + """" This file contains metrics that are not available in ignite.metrics. Specifically, it adds transformation capabilities to some metrics. """ + class QuantileLossClass(torch.nn.Module): def __init__(self, quantiles): super().__init__() - self.register_buffer('q', torch.tensor(quantiles)) + self.register_buffer("q", torch.tensor(quantiles)) def forward(self, predictions, targets): diff = predictions - targets - ql = (1-self.q)*F.relu(diff) + self.q*F.relu(-diff) + ql = (1 - self.q) * F.relu(diff) + self.q * F.relu(-diff) losses = ql.view(-1, ql.shape[-1]).mean(0) return losses + + @gin.configurable -def Quantileloss(self,predictions, targets,quantiles=[0.1,0.5,0.9]): - loss=QuantileLossClass(quantiles=quantiles) - return loss(predictions,targets) +def Quantileloss(self, predictions, targets, quantiles=[0.1, 0.5, 0.9]): + loss = QuantileLossClass(quantiles=quantiles) + return loss(predictions, targets) + + def accuracy(output, target, topk=(1,)): """Computes the accuracy over the k top predictions for the specified values of k""" with torch.no_grad(): diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 868ebe85..9dc6d96d 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -8,14 +8,14 @@ from pytorch_lightning.loggers import TensorBoardLogger from pytorch_lightning import Trainer from pytorch_lightning.callbacks import EarlyStopping, ModelCheckpoint, TQDMProgressBar -from pytorch_forecasting import TimeSeriesDataSet,TemporalFusionTransformer from pathlib import Path -from icu_benchmarks.data.loader import PredictionDataset, ImputationDataset,PredictionDatasetTFT,PredictionDatasetTFTpytorch +from icu_benchmarks.data.loader import PredictionDataset, ImputationDataset, PredictionDatasetTFT, PredictionDatasetTFTpytorch from icu_benchmarks.models.utils import save_config_file, JSONMetricsLogger from icu_benchmarks.contants import RunMode from icu_benchmarks.data.constants import DataSplit as Split from collections import OrderedDict -import pickle + + cpu_core_count = len(os.sched_getaffinity(0)) if hasattr(os, "sched_getaffinity") else os.cpu_count() @@ -71,20 +71,22 @@ def train_common( verbose: Enable detailed logging. ram_cache: Whether to cache the data in RAM. num_workers: Number of workers to use for data loading. - with open('saved_dictionary.pkl', 'wb') as f: - pickle.dump(data, f) - data["train"]["OUTCOME"].to_csv('outcome.csv', index=False) - data["train"]["FEATURES"].to_csv('features.csv', index=False) """ - logging.info(f"Training model: {model.__name__}.") - dataset_class = (ImputationDataset if mode == RunMode.imputation - else (PredictionDatasetTFT if model.__name__ == 'TFT' - else (PredictionDatasetTFTpytorch if model.__name__ == 'TFTpytorch' else PredictionDataset))) + # choose dataset_class based on the model + dataset_class = ( + ImputationDataset + if mode == RunMode.imputation + else ( + PredictionDatasetTFT + if model.__name__ == "TFT" + else (PredictionDatasetTFTpytorch if model.__name__ == "TFTpytorch" else PredictionDataset) + ) + ) logging.info(f"Logging to directory: {log_dir}.") save_config_file(log_dir) # We save the operative config before and also after training - + train_dataset = dataset_class(data, split=Split.train, ram_cache=ram_cache) val_dataset = dataset_class(data, split=Split.val, ram_cache=ram_cache) train_dataset, val_dataset = assure_minimum_length(train_dataset), assure_minimum_length(val_dataset) @@ -92,16 +94,23 @@ def train_common( test_dataset = dataset_class(data, split=test_on) logging.debug(f"Training on {len(train_dataset)} samples and validating on {len(val_dataset)} samples.") logging.info(f"Using {num_workers} workers for data loading.") - if(model.__name__ =='TFTpytorch'): - - train_loader = train_dataset.to_dataloader(train=True, batch_size=batch_size, num_workers=0,pin_memory=False,drop_last=True) - val_loader = val_dataset.to_dataloader(train=False, batch_size=batch_size ,num_workers=0,pin_memory=False,drop_last=True) - test_loader = test_dataset.to_dataloader(train=False, batch_size=min(batch_size * 4, len(test_dataset)), num_workers=0,pin_memory=False,drop_last=True,shuffle=False) - model=model(train_dataset,optimizer=optimizer, epochs=epochs, run_mode=mode) - - - - + if model.__name__ == "TFTpytorch": + train_loader = train_dataset.to_dataloader( + train=True, batch_size=batch_size, num_workers=0, pin_memory=False, drop_last=True + ) + val_loader = val_dataset.to_dataloader( + train=False, batch_size=batch_size, num_workers=0, pin_memory=False, drop_last=True + ) + test_loader = test_dataset.to_dataloader( + train=False, + batch_size=min(batch_size * 4, len(test_dataset)), + num_workers=0, + pin_memory=False, + drop_last=True, + shuffle=False, + ) + model = model(train_dataset, optimizer=optimizer, epochs=epochs, run_mode=mode) + else: train_loader = DataLoader( train_dataset, @@ -119,14 +128,13 @@ def train_common( pin_memory=True, drop_last=True, ) - - - if(isinstance(next(iter(train_loader))[0] ,OrderedDict)): + + if isinstance(next(iter(train_loader))[0], OrderedDict): model = model(optimizer=optimizer, epochs=epochs, run_mode=mode) - + else: data_shape = next(iter(train_loader))[0].shape - + model = model(optimizer=optimizer, input_size=data_shape, epochs=epochs, run_mode=mode) model.set_weight(weight, train_dataset) @@ -155,15 +163,14 @@ def train_common( max_epochs=epochs if model.needs_training else 1, callbacks=callbacks, precision=precision, - accelerator= "auto" if not cpu else "cpu", + accelerator="auto" if not cpu else "cpu", devices=max(torch.cuda.device_count(), 1), - deterministic=reproducible, + deterministic=False, benchmark=not reproducible, enable_progress_bar=verbose, logger=loggers, num_sanity_val_steps=0, ) - if model.needs_fit: logging.info("Fitting model to data.") @@ -173,30 +180,23 @@ def train_common( if model.needs_training: logging.info("Training model.") - if(type(model).__name__ =='TFTpytorch'): - trainer.fit(model, train_dataloaders=train_loader, val_dataloaders=val_loader) - else: - trainer.fit(model, train_dataloaders=train_loader, val_dataloaders=val_loader) + trainer.fit(model, train_dataloaders=train_loader, val_dataloaders=val_loader) logging.info("Training complete.") test_dataset = dataset_class(data, split=test_on) test_dataset = assure_minimum_length(test_dataset) - if(type(model).__name__ =='TFTpytorch'): - test_loader = test_dataset.to_dataloader(train=False, batch_size=min(batch_size * 4, len(test_dataset)), num_workers=num_workers,pin_memory=True,drop_last=True,shuffle=False) - test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0]["test/loss"] - else: - - test_loader = ( - DataLoader( - test_dataset, - batch_size=min(batch_size * 4, len(test_dataset)), - shuffle=False, - num_workers=num_workers, - pin_memory=True, - drop_last=True, - ) + + test_loader = ( + DataLoader( + test_dataset, + batch_size=min(batch_size * 4, len(test_dataset)), + shuffle=False, + num_workers=num_workers, + pin_memory=True, + drop_last=True, + ) if model.needs_training else DataLoader([test_dataset.to_tensor()], batch_size=1) ) - test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0]["test/loss"] + test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0]["test/loss"] save_config_file(log_dir) return test_loss diff --git a/icu_benchmarks/models/utils.py b/icu_benchmarks/models/utils.py index ddc97028..53bfcae2 100644 --- a/icu_benchmarks/models/utils.py +++ b/icu_benchmarks/models/utils.py @@ -188,8 +188,3 @@ def version(self): @rank_zero_only def log_hyperparams(self, params): pass -@gin.configurable -def heads_divisible_by_hidden(hidden): - heads_range = range(1, 9) # Range for heads from 1 to 8 - divisible_heads = (heads for heads in heads_range if hidden % heads == 0) - return divisible_heads \ No newline at end of file diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 267da7fa..901e2c8a 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -20,6 +20,7 @@ from pytorch_lightning import LightningModule from icu_benchmarks.models.constants import MLMetrics, DLMetrics from icu_benchmarks.contants import RunMode + gin.config.external_configurable(torch.nn.functional.nll_loss, module="torch.nn.functional") gin.config.external_configurable(torch.nn.functional.cross_entropy, module="torch.nn.functional") gin.config.external_configurable(torch.nn.functional.mse_loss, module="torch.nn.functional") @@ -110,7 +111,6 @@ def __init__( initialization_method: str = "normal", **kwargs, ): - """Interface for Deep Learning models.""" super().__init__() self.save_hyperparameters(ignore=["loss", "optimizer"]) @@ -134,7 +134,9 @@ def finalize_step(self, step_prefix=""): try: self.log_dict( { - f"{step_prefix}/{name}": (np.float32(metric.compute()) if isinstance(metric.compute(), np.float64 ) else metric.compute() ) + f"{step_prefix}/{name}": ( + np.float32(metric.compute()) if isinstance(metric.compute(), np.float64) else metric.compute() + ) for name, metric in self.metrics[step_prefix].items() if "_Curve" not in name }, @@ -226,30 +228,28 @@ def softmax_multi_output_transform(output): def step_fn(self, element, step_prefix=""): """Perform a step in the training loop.""" - if(isinstance(element[0] ,OrderedDict)): - data,mask=element[0], element[1].to(self.device) + if isinstance(element[0], OrderedDict): # check if the data loader is the one for the TFT nvidia implementation + data, mask = element[0], element[1].to(self.device) for key, value in data.items(): value = value.float().to(self.device) - - if value.shape[-1] == 1: value = value.squeeze(-1) if value.dim() == 3: value = value.permute(0, 2, 1) - if(key=='target'): - labels=value.squeeze() + if key == "target": + labels = value.squeeze() data[key] = value - - + elif len(element) == 2: - - if(isinstance(element[1] ,tuple)): + if (isinstance(element[1], tuple)) or ( + isinstance(element[1], list) + ): # check if the data loader is the one for the pytorch forecasring implementation data, labels = element[0], element[1][0] mask = torch.ones_like(labels).bool() else: - data, labels = element[0], element[1].to(self.device) + data, labels = element[0], (element[1]).to(self.device) if isinstance(data, list): for i in range(len(data)): data[i] = data[i].float().to(self.device) @@ -266,13 +266,14 @@ def step_fn(self, element, step_prefix=""): data = data.float().to(self.device) else: raise Exception("Loader should return either (data, label) or (data, label, mask)") + out = self(data) if len(out) == 2 and isinstance(out, tuple): out, aux_loss = out else: aux_loss = 0 prediction = torch.masked_select(out, mask.unsqueeze(-1)).reshape(-1, out.shape[-1]).to(self.device) - + target = torch.masked_select(labels, mask).to(self.device) if prediction.shape[-1] > 1 and self.run_mode == RunMode.classification: # Classification task @@ -280,7 +281,7 @@ def step_fn(self, element, step_prefix=""): # torch.long because NLL elif self.run_mode == RunMode.regression: # Regression task - + loss = self.loss(prediction[:, 0], target.float()) + aux_loss else: raise ValueError(f"Run mode {self.run_mode} not supported.") From 9ef8252d57f0ba400072792ccd9c8f4fb7aaa7d1 Mon Sep 17 00:00:00 2001 From: "youssef.mecky" Date: Thu, 7 Sep 2023 16:04:18 +0200 Subject: [PATCH 042/142] latest --- configs/prediction_models/TFT.gin | 4 +-- configs/prediction_models/TFTpytorch.gin | 6 ++-- configs/prediction_models/common/DLCommon.gin | 2 +- configs/tasks/Regression.gin | 2 +- experiments/benchmark_classification.yml | 27 +++++++------- experiments/benchmark_regression.yml | 15 ++++---- icu_benchmarks/cross_validation.py | 2 +- icu_benchmarks/data/loader.py | 7 ++-- icu_benchmarks/models/dl_models.py | 4 +-- icu_benchmarks/models/layers.py | 6 ++-- icu_benchmarks/models/train.py | 36 ++++++++++--------- 11 files changed, 58 insertions(+), 53 deletions(-) diff --git a/configs/prediction_models/TFT.gin b/configs/prediction_models/TFT.gin index 3abcb239..09fcf537 100644 --- a/configs/prediction_models/TFT.gin +++ b/configs/prediction_models/TFT.gin @@ -13,11 +13,11 @@ optimizer/hyperparameter.lr = (1e-5, 3e-4) # Encoder params model/hyperparameter.class_to_tune = @TFT model/hyperparameter.encoder_length = 24 -model/hyperparameter.hidden = 256 +model/hyperparameter.hidden = (32,64,128,256) model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.dropout = (0.0, 0.4) model/hyperparameter.dropout_att = (0.0, 0.4) -model/hyperparameter.n_heads =4 +model/hyperparameter.n_heads =(1,2,4) model/hyperparameter.example_length=25 diff --git a/configs/prediction_models/TFTpytorch.gin b/configs/prediction_models/TFTpytorch.gin index 8c320809..b10fd4b5 100644 --- a/configs/prediction_models/TFTpytorch.gin +++ b/configs/prediction_models/TFTpytorch.gin @@ -12,13 +12,13 @@ optimizer/hyperparameter.weight_decay = 1e-6 # Encoder params model/hyperparameter.class_to_tune = @TFTpytorch -model/hyperparameter.hidden = 128 +model/hyperparameter.hidden = (16,32,64) model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.dropout = (0.0, 0.4) model/hyperparameter.dropout_att = (0.0, 0.4) -model/hyperparameter.n_heads =4 +model/hyperparameter.n_heads =(1,2,4) model/hyperparameter.lr = (1e-5, 3e-4) PredictionDatasetTFTpytorch.max_encoder_length = 24 -PredictionDatasetTFTpytorch.max_prediction_length = 12 +PredictionDatasetTFTpytorch.max_prediction_length = 24 diff --git a/configs/prediction_models/common/DLCommon.gin b/configs/prediction_models/common/DLCommon.gin index 99251ea2..f41f2104 100644 --- a/configs/prediction_models/common/DLCommon.gin +++ b/configs/prediction_models/common/DLCommon.gin @@ -14,7 +14,7 @@ base_regression_preprocessor.generate_features = False # Train params train_common.optimizer = @Adam -train_common.epochs = 1000 +train_common.epochs = 50 train_common.batch_size = 64 train_common.patience = 10 train_common.min_delta = 1e-4 diff --git a/configs/tasks/Regression.gin b/configs/tasks/Regression.gin index ed7f794a..5cf3f8d9 100644 --- a/configs/tasks/Regression.gin +++ b/configs/tasks/Regression.gin @@ -8,7 +8,7 @@ include "configs/tasks/common/PredictionTaskVariables.gin" include "configs/tasks/common/CrossValidation.gin" # MODE SETTINGS -Run.mode = "RegressionTFT" +Run.mode = "Regression" NUM_CLASSES = 1 HORIZON = 24 train_common.weight = "balanced" diff --git a/experiments/benchmark_classification.yml b/experiments/benchmark_classification.yml index e9a32b4c..bc1c55c6 100644 --- a/experiments/benchmark_classification.yml +++ b/experiments/benchmark_classification.yml @@ -17,18 +17,18 @@ name: yaib_classification_benchmark parameters: data_dir: values: - - data/YAIB_Preprint/aki/miiv - #- data/YAIB_Preprint/aki/hirid - #- data/YAIB_Preprint/aki/eicu - #- data/YAIB_Preprint/aki/aumc - - data/YAIB_Preprint/morality24/miiv - - data/YAIB_Preprint/morality24/miiv - - data/YAIB_Preprint/morality24/miiv - - data/YAIB_Preprint/morality24/miiv - #- data/YAIB_Preprint/sepsis/miiv - #- data/YAIB_Preprint/sepsis/hirid - #- data/YAIB_Preprint/sepsis/eicu - #- data/YAIB_Preprint/sepsis/aumc + # - data/YAIB_Preprint/aki/miiv + # - data/YAIB_Preprint/aki/hirid + # - data/YAIB_Preprint/aki/eicu + # - data/YAIB_Preprint/aki/aumc + # - data/YAIB_Preprint/mortality24/miiv + # - data/YAIB_Preprint/mortality24/hirid + # - data/YAIB_Preprint/mortality24/eicu + # - data/YAIB_Preprint/mortality24/aumc + - data/YAIB_Preprint/sepsis/miiv + - data/YAIB_Preprint/sepsis/hirid + - data/YAIB_Preprint/sepsis/eicu + - data/YAIB_Preprint/sepsis/aumc model: values: # - LogisticRegression @@ -37,7 +37,8 @@ parameters: # - LSTM # - TCN # - Transformer - - TFT + # - TFT + - TFTpytorch seed: values: diff --git a/experiments/benchmark_regression.yml b/experiments/benchmark_regression.yml index 516a4b0f..c13929eb 100644 --- a/experiments/benchmark_regression.yml +++ b/experiments/benchmark_regression.yml @@ -27,13 +27,14 @@ parameters: - data/YAIB_Preprint/kf/aumc model: values: - - ElasticNet - - LGBMRegressor - - GRU - - LSTM - - TCN - - Transformer - - TFT + # - ElasticNet + # - LGBMRegressor + # - GRU + # - LSTM + # - TCN + # - Transformer + # - TFT + - TFTpytorch seed: values: diff --git a/icu_benchmarks/cross_validation.py b/icu_benchmarks/cross_validation.py index 81d8c3ae..0b01992b 100644 --- a/icu_benchmarks/cross_validation.py +++ b/icu_benchmarks/cross_validation.py @@ -21,7 +21,7 @@ def execute_repeated_cv( seed: int, load_weights: bool = False, source_dir: Path = None, - cv_repetitions: int = 5, + cv_repetitions: int = 1, cv_repetitions_to_train: int = None, cv_folds: int = 5, cv_folds_to_train: int = None, diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index c7e8bda2..64eb9a45 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -228,7 +228,8 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: [tensors[5], np.ones((np.shape(tensors[5])[0], self.maxlen - np.shape(tensors[5])[1])) * pad_value], axis=1 ) - tensors[6] = np.concatenate([tensors[6], np.ones(self.maxlen - np.shape(tensors[6])[0]) * pad_value], axis=0) + tensors[6] = np.concatenate([tensors[6], np.ones( + self.maxlen - np.shape(tensors[6])[0]) * pad_value], axis=0) pad_mask = np.concatenate([pad_mask, np.zeros(length_diff)], axis=0) tensors[7] = np.concatenate( [tensors[7], np.ones((np.shape(tensors[7])[0], self.maxlen - np.shape(tensors[7])[1])) * stay_id], axis=1 @@ -417,9 +418,9 @@ def __init__( time_idx="time_idx", target="label", group_ids=["stay_id"], - min_encoder_length=max_encoder_length // 2, + min_encoder_length=max_encoder_length, max_encoder_length=max_encoder_length, - min_prediction_length=2, + min_prediction_length=max_prediction_length, max_prediction_length=max_prediction_length, static_categoricals=[], static_reals=["height", "weight", "age", "sex"], diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index c5360da3..cfc538c8 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -371,7 +371,7 @@ def __init__( ) # embeddings for all variables self.static_encoder = StaticCovariateEncoder(num_static_vars, hidden, dropout) # encoding for static variables - self.TFTpart2 = TFTBack( + self.TFTpart = TFTBack( encoder_length, num_historic_vars, hidden, @@ -410,7 +410,7 @@ def forward(self, x: Dict[str, Tensor]) -> Tensor: if t_known_inp is not None: future_inputs = t_known_inp[:, self.encoder_length :] - o = self.TFTpart2(historical_inputs, cs, ch, cc, ce, future_inputs.to(historical_inputs.device)) + o = self.TFTpart(historical_inputs, cs, ch, cc, ce, future_inputs.to(historical_inputs.device)) pred = self.logit(o) return pred diff --git a/icu_benchmarks/models/layers.py b/icu_benchmarks/models/layers.py index 7307bb52..bd844d64 100644 --- a/icu_benchmarks/models/layers.py +++ b/icu_benchmarks/models/layers.py @@ -743,8 +743,8 @@ def forward(self, x: Tensor) -> Tuple[Tensor, Tensor]: class TFTBack(nn.Module): """ - Big part of TFT architecture consists of static enrichment followed by mutli-head self-attention then position wise feed forward - followed by a gate and a dense layer + Big part of TFT architecture consists of static enrichment followed by mutli-head self-attention then + position wise feed forward followed by a gate and a dense layer GRNs-->multi-head attention-->GRNs-->GLU-->Linear-->output """ @@ -803,7 +803,7 @@ def forward(self, historical_inputs, cs, ch, cc, ce, future_inputs): # Temporal self attention x, attn_prob = self.attention(enriched) - # Don't compute hictorical quantiles + # Don't compute historical quantiles x = x[:, self.encoder_length :, :] temporal_features = temporal_features[:, self.encoder_length :, :] enriched = enriched[:, self.encoder_length :, :] diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 9dc6d96d..61ac14af 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -38,7 +38,7 @@ def train_common( optimizer: type = Adam, precision=32, batch_size=64, - epochs=1000, + epochs=100, patience=20, min_delta=1e-5, test_on: str = Split.test, @@ -96,10 +96,10 @@ def train_common( logging.info(f"Using {num_workers} workers for data loading.") if model.__name__ == "TFTpytorch": train_loader = train_dataset.to_dataloader( - train=True, batch_size=batch_size, num_workers=0, pin_memory=False, drop_last=True + train=True, batch_size=batch_size, num_workers=0, pin_memory=False, drop_last=True, batch_sampler="synchronized" ) val_loader = val_dataset.to_dataloader( - train=False, batch_size=batch_size, num_workers=0, pin_memory=False, drop_last=True + train=False, batch_size=batch_size, num_workers=0, pin_memory=False, drop_last=True, batch_sampler="synchronized" ) test_loader = test_dataset.to_dataloader( train=False, @@ -108,6 +108,7 @@ def train_common( pin_memory=False, drop_last=True, shuffle=False, + batch_sampler="synchronized" ) model = model(train_dataset, optimizer=optimizer, epochs=epochs, run_mode=mode) @@ -128,6 +129,21 @@ def train_common( pin_memory=True, drop_last=True, ) + test_dataset = dataset_class(data, split=test_on) + test_dataset = assure_minimum_length(test_dataset) + + test_loader = ( + DataLoader( + test_dataset, + batch_size=min(batch_size * 4, len(test_dataset)), + shuffle=False, + num_workers=num_workers, + pin_memory=True, + drop_last=True, + ) + if model.needs_training + else DataLoader([test_dataset.to_tensor()], batch_size=1) + ) if isinstance(next(iter(train_loader))[0], OrderedDict): model = model(optimizer=optimizer, epochs=epochs, run_mode=mode) @@ -182,21 +198,7 @@ def train_common( logging.info("Training model.") trainer.fit(model, train_dataloaders=train_loader, val_dataloaders=val_loader) logging.info("Training complete.") - test_dataset = dataset_class(data, split=test_on) - test_dataset = assure_minimum_length(test_dataset) - test_loader = ( - DataLoader( - test_dataset, - batch_size=min(batch_size * 4, len(test_dataset)), - shuffle=False, - num_workers=num_workers, - pin_memory=True, - drop_last=True, - ) - if model.needs_training - else DataLoader([test_dataset.to_tensor()], batch_size=1) - ) test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0]["test/loss"] save_config_file(log_dir) return test_loss From 3c9b729a4f559b2ab584728a12217602aefc8e19 Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 12 Sep 2023 15:33:41 +0200 Subject: [PATCH 043/142] reproducible forced to be there for evaulate --- icu_benchmarks/run.py | 52 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/icu_benchmarks/run.py b/icu_benchmarks/run.py index 0f8b7859..f91f69b8 100644 --- a/icu_benchmarks/run.py +++ b/icu_benchmarks/run.py @@ -9,7 +9,11 @@ import torch.cuda -from icu_benchmarks.wandb_utils import update_wandb_config, apply_wandb_sweep, set_wandb_run_name +from icu_benchmarks.wandb_utils import ( + update_wandb_config, + apply_wandb_sweep, + set_wandb_run_name, +) from icu_benchmarks.tuning.hyperparameters import choose_and_bind_hyperparameters from scripts.plotting.utils import plot_aggregated_results from icu_benchmarks.cross_validation import execute_repeated_cv @@ -45,6 +49,7 @@ def main(my_args=tuple(sys.argv[1:])): setup_logging(date_format, log_format, verbose) # Load weights if in evaluation mode + load_weights = args.command == "evaluate" data_dir = Path(args.data_dir) @@ -52,7 +57,8 @@ def main(my_args=tuple(sys.argv[1:])): name = args.name task = args.task model = args.model - reproducible = args.reproducible + + reproducible = args.reproducible if args.command != "evaluate" else False # Set experiment name if name is None: @@ -71,7 +77,9 @@ def main(my_args=tuple(sys.argv[1:])): logging.info(f"Task mode: {mode}.") experiment = args.experiment - pretrained_imputation_model = load_pretrained_imputation_model(args.pretrained_imputation) + pretrained_imputation_model = load_pretrained_imputation_model( + args.pretrained_imputation + ) # Log imputation model to wandb update_wandb_config( @@ -86,29 +94,43 @@ def main(my_args=tuple(sys.argv[1:])): log_dir = ( (log_dir_name / experiment) if experiment - else (log_dir_name / (args.task_name if args.task_name is not None else args.task) / model) + else ( + log_dir_name + / (args.task_name if args.task_name is not None else args.task) + / model + ) ) if torch.cuda.is_available(): for name in range(0, torch.cuda.device_count()): - log_full_line(f"Available GPU {name}: {torch.cuda.get_device_name(name)}", level=logging.INFO) + log_full_line( + f"Available GPU {name}: {torch.cuda.get_device_name(name)}", + level=logging.INFO, + ) else: log_full_line( - "No GPUs available: please check your device and Torch,Cuda installation if unintended.", level=logging.WARNING + "No GPUs available: please check your device and Torch,Cuda installation if unintended.", + level=logging.WARNING, ) log_full_line(f"Logging to {log_dir}.", logging.INFO) if args.preprocessor: # Import custom supplied preprocessor - log_full_line(f"Importing custom preprocessor from {args.preprocessor}.", logging.INFO) + log_full_line( + f"Importing custom preprocessor from {args.preprocessor}.", logging.INFO + ) try: - spec = importlib.util.spec_from_file_location("CustomPreprocessor", args.preprocessor) + spec = importlib.util.spec_from_file_location( + "CustomPreprocessor", args.preprocessor + ) module = importlib.util.module_from_spec(spec) sys.modules["preprocessor"] = module spec.loader.exec_module(module) gin.bind_parameter("preprocess.preprocessor", module.CustomPreprocessor) except Exception as e: - logging.error(f"Could not import custom preprocessor from {args.preprocessor}: {e}") + logging.error( + f"Could not import custom preprocessor from {args.preprocessor}: {e}" + ) if load_weights: # Evaluate @@ -120,14 +142,22 @@ def main(my_args=tuple(sys.argv[1:])): # Train checkpoint = log_dir / args.checkpoint if args.checkpoint else None model_path = ( - Path("configs") / ("imputation_models" if mode == RunMode.imputation else "prediction_models") / f"{model}.gin" + Path("configs") + / ( + "imputation_models" + if mode == RunMode.imputation + else "prediction_models" + ) + / f"{model}.gin" ) gin_config_files = ( [Path(f"configs/experiments/{args.experiment}.gin")] if args.experiment else [model_path, Path(f"configs/tasks/{task}.gin")] ) - gin.parse_config_files_and_bindings(gin_config_files, args.hyperparams, finalize_config=False) + gin.parse_config_files_and_bindings( + gin_config_files, args.hyperparams, finalize_config=False + ) log_full_line(f"Data directory: {data_dir.resolve()}", level=logging.INFO) run_dir = create_run_dir(log_dir) choose_and_bind_hyperparameters( From 03ad5c2e550719c9775ed6f8f5c6c18e72f99129 Mon Sep 17 00:00:00 2001 From: youssef mecky Date: Tue, 12 Sep 2023 15:35:34 +0200 Subject: [PATCH 044/142] added v1 to load model --- icu_benchmarks/models/train.py | 65 ++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 61ac14af..8de301c5 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -9,14 +9,21 @@ from pytorch_lightning import Trainer from pytorch_lightning.callbacks import EarlyStopping, ModelCheckpoint, TQDMProgressBar from pathlib import Path -from icu_benchmarks.data.loader import PredictionDataset, ImputationDataset, PredictionDatasetTFT, PredictionDatasetTFTpytorch +from icu_benchmarks.data.loader import ( + PredictionDataset, + ImputationDataset, + PredictionDatasetTFT, + PredictionDatasetTFTpytorch, +) from icu_benchmarks.models.utils import save_config_file, JSONMetricsLogger from icu_benchmarks.contants import RunMode from icu_benchmarks.data.constants import DataSplit as Split from collections import OrderedDict -cpu_core_count = len(os.sched_getaffinity(0)) if hasattr(os, "sched_getaffinity") else os.cpu_count() +cpu_core_count = ( + len(os.sched_getaffinity(0)) if hasattr(os, "sched_getaffinity") else os.cpu_count() +) def assure_minimum_length(dataset): @@ -46,7 +53,11 @@ def train_common( cpu: bool = False, verbose=True, ram_cache=False, - num_workers: int = min(cpu_core_count, torch.cuda.device_count() * 4 * int(torch.cuda.is_available()), 32), + num_workers: int = min( + cpu_core_count, + torch.cuda.device_count() * 4 * int(torch.cuda.is_available()), + 32, + ), ): """Common wrapper to train all benchmarked models. @@ -80,26 +91,46 @@ def train_common( else ( PredictionDatasetTFT if model.__name__ == "TFT" - else (PredictionDatasetTFTpytorch if model.__name__ == "TFTpytorch" else PredictionDataset) + else ( + PredictionDatasetTFTpytorch + if model.__name__ == "TFTpytorch" + else PredictionDataset + ) ) ) logging.info(f"Logging to directory: {log_dir}.") - save_config_file(log_dir) # We save the operative config before and also after training + save_config_file( + log_dir + ) # We save the operative config before and also after training train_dataset = dataset_class(data, split=Split.train, ram_cache=ram_cache) val_dataset = dataset_class(data, split=Split.val, ram_cache=ram_cache) - train_dataset, val_dataset = assure_minimum_length(train_dataset), assure_minimum_length(val_dataset) + train_dataset, val_dataset = assure_minimum_length( + train_dataset + ), assure_minimum_length(val_dataset) batch_size = min(batch_size, len(train_dataset), len(val_dataset)) test_dataset = dataset_class(data, split=test_on) - logging.debug(f"Training on {len(train_dataset)} samples and validating on {len(val_dataset)} samples.") + logging.debug( + f"Training on {len(train_dataset)} samples and validating on {len(val_dataset)} samples." + ) logging.info(f"Using {num_workers} workers for data loading.") if model.__name__ == "TFTpytorch": train_loader = train_dataset.to_dataloader( - train=True, batch_size=batch_size, num_workers=0, pin_memory=False, drop_last=True, batch_sampler="synchronized" + train=True, + batch_size=batch_size, + num_workers=0, + pin_memory=False, + drop_last=True, + batch_sampler="synchronized", ) val_loader = val_dataset.to_dataloader( - train=False, batch_size=batch_size, num_workers=0, pin_memory=False, drop_last=True, batch_sampler="synchronized" + train=False, + batch_size=batch_size, + num_workers=0, + pin_memory=False, + drop_last=True, + batch_sampler="synchronized", ) test_loader = test_dataset.to_dataloader( train=False, @@ -108,7 +139,7 @@ def train_common( pin_memory=False, drop_last=True, shuffle=False, - batch_sampler="synchronized" + batch_sampler="synchronized", ) model = model(train_dataset, optimizer=optimizer, epochs=epochs, run_mode=mode) @@ -151,13 +182,15 @@ def train_common( else: data_shape = next(iter(train_loader))[0].shape - model = model(optimizer=optimizer, input_size=data_shape, epochs=epochs, run_mode=mode) + model = model( + optimizer=optimizer, input_size=data_shape, epochs=epochs, run_mode=mode + ) model.set_weight(weight, train_dataset) if load_weights: if source_dir.exists(): # if not model.needs_training: - checkpoint = torch.load(source_dir / "model.ckpt") + checkpoint = torch.load(source_dir / "model-v1.ckpt") # else: model = model.load_state_dict(checkpoint["state_dict"]) else: @@ -168,7 +201,9 @@ def train_common( loggers = [TensorBoardLogger(log_dir), JSONMetricsLogger(log_dir)] callbacks = [ - EarlyStopping(monitor="val/loss", min_delta=min_delta, patience=patience, strict=False), + EarlyStopping( + monitor="val/loss", min_delta=min_delta, patience=patience, strict=False + ), ModelCheckpoint(log_dir, filename="model", save_top_k=1, save_last=True), ] if verbose: @@ -199,6 +234,8 @@ def train_common( trainer.fit(model, train_dataloaders=train_loader, val_dataloaders=val_loader) logging.info("Training complete.") - test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0]["test/loss"] + test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0][ + "test/loss" + ] save_config_file(log_dir) return test_loss From be61df80806026236a86363ad6a68a6ab4a48736 Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Tue, 12 Sep 2023 17:43:44 +0200 Subject: [PATCH 045/142] merged --- icu_benchmarks/data/Checking data-Copy1.ipynb | 582 ++++++++++++++++++ output.txt | 475 ++++++++++++++ saved_dictionary.pkl | Bin 0 -> 20818976 bytes 3 files changed, 1057 insertions(+) create mode 100644 icu_benchmarks/data/Checking data-Copy1.ipynb create mode 100644 output.txt create mode 100644 saved_dictionary.pkl diff --git a/icu_benchmarks/data/Checking data-Copy1.ipynb b/icu_benchmarks/data/Checking data-Copy1.ipynb new file mode 100644 index 00000000..9abe4d48 --- /dev/null +++ b/icu_benchmarks/data/Checking data-Copy1.ipynb @@ -0,0 +1,582 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 4, + "id": "6b471a18", + "metadata": {}, + "outputs": [ + { + "ename": "ModuleNotFoundError", + "evalue": "No module named 'icu_benchmarks'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)", + "Input \u001b[0;32mIn [4]\u001b[0m, in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01micu_benchmarks\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mdata\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01msplit_process_data\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m preprocess_data\n", + "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'icu_benchmarks'" + ] + } + ], + "source": [ + "from icu_benchmarks.data.split_process_data import preprocess_data" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "9786d96d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n" + ] + } + ], + "source": [ + "print(1-1)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "b9f28515", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "dyn=pd.read_parquet('../../demo_data/los/eicu_demo/dyn.parquet', engine='pyarrow')\n", + "outc=pd.read_parquet('../../demo_data/los/eicu_demo/outc.parquet', engine='pyarrow')\n", + "sta=pd.read_parquet('../../demo_data/los/eicu_demo/sta.parquet', engine='pyarrow')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "35342dde", + "metadata": {}, + "outputs": [], + "source": [ + "dyn.columns" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c6f3e4ae", + "metadata": {}, + "outputs": [], + "source": [ + "dyn.columns" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "64c217e9", + "metadata": {}, + "outputs": [], + "source": [ + "outc.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "87724d04", + "metadata": {}, + "outputs": [], + "source": [ + "sta.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fddac54c", + "metadata": {}, + "outputs": [], + "source": [ + "sta.columns" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "265af37f", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import List\n", + "from pandas import DataFrame\n", + "import numpy as np\n", + "import gin\n", + "from torch import Tensor, cat, from_numpy, float32,empty,stack\n", + "from torch.utils.data import Dataset\n", + "import logging\n", + "from typing import Dict, Tuple\n", + "from constants import DataSegment as Segment\n", + "from constants import DataSplit as Split\n", + "from collections import OrderedDict\n", + "\n", + "FEAT_NAMES = ['s_cat' , 's_cont' , 'k_cat' , 'k_cont' , 'o_cat' , 'o_cont' , 'target', 'id']\n", + "def ampute_data(data, mechanism, p_miss, p_obs=0.3):\n", + " \"\"\"\n", + " Generate missing values for specifics missing-data mechanism and proportion of missing values.\n", + "\n", + " Parameters\n", + " ----------\n", + " data : DataFrame\n", + " Data for which missing values will be simulated.\n", + " mechanism : str,\n", + " Indicates the missing-data mechanism to be used. (\"MCAR\", \"MAR\" or \"MNAR\")\n", + " p_miss : float\n", + " Proportion of missing values to generate for variables which will have missing values.\n", + " p_obs : float\n", + " If mecha = \"MAR\" or \"MNAR\", proportion of variables with *no* missing values\n", + " that will be used for the logistic masking model.\n", + "\n", + " Returns\n", + " ----------\n", + " imputed_data: DataFrame\n", + " The data with the generated missing values.\n", + " \"\"\"\n", + " logging.info(f\"Applying {mechanism} amputation.\")\n", + " X = torch.tensor(data.values.astype(np.float32))\n", + "\n", + " if mechanism == \"MAR\":\n", + " mask = MAR_logistic_mask(X, p_miss, p_obs)\n", + " elif mechanism == \"MNAR\":\n", + " mask = MNAR_logistic_mask(X, p_miss, p_obs)\n", + " elif mechanism == \"MCAR\":\n", + " mask = MCAR_mask(X, p_miss)\n", + " elif mechanism == \"BO\":\n", + " mask = BO_mask(X, p_miss)\n", + " else:\n", + " logging.error(\"Not a valid amputation mechanism. Missing-data mechanisms to be used are MCAR, MAR or MNAR.\")\n", + "\n", + " amputed_data = data.mask(mask)\n", + " return amputed_data, mask\n", + "class CommonDataset(Dataset):\n", + " \"\"\"Common dataset: subclass of Torch Dataset that represents the data to learn on.\n", + "\n", + " Args: data: Dict of the different splits of the data. split: Either 'train','val' or 'test'. vars: Contains the names of\n", + " columns in the data. grouping_segment: str, optional: The segment of the data contains the grouping column with only\n", + " unique values. Defaults to Segment.outcome. Is used to calculate the number of stays in the data.\n", + " \"\"\"\n", + "\n", + " def __init__(\n", + " self,\n", + " data: dict,\n", + " split: str = Split.train,\n", + " vars: Dict[str, str] = gin.REQUIRED,\n", + " grouping_segment: str = Segment.outcome,\n", + " ):\n", + " self.split = split\n", + " self.vars = vars\n", + " self.grouping_df = data[split][grouping_segment].set_index(self.vars[\"GROUP\"])\n", + " self.features_df = (\n", + " #drops time coulmn and sets index to stay_id\n", + " data[split][Segment.features].set_index(self.vars[\"GROUP\"]).drop(labels=self.vars[\"SEQUENCE\"], axis=1)\n", + " )\n", + "\n", + " # calculate basic info for the data\n", + " self.num_stays = self.grouping_df.index.unique().shape[0]\n", + " self.maxlen = self.features_df.groupby([self.vars[\"GROUP\"]]).size().max()\n", + "\n", + " def ram_cache(self, cache: bool = True):\n", + " self._cached_dataset = None\n", + " if cache:\n", + " logging.info(\"Caching dataset in ram.\")\n", + " self._cached_dataset = [self[i] for i in range(len(self))]\n", + "\n", + " def __len__(self) -> int:\n", + " \"\"\"Returns number of stays in the data.\n", + "\n", + " Returns:\n", + " number of stays in the data\n", + " \"\"\"\n", + " return self.num_stays\n", + "\n", + " def get_feature_names(self):\n", + " return self.features_df.columns\n", + "\n", + " def to_tensor(self):\n", + " values = []\n", + " for entry in self:\n", + " for i, value in enumerate(entry):\n", + " if len(values) <= i:\n", + " values.append([])\n", + " values[i].append(value.unsqueeze(0))\n", + " return [cat(value, dim=0) for value in values]\n", + "\n", + "\n", + "@gin.configurable(\"PredictionDataset\")\n", + "class PredictionDataset(CommonDataset):\n", + " \"\"\"Subclass of common dataset for prediction tasks.\n", + "\n", + " Args:\n", + " ram_cache (bool, optional): Whether the complete dataset should be stored in ram. Defaults to True.\n", + " \"\"\"\n", + "\n", + " def __init__(self, *args, ram_cache: bool = True, **kwargs):\n", + " super().__init__(*args, grouping_segment=Segment.outcome, **kwargs)\n", + " self.outcome_df = self.grouping_df\n", + " self.ram_cache(ram_cache)\n", + "\n", + " def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]:\n", + " \"\"\"Function to sample from the data split of choice. Used for deep learning implementations.\n", + "\n", + " Args:\n", + " idx: A specific row index to sample.\n", + "\n", + " Returns:\n", + " A sample from the data, consisting of data, labels and padding mask.\n", + " \"\"\"\n", + " if self._cached_dataset is not None:\n", + " return self._cached_dataset[idx]\n", + "\n", + " pad_value = 0.0\n", + " stay_id = self.outcome_df.index.unique()[idx] # [self.vars[\"GROUP\"]]\n", + "\n", + " # slice to make sure to always return a DF\n", + " window = self.features_df.loc[stay_id:stay_id].to_numpy()\n", + " labels = self.outcome_df.loc[stay_id:stay_id][self.vars[\"LABEL\"]].to_numpy(dtype=float)\n", + "\n", + " if len(labels) == 1:\n", + " # only one label per stay, align with window\n", + " labels = np.concatenate([np.empty(window.shape[0] - 1) * np.nan, labels], axis=0)\n", + "\n", + " length_diff = self.maxlen - window.shape[0]\n", + " \n", + " \n", + " pad_mask = np.ones(window.shape[0])\n", + "\n", + " # Padding the array to fulfill size requirement\n", + " if length_diff > 0:\n", + " # window shorter than the longest window in dataset, pad to same length\n", + " window = np.concatenate([window, np.ones((length_diff, window.shape[1])) * pad_value], axis=0)\n", + " labels = np.concatenate([labels, np.ones(length_diff) * pad_value], axis=0)\n", + " pad_mask = np.concatenate([pad_mask, np.zeros(length_diff)], axis=0)\n", + "\n", + " not_labeled = np.argwhere(np.isnan(labels))\n", + " if len(not_labeled) > 0:\n", + " labels[not_labeled] = -1\n", + " pad_mask[not_labeled] = 0\n", + "\n", + " pad_mask = pad_mask.astype(bool)\n", + " labels = labels.astype(np.float32)\n", + " data = window.astype(np.float32)\n", + "\n", + " return from_numpy(data), from_numpy(labels), from_numpy(pad_mask)\n", + "\n", + " def get_balance(self) -> list:\n", + " \"\"\"Return the weight balance for the split of interest.\n", + "\n", + " Returns:\n", + " Weights for each label.\n", + " \"\"\"\n", + " counts = self.outcome_df[self.vars[\"LABEL\"]].value_counts()\n", + " return list((1 / counts) * np.sum(counts) / counts.shape[0])\n", + "\n", + " def get_data_and_labels(self) -> Tuple[np.array, np.array]:\n", + " \"\"\"Function to return all the data and labels aligned at once.\n", + "\n", + " We use this function for the ML methods which don't require an iterator.\n", + "\n", + " Returns:\n", + " A Tuple containing data points and label for the split.\n", + " \"\"\"\n", + " labels = self.outcome_df[self.vars[\"LABEL\"]].to_numpy().astype(float)\n", + " rep = self.features_df\n", + " if len(labels) == self.num_stays:\n", + " # order of groups could be random, we make sure not to change it\n", + " rep = rep.groupby(level=self.vars[\"GROUP\"], sort=False).last()\n", + " rep = rep.to_numpy().astype(float)\n", + " \n", + " return rep, labels\n", + "\n", + " def to_tensor(self):\n", + " data, labels = self.get_data_and_labels()\n", + " return from_numpy(data).to(float32), from_numpy(labels).to(float32)\n", + "\n", + "@gin.configurable(\"PredictionDatasetTFT\")\n", + "class PredictionDatasetTFT(PredictionDataset):\n", + " \"\"\"Subclass of prediction dataset for TFT as we need to define if variables are cont,static,known or observed.\n", + "\n", + " Args:\n", + " ram_cache (bool, optional): Whether the complete dataset should be stored in ram. Defaults to True.\n", + " \"\"\"\n", + "\n", + " def __init__(self, *args, ram_cache: bool = True, **kwargs):\n", + " super().__init__(*args,ram_cache = True, **kwargs)\n", + " \n", + "\n", + " def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]:\n", + " \"\"\"Function to sample from the data split of choice. Used for TFT.\n", + "\n", + " Args:\n", + " idx: A specific row index to sample.\n", + "\n", + " Returns:\n", + " A sample from the data, consisting of data, labels and padding mask.\n", + " \"\"\"\n", + " if self._cached_dataset is not None:\n", + " return self._cached_dataset[idx]\n", + "\n", + " pad_value = 0.0\n", + " stay_id = self.outcome_df.index.unique()[idx] # [self.vars[\"GROUP\"]]\n", + "\n", + " \n", + " # We need to be sure that tensors are returned in the correct order to be processed correclty by tft\n", + " tensors = [[] for _ in range(8)]\n", + " for var in self.features_df.columns:\n", + " if var == 'sex' :\n", + " tensors[0].append(self.features_df.loc[stay_id:stay_id][var].to_numpy())\n", + " elif var == 'age' or var== 'height' or var== 'weight':\n", + " tensors[1].append(self.features_df.loc[stay_id:stay_id][var].to_numpy())\n", + " else :\n", + " tensors[5].append(self.features_df.loc[stay_id:stay_id][var].to_numpy())\n", + " \n", + " \n", + " tensors[6].extend(self.outcome_df.loc[stay_id:stay_id][self.vars[\"LABEL\"]].to_numpy(dtype=float))\n", + " tensors[7].append(np.asarray([stay_id]))\n", + " \n", + " window_shape0=np.shape(tensors[0])[1]\n", + " # window = self.features_df.loc[stay_id:stay_id].to_numpy()\n", + " # labels = self.outcome_df.loc[stay_id:stay_id][self.vars[\"LABEL\"]].to_numpy(dtype=float)\n", + " if len(tensors[6]) == 1:\n", + " # only one label per stay, align with window\n", + " tensors[6] = np.concatenate([np.empty(window_shape0 - 1) * np.nan, tensors[6]], axis=0)\n", + "\n", + " length_diff = self.maxlen - window_shape0\n", + " pad_mask = np.ones(window_shape0)\n", + "\n", + " # Padding the array to fulfill size requirement\n", + " if length_diff > 0:\n", + " # window shorter than the longest window in dataset, pad to same length\n", + " tensors[0]= np.concatenate([tensors[0], np.ones((length_diff, np.shape(tensors[0])[0])) * pad_value], axis=0)\n", + " tensors[1]= np.concatenate([tensors[1], np.ones((length_diff, np.shape(tensors[1])[0])) * pad_value], axis=0)\n", + " # tensors[2]= np.concatenate([tensors[2], np.ones((length_diff, np.shape(tensors[2])[0])) * pad_value], axis=0)\n", + " # tensors[3]= np.concatenate([tensors[3], np.ones((length_diff, np.shape(tensors[3])[0])) * pad_value], axis=0)\n", + " # tensors[4]= np.concatenate([tensors[4], np.ones((length_diff, np.shape(tensors[4])[0])) * pad_value], axis=0)\n", + " tensors[5]= np.concatenate([tensors[5], np.ones((length_diff, np.shape(tensors[5])[0])) * pad_value], axis=0)\n", + " tensors[7]= np.concatenate([tensors[7], np.ones((length_diff, np.shape(tensors[7])[0])) * stay_id], axis=0)\n", + " # window = np.concatenate([window, np.ones((length_diff, window.shape[1])) * pad_value], axis=0)\n", + " \n", + " tensors[6] = np.concatenate([tensors[6], np.ones(length_diff) * pad_value], axis=0)\n", + " pad_mask = np.concatenate([pad_mask, np.zeros(length_diff)], axis=0)\n", + "\n", + " not_labeled = np.argwhere(np.isnan(tensors[6]))\n", + " if len(not_labeled) > 0:\n", + " tensors[6][not_labeled] = -1\n", + " pad_mask[not_labeled] = 0\n", + " tensors[6]=[tensors[6]]\n", + " pad_mask = pad_mask.astype(bool)\n", + " # data = window.astype(np.float32)\n", + " tensors = (from_numpy(np.array(tensor)).to(float32) for tensor in tensors)\n", + " tensors = [stack((x,), dim=-1) if x.numel() > 0 else empty(0) for x in tensors]\n", + " \n", + " return OrderedDict(zip(FEAT_NAMES, tensors)),from_numpy(pad_mask)\n", + "\n", + " \n", + "\n", + "\n", + "\n", + "@gin.configurable(\"ImputationDataset\")\n", + "class ImputationDataset(CommonDataset):\n", + " \"\"\"Subclass of Common Dataset that contains data for imputation models.\"\"\"\n", + "\n", + " def __init__(\n", + " self,\n", + " data: Dict[str, DataFrame],\n", + " split: str = Split.train,\n", + " vars: Dict[str, str] = gin.REQUIRED,\n", + " mask_proportion=0.3,\n", + " mask_method=\"MCAR\",\n", + " mask_observation_proportion=0.3,\n", + " ram_cache: bool = True,\n", + " ):\n", + " \"\"\"\n", + " Args:\n", + " data (Dict[str, DataFrame]): data to use\n", + " split (str, optional): split to apply. Defaults to Split.train.\n", + " vars (Dict[str, str], optional): contains names of columns in the data. Defaults to gin.REQUIRED.\n", + " mask_proportion (float, optional): proportion to artificially mask for amputation. Defaults to 0.3.\n", + " mask_method (str, optional): masking mechanism. Defaults to \"MCAR\".\n", + " mask_observation_proportion (float, optional): poportion of the observed data to be masked. Defaults to 0.3.\n", + " ram_cache (bool, optional): if the dataset should be completely stored in ram and not generated on the fly during\n", + " training. Defaults to True.\n", + " \"\"\"\n", + " super().__init__(data, split, vars, grouping_segment=Segment.static)\n", + " self.amputated_values, self.amputation_mask = ampute_data(\n", + " self.features_df, mask_method, mask_proportion, mask_observation_proportion\n", + " )\n", + " self.amputation_mask = (self.amputation_mask + self.features_df.isna().values).bool()\n", + " self.amputation_mask = DataFrame(self.amputation_mask, columns=self.vars[Segment.dynamic])\n", + " self.amputation_mask[self.vars[\"GROUP\"]] = self.features_df.index\n", + " self.amputation_mask.set_index(self.vars[\"GROUP\"], inplace=True)\n", + "\n", + " self.target_missingness_mask = self.features_df.isna()\n", + " self.features_df.fillna(0, inplace=True)\n", + " self.ram_cache(ram_cache)\n", + "\n", + " def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]:\n", + " \"\"\"Function to sample from the data split of choice.\n", + "\n", + " Used for deep learning implementations.\n", + "\n", + " Args:\n", + " idx: A specific row index to sample.\n", + "\n", + " Returns:\n", + " A sample from the data, consisting of data, labels and padding mask.\n", + " \"\"\"\n", + " if self._cached_dataset is not None:\n", + " return self._cached_dataset[idx]\n", + " stay_id = self.grouping_df.iloc[idx].name\n", + "\n", + " # slice to make sure to always return a DF\n", + " window = self.features_df.loc[stay_id:stay_id, self.vars[Segment.dynamic]]\n", + " window_missingness_mask = self.target_missingness_mask.loc[stay_id:stay_id, self.vars[Segment.dynamic]]\n", + " amputated_window = self.amputated_values.loc[stay_id:stay_id, self.vars[Segment.dynamic]]\n", + " amputation_mask = self.amputation_mask.loc[stay_id:stay_id, self.vars[Segment.dynamic]]\n", + "\n", + " return (\n", + " from_numpy(amputated_window.values).to(float32),\n", + " from_numpy(amputation_mask.values).to(float32),\n", + " from_numpy(window.values).to(float32),\n", + " from_numpy(window_missingness_mask.values).to(float32),\n", + " )\n", + "\n", + "\n", + "@gin.configurable(\"ImputationPredictionDataset\")\n", + "class ImputationPredictionDataset(Dataset):\n", + " \"\"\"Subclass of torch dataset that represents data with missingness for imputation.\n", + "\n", + " Args:\n", + " data (DataFrame): dict of the different splits of the data\n", + " grouping_column (str, optional): column that is used for grouping. Defaults to \"stay_id\".\n", + " select_columns (List[str], optional): the columns to serve as input for the imputation model. Defaults to None.\n", + " ram_cache (bool, optional): wether the dataset should be stored in ram. Defaults to True.\n", + " \"\"\"\n", + "\n", + " def __init__(\n", + " self,\n", + " data: DataFrame,\n", + " grouping_column: str = \"stay_id\",\n", + " select_columns: List[str] = None,\n", + " ram_cache: bool = True,\n", + " ):\n", + " self.dyn_df = data\n", + "\n", + " if select_columns is not None:\n", + " self.dyn_df = self.dyn_df[list(select_columns) + grouping_column]\n", + "\n", + " if grouping_column is not None:\n", + " self.dyn_df = self.dyn_df.set_index(grouping_column)\n", + " else:\n", + " self.dyn_df = data\n", + "\n", + " # calculate basic info for the data\n", + " self.group_indices = self.dyn_df.index.unique()\n", + " self.maxlen = self.dyn_df.groupby(grouping_column).size().max()\n", + "\n", + " self._cached_dataset = None\n", + " if ram_cache:\n", + " logging.info(\"Caching dataset in ram.\")\n", + " self._cached_dataset = [self[i] for i in range(len(self))]\n", + "\n", + " def __len__(self) -> int:\n", + " \"\"\"Returns number of stays in the data.\n", + "\n", + " Returns:\n", + " number of stays in the data\n", + " \"\"\"\n", + " return self.group_indices.shape[0]\n", + "\n", + " def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]:\n", + " \"\"\"Function to sample from the data split of choice.\n", + "\n", + " Used for deep learning implementations.\n", + "\n", + " Args:\n", + " idx: A specific row index to sample.\n", + "\n", + " Returns:\n", + " A sample from the data, consisting of data, labels and padding mask.\n", + " \"\"\"\n", + " if self._cached_dataset is not None:\n", + " return self._cached_dataset[idx]\n", + " stay_id = self.group_indices[idx]\n", + "\n", + " # slice to make sure to always return a DF\n", + " window = self.dyn_df.loc[stay_id:stay_id, :]\n", + "\n", + " return from_numpy(window.values).to(float32)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "504fd6c4", + "metadata": {}, + "outputs": [ + { + "ename": "KeyError", + "evalue": "'train'\n In call to configurable 'PredictionDataset' ()", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "Input \u001b[0;32mIn [9]\u001b[0m, in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0m train_dataset \u001b[38;5;241m=\u001b[39m \u001b[43mPredictionDataset\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdyn\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msplit\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mSplit\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtrain\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/opt/anaconda3/lib/python3.9/site-packages/gin/config.py:1605\u001b[0m, in \u001b[0;36m_make_gin_wrapper..gin_wrapper\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 1603\u001b[0m scope_info \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m in scope \u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;241m.\u001b[39mformat(scope_str) \u001b[38;5;28;01mif\u001b[39;00m scope_str \u001b[38;5;28;01melse\u001b[39;00m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[1;32m 1604\u001b[0m err_str \u001b[38;5;241m=\u001b[39m err_str\u001b[38;5;241m.\u001b[39mformat(name, fn_or_cls, scope_info)\n\u001b[0;32m-> 1605\u001b[0m \u001b[43mutils\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43maugment_exception_message_and_reraise\u001b[49m\u001b[43m(\u001b[49m\u001b[43me\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43merr_str\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/opt/anaconda3/lib/python3.9/site-packages/gin/utils.py:41\u001b[0m, in \u001b[0;36maugment_exception_message_and_reraise\u001b[0;34m(exception, message)\u001b[0m\n\u001b[1;32m 39\u001b[0m proxy \u001b[38;5;241m=\u001b[39m ExceptionProxy()\n\u001b[1;32m 40\u001b[0m ExceptionProxy\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__qualname__\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mtype\u001b[39m(exception)\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__qualname__\u001b[39m\n\u001b[0;32m---> 41\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m proxy\u001b[38;5;241m.\u001b[39mwith_traceback(exception\u001b[38;5;241m.\u001b[39m__traceback__) \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;28mNone\u001b[39m\n", + "File \u001b[0;32m~/opt/anaconda3/lib/python3.9/site-packages/gin/config.py:1582\u001b[0m, in \u001b[0;36m_make_gin_wrapper..gin_wrapper\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 1579\u001b[0m new_kwargs\u001b[38;5;241m.\u001b[39mupdate(kwargs)\n\u001b[1;32m 1581\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m-> 1582\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mnew_args\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mnew_kwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1583\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e: \u001b[38;5;66;03m# pylint: disable=broad-except\u001b[39;00m\n\u001b[1;32m 1584\u001b[0m err_str \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m'\u001b[39m\n", + "Input \u001b[0;32mIn [3]\u001b[0m, in \u001b[0;36mPredictionDataset.__init__\u001b[0;34m(self, ram_cache, *args, **kwargs)\u001b[0m\n\u001b[1;32m 113\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__init__\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;241m*\u001b[39margs, ram_cache: \u001b[38;5;28mbool\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mTrue\u001b[39;00m, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[0;32m--> 114\u001b[0m \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[38;5;21;43m__init__\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mgrouping_segment\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mSegment\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43moutcome\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 115\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39moutcome_df \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mgrouping_df\n\u001b[1;32m 116\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mram_cache(ram_cache)\n", + "Input \u001b[0;32mIn [3]\u001b[0m, in \u001b[0;36mCommonDataset.__init__\u001b[0;34m(self, data, split, vars, grouping_segment)\u001b[0m\n\u001b[1;32m 66\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39msplit \u001b[38;5;241m=\u001b[39m split\n\u001b[1;32m 67\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mvars \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mvars\u001b[39m\n\u001b[0;32m---> 68\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mgrouping_df \u001b[38;5;241m=\u001b[39m \u001b[43mdata\u001b[49m\u001b[43m[\u001b[49m\u001b[43msplit\u001b[49m\u001b[43m]\u001b[49m[grouping_segment]\u001b[38;5;241m.\u001b[39mset_index(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mvars[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mGROUP\u001b[39m\u001b[38;5;124m\"\u001b[39m])\n\u001b[1;32m 69\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfeatures_df \u001b[38;5;241m=\u001b[39m (\n\u001b[1;32m 70\u001b[0m \u001b[38;5;66;03m#drops time coulmn and sets index to stay_id\u001b[39;00m\n\u001b[1;32m 71\u001b[0m data[split][Segment\u001b[38;5;241m.\u001b[39mfeatures]\u001b[38;5;241m.\u001b[39mset_index(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mvars[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mGROUP\u001b[39m\u001b[38;5;124m\"\u001b[39m])\u001b[38;5;241m.\u001b[39mdrop(labels\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mvars[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mSEQUENCE\u001b[39m\u001b[38;5;124m\"\u001b[39m], axis\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m1\u001b[39m)\n\u001b[1;32m 72\u001b[0m )\n\u001b[1;32m 74\u001b[0m \u001b[38;5;66;03m# calculate basic info for the data\u001b[39;00m\n", + "File \u001b[0;32m~/opt/anaconda3/lib/python3.9/site-packages/pandas/core/frame.py:3505\u001b[0m, in \u001b[0;36mDataFrame.__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 3503\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mcolumns\u001b[38;5;241m.\u001b[39mnlevels \u001b[38;5;241m>\u001b[39m \u001b[38;5;241m1\u001b[39m:\n\u001b[1;32m 3504\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_getitem_multilevel(key)\n\u001b[0;32m-> 3505\u001b[0m indexer \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcolumns\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget_loc\u001b[49m\u001b[43m(\u001b[49m\u001b[43mkey\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 3506\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m is_integer(indexer):\n\u001b[1;32m 3507\u001b[0m indexer \u001b[38;5;241m=\u001b[39m [indexer]\n", + "File \u001b[0;32m~/opt/anaconda3/lib/python3.9/site-packages/pandas/core/indexes/base.py:3631\u001b[0m, in \u001b[0;36mIndex.get_loc\u001b[0;34m(self, key, method, tolerance)\u001b[0m\n\u001b[1;32m 3629\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_engine\u001b[38;5;241m.\u001b[39mget_loc(casted_key)\n\u001b[1;32m 3630\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m err:\n\u001b[0;32m-> 3631\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m(key) \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01merr\u001b[39;00m\n\u001b[1;32m 3632\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m:\n\u001b[1;32m 3633\u001b[0m \u001b[38;5;66;03m# If we have a listlike key, _check_indexing_error will raise\u001b[39;00m\n\u001b[1;32m 3634\u001b[0m \u001b[38;5;66;03m# InvalidIndexError. Otherwise we fall through and re-raise\u001b[39;00m\n\u001b[1;32m 3635\u001b[0m \u001b[38;5;66;03m# the TypeError.\u001b[39;00m\n\u001b[1;32m 3636\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_check_indexing_error(key)\n", + "\u001b[0;31mKeyError\u001b[0m: 'train'\n In call to configurable 'PredictionDataset' ()" + ] + } + ], + "source": [ + "train_dataset = PredictionDataset(dyn, split=Split.train)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c7f48f70", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "yaib", + "language": "python", + "name": "yaib" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/output.txt b/output.txt new file mode 100644 index 00000000..c197bcbd --- /dev/null +++ b/output.txt @@ -0,0 +1,475 @@ +{'encoder_cat': tensor([], size=(64, 6, 0), dtype=torch.int64), 'encoder_cont': tensor([[[ 0.1205, -1.3078, -2.2605, ..., 0.0569, 0.5067, 0.2127], + [ 0.1205, -1.3078, -2.2605, ..., 0.0569, 0.5067, 0.2127], + [ 0.1205, -1.3078, -2.2605, ..., 0.0569, 0.5067, 0.2127], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000]], + + [[-0.9524, -0.5048, -0.2978, ..., 0.0569, 0.5067, 0.2127], + [-0.9524, -0.5048, -0.2978, ..., 0.0569, 0.5067, 0.2127], + [-0.9524, -0.5048, -0.2978, ..., 0.0569, 0.5067, 0.2127], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000]], + + [[ 0.1205, -0.6483, -2.2605, ..., 0.0569, 0.5067, 0.2127], + [ 0.1205, -0.6483, -2.2605, ..., 0.0569, 0.5067, 0.2127], + [ 0.1205, -0.6483, -2.2605, ..., 0.0569, 0.5067, 0.2127], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000]], + + ..., + + [[ 0.3433, 0.4882, 1.6016, ..., 0.0569, 0.5067, 0.2127], + [ 0.3433, 0.4882, 1.6016, ..., 0.0569, 0.5067, 0.2127], + [ 0.3433, 0.4882, 1.6016, ..., 0.0569, 0.5067, 0.2127], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000]], + + [[ 0.1205, 0.4882, -1.0575, ..., 0.0569, 0.5067, 0.2127], + [ 0.1205, 0.4882, -1.0575, ..., 0.0569, -1.9736, -4.7018], + [ 0.1205, 0.4882, -1.0575, ..., 0.0569, 0.5067, 0.2127], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000]], + + [[-0.5397, 1.7179, 0.7785, ..., 0.0569, 0.5067, -4.7018], + [-0.5397, 1.7179, 0.7785, ..., 0.0569, 0.5067, 0.2127], + [-0.5397, 1.7179, 0.7785, ..., 0.0569, -1.9736, 0.2127], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000]]]), 'encoder_target': tensor([[0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 1, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [1, 1, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0]]), 'encoder_lengths': tensor([3, 3, 3, 3, 3, 6, 6, 6, 6, 6, 3, 6, 6, 6, 6, 3, 3, 3, 3, 5, 6, 6, 3, 3, + 6, 6, 6, 6, 3, 6, 6, 6, 6, 6, 3, 6, 6, 6, 6, 3, 6, 6, 6, 3, 6, 3, 6, 6, + 6, 6, 3, 3, 6, 3, 3, 5, 6, 6, 5, 3, 6, 3, 3, 3]), 'decoder_cat': tensor([], size=(64, 12, 0), dtype=torch.int64), 'decoder_cont': tensor([[[ 0.1205, -1.3078, -2.2605, ..., 0.0569, 0.5067, 0.2127], + [ 0.1205, -1.3078, -2.2605, ..., 0.0569, 0.5067, 0.2127], + [ 0.1205, -1.3078, -2.2605, ..., 0.0569, 0.5067, 0.2127], + ..., + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000]], + + [[-0.9524, -0.5048, -0.2978, ..., 0.0569, 0.5067, 0.2127], + [-0.9524, -0.5048, -0.2978, ..., 0.0569, 0.5067, 0.2127], + [-0.9524, -0.5048, -0.2978, ..., 0.0569, 0.5067, 0.2127], + ..., + [-0.9524, -0.5048, -0.2978, ..., 0.0569, 0.5067, 0.2127], + [-0.9524, -0.5048, -0.2978, ..., 0.0569, 0.5067, -4.7018], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000]], + + [[ 0.1205, -0.6483, -2.2605, ..., 0.0569, 0.5067, 0.2127], + [ 0.1205, -0.6483, -2.2605, ..., 0.0569, 0.5067, 0.2127], + [ 0.1205, -0.6483, -2.2605, ..., 0.0569, 0.5067, 0.2127], + ..., + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000]], + + ..., + + [[ 0.3433, 0.4882, 1.6016, ..., 0.0569, 0.5067, 0.2127], + [ 0.3433, 0.4882, 1.6016, ..., 0.0569, 0.5067, 0.2127], + [ 0.3433, 0.4882, 1.6016, ..., 0.0569, 0.5067, 0.2127], + ..., + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000]], + + [[ 0.1205, 0.4882, -1.0575, ..., 0.0569, 0.5067, 0.2127], + [ 0.1205, 0.4882, -1.0575, ..., 0.0569, 0.5067, 0.2127], + [ 0.1205, 0.4882, -1.0575, ..., 0.0569, 0.5067, 0.2127], + ..., + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000]], + + [[-0.5397, 1.7179, 0.7785, ..., 0.0569, 0.5067, 0.2127], + [-0.5397, 1.7179, 0.7785, ..., 0.0569, 0.5067, 0.2127], + [-0.5397, 1.7179, 0.7785, ..., 0.0569, 0.5067, 0.2127], + ..., + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], + [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000]]]), 'decoder_target': tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]), 'decoder_lengths': tensor([ 3, 11, 8, 8, 9, 12, 12, 12, 12, 12, 10, 12, 12, 12, 12, 7, 6, 3, + 8, 12, 12, 12, 2, 12, 12, 12, 12, 12, 5, 12, 12, 12, 12, 12, 11, 12, + 12, 12, 12, 8, 12, 12, 12, 3, 12, 7, 12, 12, 12, 12, 9, 6, 12, 12, + 4, 12, 12, 12, 12, 7, 12, 7, 5, 4]), 'decoder_time_idx': tensor([[ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21], + [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], + [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], + [ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28], + [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], + [ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26], + [ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56], + [ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59], + [ 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66], + [ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22], + [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], + [ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47], + [128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139], + [ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17], + [ 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83], + [ 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97], + [106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117], + [ 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35], + [ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78], + [ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], + [ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50], + [ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49], + [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], + [ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19], + [ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18], + [ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73], + [ 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45], + [ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37], + [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], + [114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125], + [ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49], + [125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136], + [ 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44], + [109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120], + [ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17], + [ 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34], + [ 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72], + [ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49], + [ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21], + [ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25], + [ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17], + [ 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70], + [ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46], + [ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49], + [ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36], + [ 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68], + [ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19], + [ 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53], + [ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80], + [ 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39], + [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], + [ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57], + [ 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48], + [ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41], + [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], + [ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], + [ 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68], + [ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91], + [ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55], + [ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21], + [ 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103], + [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], + [ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21], + [113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124]]), 'groups': tensor([[173], + [283], + [107], + [ 34], + [388], + [429], + [126], + [376], + [265], + [213], + [314], + [431], + [185], + [360], + [247], + [203], + [245], + [222], + [299], + [ 43], + [190], + [181], + [345], + [ 16], + [278], + [452], + [395], + [518], + [564], + [266], + [ 76], + [187], + [464], + [266], + [527], + [ 11], + [315], + [ 42], + [513], + [105], + [468], + [313], + [188], + [525], + [201], + [554], + [559], + [551], + [181], + [549], + [335], + [382], + [369], + [436], + [389], + [145], + [475], + [442], + [ 43], + [527], + [207], + [554], + [331], + [239]]), 'target_scale': tensor([[0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.], + [0., 0.]])} +(tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]), None) diff --git a/saved_dictionary.pkl b/saved_dictionary.pkl new file mode 100644 index 0000000000000000000000000000000000000000..8a15fe6a723a8fdaa797ab67c82361959fa6c0a4 GIT binary patch literal 20818976 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 z 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ΦTgGlbb)SHPIu3fdhCf0|5{K0T2KI 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�SG_<0uX?JJb~?Z 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 z0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI z5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X z009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH z0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI z5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X z009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH z0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI z5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X z009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH z0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI z5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X z009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH z0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI z5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X z009sH0T2KI5C8!XP!Q<1^?w4IdNp-#+@N9Eh7Jw&mo^>L)X?Jlzl|LmS7=(1;xxvVxhvhCT+-M0MHQpoD7Ig|(l zKmY_l00cmwRtapm{ww#-KjgzQHF?9<4J{_On^Ky}OLpnIaNox!mqMmeua|GK$;hAg z8dpNb<&a8yfE^$J0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4ea zAOHd&00JP8Gl88Sy?T26oEZhT++pSo`r( zSLWRIV0-=q<_u`Mw9hT$O5d{URZmPDyz=NWRhfTH`YA!vgO|1F*m`)8tNi-HGmcvB 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%0D&5Lse`|^n9$$E%(Tsga|J^ISE zmsXyJ^a84{AM~VQ9G7}Yh(7((5B*Xe(R?)x=@t7u6Q z@?Z9+_O1L56wmNO;z0gLT*u#t|Fjo}@~iBEb9?#xCI8RP^8fsta&aTO>>j1(WBj9d zf!|jjU3z0Y-F@^fXY<3Zt8bi8{2`4_tnb{^`AVdVU;AA9fAq=K{6qR}zb{Ur zx7yR!m=?!Fdai$ZKrZM?(`#s6p!QI`yl6cdukqjm-}*Hl^2LYzm~#*EpjYD3y!`cA z-~8fNJer2$3iB7`!7iCk@hv-O{Opi)JU1jydTo6`eCtnpdd?oPKh~rEj9*%N{X%j^ zSDYxVUDVSk7y76_e3O$nK-?hi@%+p4N9$UB`#gxQ{>&5oh4>>+`S{abenj=OCl~UP zubuiZtzP60J^AQB{TMGfK;wb(BbrC`p!+YL`+MGGp2TmS>zEJczw9u(f^YrfpFTOy zl`qZS@H70c{V#n=L(iR{_yjsHvk#?jN#jFDJ$4g4&yCQNPd?&gsGjFp>dR+OjGsNV zU*@0WtH%zrOX}m#IP_yY^39|E^`kvLjbFS+p7JB&BcgsFf1$nS5YomcjbC)}3(+Mf z^yEY3(&$0@PR{htdStKZebi_6**cH*fxcUh{43;_p!qV7#zj8(Bv0`uzM*>j6}dw5 zN>Ayt=eOpceDI|nBu8{1yUGvRr}1mzaB;Eyi~UyI2Z?jli|2RFN5sFvo;2fHL{Ky}9FX(7~?N96*vyNN)SU)L0 z)=Nv9cuX@tz%a=~V*e=#%2iYO^%0AV;iv1F=Sl`gPkH~+B10nmN9Li6tN8har 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≧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

A@4=6dJ^n=PnQSwF2gR*CopQCU9Rp&*; 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

E(RuQ$H*Jr@YgLF7NC9U#xki#V#M~_|Lp2 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 z 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 z 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⁡ip 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 z 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%0AClIq(=1jMU#X=(&2sIq6-L4kMLIxiu3i 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%0D_sFEw3)}BZ*Q?g; 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 z zCF zgIA3le6B%mh^_3u6z%KDXGv3K!M zTxCD@`QF!5-#Hk$^z$C7af|EnLiL?5Id9Uh=PKfT7_W_6K5IR(zM6mX=q*olA9~&- zuQv|)EV+1B&v}ac);xJ{_FXR=z1EU{n0Fw=(0%08Pee)-7 z@K56uNBH}KANsneA>xcgM5Z|EogY|>&=U4~WMW`S7_)yP!VgG8J;*s}u_^r5Op1gl$ey!`y2Sem1 z`WY%{T4;}AnR z%Hp8-qKtR>CqCJw@u_bf#7q7IOMaYriKS6Bqay|1po^g#4JF(gz>Vc#N0+#>L)TSKs`LpZF0+yDy zAI=wkAiENWw1dv!W^RG+kNpE-||Lf_Cim3gyUDwI3Ydt2l1GN#$z7wV7}cq zuljM{c-5nSXz#|se#q0#{)K(JZr+XCd@CD2`PL6{UK!%ue#^Yz3+k7ANH2(o@V_^0nvJA)0@3+$YC6Ek0N;!~V1jQGIBil7;q{)+_NqT(@q8 z`R&Ln_nSCvsksJRSC1cCC$mtz7iYD%zKNf%E89=gS9@{IIh%g1(_uYw#BKeuBhT^p z2Y(Vb?dQpHo&8(ytl#cuwKE=Z*gPonV{-VvcA*~p!hDD;+Cy<3zvSYRzV2)1x-vQX zF>mrH?X5%Jn-V{u{>6FmT>j?0bn`1e^FBM?{eDAPJKv!gvG18DjX(UgRQ`6{)5p#I z;B6xYl;wf!)b}Cyh2P)O>#|E{OxR|*5d-vMcjWoqv-<3jeJIlxf6D9=;uHTNs%QQ~ z`%tEjvUxR+`k{yUqc8dFg*hlBgmFY{qyoJ8nhjRIg`_@5mz_`pm zdHRFmLc7S1v(Wz7xai9s@P&8xt$W5{T=EF@@u4igbZ(xt-dfkh6+A$G3&lZb-k|e6 za-nrpKk_f_+-INSHNA~jd+VUzGsr8*wa$B=Ond7syVK4(U>#S_d7^cleCsYA=q2Cy z_U4~o_wL^<(ihs1``(xK9YFaMJ&m7y`+N84OP+HO`WY9bzkI~JWTEF-@{~tSqxIbRuee1nJNMj$ ze$Eg1FTK2HtG@Sdtyk75>(C#*yU;zWoHlyE^EKaz{l*@BtIYSwdHX!Ka*pKO={rXq zbl#Ekyf*mvyJkP~-0ZKsZ}#B#uARGw_53b^A2_%1dokxauKT?TJ9gdqJbj$wL;85H zm|Xq%9f30W>O--iv=M{gA55L-rBgPHA|Au$#CV$ee`sDF*aZA7a z8~@^hIA#9G;lFs+pYhO7o+^&0FV7{`;H~PJi_F-lDunzTsTVzDYc^u7!3_fA1;kU*61q@IW5D&8zV^=V8yrD_+X$#9L@R zw%?Y|7#|+#fp77~ef@{|20QmV4)f%FNOogg!!teDC%gB&njZRhZfo6A&-~$ueDlPQ zJcqPCkz;-H+}nCX4xW_Bu@1?f>~oaq>$y9B4CT0Q9b^Z_%}&@8Ir?!vflp|h^jF_H z%0A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�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

jyeZ99o+w{i( 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 WW(c2cG%T$6|HXo!8GbWn^!zWIX7f1! literal 0 HcmV?d00001 From 42815d7683318dc22340e8e83e2da2a7d19f8716 Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Tue, 12 Sep 2023 18:00:38 +0200 Subject: [PATCH 046/142] match original --- icu_benchmarks/run.py | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/icu_benchmarks/run.py b/icu_benchmarks/run.py index 5b00d31d..a729fb1b 100644 --- a/icu_benchmarks/run.py +++ b/icu_benchmarks/run.py @@ -5,11 +5,10 @@ import sys from pathlib import Path import torch.cuda - from icu_benchmarks.wandb_utils import ( update_wandb_config, apply_wandb_sweep, - set_wandb_run_name, + set_wandb_experiment_name, ) from icu_benchmarks.tuning.hyperparameters import choose_and_bind_hyperparameters from scripts.plotting.utils import plot_aggregated_results @@ -44,14 +43,19 @@ def main(my_args=tuple(sys.argv[1:])): date_format = "%Y-%m-%d %H:%M:%S" verbose = args.verbose setup_logging(date_format, log_format, verbose) - - # Load weights if in evaluation mode - load_weights = args.command == "evaluate" + # Get arguments data_dir = Path(args.data_dir) name = args.name task = args.task model = args.model reproducible = args.reproducible + evaluate = args.eval + experiment = args.experiment + source_dir = args.source_dir + # Load task config + gin.parse_config_file(f"configs/tasks/{task}.gin") + mode = get_mode() + # Set arguments for wandb sweep # Set experiment name if name is None: @@ -73,7 +77,6 @@ def main(my_args=tuple(sys.argv[1:])): pretrained_imputation_model = load_pretrained_imputation_model( args.pretrained_imputation ) - # Log imputation model to wandb update_wandb_config( { @@ -109,22 +112,7 @@ def main(my_args=tuple(sys.argv[1:])): ) if args.preprocessor: - # Import custom supplied preprocessor - log_full_line( - f"Importing custom preprocessor from {args.preprocessor}.", logging.INFO - ) - try: - spec = importlib.util.spec_from_file_location( - "CustomPreprocessor", args.preprocessor - ) - module = importlib.util.module_from_spec(spec) - sys.modules["preprocessor"] = module - spec.loader.exec_module(module) - gin.bind_parameter("preprocess.preprocessor", module.CustomPreprocessor) - except Exception as e: - logging.error( - f"Could not import custom preprocessor from {args.preprocessor}: {e}" - ) + import_preprocessor(args.preprocessor) # Load pretrained model in evaluate mode or when finetuning if load_weights: From b07152058b1c8bdb48e0234916659ea6ab92a9e3 Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Tue, 12 Sep 2023 18:00:52 +0200 Subject: [PATCH 047/142] readded tft dataloader --- icu_benchmarks/data/loader.py | 129 ++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 5644ec69..8bf38fef 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -172,6 +172,135 @@ def to_tensor(self): return from_numpy(data), from_numpy(labels) +@gin.configurable("PredictionDatasetTFT") +class PredictionDatasetTFT(PredictionDataset): + """Subclass of prediction dataset for TFT as we need to define if variables are cont,static,known or observed. + We also need to feed the model the variables in a specific order + Args: + ram_cache (bool, optional): Whether the complete dataset should be stored in ram. Defaults to True. + """ + + def __init__(self, *args, ram_cache: bool = True, **kwargs): + super().__init__(*args, ram_cache=True, **kwargs) + + def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: + """Function to sample from the data split of choice. Used for TFT. + The data needs to be given to the model in the following order + [static categorical,static contious,known catergorical,known continous, observed categorical, observed continous,target ,id] + Args: + idx: A specific row index to sample. + Returns: + A sample from the data, consisting of data, labels and padding mask. + """ + if self._cached_dataset is not None: + return self._cached_dataset[idx] + + pad_value = 0.0 + stay_id = self.outcome_df.index.unique()[idx] + + # We need to be sure that tensors are returned in the correct order to be processed correclty by tft + tensors = [[] for _ in range(8)] + for var in self.features_df.columns: + if var == "sex": + tensors[0].append(self.features_df.loc[stay_id:stay_id][var].to_numpy()) + elif var == "age" or var == "height" or var == "weight": + tensors[1].append(self.features_df.loc[stay_id:stay_id][var].to_numpy()) + elif "MissingIndicator" in var: + tensors[4].append(self.features_df.loc[stay_id:stay_id][var].to_numpy()) + else: + tensors[5].append(self.features_df.loc[stay_id:stay_id][var].to_numpy()) + + tensors[6].extend( + self.outcome_df.loc[stay_id:stay_id][self.vars["LABEL"]].to_numpy( + dtype=float + ) + ) + tensors[7].append(np.asarray([stay_id])) + window_shape0 = np.shape(tensors[0])[1] + + if len(tensors[6]) == 1: + # only one label per stay, align with window + tensors[6] = np.concatenate( + [np.empty(window_shape0 - 1) * np.nan, tensors[6]], axis=0 + ) + + length_diff = self.maxlen - window_shape0 + pad_mask = np.ones(window_shape0) + # Padding the array to fulfill size requirement + + if length_diff > 0: + # window shorter than the longest window in dataset, pad to same length + tensors[0] = np.concatenate( + [ + tensors[0], + np.ones( + (np.shape(tensors[0])[0], self.maxlen - np.shape(tensors[0])[1]) + ) + * pad_value, + ], + axis=1, + ) + tensors[1] = np.concatenate( + [ + tensors[1], + np.ones( + (np.shape(tensors[1])[0], self.maxlen - np.shape(tensors[1])[1]) + ) + * pad_value, + ], + axis=1, + ) + tensors[4] = np.concatenate( + [ + tensors[4], + np.ones( + (np.shape(tensors[4])[0], self.maxlen - np.shape(tensors[4])[1]) + ) + * pad_value, + ], + axis=1, + ) + tensors[5] = np.concatenate( + [ + tensors[5], + np.ones( + (np.shape(tensors[5])[0], self.maxlen - np.shape(tensors[5])[1]) + ) + * pad_value, + ], + axis=1, + ) + + tensors[6] = np.concatenate( + [ + tensors[6], + np.ones(self.maxlen - np.shape(tensors[6])[0]) * pad_value, + ], + axis=0, + ) + pad_mask = np.concatenate([pad_mask, np.zeros(length_diff)], axis=0) + tensors[7] = np.concatenate( + [ + tensors[7], + np.ones( + (np.shape(tensors[7])[0], self.maxlen - np.shape(tensors[7])[1]) + ) + * stay_id, + ], + axis=1, + ) # should be done regardless of length_diff + not_labeled = np.argwhere(np.isnan(tensors[6])) + if len(not_labeled) > 0: + tensors[6][not_labeled] = -1 + pad_mask[not_labeled] = 0 + tensors[6] = [tensors[6]] + pad_mask = pad_mask.astype(bool) + + tensors = (from_numpy(np.array(tensor)).to(float32) for tensor in tensors) + tensors = [stack((x,), dim=-1) if x.numel() > 0 else empty(0) for x in tensors] + return OrderedDict(zip(Features.FEAT_NAMES, tensors)), from_numpy(pad_mask) + + @gin.configurable("ImputationDataset") class ImputationDataset(CommonDataset): """Subclass of Common Dataset that contains data for imputation models.""" From 778c619025d58d7e487f715b5a6e9bc3ecba5c05 Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Tue, 12 Sep 2023 18:04:24 +0200 Subject: [PATCH 048/142] readded ordered dict --- icu_benchmarks/models/wrappers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 4b0fb7e9..3426b71b 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -10,7 +10,7 @@ import torch.nn as nn from torch import Tensor, FloatTensor from torch.optim import Optimizer, Adam - +from collections import OrderedDict import inspect import gin import numpy as np From 3bcb969104a9f108450ff13faf2ac10eba9600f1 Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Tue, 12 Sep 2023 18:04:43 +0200 Subject: [PATCH 049/142] merged with dev --- icu_benchmarks/models/train.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 2f7401f4..d1b8dfb8 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -190,7 +190,7 @@ def train_common( pin_memory=True, drop_last=True, ) - if model.needs_training + if model.requires_backprop else DataLoader([test_dataset.to_tensor()], batch_size=1) ) From 9d4f4a7dffd9a30361bb6b1176f103a95270844a Mon Sep 17 00:00:00 2001 From: "youssef.mecky" Date: Wed, 13 Sep 2023 13:39:35 +0200 Subject: [PATCH 050/142] changed if condition for loading models --- icu_benchmarks/models/train.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index d1b8dfb8..8e3cbfed 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -126,7 +126,8 @@ def train_common( train_dataset ), assure_minimum_length(val_dataset) batch_size = min(batch_size, len(train_dataset), len(val_dataset)) - + test_dataset = dataset_class(data, split=test_on) + test_dataset = assure_minimum_length(test_dataset) if not eval_only: logging.info( f"Training on {train_dataset.name} with {len(train_dataset)} samples and validating on {val_dataset.name} with" @@ -137,7 +138,7 @@ def train_common( train_loader = train_dataset.to_dataloader( train=True, batch_size=batch_size, - num_workers=0, + num_workers=num_workers, pin_memory=False, drop_last=True, batch_sampler="synchronized", @@ -145,7 +146,7 @@ def train_common( val_loader = val_dataset.to_dataloader( train=False, batch_size=batch_size, - num_workers=0, + num_workers=num_workers, pin_memory=False, drop_last=True, batch_sampler="synchronized", @@ -153,7 +154,7 @@ def train_common( test_loader = test_dataset.to_dataloader( train=False, batch_size=min(batch_size * 4, len(test_dataset)), - num_workers=0, + num_workers=num_workers, pin_memory=False, drop_last=True, shuffle=False, @@ -178,8 +179,6 @@ def train_common( pin_memory=True, drop_last=True, ) - test_dataset = dataset_class(data, split=test_on) - test_dataset = assure_minimum_length(test_dataset) test_loader = ( DataLoader( @@ -199,13 +198,12 @@ def train_common( else: data_shape = next(iter(train_loader))[0].shape + model = model( + optimizer=optimizer, input_size=data_shape, epochs=epochs, run_mode=mode + ) if load_weights: model = load_model(model, source_dir, pl_model=pl_model) - else: - model = model( - optimizer=optimizer, input_size=data_shape, epochs=epochs, run_mode=mode - ) model.set_weight(weight, train_dataset) From a15fd405b886764e2868689567567deec60a72da Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Wed, 13 Sep 2023 13:57:14 +0200 Subject: [PATCH 051/142] reformatted --- icu_benchmarks/cross_validation.py | 6 +-- icu_benchmarks/data/loader.py | 3 +- icu_benchmarks/data/pooling.py | 56 +++++++++---------- icu_benchmarks/imputation/diffwave.py | 71 +++++++----------------- icu_benchmarks/models/layers.py | 2 +- icu_benchmarks/models/metrics.py | 16 ++---- icu_benchmarks/models/train.py | 38 ++++--------- icu_benchmarks/models/wrappers.py | 78 +++++++-------------------- icu_benchmarks/run.py | 42 +++------------ 9 files changed, 92 insertions(+), 220 deletions(-) diff --git a/icu_benchmarks/cross_validation.py b/icu_benchmarks/cross_validation.py index e68610b4..bdcfdd43 100644 --- a/icu_benchmarks/cross_validation.py +++ b/icu_benchmarks/cross_validation.py @@ -37,7 +37,7 @@ def execute_repeated_cv( cpu: bool = False, verbose: bool = False, wandb: bool = False, - complete_train: bool = False + complete_train: bool = False, ) -> float: """Preprocesses data and trains a model for each fold. @@ -101,7 +101,7 @@ def execute_repeated_cv( fold_index=fold_index, pretrained_imputation_model=pretrained_imputation_model, runmode=mode, - complete_train=complete_train + complete_train=complete_train, ) preprocess_time = datetime.now() - start_time @@ -118,7 +118,7 @@ def execute_repeated_cv( cpu=cpu, verbose=verbose, use_wandb=wandb, - train_only=complete_train + train_only=complete_train, ) train_time = datetime.now() - start_time diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 8bf38fef..d09ff055 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -186,7 +186,8 @@ def __init__(self, *args, ram_cache: bool = True, **kwargs): def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: """Function to sample from the data split of choice. Used for TFT. The data needs to be given to the model in the following order - [static categorical,static contious,known catergorical,known continous, observed categorical, observed continous,target ,id] + [static categorical,static contious,known catergorical,known continous, + observed categorical, observed continous,target ,id] Args: idx: A specific row index to sample. Returns: diff --git a/icu_benchmarks/data/pooling.py b/icu_benchmarks/data/pooling.py index 7eeb2949..48689d35 100644 --- a/icu_benchmarks/data/pooling.py +++ b/icu_benchmarks/data/pooling.py @@ -16,16 +16,17 @@ class PooledDataset: class PooledData: - def __init__(self, - data_dir, - vars, - datasets, - file_names, - shuffle=False, - stratify=None, - runmode=RunMode.classification, - save_test=True, - ): + def __init__( + self, + data_dir, + vars, + datasets, + file_names, + shuffle=False, + stratify=None, + runmode=RunMode.classification, + save_test=True, + ): """ Generate pooled data from existing datasets. Args: @@ -48,10 +49,10 @@ def __init__(self, self.save_test = save_test def generate( - self, - datasets, - samples=10000, - seed=42, + self, + datasets, + samples=10000, + seed=42, ): """ Generate pooled data from existing datasets. @@ -65,8 +66,8 @@ def generate( if folder.is_dir(): if folder.name in datasets: data[folder.name] = { - f: pq.read_table(folder / self.file_names[f]).to_pandas(self_destruct=True) for f in - self.file_names.keys() + f: pq.read_table(folder / self.file_names[f]).to_pandas(self_destruct=True) + for f in self.file_names.keys() } data = self._pool_datasets( datasets=data, @@ -101,15 +102,15 @@ def _save_pooled_data(self, data_dir, data, datasets, file_names, samples=10000) logging.info(f"Saved pooled data at {save_dir}") def _pool_datasets( - self, - datasets={}, - samples=10000, - vars=[], - seed=42, - shuffle=True, - runmode=RunMode.classification, - data_dir=Path("data"), - save_test=True, + self, + datasets={}, + samples=10000, + vars=[], + seed=42, + shuffle=True, + runmode=RunMode.classification, + data_dir=Path("data"), + save_test=True, ): """ Pool datasets into a single dataset. @@ -144,8 +145,9 @@ def _pool_datasets( # If we have more outcomes than stays, check max label value per stay id labels = outcome.groupby(id).max()[vars[Var.label]].reset_index(drop=True) # if pd.Series(outcome[id].unique()) is outcome[id]): - selected_stays = train_test_split(stays, stratify=labels, shuffle=shuffle, random_state=seed, - train_size=samples) + selected_stays = train_test_split( + stays, stratify=labels, shuffle=shuffle, random_state=seed, train_size=samples + ) else: selected_stays = train_test_split(stays, shuffle=shuffle, random_state=seed, train_size=samples) # Select only stays that are in the selected_stays diff --git a/icu_benchmarks/imputation/diffwave.py b/icu_benchmarks/imputation/diffwave.py index 1ba9b705..c2f8ecf1 100644 --- a/icu_benchmarks/imputation/diffwave.py +++ b/icu_benchmarks/imputation/diffwave.py @@ -47,9 +47,7 @@ def __init__( **kwargs, ) - self.init_conv = nn.Sequential( - Conv(in_channels, res_channels, kernel_size=1), nn.ReLU() - ) + self.init_conv = nn.Sequential(Conv(in_channels, res_channels, kernel_size=1), nn.ReLU()) self.residual_layer = Residual_group( res_channels=res_channels, @@ -68,15 +66,11 @@ def __init__( ZeroConv1d(skip_channels, out_channels), ) - self.diffusion_parameters = calc_diffusion_hyperparams( - diffusion_time_steps, beta_0, beta_T - ) + self.diffusion_parameters = calc_diffusion_hyperparams(diffusion_time_steps, beta_0, beta_T) def on_fit_start(self) -> None: self.diffusion_parameters = { - k: v.to(self.device) - for k, v in self.diffusion_parameters.items() - if isinstance(v, torch.Tensor) + k: v.to(self.device) for k, v in self.diffusion_parameters.items() if isinstance(v, torch.Tensor) } return super().on_fit_start() @@ -107,15 +101,12 @@ def step_fn(self, batch, step_prefix=""): ) B, C, L = amputated_data.shape # B is batchsize, C=1, L is audio length - diffusion_steps = torch.randint(T, size=(B, 1, 1)).to( - self.device - ) # randomly sample diffusion steps from 1~T + diffusion_steps = torch.randint(T, size=(B, 1, 1)).to(self.device) # randomly sample diffusion steps from 1~T z = std_normal(amputated_data.shape, self.device) z = amputated_data * observed_mask.float() + z * (1 - observed_mask).float() transformed_X = ( - torch.sqrt(Alpha_bar[diffusion_steps]) * amputated_data - + torch.sqrt(1 - Alpha_bar[diffusion_steps]) * z + torch.sqrt(Alpha_bar[diffusion_steps]) * amputated_data + torch.sqrt(1 - Alpha_bar[diffusion_steps]) * z ) # compute x_t from q(x_t|x_0) epsilon_theta = self( ( @@ -126,16 +117,12 @@ def step_fn(self, batch, step_prefix=""): ) ) # predict \epsilon according to \epsilon_\theta - loss = self.loss( - epsilon_theta[amputation_mask.bool()], z[amputation_mask.bool()] - ) + loss = self.loss(epsilon_theta[amputation_mask.bool()], z[amputation_mask.bool()]) else: target = target.permute(0, 2, 1) target_missingness = target_missingness.permute(0, 2, 1) imputed_data = self.sampling(amputated_data, observed_mask) - amputated_data[amputation_mask.bool()] = imputed_data[ - amputation_mask.bool() - ] + amputated_data[amputation_mask.bool()] = imputed_data[amputation_mask.bool()] amputated_data[target_missingness > 0] = target[target_missingness > 0] loss = self.loss(amputated_data, target) for metric in self.metrics[step_prefix].values(): @@ -180,9 +167,7 @@ def sampling(self, cond, mask): for t in range(T - 1, -1, -1): x = x * (1 - mask).float() + cond * mask.float() - diffusion_steps = (t * torch.ones((B, 1))).to( - self.device - ) # use the corresponding reverse step + diffusion_steps = (t * torch.ones((B, 1))).to(self.device) # use the corresponding reverse step epsilon_theta = self( ( x, @@ -192,13 +177,9 @@ def sampling(self, cond, mask): ) ) # predict \epsilon according to \epsilon_\theta # update x_{t-1} to \mu_\theta(x_t) - x = ( - x - (1 - Alpha[t]) / torch.sqrt(1 - Alpha_bar[t]) * epsilon_theta - ) / torch.sqrt(Alpha[t]) + x = (x - (1 - Alpha[t]) / torch.sqrt(1 - Alpha_bar[t]) * epsilon_theta) / torch.sqrt(Alpha[t]) if t > 0: - x = x + Sigma[t] * std_normal( - cond.shape, self.device - ) # add the variance term to x_{t-1} + x = x + Sigma[t] * std_normal(cond.shape, self.device) # add the variance term to x_{t-1} return x @@ -254,9 +235,7 @@ def calc_diffusion_hyperparams(diffusion_time_steps, beta_0, beta_T): Beta_tilde = Beta + 0 for t in range(1, diffusion_time_steps): Alpha_bar[t] *= Alpha_bar[t - 1] # \bar{\alpha}_t = \prod_{s=1}^t \alpha_s - Beta_tilde[t] *= (1 - Alpha_bar[t - 1]) / ( - 1 - Alpha_bar[t] - ) # \tilde{\beta}_t = \beta_t * (1-\bar{\alpha}_{t-1}) + Beta_tilde[t] *= (1 - Alpha_bar[t - 1]) / (1 - Alpha_bar[t]) # \tilde{\beta}_t = \beta_t * (1-\bar{\alpha}_{t-1}) # / (1-\bar{\alpha}_t) Sigma = torch.sqrt(Beta_tilde) # \sigma_t^2 = \tilde{\beta}_t @@ -319,14 +298,10 @@ def __init__( self.fc_t = nn.Linear(diffusion_step_embed_dim_out, self.res_channels) # dilated conv layer - self.dilated_conv_layer = Conv( - self.res_channels, 2 * self.res_channels, kernel_size=3, dilation=dilation - ) + self.dilated_conv_layer = Conv(self.res_channels, 2 * self.res_channels, kernel_size=3, dilation=dilation) # add mel spectrogram upsampler and conditioner conv1x1 layer (In adapted to S4 output) - self.cond_conv = Conv( - 2 * in_channels, 2 * self.res_channels, kernel_size=1 - ) # 80 is mel bands + self.cond_conv = Conv(2 * in_channels, 2 * self.res_channels, kernel_size=1) # 80 is mel bands # residual conv1x1 layer, connect to next residual layer self.res_conv = nn.Conv1d(res_channels, res_channels, kernel_size=1) @@ -355,9 +330,7 @@ def forward(self, input_data): cond = self.cond_conv(cond) h += cond - out = torch.tanh(h[:, : self.res_channels, :]) * torch.sigmoid( - h[:, self.res_channels :, :] - ) + out = torch.tanh(h[:, : self.res_channels, :]) * torch.sigmoid(h[:, self.res_channels :, :]) res = self.res_conv(out) assert x.shape == res.shape @@ -382,12 +355,8 @@ def __init__( self.num_res_layers = num_res_layers self.diffusion_step_embed_dim_in = diffusion_step_embed_dim_in - self.fc_t1 = nn.Linear( - diffusion_step_embed_dim_in, diffusion_step_embed_dim_mid - ) - self.fc_t2 = nn.Linear( - diffusion_step_embed_dim_mid, diffusion_step_embed_dim_out - ) + self.fc_t1 = nn.Linear(diffusion_step_embed_dim_in, diffusion_step_embed_dim_mid) + self.fc_t2 = nn.Linear(diffusion_step_embed_dim_mid, diffusion_step_embed_dim_out) self.residual_blocks = nn.ModuleList() for n in range(self.num_res_layers): @@ -416,14 +385,10 @@ def forward(self, input_data): h = noise skip = 0 for n in range(self.num_res_layers): - h, skip_n = self.residual_blocks[n]( - (noise, conditional, diffusion_step_embed) - ) + h, skip_n = self.residual_blocks[n]((noise, conditional, diffusion_step_embed)) skip += skip_n - return skip * math.sqrt( - 1.0 / self.num_res_layers - ) # normalize for training stability + return skip * math.sqrt(1.0 / self.num_res_layers) # normalize for training stability def std_normal(size, device): diff --git a/icu_benchmarks/models/layers.py b/icu_benchmarks/models/layers.py index bd844d64..c5fd7550 100644 --- a/icu_benchmarks/models/layers.py +++ b/icu_benchmarks/models/layers.py @@ -743,7 +743,7 @@ def forward(self, x: Tensor) -> Tuple[Tensor, Tensor]: class TFTBack(nn.Module): """ - Big part of TFT architecture consists of static enrichment followed by mutli-head self-attention then + Big part of TFT architecture consists of static enrichment followed by mutli-head self-attention then position wise feed forward followed by a gate and a dense layer GRNs-->multi-head attention-->GRNs-->GLU-->Linear-->output """ diff --git a/icu_benchmarks/models/metrics.py b/icu_benchmarks/models/metrics.py index d46a22fe..4c2ffd2b 100644 --- a/icu_benchmarks/models/metrics.py +++ b/icu_benchmarks/models/metrics.py @@ -49,9 +49,7 @@ def accuracy(output, target, topk=(1,)): return res -def balanced_accuracy_compute_fn( - y_preds: torch.Tensor, y_targets: torch.Tensor -) -> float: +def balanced_accuracy_compute_fn(y_preds: torch.Tensor, y_targets: torch.Tensor) -> float: y_true = y_targets.numpy() y_pred = np.argmax(y_preds.numpy(), axis=-1) return balanced_accuracy_score(y_true, y_pred) @@ -63,9 +61,7 @@ def ece_curve_compute_fn(y_preds: torch.Tensor, y_targets: torch.Tensor) -> floa return calibration_curve(y_true, y_pred, n_bins=10) -def mae_with_invert_compute_fn( - y_preds: torch.Tensor, y_targets: torch.Tensor, invert_fn=Callable -) -> float: +def mae_with_invert_compute_fn(y_preds: torch.Tensor, y_targets: torch.Tensor, invert_fn=Callable) -> float: y_true = invert_fn(y_targets.numpy().reshape(-1, 1))[:, 0] y_pred = invert_fn(y_preds.numpy().reshape(-1, 1))[:, 0] return mean_absolute_error(y_true, y_pred) @@ -76,9 +72,7 @@ def JSD_fn(y_preds: torch.Tensor, y_targets: torch.Tensor): class BalancedAccuracy(EpochMetric): - def __init__( - self, output_transform: Callable = lambda x: x, check_compute_fn: bool = False - ) -> None: + def __init__(self, output_transform: Callable = lambda x: x, check_compute_fn: bool = False) -> None: super(BalancedAccuracy, self).__init__( balanced_accuracy_compute_fn, output_transform=output_transform, @@ -87,9 +81,7 @@ def __init__( class CalibrationCurve(EpochMetric): - def __init__( - self, output_transform: Callable = lambda x: x, check_compute_fn: bool = False - ) -> None: + def __init__(self, output_transform: Callable = lambda x: x, check_compute_fn: bool = False) -> None: super(CalibrationCurve, self).__init__( ece_curve_compute_fn, output_transform=output_transform, diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 8e3cbfed..febfcb59 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -27,9 +27,7 @@ from collections import OrderedDict -cpu_core_count = ( - len(os.sched_getaffinity(0)) if hasattr(os, "sched_getaffinity") else os.cpu_count() -) +cpu_core_count = len(os.sched_getaffinity(0)) if hasattr(os, "sched_getaffinity") else os.cpu_count() def assure_minimum_length(dataset): @@ -103,28 +101,16 @@ def train_common( else ( PredictionDatasetTFT if model.__name__ == "TFT" - else ( - PredictionDatasetTFTpytorch - if model.__name__ == "TFTpytorch" - else PredictionDataset - ) + else (PredictionDatasetTFTpytorch if model.__name__ == "TFTpytorch" else PredictionDataset) ) ) logging.info(f"Logging to directory: {log_dir}.") - save_config_file( - log_dir - ) # We save the operative config before and also after training + save_config_file(log_dir) # We save the operative config before and also after training - train_dataset = dataset_class( - data, split=Split.train, ram_cache=ram_cache, name=dataset_names["train"] - ) - val_dataset = dataset_class( - data, split=Split.val, ram_cache=ram_cache, name=dataset_names["val"] - ) - train_dataset, val_dataset = assure_minimum_length( - train_dataset - ), assure_minimum_length(val_dataset) + train_dataset = dataset_class(data, split=Split.train, ram_cache=ram_cache, name=dataset_names["train"]) + val_dataset = dataset_class(data, split=Split.val, ram_cache=ram_cache, name=dataset_names["val"]) + train_dataset, val_dataset = assure_minimum_length(train_dataset), assure_minimum_length(val_dataset) batch_size = min(batch_size, len(train_dataset), len(val_dataset)) test_dataset = dataset_class(data, split=test_on) test_dataset = assure_minimum_length(test_dataset) @@ -198,9 +184,7 @@ def train_common( else: data_shape = next(iter(train_loader))[0].shape - model = model( - optimizer=optimizer, input_size=data_shape, epochs=epochs, run_mode=mode - ) + model = model(optimizer=optimizer, input_size=data_shape, epochs=epochs, run_mode=mode) if load_weights: model = load_model(model, source_dir, pl_model=pl_model) @@ -243,9 +227,7 @@ def train_common( if not eval_only: if model.requires_backprop: logging.info("Training DL model.") - trainer.fit( - model, train_dataloaders=train_loader, val_dataloaders=val_loader - ) + trainer.fit(model, train_dataloaders=train_loader, val_dataloaders=val_loader) logging.info("Training complete.") else: logging.info("Training ML model.") @@ -273,9 +255,7 @@ def train_common( ) model.set_weight("balanced", train_dataset) - test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0][ - "test/loss" - ] + test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0]["test/loss"] save_config_file(log_dir) return test_loss diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 3426b71b..fdf7bf13 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -23,9 +23,7 @@ from icu_benchmarks.contants import RunMode gin.config.external_configurable(nn.functional.nll_loss, module="torch.nn.functional") -gin.config.external_configurable( - nn.functional.cross_entropy, module="torch.nn.functional" -) +gin.config.external_configurable(nn.functional.cross_entropy, module="torch.nn.functional") gin.config.external_configurable(nn.functional.mse_loss, module="torch.nn.functional") gin.config.external_configurable(mean_squared_error, module="sklearn.metrics") @@ -90,9 +88,7 @@ def save_model(self, save_path, file_name, file_extension): def check_supported_runmode(self, runmode: RunMode): if runmode not in self._supported_run_modes: - raise ValueError( - f"Runmode {runmode} not supported for {self.__class__.__name__}" - ) + raise ValueError(f"Runmode {runmode} not supported for {self.__class__.__name__}") return True @@ -165,9 +161,7 @@ def finalize_step(self, step_prefix=""): self.log_dict( { f"{step_prefix}/{name}": ( - np.float32(metric.compute()) - if isinstance(metric.compute(), np.float64) - else metric.compute() + np.float32(metric.compute()) if isinstance(metric.compute(), np.float64) else metric.compute() ) for name, metric in self.metrics[step_prefix].items() if "_Curve" not in name @@ -208,10 +202,7 @@ def configure_optimizers(self): def on_test_epoch_start(self) -> None: self.metrics = { - step_name: { - metric_name: metric() - for metric_name, metric in self.set_metrics().items() - } + step_name: {metric_name: metric() for metric_name, metric in self.set_metrics().items()} for step_name in ["train", "val", "test"] } return super().on_test_epoch_start() @@ -317,9 +308,7 @@ def step_fn(self, element, step_prefix=""): element (object): step_prefix (str): Step type, by default: test, train, val. """ - if isinstance( - element[0], OrderedDict - ): # check if the data loader is the one for the TFT nvidia implementation + if isinstance(element[0], OrderedDict): # check if the data loader is the one for the TFT nvidia implementation data, mask = element[0], element[1].to(self.device) for key, value in data.items(): @@ -360,9 +349,7 @@ def step_fn(self, element, step_prefix=""): else: data = data.float().to(self.device) else: - raise Exception( - "Loader should return either (data, label) or (data, label, mask)" - ) + raise Exception("Loader should return either (data, label) or (data, label, mask)") out = self(data) @@ -372,31 +359,20 @@ def step_fn(self, element, step_prefix=""): else: aux_loss = 0 # Get prediction and target - prediction = ( - torch.masked_select(out, mask.unsqueeze(-1)) - .reshape(-1, out.shape[-1]) - .to(self.device) - ) + prediction = torch.masked_select(out, mask.unsqueeze(-1)).reshape(-1, out.shape[-1]).to(self.device) target = torch.masked_select(labels, mask).to(self.device) if prediction.shape[-1] > 1 and self.run_mode == RunMode.classification: # Classification task - loss = ( - self.loss( - prediction, target.long(), weight=self.loss_weights.to(self.device) - ) - + aux_loss - ) + loss = self.loss(prediction, target.long(), weight=self.loss_weights.to(self.device)) + aux_loss # Returns torch.long because negative log likelihood loss elif self.run_mode == RunMode.regression: # Regression task loss = self.loss(prediction[:, 0], target.float()) + aux_loss else: - raise ValueError( - f"Run mode {self.run_mode} not yet supported. Please implement it." - ) + raise ValueError(f"Run mode {self.run_mode} not yet supported. Please implement it.") transformed_output = self.output_transform((prediction, target)) for key, value in self.metrics[step_prefix].items(): @@ -413,9 +389,7 @@ def step_fn(self, element, step_prefix=""): value.update(transformed_output[0], transformed_output[1]) else: value.update(transformed_output) - self.log( - f"{step_prefix}/loss", loss, on_step=False, on_epoch=True, sync_dist=True - ) + self.log(f"{step_prefix}/loss", loss, on_step=False, on_epoch=True, sync_dist=True) return loss @@ -462,15 +436,9 @@ def set_metrics(self, labels): # Regression else: - if ( - self.scaler is not None - ): # We invert transform the labels and predictions if they were scaled. - self.output_transform = lambda x: self.scaler.inverse_transform( - x.reshape(-1, 1) - ) - self.label_transform = lambda x: self.scaler.inverse_transform( - x.reshape(-1, 1) - ) + if self.scaler is not None: # We invert transform the labels and predictions if they were scaled. + self.output_transform = lambda x: self.scaler.inverse_transform(x.reshape(-1, 1)) + self.label_transform = lambda x: self.scaler.inverse_transform(x.reshape(-1, 1)) else: self.output_transform = lambda x: x self.label_transform = lambda x: x @@ -505,9 +473,7 @@ def fit_model(self, train_data, train_labels, val_data, val_labels): def validation_step(self, val_dataset, _): val_rep, val_label = val_dataset.get_data_and_labels() - val_rep, val_label = torch.from_numpy(val_rep).to( - self.device - ), torch.from_numpy(val_label).to(self.device) + val_rep, val_label = torch.from_numpy(val_rep).to(self.device), torch.from_numpy(val_label).to(self.device) self.set_metrics(val_label) val_pred = self.predict(val_rep) @@ -550,9 +516,7 @@ def log_metrics(self, label, pred, metric_type): self.log_dict( { # MPS dependent type casting - f"{metric_type}/{name}": metric( - self.label_transform(label), self.output_transform(pred) - ) + f"{metric_type}/{name}": metric(self.label_transform(label), self.output_transform(pred)) if not self.mps else metric(self.label_transform(label), self.output_transform(pred)) # Fore very metric @@ -590,9 +554,7 @@ def set_model_args(self, model, *args, **kwargs): # Get passed keyword arguments arguments = locals()["kwargs"] # Get valid hyperparameters - hyperparams = { - key: value for key, value in arguments.items() if key in possible_hps - } + hyperparams = {key: value for key, value in arguments.items() if key in possible_hps} logging.debug(f"Creating model with: {hyperparams}.") return model(**hyperparams) @@ -645,9 +607,7 @@ def set_metrics(self): def init_weights(self, init_type="normal", gain=0.02): def init_func(m): classname = m.__class__.__name__ - if hasattr(m, "weight") and ( - classname.find("Conv") != -1 or classname.find("Linear") != -1 - ): + if hasattr(m, "weight") and (classname.find("Conv") != -1 or classname.find("Linear") != -1): if init_type == ImputationInit.NORMAL: nn.init.normal_(m.weight.data, 0.0, gain) elif init_type == ImputationInit.XAVIER: @@ -657,9 +617,7 @@ def init_func(m): elif init_type == ImputationInit.ORTHOGONAL: nn.init.orthogonal_(m.weight.data, gain=gain) else: - raise NotImplementedError( - f"Initialization method {init_type} is not implemented" - ) + raise NotImplementedError(f"Initialization method {init_type} is not implemented") if hasattr(m, "bias") and m.bias is not None: nn.init.constant_(m.bias.data, 0.0) elif classname.find("BatchNorm2d") != -1: diff --git a/icu_benchmarks/run.py b/icu_benchmarks/run.py index a729fb1b..90af2c27 100644 --- a/icu_benchmarks/run.py +++ b/icu_benchmarks/run.py @@ -64,19 +64,11 @@ def main(my_args=tuple(sys.argv[1:])): logging.info(f"Task mode: {mode}.") # Set train size to fine tune size if fine tune is set, else use custom train size - train_size = ( - args.fine_tune - if args.fine_tune is not None - else args.samples - if args.samples is not None - else None - ) + train_size = args.fine_tune if args.fine_tune is not None else args.samples if args.samples is not None else None # Whether to load weights from a previous run load_weights = evaluate or args.fine_tune is not None - pretrained_imputation_model = load_pretrained_imputation_model( - args.pretrained_imputation - ) + pretrained_imputation_model = load_pretrained_imputation_model(args.pretrained_imputation) # Log imputation model to wandb update_wandb_config( { @@ -90,11 +82,7 @@ def main(my_args=tuple(sys.argv[1:])): log_dir = ( (log_dir_name / experiment) if experiment - else ( - log_dir_name - / (args.task_name if args.task_name is not None else args.task) - / model - ) + else (log_dir_name / (args.task_name if args.task_name is not None else args.task) / model) ) log_full_line(f"Logging to {log_dir}.", logging.INFO) @@ -117,9 +105,7 @@ def main(my_args=tuple(sys.argv[1:])): # Load pretrained model in evaluate mode or when finetuning if load_weights: if args.source_dir is None: - raise ValueError( - "Please specify a source directory when evaluating or fine-tuning." - ) + raise ValueError("Please specify a source directory when evaluating or fine-tuning.") log_dir /= f"_from_{args.source_name}" name_datasets(args.source_name, args.source_name, args.name) if args.fine_tune: @@ -127,13 +113,9 @@ def main(my_args=tuple(sys.argv[1:])): name_datasets(args.name, args.name, args.name) run_dir = create_run_dir(log_dir) source_dir = args.source_dir - logging.info( - f"Will load weights from {source_dir} and bind train gin-config. Note: this might override your config." - ) + logging.info(f"Will load weights from {source_dir} and bind train gin-config. Note: this might override your config.") gin.parse_config_file(source_dir / "train_config.gin") - elif ( - args.samples and args.source_dir is not None - ): # Train model with limited samples and bind existing config + elif args.samples and args.source_dir is not None: # Train model with limited samples and bind existing config logging.info("Binding train gin-config. Note: this might override your config.") gin.parse_config_file(args.source_dir / "train_config.gin") log_dir /= f"samples_{args.fine_tune}" @@ -144,22 +126,14 @@ def main(my_args=tuple(sys.argv[1:])): name_datasets(args.name, args.name, args.name) hp_checkpoint = log_dir / args.hp_checkpoint if args.hp_checkpoint else None model_path = ( - Path("configs") - / ( - "imputation_models" - if mode == RunMode.imputation - else "prediction_models" - ) - / f"{model}.gin" + Path("configs") / ("imputation_models" if mode == RunMode.imputation else "prediction_models") / f"{model}.gin" ) gin_config_files = ( [Path(f"configs/experiments/{args.experiment}.gin")] if args.experiment else [model_path, Path(f"configs/tasks/{task}.gin")] ) - gin.parse_config_files_and_bindings( - gin_config_files, args.hyperparams, finalize_config=False - ) + gin.parse_config_files_and_bindings(gin_config_files, args.hyperparams, finalize_config=False) log_full_line(f"Data directory: {data_dir.resolve()}", level=logging.INFO) run_dir = create_run_dir(log_dir) choose_and_bind_hyperparameters( From 54563e4419ca4bf27af74f3c295db5c9b7b98205 Mon Sep 17 00:00:00 2001 From: "youssef.mecky" Date: Mon, 18 Sep 2023 14:41:45 +0200 Subject: [PATCH 052/142] added scheduler removed lr --- configs/prediction_models/TFTpytorch.gin | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/configs/prediction_models/TFTpytorch.gin b/configs/prediction_models/TFTpytorch.gin index b10fd4b5..27e2cf25 100644 --- a/configs/prediction_models/TFTpytorch.gin +++ b/configs/prediction_models/TFTpytorch.gin @@ -8,16 +8,16 @@ train_common.model = @TFTpytorch optimizer/hyperparameter.class_to_tune = @Adam optimizer/hyperparameter.weight_decay = 1e-6 -#optimizer/hyperparameter.lr = (1e-5, 3e-4) + # Encoder params model/hyperparameter.class_to_tune = @TFTpytorch -model/hyperparameter.hidden = (16,32,64) +model/hyperparameter.hidden = 16 model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.dropout = (0.0, 0.4) model/hyperparameter.dropout_att = (0.0, 0.4) -model/hyperparameter.n_heads =(1,2,4) -model/hyperparameter.lr = (1e-5, 3e-4) +model/hyperparameter.n_heads =1 +model/hyperparameter.lr_scheduler = "exponential" PredictionDatasetTFTpytorch.max_encoder_length = 24 PredictionDatasetTFTpytorch.max_prediction_length = 24 From 739920286bf09324e8469fa29d5b4d5a753d5c27 Mon Sep 17 00:00:00 2001 From: "youssef.mecky" Date: Mon, 18 Sep 2023 14:42:03 +0200 Subject: [PATCH 053/142] removed lr as a required input --- icu_benchmarks/models/dl_models.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index cfc538c8..e46f07b5 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -408,7 +408,7 @@ def forward(self, x: Dict[str, Tensor]) -> Tensor: historical_inputs = cat(_historical_inputs, dim=-2) future_inputs = Tensor() if t_known_inp is not None: - future_inputs = t_known_inp[:, self.encoder_length :] + future_inputs = t_known_inp[:, self.encoder_length:] o = self.TFTpart(historical_inputs, cs, ch, cc, ce, future_inputs.to(historical_inputs.device)) pred = self.logit(o) @@ -423,14 +423,14 @@ class TFTpytorch(DLPredictionWrapper): supported_run_modes = [RunMode.classification, RunMode.regression] - def __init__(self, dataset, hidden, dropout, n_heads, dropout_att, lr, optimizer, num_classes, *args, **kwargs): - super().__init__(lr=lr, optimizer=optimizer, *args, **kwargs) + def __init__(self, dataset, hidden, dropout, n_heads, dropout_att, optimizer, num_classes, *args, **kwargs): + super().__init__(optimizer=optimizer, *args, **kwargs) self.model = TemporalFusionTransformer.from_dataset( dataset=dataset, hidden_size=hidden, dropout=dropout, attention_head_size=n_heads, - learning_rate=lr, + optimizer=optimizer, loss=QuantileLoss(), hidden_continuous_size=hidden, From bf330d4256197fadc8f050aba112e8ed91a4c82c Mon Sep 17 00:00:00 2001 From: "youssef.mecky" Date: Mon, 18 Sep 2023 16:53:02 +0200 Subject: [PATCH 054/142] removed extra test dataloader --- icu_benchmarks/models/train.py | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index febfcb59..9b5246e1 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -112,7 +112,7 @@ def train_common( val_dataset = dataset_class(data, split=Split.val, ram_cache=ram_cache, name=dataset_names["val"]) train_dataset, val_dataset = assure_minimum_length(train_dataset), assure_minimum_length(val_dataset) batch_size = min(batch_size, len(train_dataset), len(val_dataset)) - test_dataset = dataset_class(data, split=test_on) + test_dataset = dataset_class(data, split=test_on, name=dataset_names["test"]) test_dataset = assure_minimum_length(test_dataset) if not eval_only: logging.info( @@ -238,21 +238,6 @@ def train_common( logging.info("Finished training full model.") save_config_file(log_dir) return 0 - test_dataset = dataset_class(data, split=test_on, name=dataset_names["test"]) - test_dataset = assure_minimum_length(test_dataset) - logging.info(f"Testing on {test_dataset.name} with {len(test_dataset)} samples.") - test_loader = ( - DataLoader( - test_dataset, - batch_size=min(batch_size * 4, len(test_dataset)), - shuffle=False, - num_workers=num_workers, - pin_memory=True, - drop_last=True, - ) - if model.requires_backprop - else DataLoader([test_dataset.to_tensor()], batch_size=1) - ) model.set_weight("balanced", train_dataset) test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0]["test/loss"] From bf475975b24bb5511b9c930462f6a032791036d9 Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Mon, 25 Sep 2023 14:01:41 +0200 Subject: [PATCH 055/142] added name to tft loader --- icu_benchmarks/data/loader.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index d09ff055..871dec1d 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -465,6 +465,7 @@ def __init__( max_encoder_length: int, *args, ram_cache: bool = False, + name: str = "", **kwargs, ): data[split]["FEATURES"]["time_idx"] = ( @@ -475,6 +476,7 @@ def __init__( data = data.get(split) # get split labels = data["OUTCOME"] features = data["FEATURES"] + self.name = name self.data = pd.merge( labels, features, on=["stay_id", "time"] ) # combine labels and features From 69c0287f2d787ec5a1f098646b462dfc110fea46 Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Mon, 25 Sep 2023 14:03:40 +0200 Subject: [PATCH 056/142] reduced cv repetitions to 2 --- configs/tasks/common/CrossValidation.gin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/tasks/common/CrossValidation.gin b/configs/tasks/common/CrossValidation.gin index f3efd9ef..519ad3dd 100644 --- a/configs/tasks/common/CrossValidation.gin +++ b/configs/tasks/common/CrossValidation.gin @@ -1,3 +1,3 @@ # CROSS-VALIDATION SETTINGS -execute_repeated_cv.cv_repetitions = 5 +execute_repeated_cv.cv_repetitions = 2 execute_repeated_cv.cv_folds = 5 \ No newline at end of file From 41967796eabc2c4afb74b3d2abd46e61bc85b880 Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Tue, 26 Sep 2023 14:07:53 +0200 Subject: [PATCH 057/142] added explain flag --- icu_benchmarks/run.py | 44 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/icu_benchmarks/run.py b/icu_benchmarks/run.py index 90af2c27..345d7948 100644 --- a/icu_benchmarks/run.py +++ b/icu_benchmarks/run.py @@ -52,6 +52,7 @@ def main(my_args=tuple(sys.argv[1:])): evaluate = args.eval experiment = args.experiment source_dir = args.source_dir + explain = True if args.explain else False # Load task config gin.parse_config_file(f"configs/tasks/{task}.gin") mode = get_mode() @@ -64,11 +65,19 @@ def main(my_args=tuple(sys.argv[1:])): logging.info(f"Task mode: {mode}.") # Set train size to fine tune size if fine tune is set, else use custom train size - train_size = args.fine_tune if args.fine_tune is not None else args.samples if args.samples is not None else None + train_size = ( + args.fine_tune + if args.fine_tune is not None + else args.samples + if args.samples is not None + else None + ) # Whether to load weights from a previous run load_weights = evaluate or args.fine_tune is not None - pretrained_imputation_model = load_pretrained_imputation_model(args.pretrained_imputation) + pretrained_imputation_model = load_pretrained_imputation_model( + args.pretrained_imputation + ) # Log imputation model to wandb update_wandb_config( { @@ -82,7 +91,11 @@ def main(my_args=tuple(sys.argv[1:])): log_dir = ( (log_dir_name / experiment) if experiment - else (log_dir_name / (args.task_name if args.task_name is not None else args.task) / model) + else ( + log_dir_name + / (args.task_name if args.task_name is not None else args.task) + / model + ) ) log_full_line(f"Logging to {log_dir}.", logging.INFO) @@ -105,7 +118,9 @@ def main(my_args=tuple(sys.argv[1:])): # Load pretrained model in evaluate mode or when finetuning if load_weights: if args.source_dir is None: - raise ValueError("Please specify a source directory when evaluating or fine-tuning.") + raise ValueError( + "Please specify a source directory when evaluating or fine-tuning." + ) log_dir /= f"_from_{args.source_name}" name_datasets(args.source_name, args.source_name, args.name) if args.fine_tune: @@ -113,9 +128,13 @@ def main(my_args=tuple(sys.argv[1:])): name_datasets(args.name, args.name, args.name) run_dir = create_run_dir(log_dir) source_dir = args.source_dir - logging.info(f"Will load weights from {source_dir} and bind train gin-config. Note: this might override your config.") + logging.info( + f"Will load weights from {source_dir} and bind train gin-config. Note: this might override your config." + ) gin.parse_config_file(source_dir / "train_config.gin") - elif args.samples and args.source_dir is not None: # Train model with limited samples and bind existing config + elif ( + args.samples and args.source_dir is not None + ): # Train model with limited samples and bind existing config logging.info("Binding train gin-config. Note: this might override your config.") gin.parse_config_file(args.source_dir / "train_config.gin") log_dir /= f"samples_{args.fine_tune}" @@ -126,14 +145,22 @@ def main(my_args=tuple(sys.argv[1:])): name_datasets(args.name, args.name, args.name) hp_checkpoint = log_dir / args.hp_checkpoint if args.hp_checkpoint else None model_path = ( - Path("configs") / ("imputation_models" if mode == RunMode.imputation else "prediction_models") / f"{model}.gin" + Path("configs") + / ( + "imputation_models" + if mode == RunMode.imputation + else "prediction_models" + ) + / f"{model}.gin" ) gin_config_files = ( [Path(f"configs/experiments/{args.experiment}.gin")] if args.experiment else [model_path, Path(f"configs/tasks/{task}.gin")] ) - gin.parse_config_files_and_bindings(gin_config_files, args.hyperparams, finalize_config=False) + gin.parse_config_files_and_bindings( + gin_config_files, args.hyperparams, finalize_config=False + ) log_full_line(f"Data directory: {data_dir.resolve()}", level=logging.INFO) run_dir = create_run_dir(log_dir) choose_and_bind_hyperparameters( @@ -177,6 +204,7 @@ def main(my_args=tuple(sys.argv[1:])): cpu=args.cpu, wandb=args.wandb_sweep, complete_train=args.complete_train, + explain=explain, ) log_full_line("FINISHED TRAINING", level=logging.INFO, char="=", num_newlines=3) From 0e31937f370140da8b523445a9a0d378ad63d151 Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Tue, 26 Sep 2023 14:08:08 +0200 Subject: [PATCH 058/142] added explain flag --- icu_benchmarks/cross_validation.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/icu_benchmarks/cross_validation.py b/icu_benchmarks/cross_validation.py index bdcfdd43..5315e586 100644 --- a/icu_benchmarks/cross_validation.py +++ b/icu_benchmarks/cross_validation.py @@ -38,6 +38,7 @@ def execute_repeated_cv( verbose: bool = False, wandb: bool = False, complete_train: bool = False, + explain: bool = False, ) -> float: """Preprocesses data and trains a model for each fold. @@ -80,11 +81,15 @@ def execute_repeated_cv( cv_folds_to_train = 1 else: - logging.info(f"Starting nested CV with {cv_repetitions_to_train} repetitions of {cv_folds_to_train} folds.") + logging.info( + f"Starting nested CV with {cv_repetitions_to_train} repetitions of {cv_folds_to_train} folds." + ) for repetition in range(cv_repetitions_to_train): for fold_index in range(cv_folds_to_train): - repetition_fold_dir = log_dir / f"repetition_{repetition}" / f"fold_{fold_index}" + repetition_fold_dir = ( + log_dir / f"repetition_{repetition}" / f"fold_{fold_index}" + ) repetition_fold_dir.mkdir(parents=True, exist_ok=True) start_time = datetime.now() @@ -119,6 +124,7 @@ def execute_repeated_cv( verbose=verbose, use_wandb=wandb, train_only=complete_train, + explain=explain, ) train_time = datetime.now() - start_time @@ -127,7 +133,10 @@ def execute_repeated_cv( f"FINISHED FOLD {fold_index}| PREPROCESSING DURATION {preprocess_time}| PROCEDURE DURATION {train_time}", level=logging.INFO, ) - durations = {"preprocessing_duration": preprocess_time, "train_duration": train_time} + durations = { + "preprocessing_duration": preprocess_time, + "train_duration": train_time, + } with open(repetition_fold_dir / "durations.json", "w") as f: json.dump(durations, f, cls=JsonResultLoggingEncoder) @@ -135,6 +144,11 @@ def execute_repeated_cv( wandb_log({"Iteration": repetition * cv_folds_to_train + fold_index}) if repetition * cv_folds_to_train + fold_index > 1: aggregate_results(log_dir) - log_full_line(f"FINISHED CV REPETITION {repetition}", level=logging.INFO, char="=", num_newlines=3) + log_full_line( + f"FINISHED CV REPETITION {repetition}", + level=logging.INFO, + char="=", + num_newlines=3, + ) return agg_loss / (cv_repetitions_to_train * cv_folds_to_train) From 008aa846beb2943929cd689121c43857b8830166 Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Tue, 26 Sep 2023 14:08:21 +0200 Subject: [PATCH 059/142] added explain flag --- icu_benchmarks/models/train.py | 68 ++++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 15 deletions(-) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 9b5246e1..4d5b4d7f 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -1,6 +1,7 @@ import os import gin import torch +import pickle import logging import pandas as pd from joblib import load @@ -25,9 +26,11 @@ from icu_benchmarks.contants import RunMode from icu_benchmarks.data.constants import DataSplit as Split from collections import OrderedDict +from captum.attr import IntegratedGradients - -cpu_core_count = len(os.sched_getaffinity(0)) if hasattr(os, "sched_getaffinity") else os.cpu_count() +cpu_core_count = ( + len(os.sched_getaffinity(0)) if hasattr(os, "sched_getaffinity") else os.cpu_count() +) def assure_minimum_length(dataset): @@ -66,6 +69,7 @@ def train_common( torch.cuda.device_count() * 8 * int(torch.cuda.is_available()), 32, ), + explain: bool = False, ): """Common wrapper to train all benchmarked models. @@ -93,6 +97,8 @@ def train_common( pl_model: Loading a pytorch lightning model. num_workers: Number of workers to use for data loading. """ + with open("data.pkl", "wb") as fp: + pickle.dump(data, fp) logging.info(f"Training model: {model.__name__}.") # choose dataset_class based on the model dataset_class = ( @@ -101,16 +107,28 @@ def train_common( else ( PredictionDatasetTFT if model.__name__ == "TFT" - else (PredictionDatasetTFTpytorch if model.__name__ == "TFTpytorch" else PredictionDataset) + else ( + PredictionDatasetTFTpytorch + if (model.__name__ == "TFTpytorch") + else PredictionDataset + ) ) ) logging.info(f"Logging to directory: {log_dir}.") - save_config_file(log_dir) # We save the operative config before and also after training + save_config_file( + log_dir + ) # We save the operative config before and also after training - train_dataset = dataset_class(data, split=Split.train, ram_cache=ram_cache, name=dataset_names["train"]) - val_dataset = dataset_class(data, split=Split.val, ram_cache=ram_cache, name=dataset_names["val"]) - train_dataset, val_dataset = assure_minimum_length(train_dataset), assure_minimum_length(val_dataset) + train_dataset = dataset_class( + data, split=Split.train, ram_cache=ram_cache, name=dataset_names["train"] + ) + val_dataset = dataset_class( + data, split=Split.val, ram_cache=ram_cache, name=dataset_names["val"] + ) + train_dataset, val_dataset = assure_minimum_length( + train_dataset + ), assure_minimum_length(val_dataset) batch_size = min(batch_size, len(train_dataset), len(val_dataset)) test_dataset = dataset_class(data, split=test_on, name=dataset_names["test"]) test_dataset = assure_minimum_length(test_dataset) @@ -119,7 +137,9 @@ def train_common( f"Training on {train_dataset.name} with {len(train_dataset)} samples and validating on {val_dataset.name} with" f" {len(val_dataset)} samples." ) + logging.info(f"Using {num_workers} workers for data loading.") + if model.__name__ == "TFTpytorch": train_loader = train_dataset.to_dataloader( train=True, @@ -127,7 +147,6 @@ def train_common( num_workers=num_workers, pin_memory=False, drop_last=True, - batch_sampler="synchronized", ) val_loader = val_dataset.to_dataloader( train=False, @@ -135,7 +154,6 @@ def train_common( num_workers=num_workers, pin_memory=False, drop_last=True, - batch_sampler="synchronized", ) test_loader = test_dataset.to_dataloader( train=False, @@ -144,7 +162,6 @@ def train_common( pin_memory=False, drop_last=True, shuffle=False, - batch_sampler="synchronized", ) model = model(train_dataset, optimizer=optimizer, epochs=epochs, run_mode=mode) @@ -184,11 +201,16 @@ def train_common( else: data_shape = next(iter(train_loader))[0].shape - model = model(optimizer=optimizer, input_size=data_shape, epochs=epochs, run_mode=mode) + model = model( + optimizer=optimizer, input_size=data_shape, epochs=epochs, run_mode=mode + ) if load_weights: model = load_model(model, source_dir, pl_model=pl_model) - + logging.info( + f"train_dataloader with {len(train_loader)} samples and validating on loader with" + f" {len(val_loader)} samples." + ) model.set_weight(weight, train_dataset) model.set_trained_columns(train_dataset.get_feature_names()) @@ -223,11 +245,14 @@ def train_common( logger=loggers, num_sanity_val_steps=-1, log_every_n_steps=5, + check_val_every_n_epoch=1, ) if not eval_only: if model.requires_backprop: logging.info("Training DL model.") - trainer.fit(model, train_dataloaders=train_loader, val_dataloaders=val_loader) + trainer.fit( + model, train_dataloaders=train_loader, val_dataloaders=val_loader + ) logging.info("Training complete.") else: logging.info("Training ML model.") @@ -238,9 +263,22 @@ def train_common( logging.info("Finished training full model.") save_config_file(log_dir) return 0 - + if explain: + test = next(iter(test_loader)) + test[0] = test[0].to(model.device) + pred = model(test[0]) + ig = IntegratedGradients(model) + # Reformat attributions. + test[0].requires_grad_() + print(pred.shape) + print(test[0].shape) + attr, delta = ig.attribute(test[0], target=0, return_convergence_delta=True) + attr = attr.detach().numpy() + print(attr) model.set_weight("balanced", train_dataset) - test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0]["test/loss"] + test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0][ + "test/loss" + ] save_config_file(log_dir) return test_loss From f9ac9ea40bef0288d94e38ff439b40924a022c0a Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Tue, 26 Sep 2023 14:13:14 +0200 Subject: [PATCH 060/142] added explain flag --- icu_benchmarks/run_utils.py | 227 +++++++++++++++++++++++++++++------- 1 file changed, 184 insertions(+), 43 deletions(-) diff --git a/icu_benchmarks/run_utils.py b/icu_benchmarks/run_utils.py index 85b676ac..f6e54e41 100644 --- a/icu_benchmarks/run_utils.py +++ b/icu_benchmarks/run_utils.py @@ -23,35 +23,134 @@ def build_parser() -> ArgumentParser: Returns: The configured ArgumentParser. """ - parser = ArgumentParser(description="Framework for benchmarking ML/DL models on ICU data") + parser = ArgumentParser( + description="Framework for benchmarking ML/DL models on ICU data" + ) - parser.add_argument("-d", "--data-dir", required=True, type=Path, help="Path to the parquet data directory.") - parser.add_argument("-t", "--task", default="BinaryClassification", required=True, help="Name of the task gin.") + parser.add_argument( + "-d", + "--data-dir", + required=True, + type=Path, + help="Path to the parquet data directory.", + ) + parser.add_argument( + "-t", + "--task", + default="BinaryClassification", + required=True, + help="Name of the task gin.", + ) parser.add_argument("-n", "--name", help="Name of the (target) dataset.") - parser.add_argument("-tn", "--task-name", help="Name of the task, used for naming experiments.") - parser.add_argument("-m", "--model", default="LGBMClassifier", help="Name of the model gin.") + parser.add_argument( + "-tn", "--task-name", help="Name of the task, used for naming experiments." + ) + parser.add_argument( + "-m", "--model", default="LGBMClassifier", help="Name of the model gin." + ) parser.add_argument("-e", "--experiment", help="Name of the experiment gin.") - parser.add_argument("-l", "--log-dir", default=Path("../yaib_logs/"), type=Path, help="Log directory for model weights.") - parser.add_argument("-s", "--seed", default=1234, type=int, help="Random seed for processing, tuning and training.") - parser.add_argument("-v", "--verbose", default=False, action=BOA, help="Set to log verbosly. Disable for clean logs.") + parser.add_argument( + "-l", + "--log-dir", + default=Path("../yaib_logs/"), + type=Path, + help="Log directory for model weights.", + ) + parser.add_argument( + "-s", + "--seed", + default=1234, + type=int, + help="Random seed for processing, tuning and training.", + ) + parser.add_argument( + "-v", + "--verbose", + default=False, + action=BOA, + help="Set to log verbosly. Disable for clean logs.", + ) parser.add_argument("--cpu", default=False, action=BOA, help="Set to use CPU.") - parser.add_argument("-db", "--debug", default=False, action=BOA, help="Set to load less data.") - parser.add_argument("--reproducible", default=True, action=BOA, help="Make torch reproducible.") - parser.add_argument("-lc", "--load_cache", default=False, action=BOA, help="Set to load generated data cache.") - parser.add_argument("-gc", "--generate_cache", default=False, action=BOA, help="Set to generate data cache.") - parser.add_argument("-p", "--preprocessor", type=Path, help="Load custom preprocessor from file.") + parser.add_argument( + "-db", "--debug", default=False, action=BOA, help="Set to load less data." + ) + parser.add_argument( + "--reproducible", default=True, action=BOA, help="Make torch reproducible." + ) + parser.add_argument( + "-lc", + "--load_cache", + default=False, + action=BOA, + help="Set to load generated data cache.", + ) + parser.add_argument( + "-gc", + "--generate_cache", + default=False, + action=BOA, + help="Set to generate data cache.", + ) + parser.add_argument( + "-p", "--preprocessor", type=Path, help="Load custom preprocessor from file." + ) parser.add_argument("-pl", "--plot", action=BOA, help="Generate common plots.") - parser.add_argument("-wd", "--wandb-sweep", action="store_true", help="Activates wandb hyper parameter sweep.") - parser.add_argument("-imp", "--pretrained-imputation", type=str, help="Path to pretrained imputation model.") - parser.add_argument("-hp", "--hyperparams", nargs="+", help="Hyperparameters for model.") - parser.add_argument("--tune", default=False, action=BOA, help="Find best hyperparameters.") - parser.add_argument("--hp-checkpoint", type=Path, help="Use previous hyperparameter checkpoint.") - parser.add_argument("--eval", default=False, action=BOA, help="Only evaluate model, skip training.") - parser.add_argument("--complete-train", default=False, action=BOA, help="Use all data to train model, skip testing.") - parser.add_argument("-ft", "--fine-tune", default=None, type=int, help="Finetune model with amount of train data.") - parser.add_argument("-sn", "--source-name", type=Path, help="Name of the source dataset.") - parser.add_argument("--source-dir", type=Path, help="Directory containing gin and model weights.") - parser.add_argument("-sa", "--samples", type=int, default=None, help="Number of samples to use for evaluation.") + parser.add_argument( + "-wd", + "--wandb-sweep", + action="store_true", + help="Activates wandb hyper parameter sweep.", + ) + parser.add_argument( + "-imp", + "--pretrained-imputation", + type=str, + help="Path to pretrained imputation model.", + ) + parser.add_argument( + "-hp", "--hyperparams", nargs="+", help="Hyperparameters for model." + ) + parser.add_argument( + "--tune", default=False, action=BOA, help="Find best hyperparameters." + ) + parser.add_argument( + "--hp-checkpoint", type=Path, help="Use previous hyperparameter checkpoint." + ) + parser.add_argument( + "--eval", default=False, action=BOA, help="Only evaluate model, skip training." + ) + parser.add_argument( + "--complete-train", + default=False, + action=BOA, + help="Use all data to train model, skip testing.", + ) + parser.add_argument( + "-ft", + "--fine-tune", + default=None, + type=int, + help="Finetune model with amount of train data.", + ) + parser.add_argument( + "-sn", "--source-name", type=Path, help="Name of the source dataset." + ) + parser.add_argument( + "--source-dir", type=Path, help="Directory containing gin and model weights." + ) + parser.add_argument( + "-sa", + "--samples", + type=int, + default=None, + help="Number of samples to use for evaluation.", + ) + parser.add_argument( + "--explain", + default=False, + action=BOA, + help="Provide explaintations for predictions.", + ) return parser @@ -80,15 +179,21 @@ def create_run_dir(log_dir: Path, randomly_searched_params: str = None) -> Path: def import_preprocessor(preprocessor_path: str): # Import custom supplied preprocessor - log_full_line(f"Importing custom preprocessor from {preprocessor_path}.", logging.INFO) + log_full_line( + f"Importing custom preprocessor from {preprocessor_path}.", logging.INFO + ) try: - spec = importlib.util.spec_from_file_location("CustomPreprocessor", preprocessor_path) + spec = importlib.util.spec_from_file_location( + "CustomPreprocessor", preprocessor_path + ) module = importlib.util.module_from_spec(spec) sys.modules["preprocessor"] = module spec.loader.exec_module(module) gin.bind_parameter("preprocess.preprocessor", module.CustomPreprocessor) except Exception as e: - logging.error(f"Could not import custom preprocessor from {preprocessor_path}: {e}") + logging.error( + f"Could not import custom preprocessor from {preprocessor_path}: {e}" + ) def aggregate_results(log_dir: Path, execution_time: timedelta = None): @@ -132,10 +237,14 @@ def aggregate_results(log_dir: Path, execution_time: timedelta = None): # Calculate the population standard deviation over aggregated results over folds/iterations # Divide by sqrt(n) to get standard deviation. - std_scores = {metric: (pstdev(list) / sqrt(len(list))) for metric, list in list_scores.items()} + std_scores = { + metric: (pstdev(list) / sqrt(len(list))) for metric, list in list_scores.items() + } confidence_interval = { - metric: (stats.t.interval(0.95, len(list) - 1, loc=mean(list), scale=stats.sem(list))) + metric: ( + stats.t.interval(0.95, len(list) - 1, loc=mean(list), scale=stats.sem(list)) + ) for metric, list in list_scores.items() } @@ -143,7 +252,9 @@ def aggregate_results(log_dir: Path, execution_time: timedelta = None): "avg": averaged_scores, "std": std_scores, "CI_0.95": confidence_interval, - "execution_time": execution_time.total_seconds() if execution_time is not None else 0.0, + "execution_time": execution_time.total_seconds() + if execution_time is not None + else 0.0, } with open(log_dir / "aggregated_test_metrics.json", "w") as f: @@ -159,10 +270,14 @@ def aggregate_results(log_dir: Path, execution_time: timedelta = None): def name_datasets(train="default", val="default", test="default"): """Names the datasets for logging (optional).""" - gin.bind_parameter("train_common.dataset_names", {"train": train, "val": val, "test": test}) + gin.bind_parameter( + "train_common.dataset_names", {"train": train, "val": val, "test": test} + ) -def log_full_line(msg: str, level: int = logging.INFO, char: str = "-", num_newlines: int = 0): +def log_full_line( + msg: str, level: int = logging.INFO, char: str = "-", num_newlines: int = 0 +): """Logs a full line of a given character with a message centered. Args: @@ -175,7 +290,12 @@ def log_full_line(msg: str, level: int = logging.INFO, char: str = "-", num_newl reserved_chars = len(logging.getLevelName(level)) + 28 logging.log( level, - "{0:{char}^{width}}{1}".format(msg, "\n" * num_newlines, char=char, width=terminal_size.columns - reserved_chars), + "{0:{char}^{width}}{1}".format( + msg, + "\n" * num_newlines, + char=char, + width=terminal_size.columns - reserved_chars, + ), ) @@ -185,24 +305,43 @@ def load_pretrained_imputation_model(use_pretrained_imputation): Args: use_pretrained_imputation: Path to the pretrained imputation model. """ - if use_pretrained_imputation is not None and not Path(use_pretrained_imputation).exists(): + if ( + use_pretrained_imputation is not None + and not Path(use_pretrained_imputation).exists() + ): logging.warning("The specified pretrained imputation model does not exist.") use_pretrained_imputation = None if use_pretrained_imputation is not None: - logging.info("Using pretrained imputation from" + str(use_pretrained_imputation)) - pretrained_imputation_model_checkpoint = torch.load(use_pretrained_imputation, map_location=torch.device("cpu")) + logging.info( + "Using pretrained imputation from" + str(use_pretrained_imputation) + ) + pretrained_imputation_model_checkpoint = torch.load( + use_pretrained_imputation, map_location=torch.device("cpu") + ) if isinstance(pretrained_imputation_model_checkpoint, dict): imputation_model_class = pretrained_imputation_model_checkpoint["class"] - pretrained_imputation_model = imputation_model_class(**pretrained_imputation_model_checkpoint["hyper_parameters"]) - pretrained_imputation_model.set_trained_columns(pretrained_imputation_model_checkpoint["trained_columns"]) - pretrained_imputation_model.load_state_dict(pretrained_imputation_model_checkpoint["state_dict"]) + pretrained_imputation_model = imputation_model_class( + **pretrained_imputation_model_checkpoint["hyper_parameters"] + ) + pretrained_imputation_model.set_trained_columns( + pretrained_imputation_model_checkpoint["trained_columns"] + ) + pretrained_imputation_model.load_state_dict( + pretrained_imputation_model_checkpoint["state_dict"] + ) else: pretrained_imputation_model = pretrained_imputation_model_checkpoint - pretrained_imputation_model = pretrained_imputation_model.to("cuda" if torch.cuda.is_available() else "cpu") + pretrained_imputation_model = pretrained_imputation_model.to( + "cuda" if torch.cuda.is_available() else "cpu" + ) try: - logging.info(f"imputation model device: {next(pretrained_imputation_model.parameters()).device}") - pretrained_imputation_model.device = next(pretrained_imputation_model.parameters()).device + logging.info( + f"imputation model device: {next(pretrained_imputation_model.parameters()).device}" + ) + pretrained_imputation_model.device = next( + pretrained_imputation_model.parameters() + ).device except Exception as e: logging.debug(f"Could not set device of imputation model: {e}") else: @@ -223,7 +362,9 @@ def setup_logging(date_format, log_format, verbose): logging.basicConfig(format=log_format, datefmt=date_format) loggers = ["pytorch_lightning", "lightning_fabric"] for logger in loggers: - logging.getLogger(logger).handlers[0].setFormatter(logging.Formatter(log_format, datefmt=date_format)) + logging.getLogger(logger).handlers[0].setFormatter( + logging.Formatter(log_format, datefmt=date_format) + ) if not verbose: logging.getLogger().setLevel(logging.INFO) From 3bf60c700f8ca8492099234630e8e1860767bbdc Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Tue, 26 Sep 2023 15:44:39 +0200 Subject: [PATCH 061/142] cleaned conditons and added flag for pytorch forecasting --- icu_benchmarks/cross_validation.py | 2 + icu_benchmarks/data/loader.py | 24 ++++---- icu_benchmarks/models/dl_models.py | 96 ++++++++++++++++++++++++------ icu_benchmarks/models/wrappers.py | 86 ++++++++++++++++++++------ icu_benchmarks/run.py | 4 +- icu_benchmarks/run_utils.py | 6 ++ 6 files changed, 170 insertions(+), 48 deletions(-) diff --git a/icu_benchmarks/cross_validation.py b/icu_benchmarks/cross_validation.py index 5315e586..bcf2f5f5 100644 --- a/icu_benchmarks/cross_validation.py +++ b/icu_benchmarks/cross_validation.py @@ -39,6 +39,7 @@ def execute_repeated_cv( wandb: bool = False, complete_train: bool = False, explain: bool = False, + pytorch_forecasting: bool = False, ) -> float: """Preprocesses data and trains a model for each fold. @@ -125,6 +126,7 @@ def execute_repeated_cv( use_wandb=wandb, train_only=complete_train, explain=explain, + pytorch_forecasting=pytorch_forecasting, ) train_time = datetime.now() - start_time diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 871dec1d..5dde7809 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -172,19 +172,20 @@ def to_tensor(self): return from_numpy(data), from_numpy(labels) +""" @gin.configurable("PredictionDatasetTFT") class PredictionDatasetTFT(PredictionDataset): - """Subclass of prediction dataset for TFT as we need to define if variables are cont,static,known or observed. + Subclass of prediction dataset for TFT as we need to define if variables are cont,static,known or observed. We also need to feed the model the variables in a specific order Args: ram_cache (bool, optional): Whether the complete dataset should be stored in ram. Defaults to True. - """ + def __init__(self, *args, ram_cache: bool = True, **kwargs): super().__init__(*args, ram_cache=True, **kwargs) def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: - """Function to sample from the data split of choice. Used for TFT. + Function to sample from the data split of choice. Used for TFT. The data needs to be given to the model in the following order [static categorical,static contious,known catergorical,known continous, observed categorical, observed continous,target ,id] @@ -192,7 +193,7 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: idx: A specific row index to sample. Returns: A sample from the data, consisting of data, labels and padding mask. - """ + if self._cached_dataset is not None: return self._cached_dataset[idx] @@ -300,6 +301,7 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: tensors = (from_numpy(np.array(tensor)).to(float32) for tensor in tensors) tensors = [stack((x,), dim=-1) if x.numel() > 0 else empty(0) for x in tensors] return OrderedDict(zip(Features.FEAT_NAMES, tensors)), from_numpy(pad_mask) +""" @gin.configurable("ImputationDataset") @@ -461,11 +463,11 @@ def __init__( self, data: dict, split: str, - max_prediction_length: int, - max_encoder_length: int, *args, ram_cache: bool = False, name: str = "", + max_prediction_length: int = 24, + max_encoder_length: int = 24, **kwargs, ): data[split]["FEATURES"]["time_idx"] = ( @@ -480,6 +482,7 @@ def __init__( self.data = pd.merge( labels, features, on=["stay_id", "time"] ) # combine labels and features + # self.data["sex"].replace([0, 1], ["Female", "Male"], inplace=True) self.split = split self.args = args self.ram_cache = ram_cache @@ -495,10 +498,10 @@ def __init__( min_prediction_length=max_prediction_length, max_prediction_length=max_prediction_length, static_categoricals=[], - static_reals=["height", "weight", "age", "sex"], - time_varying_known_categoricals=[], - time_varying_known_reals=[], - time_varying_unknown_categoricals=[], + static_reals=["height", "weight", "age", "stay_id", "sex"], + # time_varying_known_categoricals=[], + time_varying_known_reals=["time_idx"], + # time_varying_unknown_categoricals=[], time_varying_unknown_reals=[ "alb", "alp", @@ -596,6 +599,7 @@ def __init__( "MissingIndicator_46", "MissingIndicator_47", "MissingIndicator_48", + "label", ], add_relative_time_idx=True, add_target_scales=True, diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index e46f07b5..5dd067f4 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -15,7 +15,7 @@ from typing import Dict from icu_benchmarks.models.wrappers import DLPredictionWrapper from torch import Tensor, cat, FloatTensor -from pytorch_forecasting import TemporalFusionTransformer +from pytorch_forecasting import TemporalFusionTransformer, RecurrentNetwork from pytorch_forecasting.metrics import QuantileLoss @@ -28,7 +28,12 @@ class RNNet(DLPredictionWrapper): def __init__(self, input_size, hidden_dim, layer_dim, num_classes, *args, **kwargs): super().__init__( - input_size=input_size, hidden_dim=hidden_dim, layer_dim=layer_dim, num_classes=num_classes, *args, **kwargs + input_size=input_size, + hidden_dim=hidden_dim, + layer_dim=layer_dim, + num_classes=num_classes, + *args, + **kwargs, ) self.hidden_dim = hidden_dim self.layer_dim = layer_dim @@ -54,7 +59,12 @@ class LSTMNet(DLPredictionWrapper): def __init__(self, input_size, hidden_dim, layer_dim, num_classes, *args, **kwargs): super().__init__( - input_size=input_size, hidden_dim=hidden_dim, layer_dim=layer_dim, num_classes=num_classes, *args, **kwargs + input_size=input_size, + hidden_dim=hidden_dim, + layer_dim=layer_dim, + num_classes=num_classes, + *args, + **kwargs, ) self.hidden_dim = hidden_dim self.layer_dim = layer_dim @@ -81,7 +91,12 @@ class GRUNet(DLPredictionWrapper): def __init__(self, input_size, hidden_dim, layer_dim, num_classes, *args, **kwargs): super().__init__( - input_size=input_size, hidden_dim=hidden_dim, layer_dim=layer_dim, num_classes=num_classes, *args, **kwargs + input_size=input_size, + hidden_dim=hidden_dim, + layer_dim=layer_dim, + num_classes=num_classes, + *args, + **kwargs, ) self.hidden_dim = hidden_dim self.layer_dim = layer_dim @@ -136,7 +151,9 @@ def __init__( **kwargs, ) hidden = hidden if hidden % 2 == 0 else hidden + 1 # Make sure hidden is even - self.input_embedding = nn.Linear(input_size[2], hidden) # This acts as a time-distributed layer by defaults + self.input_embedding = nn.Linear( + input_size[2], hidden + ) # This acts as a time-distributed layer by defaults if pos_encoding: self.pos_encoder = PositionalEncoding(hidden) else: @@ -207,7 +224,9 @@ def __init__( ) hidden = hidden if hidden % 2 == 0 else hidden + 1 # Make sure hidden is even - self.input_embedding = nn.Linear(input_size[2], hidden) # This acts as a time-distributed layer by defaults + self.input_embedding = nn.Linear( + input_size[2], hidden + ) # This acts as a time-distributed layer by defaults if pos_encoding: self.pos_encoder = PositionalEncoding(hidden) else: @@ -248,7 +267,17 @@ class TemporalConvNet(DLPredictionWrapper): _supported_run_modes = [RunMode.classification, RunMode.regression] - def __init__(self, input_size, num_channels, num_classes, *args, max_seq_length=0, kernel_size=2, dropout=0.0, **kwargs): + def __init__( + self, + input_size, + num_channels, + num_classes, + *args, + max_seq_length=0, + kernel_size=2, + dropout=0.0, + **kwargs, + ): super().__init__( input_size=input_size, num_channels=num_channels, @@ -263,9 +292,13 @@ def __init__(self, input_size, num_channels, num_classes, *args, max_seq_length= # We compute automatically the depth based on the desired seq_length. if isinstance(num_channels, Integral) and max_seq_length: - num_channels = [num_channels] * int(np.ceil(np.log(max_seq_length / 2) / np.log(kernel_size))) + num_channels = [num_channels] * int( + np.ceil(np.log(max_seq_length / 2) / np.log(kernel_size)) + ) elif isinstance(num_channels, Integral) and not max_seq_length: - raise Exception("a maximum sequence length needs to be provided if num_channels is int") + raise Exception( + "a maximum sequence length needs to be provided if num_channels is int" + ) num_levels = len(num_channels) for i in range(num_levels): @@ -295,6 +328,7 @@ def forward(self, x): return pred +''' @gin.configurable class TFT(DLPredictionWrapper): """ @@ -316,7 +350,9 @@ def __init__( quantiles=[0.1, 0.5, 0.9], # quantiles to produce static_categorical_inp_size=[2], # number of catergories temporal_known_categorical_inp_size=[], - temporal_observed_categorical_inp_size=[48], # number of categorical observed variables + temporal_observed_categorical_inp_size=[ + 48 + ], # number of categorical observed variables static_continuous_inp_size=3, # number of static coutinous variables temporal_known_continuous_inp_size=0, temporal_observed_continuous_inp_size=48, @@ -325,7 +361,10 @@ def __init__( ): # derived variables num_static_vars = len(static_categorical_inp_size) + static_continuous_inp_size - num_future_vars = len(temporal_known_categorical_inp_size) + temporal_known_continuous_inp_size + num_future_vars = ( + len(temporal_known_categorical_inp_size) + + temporal_known_continuous_inp_size + ) num_historic_vars = sum( [ num_future_vars, @@ -370,7 +409,9 @@ def __init__( hidden, ) # embeddings for all variables - self.static_encoder = StaticCovariateEncoder(num_static_vars, hidden, dropout) # encoding for static variables + self.static_encoder = StaticCovariateEncoder( + num_static_vars, hidden, dropout + ) # encoding for static variables self.TFTpart = TFTBack( encoder_length, num_historic_vars, @@ -408,11 +449,19 @@ def forward(self, x: Dict[str, Tensor]) -> Tensor: historical_inputs = cat(_historical_inputs, dim=-2) future_inputs = Tensor() if t_known_inp is not None: - future_inputs = t_known_inp[:, self.encoder_length:] - - o = self.TFTpart(historical_inputs, cs, ch, cc, ce, future_inputs.to(historical_inputs.device)) + future_inputs = t_known_inp[:, self.encoder_length :] + + o = self.TFTpart( + historical_inputs, + cs, + ch, + cc, + ce, + future_inputs.to(historical_inputs.device), + ) pred = self.logit(o) return pred +''' @gin.configurable @@ -423,14 +472,25 @@ class TFTpytorch(DLPredictionWrapper): supported_run_modes = [RunMode.classification, RunMode.regression] - def __init__(self, dataset, hidden, dropout, n_heads, dropout_att, optimizer, num_classes, *args, **kwargs): - super().__init__(optimizer=optimizer, *args, **kwargs) + def __init__( + self, + dataset, + hidden, + dropout, + n_heads, + dropout_att, + optimizer, + num_classes, + *args, + **kwargs, + ): + super().__init__(optimizer=optimizer, pytorch_forecasting=True, *args, **kwargs) + self.model = TemporalFusionTransformer.from_dataset( dataset=dataset, hidden_size=hidden, dropout=dropout, attention_head_size=n_heads, - optimizer=optimizer, loss=QuantileLoss(), hidden_continuous_size=hidden, diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index fdf7bf13..ff70db34 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -23,7 +23,9 @@ from icu_benchmarks.contants import RunMode gin.config.external_configurable(nn.functional.nll_loss, module="torch.nn.functional") -gin.config.external_configurable(nn.functional.cross_entropy, module="torch.nn.functional") +gin.config.external_configurable( + nn.functional.cross_entropy, module="torch.nn.functional" +) gin.config.external_configurable(nn.functional.mse_loss, module="torch.nn.functional") gin.config.external_configurable(mean_squared_error, module="sklearn.metrics") @@ -88,7 +90,9 @@ def save_model(self, save_path, file_name, file_extension): def check_supported_runmode(self, runmode: RunMode): if runmode not in self._supported_run_modes: - raise ValueError(f"Runmode {runmode} not supported for {self.__class__.__name__}") + raise ValueError( + f"Runmode {runmode} not supported for {self.__class__.__name__}" + ) return True @@ -161,7 +165,9 @@ def finalize_step(self, step_prefix=""): self.log_dict( { f"{step_prefix}/{name}": ( - np.float32(metric.compute()) if isinstance(metric.compute(), np.float64) else metric.compute() + np.float32(metric.compute()) + if isinstance(metric.compute(), np.float64) + else metric.compute() ) for name, metric in self.metrics[step_prefix].items() if "_Curve" not in name @@ -202,7 +208,10 @@ def configure_optimizers(self): def on_test_epoch_start(self) -> None: self.metrics = { - step_name: {metric_name: metric() for metric_name, metric in self.set_metrics().items()} + step_name: { + metric_name: metric() + for metric_name, metric in self.set_metrics().items() + } for step_name in ["train", "val", "test"] } return super().on_test_epoch_start() @@ -236,6 +245,7 @@ def __init__( epochs: int = 100, input_size: Tensor = None, initialization_method: str = "normal", + pytorch_forecasting: bool = False, **kwargs, ): super().__init__( @@ -255,6 +265,7 @@ def __init__( ) self.output_transform = None self.loss_weights = None + self.pytorch_forecasting = pytorch_forecasting def set_weight(self, weight, dataset): """Set the weight for the loss function.""" @@ -308,6 +319,7 @@ def step_fn(self, element, step_prefix=""): element (object): step_prefix (str): Step type, by default: test, train, val. """ + """ if isinstance(element[0], OrderedDict): # check if the data loader is the one for the TFT nvidia implementation data, mask = element[0], element[1].to(self.device) @@ -321,10 +333,15 @@ def step_fn(self, element, step_prefix=""): if key == "target": labels = value.squeeze() data[key] = value - - elif len(element) == 2: + """ + if len(element) == 2: + """ if (isinstance(element[1], tuple)) or ( isinstance(element[1], list) + ): + """ + if ( + self.pytorch_forecasting ): # check if the data loader is the one for the pytorch forecasring implementation data, labels = element[0], element[1][0] mask = torch.ones_like(labels).bool() @@ -349,7 +366,9 @@ def step_fn(self, element, step_prefix=""): else: data = data.float().to(self.device) else: - raise Exception("Loader should return either (data, label) or (data, label, mask)") + raise Exception( + "Loader should return either (data, label) or (data, label, mask)" + ) out = self(data) @@ -359,20 +378,31 @@ def step_fn(self, element, step_prefix=""): else: aux_loss = 0 # Get prediction and target - prediction = torch.masked_select(out, mask.unsqueeze(-1)).reshape(-1, out.shape[-1]).to(self.device) + prediction = ( + torch.masked_select(out, mask.unsqueeze(-1)) + .reshape(-1, out.shape[-1]) + .to(self.device) + ) target = torch.masked_select(labels, mask).to(self.device) if prediction.shape[-1] > 1 and self.run_mode == RunMode.classification: # Classification task - loss = self.loss(prediction, target.long(), weight=self.loss_weights.to(self.device)) + aux_loss + loss = ( + self.loss( + prediction, target.long(), weight=self.loss_weights.to(self.device) + ) + + aux_loss + ) # Returns torch.long because negative log likelihood loss elif self.run_mode == RunMode.regression: # Regression task loss = self.loss(prediction[:, 0], target.float()) + aux_loss else: - raise ValueError(f"Run mode {self.run_mode} not yet supported. Please implement it.") + raise ValueError( + f"Run mode {self.run_mode} not yet supported. Please implement it." + ) transformed_output = self.output_transform((prediction, target)) for key, value in self.metrics[step_prefix].items(): @@ -389,7 +419,9 @@ def step_fn(self, element, step_prefix=""): value.update(transformed_output[0], transformed_output[1]) else: value.update(transformed_output) - self.log(f"{step_prefix}/loss", loss, on_step=False, on_epoch=True, sync_dist=True) + self.log( + f"{step_prefix}/loss", loss, on_step=False, on_epoch=True, sync_dist=True + ) return loss @@ -436,9 +468,15 @@ def set_metrics(self, labels): # Regression else: - if self.scaler is not None: # We invert transform the labels and predictions if they were scaled. - self.output_transform = lambda x: self.scaler.inverse_transform(x.reshape(-1, 1)) - self.label_transform = lambda x: self.scaler.inverse_transform(x.reshape(-1, 1)) + if ( + self.scaler is not None + ): # We invert transform the labels and predictions if they were scaled. + self.output_transform = lambda x: self.scaler.inverse_transform( + x.reshape(-1, 1) + ) + self.label_transform = lambda x: self.scaler.inverse_transform( + x.reshape(-1, 1) + ) else: self.output_transform = lambda x: x self.label_transform = lambda x: x @@ -473,7 +511,9 @@ def fit_model(self, train_data, train_labels, val_data, val_labels): def validation_step(self, val_dataset, _): val_rep, val_label = val_dataset.get_data_and_labels() - val_rep, val_label = torch.from_numpy(val_rep).to(self.device), torch.from_numpy(val_label).to(self.device) + val_rep, val_label = torch.from_numpy(val_rep).to( + self.device + ), torch.from_numpy(val_label).to(self.device) self.set_metrics(val_label) val_pred = self.predict(val_rep) @@ -516,7 +556,9 @@ def log_metrics(self, label, pred, metric_type): self.log_dict( { # MPS dependent type casting - f"{metric_type}/{name}": metric(self.label_transform(label), self.output_transform(pred)) + f"{metric_type}/{name}": metric( + self.label_transform(label), self.output_transform(pred) + ) if not self.mps else metric(self.label_transform(label), self.output_transform(pred)) # Fore very metric @@ -554,7 +596,9 @@ def set_model_args(self, model, *args, **kwargs): # Get passed keyword arguments arguments = locals()["kwargs"] # Get valid hyperparameters - hyperparams = {key: value for key, value in arguments.items() if key in possible_hps} + hyperparams = { + key: value for key, value in arguments.items() if key in possible_hps + } logging.debug(f"Creating model with: {hyperparams}.") return model(**hyperparams) @@ -607,7 +651,9 @@ def set_metrics(self): def init_weights(self, init_type="normal", gain=0.02): def init_func(m): classname = m.__class__.__name__ - if hasattr(m, "weight") and (classname.find("Conv") != -1 or classname.find("Linear") != -1): + if hasattr(m, "weight") and ( + classname.find("Conv") != -1 or classname.find("Linear") != -1 + ): if init_type == ImputationInit.NORMAL: nn.init.normal_(m.weight.data, 0.0, gain) elif init_type == ImputationInit.XAVIER: @@ -617,7 +663,9 @@ def init_func(m): elif init_type == ImputationInit.ORTHOGONAL: nn.init.orthogonal_(m.weight.data, gain=gain) else: - raise NotImplementedError(f"Initialization method {init_type} is not implemented") + raise NotImplementedError( + f"Initialization method {init_type} is not implemented" + ) if hasattr(m, "bias") and m.bias is not None: nn.init.constant_(m.bias.data, 0.0) elif classname.find("BatchNorm2d") != -1: diff --git a/icu_benchmarks/run.py b/icu_benchmarks/run.py index 345d7948..00fc3ef4 100644 --- a/icu_benchmarks/run.py +++ b/icu_benchmarks/run.py @@ -52,7 +52,8 @@ def main(my_args=tuple(sys.argv[1:])): evaluate = args.eval experiment = args.experiment source_dir = args.source_dir - explain = True if args.explain else False + explain = args.explain + pytorch_forecasting = args.pytorch_forecasting # Load task config gin.parse_config_file(f"configs/tasks/{task}.gin") mode = get_mode() @@ -205,6 +206,7 @@ def main(my_args=tuple(sys.argv[1:])): wandb=args.wandb_sweep, complete_train=args.complete_train, explain=explain, + pytorch_forecasting=pytorch_forecasting, ) log_full_line("FINISHED TRAINING", level=logging.INFO, char="=", num_newlines=3) diff --git a/icu_benchmarks/run_utils.py b/icu_benchmarks/run_utils.py index f6e54e41..94f95c61 100644 --- a/icu_benchmarks/run_utils.py +++ b/icu_benchmarks/run_utils.py @@ -151,6 +151,12 @@ def build_parser() -> ArgumentParser: action=BOA, help="Provide explaintations for predictions.", ) + parser.add_argument( + "--pytorch-forecasting", + default=False, + action=BOA, + help="use pytorch forecasting library ", + ) return parser From ec011c8e971266ced2487d9102ef2019ed5ac9bd Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Tue, 26 Sep 2023 16:27:48 +0200 Subject: [PATCH 062/142] added pytorch tft built in methods --- icu_benchmarks/models/dl_models.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 5dd067f4..f55c9a9a 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -512,3 +512,28 @@ def forward(self, x): out = self.model(x) pred = self.logit(out["prediction"]) return pred + + def actual_vs_predictions_plot(self, dataloader): + predictions = self.model.predict(dataloader, return_x=True) + predictions_vs_actuals = self.model.calculate_prediction_actual_by_variable( + predictions.x, predictions.output + ) + self.model.plot_prediction_actual_by_variable(predictions_vs_actuals) + return predictions_vs_actuals + + def interpertations(self,dataloader): + raw_predictions = self.model.predict(dataloader, return_x=True mode="raw") + interpretation = self.model.interpret_output(raw_predictions.output, reduction="sum") + self.model.plot_interpretation(interpretation) + return interpretation + def partial_depenecdy(self,dataloader,variable): + dependency = self.model.predict_dependency( + dataloader.dataset, variable, np.linspace(0, 30, 30), show_progress_bar=True, mode="dataframe" + ) + # plotting median and 25% and 75% percentile + agg_dependency = dependency.groupby(variable).normalized_prediction.agg( + median="median", q25=lambda x: x.quantile(0.25), q75=lambda x: x.quantile(0.75) + ) + ax = agg_dependency.plot(y="median") + ax.fill_between(agg_dependency.index, agg_dependency.q25, agg_dependency.q75, alpha=0.3) + return dependency \ No newline at end of file From 2f1b74ab63ed1a3b23975472e515e25eac71ffa1 Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Tue, 26 Sep 2023 16:31:21 +0200 Subject: [PATCH 063/142] added coma --- icu_benchmarks/models/dl_models.py | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index f55c9a9a..69933aca 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -520,20 +520,31 @@ def actual_vs_predictions_plot(self, dataloader): ) self.model.plot_prediction_actual_by_variable(predictions_vs_actuals) return predictions_vs_actuals - - def interpertations(self,dataloader): - raw_predictions = self.model.predict(dataloader, return_x=True mode="raw") - interpretation = self.model.interpret_output(raw_predictions.output, reduction="sum") + + def interpertations(self, dataloader): + raw_predictions = self.model.predict(dataloader, return_x=True, mode="raw") + interpretation = self.model.interpret_output( + raw_predictions.output, reduction="sum" + ) self.model.plot_interpretation(interpretation) return interpretation - def partial_depenecdy(self,dataloader,variable): + + def partial_depenecdy(self, dataloader, variable): dependency = self.model.predict_dependency( - dataloader.dataset, variable, np.linspace(0, 30, 30), show_progress_bar=True, mode="dataframe" + dataloader.dataset, + variable, + np.linspace(0, 30, 30), + show_progress_bar=True, + mode="dataframe", ) # plotting median and 25% and 75% percentile agg_dependency = dependency.groupby(variable).normalized_prediction.agg( - median="median", q25=lambda x: x.quantile(0.25), q75=lambda x: x.quantile(0.75) + median="median", + q25=lambda x: x.quantile(0.25), + q75=lambda x: x.quantile(0.75), ) ax = agg_dependency.plot(y="median") - ax.fill_between(agg_dependency.index, agg_dependency.q25, agg_dependency.q75, alpha=0.3) - return dependency \ No newline at end of file + ax.fill_between( + agg_dependency.index, agg_dependency.q25, agg_dependency.q75, alpha=0.3 + ) + return dependency From 86d719a3a8c12755e018f06080b6817092e2a2b9 Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Tue, 26 Sep 2023 17:00:07 +0200 Subject: [PATCH 064/142] added attention explantation for tft --- icu_benchmarks/models/dl_models.py | 14 ++++--- icu_benchmarks/models/train.py | 67 ++++++++++++++++++------------ 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 69933aca..d4fccccf 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -12,12 +12,12 @@ StaticCovariateEncoder, TFTBack, ) -from typing import Dict +import matplotlib.pyplot as plt from icu_benchmarks.models.wrappers import DLPredictionWrapper from torch import Tensor, cat, FloatTensor from pytorch_forecasting import TemporalFusionTransformer, RecurrentNetwork - from pytorch_forecasting.metrics import QuantileLoss +import matplotlib.pyplot as plt @gin.configurable @@ -521,15 +521,18 @@ def actual_vs_predictions_plot(self, dataloader): self.model.plot_prediction_actual_by_variable(predictions_vs_actuals) return predictions_vs_actuals - def interpertations(self, dataloader): + def interpertations(self, dataloader, log_dir): raw_predictions = self.model.predict(dataloader, return_x=True, mode="raw") interpretation = self.model.interpret_output( raw_predictions.output, reduction="sum" ) - self.model.plot_interpretation(interpretation) + figs = self.model.plot_interpretation(interpretation) + for key, fig in figs.items(): + fig.savefig(log_dir / f"interpretation_{key}.png", bbox_inches="tight") + return interpretation - def partial_depenecdy(self, dataloader, variable): + def predict_dependency(self, dataloader, variable, log_dir): dependency = self.model.predict_dependency( dataloader.dataset, variable, @@ -547,4 +550,5 @@ def partial_depenecdy(self, dataloader, variable): ax.fill_between( agg_dependency.index, agg_dependency.q25, agg_dependency.q75, alpha=0.3 ) + plt.savefig(log_dir / "dependecy.png", bbox_inches="tight") return dependency diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 4d5b4d7f..c697bb29 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -19,7 +19,6 @@ from icu_benchmarks.data.loader import ( PredictionDataset, ImputationDataset, - PredictionDatasetTFT, PredictionDatasetTFTpytorch, ) from icu_benchmarks.models.utils import save_config_file, JSONMetricsLogger @@ -70,6 +69,7 @@ def train_common( 32, ), explain: bool = False, + pytorch_forecasting: bool = False, ): """Common wrapper to train all benchmarked models. @@ -97,21 +97,13 @@ def train_common( pl_model: Loading a pytorch lightning model. num_workers: Number of workers to use for data loading. """ - with open("data.pkl", "wb") as fp: - pickle.dump(data, fp) logging.info(f"Training model: {model.__name__}.") # choose dataset_class based on the model dataset_class = ( ImputationDataset if mode == RunMode.imputation else ( - PredictionDatasetTFT - if model.__name__ == "TFT" - else ( - PredictionDatasetTFTpytorch - if (model.__name__ == "TFTpytorch") - else PredictionDataset - ) + PredictionDatasetTFTpytorch if (pytorch_forecasting) else PredictionDataset ) ) @@ -140,7 +132,7 @@ def train_common( logging.info(f"Using {num_workers} workers for data loading.") - if model.__name__ == "TFTpytorch": + if pytorch_forecasting: train_loader = train_dataset.to_dataloader( train=True, batch_size=batch_size, @@ -163,7 +155,15 @@ def train_common( drop_last=True, shuffle=False, ) - model = model(train_dataset, optimizer=optimizer, epochs=epochs, run_mode=mode) + if load_weights: + model = load_model( + model, source_dir, pl_model=pl_model, train_dataset=train_dataset + ) + + else: + model = model( + train_dataset, optimizer=optimizer, epochs=epochs, run_mode=mode + ) else: train_loader = DataLoader( @@ -195,24 +195,20 @@ def train_common( if model.requires_backprop else DataLoader([test_dataset.to_tensor()], batch_size=1) ) - + """ if isinstance(next(iter(train_loader))[0], OrderedDict): model = model(optimizer=optimizer, epochs=epochs, run_mode=mode) + """ + data_shape = next(iter(train_loader))[0].shape + if load_weights: + model = load_model(model, source_dir, pl_model=pl_model) else: - data_shape = next(iter(train_loader))[0].shape model = model( optimizer=optimizer, input_size=data_shape, epochs=epochs, run_mode=mode ) - if load_weights: - model = load_model(model, source_dir, pl_model=pl_model) - logging.info( - f"train_dataloader with {len(train_loader)} samples and validating on loader with" - f" {len(val_loader)} samples." - ) model.set_weight(weight, train_dataset) - model.set_trained_columns(train_dataset.get_feature_names()) loggers = [TensorBoardLogger(log_dir), JSONMetricsLogger(log_dir)] if use_wandb: @@ -263,18 +259,29 @@ def train_common( logging.info("Finished training full model.") save_config_file(log_dir) return 0 + if explain: + # actual_vs_predictions = model.actual_vs_predictions_plot(test_loader) + # print("1", actual_vs_predictions) + interperations = model.interpertations(test_loader, log_dir) + print("2", interperations) + pd = model.predict_dependency(test_loader, "age", log_dir) + print("3", pd) + """ test = next(iter(test_loader)) - test[0] = test[0].to(model.device) + + for key, value in test[0].items(): + test[0][key] = test[0][key].to(model.device) + pred = model(test[0]) + ig = IntegratedGradients(model) # Reformat attributions. - test[0].requires_grad_() - print(pred.shape) - print(test[0].shape) + attr, delta = ig.attribute(test[0], target=0, return_convergence_delta=True) attr = attr.detach().numpy() print(attr) + """ model.set_weight("balanced", train_dataset) test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0][ "test/loss" @@ -283,7 +290,7 @@ def train_common( return test_loss -def load_model(model, source_dir, pl_model=True): +def load_model(model, source_dir, pl_model=True, train_dataset=None): if source_dir.exists(): if model.requires_backprop: if (source_dir / "model.ckpt").exists(): @@ -295,7 +302,13 @@ def load_model(model, source_dir, pl_model=True): else: return Exception(f"No weights to load at path : {source_dir}") if pl_model: - model = model.load_from_checkpoint(model_path) + if train_dataset is not None: + model = model.load_from_checkpoint( + model_path, dataset=train_dataset + ) + + else: + model = model.load_from_checkpoint(model_path) else: checkpoint = torch.load(model_path) model.load_from_checkpoint(checkpoint) From 00d78dd0dc29313aead9479ac3fbf5554546af98 Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Wed, 27 Sep 2023 14:07:31 +0200 Subject: [PATCH 065/142] changed forward method for tft to allow captum explantations --- icu_benchmarks/models/dl_models.py | 24 ++++++++++++++++++++++++ icu_benchmarks/models/wrappers.py | 16 ++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index d4fccccf..88517aea 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -2,6 +2,7 @@ from numbers import Integral import numpy as np import torch.nn as nn +from typing import Dict from icu_benchmarks.contants import RunMode from icu_benchmarks.models.layers import ( TransformerBlock, @@ -508,10 +509,33 @@ def set_weight(self, weight, dataset): weight = FloatTensor(dataset.get_balance()) self.loss_weights = weight + def forward( + self, + tuple_x: tuple, + ) -> Dict[str, Tensor]: + x_dict = { + "encoder_cat": tuple_x[0], + "encoder_cont": tuple_x[1], + "encoder_target": tuple_x[2], + "encoder_lengths": tuple_x[3], + "decoder_cat": tuple_x[4], + "decoder_cont": tuple_x[5], + "decoder_target": tuple_x[6], + "decoder_lengths": tuple_x[7], + "decoder_time_idx": tuple_x[8], + "groups": tuple_x[9], + "target_scale": tuple_x[10], + } + out = self.model(x_dict) + pred = self.logit(out["prediction"]) + return pred + + """ def forward(self, x): out = self.model(x) pred = self.logit(out["prediction"]) return pred + """ def actual_vs_predictions_plot(self, dataloader): predictions = self.model.predict(dataloader, return_x=True) diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index ff70db34..c60cd6a5 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -343,7 +343,20 @@ def step_fn(self, element, step_prefix=""): if ( self.pytorch_forecasting ): # check if the data loader is the one for the pytorch forecasring implementation - data, labels = element[0], element[1][0] + dic, labels = element[0], element[1][0] + data = ( + dic["encoder_cat"], + dic["encoder_cont"], + dic["encoder_target"], + dic["encoder_lengths"], + dic["decoder_cat"], + dic["decoder_cont"], + dic["decoder_target"], + dic["decoder_lengths"], + dic["decoder_time_idx"], + dic["groups"], + dic["target_scale"], + ) mask = torch.ones_like(labels).bool() else: data, labels = element[0], (element[1]).to(self.device) @@ -369,7 +382,6 @@ def step_fn(self, element, step_prefix=""): raise Exception( "Loader should return either (data, label) or (data, label, mask)" ) - out = self(data) # If aux_loss is present, it is returned as a tuple From e877c5ffa5189efca46337f149bf3ad53f5c8de2 Mon Sep 17 00:00:00 2001 From: "youssef.mecky" Date: Fri, 29 Sep 2023 14:55:06 +0200 Subject: [PATCH 066/142] cleaned files up --- configs/prediction_models/TFTpytorch.gin | 4 +- configs/prediction_models/common/DLCommon.gin | 4 +- configs/prediction_models/common/DLTuning.gin | 2 +- icu_benchmarks/cross_validation.py | 8 +- icu_benchmarks/data/loader.py | 48 ++----- icu_benchmarks/imputation/sssds4.py | 3 +- icu_benchmarks/models/dl_models.py | 38 ++---- icu_benchmarks/models/train.py | 48 ++----- icu_benchmarks/models/wrappers.py | 81 +++--------- icu_benchmarks/run.py | 42 ++---- icu_benchmarks/run_utils.py | 121 +++++------------- 11 files changed, 101 insertions(+), 298 deletions(-) diff --git a/configs/prediction_models/TFTpytorch.gin b/configs/prediction_models/TFTpytorch.gin index 27e2cf25..13c7520a 100644 --- a/configs/prediction_models/TFTpytorch.gin +++ b/configs/prediction_models/TFTpytorch.gin @@ -12,11 +12,11 @@ optimizer/hyperparameter.weight_decay = 1e-6 # Encoder params model/hyperparameter.class_to_tune = @TFTpytorch -model/hyperparameter.hidden = 16 +model/hyperparameter.hidden = (16,32,64,128) model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.dropout = (0.0, 0.4) model/hyperparameter.dropout_att = (0.0, 0.4) -model/hyperparameter.n_heads =1 +model/hyperparameter.n_heads =(1,2,4) model/hyperparameter.lr_scheduler = "exponential" PredictionDatasetTFTpytorch.max_encoder_length = 24 PredictionDatasetTFTpytorch.max_prediction_length = 24 diff --git a/configs/prediction_models/common/DLCommon.gin b/configs/prediction_models/common/DLCommon.gin index f41f2104..7c72b17a 100644 --- a/configs/prediction_models/common/DLCommon.gin +++ b/configs/prediction_models/common/DLCommon.gin @@ -14,8 +14,8 @@ base_regression_preprocessor.generate_features = False # Train params train_common.optimizer = @Adam -train_common.epochs = 50 -train_common.batch_size = 64 +train_common.epochs = 1000 +train_common.batch_size = 256 train_common.patience = 10 train_common.min_delta = 1e-4 diff --git a/configs/prediction_models/common/DLTuning.gin b/configs/prediction_models/common/DLTuning.gin index b4d13e12..1a2b7bb4 100644 --- a/configs/prediction_models/common/DLTuning.gin +++ b/configs/prediction_models/common/DLTuning.gin @@ -2,4 +2,4 @@ tune_hyperparameters.scopes = ["model", "optimizer"] tune_hyperparameters.n_initial_points = 5 tune_hyperparameters.n_calls = 30 -tune_hyperparameters.folds_to_tune_on = 2 \ No newline at end of file +tune_hyperparameters.folds_to_tune_on = 1 \ No newline at end of file diff --git a/icu_benchmarks/cross_validation.py b/icu_benchmarks/cross_validation.py index bcf2f5f5..412df3eb 100644 --- a/icu_benchmarks/cross_validation.py +++ b/icu_benchmarks/cross_validation.py @@ -82,15 +82,11 @@ def execute_repeated_cv( cv_folds_to_train = 1 else: - logging.info( - f"Starting nested CV with {cv_repetitions_to_train} repetitions of {cv_folds_to_train} folds." - ) + logging.info(f"Starting nested CV with {cv_repetitions_to_train} repetitions of {cv_folds_to_train} folds.") for repetition in range(cv_repetitions_to_train): for fold_index in range(cv_folds_to_train): - repetition_fold_dir = ( - log_dir / f"repetition_{repetition}" / f"fold_{fold_index}" - ) + repetition_fold_dir = log_dir / f"repetition_{repetition}" / f"fold_{fold_index}" repetition_fold_dir.mkdir(parents=True, exist_ok=True) start_time = datetime.now() diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 5dde7809..0e46cc51 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -3,15 +3,13 @@ import pandas as pd import gin import numpy as np -from torch import Tensor, cat, from_numpy, float32, empty, stack +from torch import Tensor, cat, from_numpy, float32 from torch.utils.data import Dataset import logging from typing import Dict, Tuple from icu_benchmarks.imputation.amputations import ampute_data from .constants import DataSegment as Segment from .constants import DataSplit as Split -from .constants import FeatType as Features -from collections import OrderedDict from pytorch_forecasting import TimeSeriesDataSet @@ -105,15 +103,11 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: # slice to make sure to always return a DF window = self.features_df.loc[stay_id:stay_id].to_numpy() - labels = self.outcome_df.loc[stay_id:stay_id][self.vars["LABEL"]].to_numpy( - dtype=float - ) + labels = self.outcome_df.loc[stay_id:stay_id][self.vars["LABEL"]].to_numpy(dtype=float) if len(labels) == 1: # only one label per stay, align with window - labels = np.concatenate( - [np.empty(window.shape[0] - 1) * np.nan, labels], axis=0 - ) + labels = np.concatenate([np.empty(window.shape[0] - 1) * np.nan, labels], axis=0) length_diff = self.maxlen - window.shape[0] pad_mask = np.ones(window.shape[0]) @@ -121,9 +115,7 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: # Padding the array to fulfill size requirement if length_diff > 0: # window shorter than the longest window in dataset, pad to same length - window = np.concatenate( - [window, np.ones((length_diff, window.shape[1])) * pad_value], axis=0 - ) + window = np.concatenate([window, np.ones((length_diff, window.shape[1])) * pad_value], axis=0) labels = np.concatenate([labels, np.ones(length_diff) * pad_value], axis=0) pad_mask = np.concatenate([pad_mask, np.zeros(length_diff)], axis=0) @@ -179,7 +171,7 @@ class PredictionDatasetTFT(PredictionDataset): We also need to feed the model the variables in a specific order Args: ram_cache (bool, optional): Whether the complete dataset should be stored in ram. Defaults to True. - + def __init__(self, *args, ram_cache: bool = True, **kwargs): super().__init__(*args, ram_cache=True, **kwargs) @@ -193,7 +185,7 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: idx: A specific row index to sample. Returns: A sample from the data, consisting of data, labels and padding mask. - + if self._cached_dataset is not None: return self._cached_dataset[idx] @@ -333,12 +325,8 @@ def __init__( self.amputated_values, self.amputation_mask = ampute_data( self.features_df, mask_method, mask_proportion, mask_observation_proportion ) - self.amputation_mask = ( - self.amputation_mask + self.features_df.isna().values - ).bool() - self.amputation_mask = DataFrame( - self.amputation_mask, columns=self.vars[Segment.dynamic] - ) + self.amputation_mask = (self.amputation_mask + self.features_df.isna().values).bool() + self.amputation_mask = DataFrame(self.amputation_mask, columns=self.vars[Segment.dynamic]) self.amputation_mask[self.vars["GROUP"]] = self.features_df.index self.amputation_mask.set_index(self.vars["GROUP"], inplace=True) @@ -363,15 +351,9 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: # slice to make sure to always return a DF window = self.features_df.loc[stay_id:stay_id, self.vars[Segment.dynamic]] - window_missingness_mask = self.target_missingness_mask.loc[ - stay_id:stay_id, self.vars[Segment.dynamic] - ] - amputated_window = self.amputated_values.loc[ - stay_id:stay_id, self.vars[Segment.dynamic] - ] - amputation_mask = self.amputation_mask.loc[ - stay_id:stay_id, self.vars[Segment.dynamic] - ] + window_missingness_mask = self.target_missingness_mask.loc[stay_id:stay_id, self.vars[Segment.dynamic]] + amputated_window = self.amputated_values.loc[stay_id:stay_id, self.vars[Segment.dynamic]] + amputation_mask = self.amputation_mask.loc[stay_id:stay_id, self.vars[Segment.dynamic]] return ( from_numpy(amputated_window.values).to(float32), @@ -470,18 +452,14 @@ def __init__( max_encoder_length: int = 24, **kwargs, ): - data[split]["FEATURES"]["time_idx"] = ( - (data[split]["FEATURES"]["time"] / pd.Timedelta(seconds=3600)) - ).astype( + data[split]["FEATURES"]["time_idx"] = ((data[split]["FEATURES"]["time"] / pd.Timedelta(seconds=3600))).astype( int ) # create an incremental column indicating the time step(required by constructor) data = data.get(split) # get split labels = data["OUTCOME"] features = data["FEATURES"] self.name = name - self.data = pd.merge( - labels, features, on=["stay_id", "time"] - ) # combine labels and features + self.data = pd.merge(labels, features, on=["stay_id", "time"]) # combine labels and features # self.data["sex"].replace([0, 1], ["Female", "Male"], inplace=True) self.split = split self.args = args diff --git a/icu_benchmarks/imputation/sssds4.py b/icu_benchmarks/imputation/sssds4.py index d359fb09..25933b68 100644 --- a/icu_benchmarks/imputation/sssds4.py +++ b/icu_benchmarks/imputation/sssds4.py @@ -414,7 +414,8 @@ def calc_diffusion_hyperparams(diffusion_time_steps, beta_0, beta_T): Beta_tilde = Beta + 0 for t in range(1, diffusion_time_steps): Alpha_bar[t] *= Alpha_bar[t - 1] # \bar{\alpha}_t = \prod_{s=1}^t \alpha_s - Beta_tilde[t] *= (1 - Alpha_bar[t - 1]) / (1 - Alpha_bar[t]) # \tilde{\beta}_t = \beta_t * (1-\bar{\alpha}_{t-1}) + # \tilde{\beta}_t = \beta_t * (1-\bar{\alpha}_{t-1}) + Beta_tilde[t] *= (1 - Alpha_bar[t - 1]) / (1 - Alpha_bar[t]) # / (1-\bar{\alpha}_t) Sigma = torch.sqrt(Beta_tilde) # \sigma_t^2 = \tilde{\beta}_t diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 88517aea..7070ffac 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -9,14 +9,11 @@ LocalBlock, TemporalBlock, PositionalEncoding, - LazyEmbedding, - StaticCovariateEncoder, - TFTBack, ) import matplotlib.pyplot as plt from icu_benchmarks.models.wrappers import DLPredictionWrapper -from torch import Tensor, cat, FloatTensor -from pytorch_forecasting import TemporalFusionTransformer, RecurrentNetwork +from torch import Tensor, FloatTensor +from pytorch_forecasting import TemporalFusionTransformer # , RecurrentNetwork from pytorch_forecasting.metrics import QuantileLoss import matplotlib.pyplot as plt @@ -152,9 +149,7 @@ def __init__( **kwargs, ) hidden = hidden if hidden % 2 == 0 else hidden + 1 # Make sure hidden is even - self.input_embedding = nn.Linear( - input_size[2], hidden - ) # This acts as a time-distributed layer by defaults + self.input_embedding = nn.Linear(input_size[2], hidden) # This acts as a time-distributed layer by defaults if pos_encoding: self.pos_encoder = PositionalEncoding(hidden) else: @@ -225,9 +220,7 @@ def __init__( ) hidden = hidden if hidden % 2 == 0 else hidden + 1 # Make sure hidden is even - self.input_embedding = nn.Linear( - input_size[2], hidden - ) # This acts as a time-distributed layer by defaults + self.input_embedding = nn.Linear(input_size[2], hidden) # This acts as a time-distributed layer by defaults if pos_encoding: self.pos_encoder = PositionalEncoding(hidden) else: @@ -293,13 +286,9 @@ def __init__( # We compute automatically the depth based on the desired seq_length. if isinstance(num_channels, Integral) and max_seq_length: - num_channels = [num_channels] * int( - np.ceil(np.log(max_seq_length / 2) / np.log(kernel_size)) - ) + num_channels = [num_channels] * int(np.ceil(np.log(max_seq_length / 2) / np.log(kernel_size))) elif isinstance(num_channels, Integral) and not max_seq_length: - raise Exception( - "a maximum sequence length needs to be provided if num_channels is int" - ) + raise Exception("a maximum sequence length needs to be provided if num_channels is int") num_levels = len(num_channels) for i in range(num_levels): @@ -333,7 +322,8 @@ def forward(self, x): @gin.configurable class TFT(DLPredictionWrapper): """ - Implementation of https://arxiv.org/abs/1912.09363 from https://github.com/NVIDIA/DeepLearningExamples/tree/master/PyTorch/Forecasting/TFT + Implementation of https://arxiv.org/abs/1912.09363 + from https://github.com/NVIDIA/DeepLearningExamples/tree/master/PyTorch/Forecasting/TFT """ _supported_run_modes = [RunMode.classification, RunMode.regression] @@ -539,17 +529,13 @@ def forward(self, x): def actual_vs_predictions_plot(self, dataloader): predictions = self.model.predict(dataloader, return_x=True) - predictions_vs_actuals = self.model.calculate_prediction_actual_by_variable( - predictions.x, predictions.output - ) + predictions_vs_actuals = self.model.calculate_prediction_actual_by_variable(predictions.x, predictions.output) self.model.plot_prediction_actual_by_variable(predictions_vs_actuals) return predictions_vs_actuals def interpertations(self, dataloader, log_dir): raw_predictions = self.model.predict(dataloader, return_x=True, mode="raw") - interpretation = self.model.interpret_output( - raw_predictions.output, reduction="sum" - ) + interpretation = self.model.interpret_output(raw_predictions.output, reduction="sum") figs = self.model.plot_interpretation(interpretation) for key, fig in figs.items(): fig.savefig(log_dir / f"interpretation_{key}.png", bbox_inches="tight") @@ -571,8 +557,6 @@ def predict_dependency(self, dataloader, variable, log_dir): q75=lambda x: x.quantile(0.75), ) ax = agg_dependency.plot(y="median") - ax.fill_between( - agg_dependency.index, agg_dependency.q25, agg_dependency.q75, alpha=0.3 - ) + ax.fill_between(agg_dependency.index, agg_dependency.q25, agg_dependency.q75, alpha=0.3) plt.savefig(log_dir / "dependecy.png", bbox_inches="tight") return dependency diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index c697bb29..1cca8970 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -27,9 +27,7 @@ from collections import OrderedDict from captum.attr import IntegratedGradients -cpu_core_count = ( - len(os.sched_getaffinity(0)) if hasattr(os, "sched_getaffinity") else os.cpu_count() -) +cpu_core_count = len(os.sched_getaffinity(0)) if hasattr(os, "sched_getaffinity") else os.cpu_count() def assure_minimum_length(dataset): @@ -102,25 +100,15 @@ def train_common( dataset_class = ( ImputationDataset if mode == RunMode.imputation - else ( - PredictionDatasetTFTpytorch if (pytorch_forecasting) else PredictionDataset - ) + else (PredictionDatasetTFTpytorch if (pytorch_forecasting) else PredictionDataset) ) logging.info(f"Logging to directory: {log_dir}.") - save_config_file( - log_dir - ) # We save the operative config before and also after training + save_config_file(log_dir) # We save the operative config before and also after training - train_dataset = dataset_class( - data, split=Split.train, ram_cache=ram_cache, name=dataset_names["train"] - ) - val_dataset = dataset_class( - data, split=Split.val, ram_cache=ram_cache, name=dataset_names["val"] - ) - train_dataset, val_dataset = assure_minimum_length( - train_dataset - ), assure_minimum_length(val_dataset) + train_dataset = dataset_class(data, split=Split.train, ram_cache=ram_cache, name=dataset_names["train"]) + val_dataset = dataset_class(data, split=Split.val, ram_cache=ram_cache, name=dataset_names["val"]) + train_dataset, val_dataset = assure_minimum_length(train_dataset), assure_minimum_length(val_dataset) batch_size = min(batch_size, len(train_dataset), len(val_dataset)) test_dataset = dataset_class(data, split=test_on, name=dataset_names["test"]) test_dataset = assure_minimum_length(test_dataset) @@ -156,14 +144,10 @@ def train_common( shuffle=False, ) if load_weights: - model = load_model( - model, source_dir, pl_model=pl_model, train_dataset=train_dataset - ) + model = load_model(model, source_dir, pl_model=pl_model, train_dataset=train_dataset) else: - model = model( - train_dataset, optimizer=optimizer, epochs=epochs, run_mode=mode - ) + model = model(train_dataset, optimizer=optimizer, epochs=epochs, run_mode=mode) else: train_loader = DataLoader( @@ -204,9 +188,7 @@ def train_common( if load_weights: model = load_model(model, source_dir, pl_model=pl_model) else: - model = model( - optimizer=optimizer, input_size=data_shape, epochs=epochs, run_mode=mode - ) + model = model(optimizer=optimizer, input_size=data_shape, epochs=epochs, run_mode=mode) model.set_weight(weight, train_dataset) model.set_trained_columns(train_dataset.get_feature_names()) @@ -246,9 +228,7 @@ def train_common( if not eval_only: if model.requires_backprop: logging.info("Training DL model.") - trainer.fit( - model, train_dataloaders=train_loader, val_dataloaders=val_loader - ) + trainer.fit(model, train_dataloaders=train_loader, val_dataloaders=val_loader) logging.info("Training complete.") else: logging.info("Training ML model.") @@ -283,9 +263,7 @@ def train_common( print(attr) """ model.set_weight("balanced", train_dataset) - test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0][ - "test/loss" - ] + test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0]["test/loss"] save_config_file(log_dir) return test_loss @@ -303,9 +281,7 @@ def load_model(model, source_dir, pl_model=True, train_dataset=None): return Exception(f"No weights to load at path : {source_dir}") if pl_model: if train_dataset is not None: - model = model.load_from_checkpoint( - model_path, dataset=train_dataset - ) + model = model.load_from_checkpoint(model_path, dataset=train_dataset) else: model = model.load_from_checkpoint(model_path) diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index c60cd6a5..8bffe10f 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -1,16 +1,13 @@ import logging from abc import ABC from typing import Dict, Any, List, Optional, Union - import torchmetrics from sklearn.metrics import log_loss, mean_squared_error - import torch from torch.nn import MSELoss, CrossEntropyLoss import torch.nn as nn from torch import Tensor, FloatTensor from torch.optim import Optimizer, Adam -from collections import OrderedDict import inspect import gin import numpy as np @@ -23,9 +20,7 @@ from icu_benchmarks.contants import RunMode gin.config.external_configurable(nn.functional.nll_loss, module="torch.nn.functional") -gin.config.external_configurable( - nn.functional.cross_entropy, module="torch.nn.functional" -) +gin.config.external_configurable(nn.functional.cross_entropy, module="torch.nn.functional") gin.config.external_configurable(nn.functional.mse_loss, module="torch.nn.functional") gin.config.external_configurable(mean_squared_error, module="sklearn.metrics") @@ -90,9 +85,7 @@ def save_model(self, save_path, file_name, file_extension): def check_supported_runmode(self, runmode: RunMode): if runmode not in self._supported_run_modes: - raise ValueError( - f"Runmode {runmode} not supported for {self.__class__.__name__}" - ) + raise ValueError(f"Runmode {runmode} not supported for {self.__class__.__name__}") return True @@ -165,9 +158,7 @@ def finalize_step(self, step_prefix=""): self.log_dict( { f"{step_prefix}/{name}": ( - np.float32(metric.compute()) - if isinstance(metric.compute(), np.float64) - else metric.compute() + np.float32(metric.compute()) if isinstance(metric.compute(), np.float64) else metric.compute() ) for name, metric in self.metrics[step_prefix].items() if "_Curve" not in name @@ -208,10 +199,7 @@ def configure_optimizers(self): def on_test_epoch_start(self) -> None: self.metrics = { - step_name: { - metric_name: metric() - for metric_name, metric in self.set_metrics().items() - } + step_name: {metric_name: metric() for metric_name, metric in self.set_metrics().items()} for step_name in ["train", "val", "test"] } return super().on_test_epoch_start() @@ -340,9 +328,7 @@ def step_fn(self, element, step_prefix=""): isinstance(element[1], list) ): """ - if ( - self.pytorch_forecasting - ): # check if the data loader is the one for the pytorch forecasring implementation + if self.pytorch_forecasting: # check if the data loader is the one for the pytorch forecasring implementation dic, labels = element[0], element[1][0] data = ( dic["encoder_cat"], @@ -379,9 +365,7 @@ def step_fn(self, element, step_prefix=""): else: data = data.float().to(self.device) else: - raise Exception( - "Loader should return either (data, label) or (data, label, mask)" - ) + raise Exception("Loader should return either (data, label) or (data, label, mask)") out = self(data) # If aux_loss is present, it is returned as a tuple @@ -390,31 +374,20 @@ def step_fn(self, element, step_prefix=""): else: aux_loss = 0 # Get prediction and target - prediction = ( - torch.masked_select(out, mask.unsqueeze(-1)) - .reshape(-1, out.shape[-1]) - .to(self.device) - ) + prediction = torch.masked_select(out, mask.unsqueeze(-1)).reshape(-1, out.shape[-1]).to(self.device) target = torch.masked_select(labels, mask).to(self.device) if prediction.shape[-1] > 1 and self.run_mode == RunMode.classification: # Classification task - loss = ( - self.loss( - prediction, target.long(), weight=self.loss_weights.to(self.device) - ) - + aux_loss - ) + loss = self.loss(prediction, target.long(), weight=self.loss_weights.to(self.device)) + aux_loss # Returns torch.long because negative log likelihood loss elif self.run_mode == RunMode.regression: # Regression task loss = self.loss(prediction[:, 0], target.float()) + aux_loss else: - raise ValueError( - f"Run mode {self.run_mode} not yet supported. Please implement it." - ) + raise ValueError(f"Run mode {self.run_mode} not yet supported. Please implement it.") transformed_output = self.output_transform((prediction, target)) for key, value in self.metrics[step_prefix].items(): @@ -431,9 +404,7 @@ def step_fn(self, element, step_prefix=""): value.update(transformed_output[0], transformed_output[1]) else: value.update(transformed_output) - self.log( - f"{step_prefix}/loss", loss, on_step=False, on_epoch=True, sync_dist=True - ) + self.log(f"{step_prefix}/loss", loss, on_step=False, on_epoch=True, sync_dist=True) return loss @@ -480,15 +451,9 @@ def set_metrics(self, labels): # Regression else: - if ( - self.scaler is not None - ): # We invert transform the labels and predictions if they were scaled. - self.output_transform = lambda x: self.scaler.inverse_transform( - x.reshape(-1, 1) - ) - self.label_transform = lambda x: self.scaler.inverse_transform( - x.reshape(-1, 1) - ) + if self.scaler is not None: # We invert transform the labels and predictions if they were scaled. + self.output_transform = lambda x: self.scaler.inverse_transform(x.reshape(-1, 1)) + self.label_transform = lambda x: self.scaler.inverse_transform(x.reshape(-1, 1)) else: self.output_transform = lambda x: x self.label_transform = lambda x: x @@ -523,9 +488,7 @@ def fit_model(self, train_data, train_labels, val_data, val_labels): def validation_step(self, val_dataset, _): val_rep, val_label = val_dataset.get_data_and_labels() - val_rep, val_label = torch.from_numpy(val_rep).to( - self.device - ), torch.from_numpy(val_label).to(self.device) + val_rep, val_label = torch.from_numpy(val_rep).to(self.device), torch.from_numpy(val_label).to(self.device) self.set_metrics(val_label) val_pred = self.predict(val_rep) @@ -568,9 +531,7 @@ def log_metrics(self, label, pred, metric_type): self.log_dict( { # MPS dependent type casting - f"{metric_type}/{name}": metric( - self.label_transform(label), self.output_transform(pred) - ) + f"{metric_type}/{name}": metric(self.label_transform(label), self.output_transform(pred)) if not self.mps else metric(self.label_transform(label), self.output_transform(pred)) # Fore very metric @@ -608,9 +569,7 @@ def set_model_args(self, model, *args, **kwargs): # Get passed keyword arguments arguments = locals()["kwargs"] # Get valid hyperparameters - hyperparams = { - key: value for key, value in arguments.items() if key in possible_hps - } + hyperparams = {key: value for key, value in arguments.items() if key in possible_hps} logging.debug(f"Creating model with: {hyperparams}.") return model(**hyperparams) @@ -663,9 +622,7 @@ def set_metrics(self): def init_weights(self, init_type="normal", gain=0.02): def init_func(m): classname = m.__class__.__name__ - if hasattr(m, "weight") and ( - classname.find("Conv") != -1 or classname.find("Linear") != -1 - ): + if hasattr(m, "weight") and (classname.find("Conv") != -1 or classname.find("Linear") != -1): if init_type == ImputationInit.NORMAL: nn.init.normal_(m.weight.data, 0.0, gain) elif init_type == ImputationInit.XAVIER: @@ -675,9 +632,7 @@ def init_func(m): elif init_type == ImputationInit.ORTHOGONAL: nn.init.orthogonal_(m.weight.data, gain=gain) else: - raise NotImplementedError( - f"Initialization method {init_type} is not implemented" - ) + raise NotImplementedError(f"Initialization method {init_type} is not implemented") if hasattr(m, "bias") and m.bias is not None: nn.init.constant_(m.bias.data, 0.0) elif classname.find("BatchNorm2d") != -1: diff --git a/icu_benchmarks/run.py b/icu_benchmarks/run.py index 00fc3ef4..40496145 100644 --- a/icu_benchmarks/run.py +++ b/icu_benchmarks/run.py @@ -66,19 +66,11 @@ def main(my_args=tuple(sys.argv[1:])): logging.info(f"Task mode: {mode}.") # Set train size to fine tune size if fine tune is set, else use custom train size - train_size = ( - args.fine_tune - if args.fine_tune is not None - else args.samples - if args.samples is not None - else None - ) + train_size = args.fine_tune if args.fine_tune is not None else args.samples if args.samples is not None else None # Whether to load weights from a previous run load_weights = evaluate or args.fine_tune is not None - pretrained_imputation_model = load_pretrained_imputation_model( - args.pretrained_imputation - ) + pretrained_imputation_model = load_pretrained_imputation_model(args.pretrained_imputation) # Log imputation model to wandb update_wandb_config( { @@ -92,11 +84,7 @@ def main(my_args=tuple(sys.argv[1:])): log_dir = ( (log_dir_name / experiment) if experiment - else ( - log_dir_name - / (args.task_name if args.task_name is not None else args.task) - / model - ) + else (log_dir_name / (args.task_name if args.task_name is not None else args.task) / model) ) log_full_line(f"Logging to {log_dir}.", logging.INFO) @@ -119,9 +107,7 @@ def main(my_args=tuple(sys.argv[1:])): # Load pretrained model in evaluate mode or when finetuning if load_weights: if args.source_dir is None: - raise ValueError( - "Please specify a source directory when evaluating or fine-tuning." - ) + raise ValueError("Please specify a source directory when evaluating or fine-tuning.") log_dir /= f"_from_{args.source_name}" name_datasets(args.source_name, args.source_name, args.name) if args.fine_tune: @@ -129,13 +115,9 @@ def main(my_args=tuple(sys.argv[1:])): name_datasets(args.name, args.name, args.name) run_dir = create_run_dir(log_dir) source_dir = args.source_dir - logging.info( - f"Will load weights from {source_dir} and bind train gin-config. Note: this might override your config." - ) + logging.info(f"Will load weights from {source_dir} and bind train gin-config. Note: this might override your config.") gin.parse_config_file(source_dir / "train_config.gin") - elif ( - args.samples and args.source_dir is not None - ): # Train model with limited samples and bind existing config + elif args.samples and args.source_dir is not None: # Train model with limited samples and bind existing config logging.info("Binding train gin-config. Note: this might override your config.") gin.parse_config_file(args.source_dir / "train_config.gin") log_dir /= f"samples_{args.fine_tune}" @@ -146,22 +128,14 @@ def main(my_args=tuple(sys.argv[1:])): name_datasets(args.name, args.name, args.name) hp_checkpoint = log_dir / args.hp_checkpoint if args.hp_checkpoint else None model_path = ( - Path("configs") - / ( - "imputation_models" - if mode == RunMode.imputation - else "prediction_models" - ) - / f"{model}.gin" + Path("configs") / ("imputation_models" if mode == RunMode.imputation else "prediction_models") / f"{model}.gin" ) gin_config_files = ( [Path(f"configs/experiments/{args.experiment}.gin")] if args.experiment else [model_path, Path(f"configs/tasks/{task}.gin")] ) - gin.parse_config_files_and_bindings( - gin_config_files, args.hyperparams, finalize_config=False - ) + gin.parse_config_files_and_bindings(gin_config_files, args.hyperparams, finalize_config=False) log_full_line(f"Data directory: {data_dir.resolve()}", level=logging.INFO) run_dir = create_run_dir(log_dir) choose_and_bind_hyperparameters( diff --git a/icu_benchmarks/run_utils.py b/icu_benchmarks/run_utils.py index 94f95c61..b23aae68 100644 --- a/icu_benchmarks/run_utils.py +++ b/icu_benchmarks/run_utils.py @@ -23,9 +23,7 @@ def build_parser() -> ArgumentParser: Returns: The configured ArgumentParser. """ - parser = ArgumentParser( - description="Framework for benchmarking ML/DL models on ICU data" - ) + parser = ArgumentParser(description="Framework for benchmarking ML/DL models on ICU data") parser.add_argument( "-d", @@ -42,12 +40,8 @@ def build_parser() -> ArgumentParser: help="Name of the task gin.", ) parser.add_argument("-n", "--name", help="Name of the (target) dataset.") - parser.add_argument( - "-tn", "--task-name", help="Name of the task, used for naming experiments." - ) - parser.add_argument( - "-m", "--model", default="LGBMClassifier", help="Name of the model gin." - ) + parser.add_argument("-tn", "--task-name", help="Name of the task, used for naming experiments.") + parser.add_argument("-m", "--model", default="LGBMClassifier", help="Name of the model gin.") parser.add_argument("-e", "--experiment", help="Name of the experiment gin.") parser.add_argument( "-l", @@ -71,12 +65,8 @@ def build_parser() -> ArgumentParser: help="Set to log verbosly. Disable for clean logs.", ) parser.add_argument("--cpu", default=False, action=BOA, help="Set to use CPU.") - parser.add_argument( - "-db", "--debug", default=False, action=BOA, help="Set to load less data." - ) - parser.add_argument( - "--reproducible", default=True, action=BOA, help="Make torch reproducible." - ) + parser.add_argument("-db", "--debug", default=False, action=BOA, help="Set to load less data.") + parser.add_argument("--reproducible", default=True, action=BOA, help="Make torch reproducible.") parser.add_argument( "-lc", "--load_cache", @@ -91,9 +81,7 @@ def build_parser() -> ArgumentParser: action=BOA, help="Set to generate data cache.", ) - parser.add_argument( - "-p", "--preprocessor", type=Path, help="Load custom preprocessor from file." - ) + parser.add_argument("-p", "--preprocessor", type=Path, help="Load custom preprocessor from file.") parser.add_argument("-pl", "--plot", action=BOA, help="Generate common plots.") parser.add_argument( "-wd", @@ -107,18 +95,10 @@ def build_parser() -> ArgumentParser: type=str, help="Path to pretrained imputation model.", ) - parser.add_argument( - "-hp", "--hyperparams", nargs="+", help="Hyperparameters for model." - ) - parser.add_argument( - "--tune", default=False, action=BOA, help="Find best hyperparameters." - ) - parser.add_argument( - "--hp-checkpoint", type=Path, help="Use previous hyperparameter checkpoint." - ) - parser.add_argument( - "--eval", default=False, action=BOA, help="Only evaluate model, skip training." - ) + parser.add_argument("-hp", "--hyperparams", nargs="+", help="Hyperparameters for model.") + parser.add_argument("--tune", default=False, action=BOA, help="Find best hyperparameters.") + parser.add_argument("--hp-checkpoint", type=Path, help="Use previous hyperparameter checkpoint.") + parser.add_argument("--eval", default=False, action=BOA, help="Only evaluate model, skip training.") parser.add_argument( "--complete-train", default=False, @@ -132,12 +112,8 @@ def build_parser() -> ArgumentParser: type=int, help="Finetune model with amount of train data.", ) - parser.add_argument( - "-sn", "--source-name", type=Path, help="Name of the source dataset." - ) - parser.add_argument( - "--source-dir", type=Path, help="Directory containing gin and model weights." - ) + parser.add_argument("-sn", "--source-name", type=Path, help="Name of the source dataset.") + parser.add_argument("--source-dir", type=Path, help="Directory containing gin and model weights.") parser.add_argument( "-sa", "--samples", @@ -185,21 +161,15 @@ def create_run_dir(log_dir: Path, randomly_searched_params: str = None) -> Path: def import_preprocessor(preprocessor_path: str): # Import custom supplied preprocessor - log_full_line( - f"Importing custom preprocessor from {preprocessor_path}.", logging.INFO - ) + log_full_line(f"Importing custom preprocessor from {preprocessor_path}.", logging.INFO) try: - spec = importlib.util.spec_from_file_location( - "CustomPreprocessor", preprocessor_path - ) + spec = importlib.util.spec_from_file_location("CustomPreprocessor", preprocessor_path) module = importlib.util.module_from_spec(spec) sys.modules["preprocessor"] = module spec.loader.exec_module(module) gin.bind_parameter("preprocess.preprocessor", module.CustomPreprocessor) except Exception as e: - logging.error( - f"Could not import custom preprocessor from {preprocessor_path}: {e}" - ) + logging.error(f"Could not import custom preprocessor from {preprocessor_path}: {e}") def aggregate_results(log_dir: Path, execution_time: timedelta = None): @@ -243,14 +213,10 @@ def aggregate_results(log_dir: Path, execution_time: timedelta = None): # Calculate the population standard deviation over aggregated results over folds/iterations # Divide by sqrt(n) to get standard deviation. - std_scores = { - metric: (pstdev(list) / sqrt(len(list))) for metric, list in list_scores.items() - } + std_scores = {metric: (pstdev(list) / sqrt(len(list))) for metric, list in list_scores.items()} confidence_interval = { - metric: ( - stats.t.interval(0.95, len(list) - 1, loc=mean(list), scale=stats.sem(list)) - ) + metric: (stats.t.interval(0.95, len(list) - 1, loc=mean(list), scale=stats.sem(list))) for metric, list in list_scores.items() } @@ -258,9 +224,7 @@ def aggregate_results(log_dir: Path, execution_time: timedelta = None): "avg": averaged_scores, "std": std_scores, "CI_0.95": confidence_interval, - "execution_time": execution_time.total_seconds() - if execution_time is not None - else 0.0, + "execution_time": execution_time.total_seconds() if execution_time is not None else 0.0, } with open(log_dir / "aggregated_test_metrics.json", "w") as f: @@ -276,14 +240,10 @@ def aggregate_results(log_dir: Path, execution_time: timedelta = None): def name_datasets(train="default", val="default", test="default"): """Names the datasets for logging (optional).""" - gin.bind_parameter( - "train_common.dataset_names", {"train": train, "val": val, "test": test} - ) + gin.bind_parameter("train_common.dataset_names", {"train": train, "val": val, "test": test}) -def log_full_line( - msg: str, level: int = logging.INFO, char: str = "-", num_newlines: int = 0 -): +def log_full_line(msg: str, level: int = logging.INFO, char: str = "-", num_newlines: int = 0): """Logs a full line of a given character with a message centered. Args: @@ -311,43 +271,24 @@ def load_pretrained_imputation_model(use_pretrained_imputation): Args: use_pretrained_imputation: Path to the pretrained imputation model. """ - if ( - use_pretrained_imputation is not None - and not Path(use_pretrained_imputation).exists() - ): + if use_pretrained_imputation is not None and not Path(use_pretrained_imputation).exists(): logging.warning("The specified pretrained imputation model does not exist.") use_pretrained_imputation = None if use_pretrained_imputation is not None: - logging.info( - "Using pretrained imputation from" + str(use_pretrained_imputation) - ) - pretrained_imputation_model_checkpoint = torch.load( - use_pretrained_imputation, map_location=torch.device("cpu") - ) + logging.info("Using pretrained imputation from" + str(use_pretrained_imputation)) + pretrained_imputation_model_checkpoint = torch.load(use_pretrained_imputation, map_location=torch.device("cpu")) if isinstance(pretrained_imputation_model_checkpoint, dict): imputation_model_class = pretrained_imputation_model_checkpoint["class"] - pretrained_imputation_model = imputation_model_class( - **pretrained_imputation_model_checkpoint["hyper_parameters"] - ) - pretrained_imputation_model.set_trained_columns( - pretrained_imputation_model_checkpoint["trained_columns"] - ) - pretrained_imputation_model.load_state_dict( - pretrained_imputation_model_checkpoint["state_dict"] - ) + pretrained_imputation_model = imputation_model_class(**pretrained_imputation_model_checkpoint["hyper_parameters"]) + pretrained_imputation_model.set_trained_columns(pretrained_imputation_model_checkpoint["trained_columns"]) + pretrained_imputation_model.load_state_dict(pretrained_imputation_model_checkpoint["state_dict"]) else: pretrained_imputation_model = pretrained_imputation_model_checkpoint - pretrained_imputation_model = pretrained_imputation_model.to( - "cuda" if torch.cuda.is_available() else "cpu" - ) + pretrained_imputation_model = pretrained_imputation_model.to("cuda" if torch.cuda.is_available() else "cpu") try: - logging.info( - f"imputation model device: {next(pretrained_imputation_model.parameters()).device}" - ) - pretrained_imputation_model.device = next( - pretrained_imputation_model.parameters() - ).device + logging.info(f"imputation model device: {next(pretrained_imputation_model.parameters()).device}") + pretrained_imputation_model.device = next(pretrained_imputation_model.parameters()).device except Exception as e: logging.debug(f"Could not set device of imputation model: {e}") else: @@ -368,9 +309,7 @@ def setup_logging(date_format, log_format, verbose): logging.basicConfig(format=log_format, datefmt=date_format) loggers = ["pytorch_lightning", "lightning_fabric"] for logger in loggers: - logging.getLogger(logger).handlers[0].setFormatter( - logging.Formatter(log_format, datefmt=date_format) - ) + logging.getLogger(logger).handlers[0].setFormatter(logging.Formatter(log_format, datefmt=date_format)) if not verbose: logging.getLogger().setLevel(logging.INFO) From 25fedafff7c49d699250df64fd8b18579bc7568f Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Mon, 2 Oct 2023 10:07:20 +0300 Subject: [PATCH 067/142] added predict_mode --- icu_benchmarks/data/loader.py | 41 ++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 0e46cc51..82807635 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -103,11 +103,15 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: # slice to make sure to always return a DF window = self.features_df.loc[stay_id:stay_id].to_numpy() - labels = self.outcome_df.loc[stay_id:stay_id][self.vars["LABEL"]].to_numpy(dtype=float) + labels = self.outcome_df.loc[stay_id:stay_id][self.vars["LABEL"]].to_numpy( + dtype=float + ) if len(labels) == 1: # only one label per stay, align with window - labels = np.concatenate([np.empty(window.shape[0] - 1) * np.nan, labels], axis=0) + labels = np.concatenate( + [np.empty(window.shape[0] - 1) * np.nan, labels], axis=0 + ) length_diff = self.maxlen - window.shape[0] pad_mask = np.ones(window.shape[0]) @@ -115,7 +119,9 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: # Padding the array to fulfill size requirement if length_diff > 0: # window shorter than the longest window in dataset, pad to same length - window = np.concatenate([window, np.ones((length_diff, window.shape[1])) * pad_value], axis=0) + window = np.concatenate( + [window, np.ones((length_diff, window.shape[1])) * pad_value], axis=0 + ) labels = np.concatenate([labels, np.ones(length_diff) * pad_value], axis=0) pad_mask = np.concatenate([pad_mask, np.zeros(length_diff)], axis=0) @@ -325,8 +331,12 @@ def __init__( self.amputated_values, self.amputation_mask = ampute_data( self.features_df, mask_method, mask_proportion, mask_observation_proportion ) - self.amputation_mask = (self.amputation_mask + self.features_df.isna().values).bool() - self.amputation_mask = DataFrame(self.amputation_mask, columns=self.vars[Segment.dynamic]) + self.amputation_mask = ( + self.amputation_mask + self.features_df.isna().values + ).bool() + self.amputation_mask = DataFrame( + self.amputation_mask, columns=self.vars[Segment.dynamic] + ) self.amputation_mask[self.vars["GROUP"]] = self.features_df.index self.amputation_mask.set_index(self.vars["GROUP"], inplace=True) @@ -351,9 +361,15 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: # slice to make sure to always return a DF window = self.features_df.loc[stay_id:stay_id, self.vars[Segment.dynamic]] - window_missingness_mask = self.target_missingness_mask.loc[stay_id:stay_id, self.vars[Segment.dynamic]] - amputated_window = self.amputated_values.loc[stay_id:stay_id, self.vars[Segment.dynamic]] - amputation_mask = self.amputation_mask.loc[stay_id:stay_id, self.vars[Segment.dynamic]] + window_missingness_mask = self.target_missingness_mask.loc[ + stay_id:stay_id, self.vars[Segment.dynamic] + ] + amputated_window = self.amputated_values.loc[ + stay_id:stay_id, self.vars[Segment.dynamic] + ] + amputation_mask = self.amputation_mask.loc[ + stay_id:stay_id, self.vars[Segment.dynamic] + ] return ( from_numpy(amputated_window.values).to(float32), @@ -452,14 +468,18 @@ def __init__( max_encoder_length: int = 24, **kwargs, ): - data[split]["FEATURES"]["time_idx"] = ((data[split]["FEATURES"]["time"] / pd.Timedelta(seconds=3600))).astype( + data[split]["FEATURES"]["time_idx"] = ( + (data[split]["FEATURES"]["time"] / pd.Timedelta(seconds=3600)) + ).astype( int ) # create an incremental column indicating the time step(required by constructor) data = data.get(split) # get split labels = data["OUTCOME"] features = data["FEATURES"] self.name = name - self.data = pd.merge(labels, features, on=["stay_id", "time"]) # combine labels and features + self.data = pd.merge( + labels, features, on=["stay_id", "time"] + ) # combine labels and features # self.data["sex"].replace([0, 1], ["Female", "Male"], inplace=True) self.split = split self.args = args @@ -582,6 +602,7 @@ def __init__( add_relative_time_idx=True, add_target_scales=True, add_encoder_length=True, + predict_mode=True, ) def get_balance(self) -> list: From e120465dd3bde19bd22522e0e0f06306b895bac5 Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Mon, 2 Oct 2023 10:43:00 +0300 Subject: [PATCH 068/142] added pytorch flag --- icu_benchmarks/run.py | 44 +++++++++++++++++++----- icu_benchmarks/tuning/hyperparameters.py | 31 +++++++++++++---- 2 files changed, 60 insertions(+), 15 deletions(-) diff --git a/icu_benchmarks/run.py b/icu_benchmarks/run.py index 40496145..7302aec8 100644 --- a/icu_benchmarks/run.py +++ b/icu_benchmarks/run.py @@ -66,11 +66,19 @@ def main(my_args=tuple(sys.argv[1:])): logging.info(f"Task mode: {mode}.") # Set train size to fine tune size if fine tune is set, else use custom train size - train_size = args.fine_tune if args.fine_tune is not None else args.samples if args.samples is not None else None + train_size = ( + args.fine_tune + if args.fine_tune is not None + else args.samples + if args.samples is not None + else None + ) # Whether to load weights from a previous run load_weights = evaluate or args.fine_tune is not None - pretrained_imputation_model = load_pretrained_imputation_model(args.pretrained_imputation) + pretrained_imputation_model = load_pretrained_imputation_model( + args.pretrained_imputation + ) # Log imputation model to wandb update_wandb_config( { @@ -84,7 +92,11 @@ def main(my_args=tuple(sys.argv[1:])): log_dir = ( (log_dir_name / experiment) if experiment - else (log_dir_name / (args.task_name if args.task_name is not None else args.task) / model) + else ( + log_dir_name + / (args.task_name if args.task_name is not None else args.task) + / model + ) ) log_full_line(f"Logging to {log_dir}.", logging.INFO) @@ -107,7 +119,9 @@ def main(my_args=tuple(sys.argv[1:])): # Load pretrained model in evaluate mode or when finetuning if load_weights: if args.source_dir is None: - raise ValueError("Please specify a source directory when evaluating or fine-tuning.") + raise ValueError( + "Please specify a source directory when evaluating or fine-tuning." + ) log_dir /= f"_from_{args.source_name}" name_datasets(args.source_name, args.source_name, args.name) if args.fine_tune: @@ -115,9 +129,13 @@ def main(my_args=tuple(sys.argv[1:])): name_datasets(args.name, args.name, args.name) run_dir = create_run_dir(log_dir) source_dir = args.source_dir - logging.info(f"Will load weights from {source_dir} and bind train gin-config. Note: this might override your config.") + logging.info( + f"Will load weights from {source_dir} and bind train gin-config. Note: this might override your config." + ) gin.parse_config_file(source_dir / "train_config.gin") - elif args.samples and args.source_dir is not None: # Train model with limited samples and bind existing config + elif ( + args.samples and args.source_dir is not None + ): # Train model with limited samples and bind existing config logging.info("Binding train gin-config. Note: this might override your config.") gin.parse_config_file(args.source_dir / "train_config.gin") log_dir /= f"samples_{args.fine_tune}" @@ -128,14 +146,22 @@ def main(my_args=tuple(sys.argv[1:])): name_datasets(args.name, args.name, args.name) hp_checkpoint = log_dir / args.hp_checkpoint if args.hp_checkpoint else None model_path = ( - Path("configs") / ("imputation_models" if mode == RunMode.imputation else "prediction_models") / f"{model}.gin" + Path("configs") + / ( + "imputation_models" + if mode == RunMode.imputation + else "prediction_models" + ) + / f"{model}.gin" ) gin_config_files = ( [Path(f"configs/experiments/{args.experiment}.gin")] if args.experiment else [model_path, Path(f"configs/tasks/{task}.gin")] ) - gin.parse_config_files_and_bindings(gin_config_files, args.hyperparams, finalize_config=False) + gin.parse_config_files_and_bindings( + gin_config_files, args.hyperparams, finalize_config=False + ) log_full_line(f"Data directory: {data_dir.resolve()}", level=logging.INFO) run_dir = create_run_dir(log_dir) choose_and_bind_hyperparameters( @@ -149,6 +175,7 @@ def main(my_args=tuple(sys.argv[1:])): generate_cache=args.generate_cache, load_cache=args.load_cache, verbose=verbose, + pytorch_forecasting=pytorch_forecasting, ) log_full_line(f"Logging to {run_dir.resolve()}", level=logging.INFO) @@ -161,6 +188,7 @@ def main(my_args=tuple(sys.argv[1:])): log_full_line(mode_string, level=logging.INFO, char="=", num_newlines=3) start_time = datetime.now() + execute_repeated_cv( data_dir, run_dir, diff --git a/icu_benchmarks/tuning/hyperparameters.py b/icu_benchmarks/tuning/hyperparameters.py index 212a7753..cf3d0c17 100644 --- a/icu_benchmarks/tuning/hyperparameters.py +++ b/icu_benchmarks/tuning/hyperparameters.py @@ -36,6 +36,7 @@ def choose_and_bind_hyperparameters( debug: bool = False, verbose: bool = False, wandb: bool = False, + pytorch_forecasting: bool = False, ): """Choose hyperparameters to tune and bind them to gin. @@ -67,7 +68,9 @@ def choose_and_bind_hyperparameters( return # Collect hyperparameters. - hyperparams_bounds, hyperparams_names = collect_bound_hyperparameters(hyperparams, scopes) + hyperparams_bounds, hyperparams_names = collect_bound_hyperparameters( + hyperparams, scopes + ) if do_tune and not hyperparams_bounds: logging.info("No hyperparameters to tune, skipping tuning.") @@ -78,17 +81,26 @@ def choose_and_bind_hyperparameters( if checkpoint: checkpoint_path = checkpoint / checkpoint_file if not checkpoint_path.exists(): - logging.warning(f"Hyperparameter checkpoint {checkpoint_path} does not exist.") + logging.warning( + f"Hyperparameter checkpoint {checkpoint_path} does not exist." + ) logging.info("Attempting to find latest checkpoint file.") checkpoint_path = find_checkpoint(log_dir.parent, checkpoint_file) # Check if we found a checkpoint file if checkpoint_path: - n_calls, configuration, evaluation = load_checkpoint(checkpoint_path, n_calls) + n_calls, configuration, evaluation = load_checkpoint( + checkpoint_path, n_calls + ) # Check if we surpassed maximum tuning iterations if n_calls <= 0: - logging.log(TUNE, "No more hyperparameter tuning iterations left, skipping tuning.") + logging.log( + TUNE, + "No more hyperparameter tuning iterations left, skipping tuning.", + ) logging.info("Training with these hyperparameters:") - bind_gin_params(hyperparams_names, configuration[np.argmin(evaluation)]) # bind best hyperparameters + bind_gin_params( + hyperparams_names, configuration[np.argmin(evaluation)] + ) # bind best hyperparameters return else: logging.warning("No checkpoint file found, starting from scratch.") @@ -112,6 +124,7 @@ def bind_params_and_train(hyperparams): debug=debug, verbose=verbose, wandb=wandb, + pytorch_forecasting=pytorch_forecasting, ) header = ["ITERATION"] + hyperparams_names + ["LOSS AT ITERATION"] @@ -126,7 +139,9 @@ def tune_step_callback(res): table_cells = [len(res.x_iters)] + res.x_iters[-1] + [res.func_vals[-1]] highlight = res.x_iters[-1] == res.x # highlight if best so far log_table_row(header, TUNE) - log_table_row(table_cells, TUNE, align=Align.RIGHT, header=header, highlight=highlight) + log_table_row( + table_cells, TUNE, align=Align.RIGHT, header=header, highlight=highlight + ) wandb_log({"hp-iteration": len(res.x_iters)}) if do_tune: @@ -141,7 +156,9 @@ def tune_step_callback(res): logging.log(TUNE, "Hyperparameter tuning disabled") if configuration: # We have loaded a checkpoint, use the best hyperparameters. - logging.info("Training with the best hyperparameters from loaded checkpoint:") + logging.info( + "Training with the best hyperparameters from loaded checkpoint:" + ) bind_gin_params(hyperparams_names, configuration[np.argmin(evaluation)]) else: logging.log(TUNE, "Choosing hyperparameters randomly from bounds.") From b4d6b426a6e00096ad2d6bdafd6e7d9a0d6be116 Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Mon, 2 Oct 2023 10:43:46 +0300 Subject: [PATCH 069/142] added LSTM from pytorch --- icu_benchmarks/models/dl_models.py | 95 +++++++++++++++++++++++++++--- 1 file changed, 87 insertions(+), 8 deletions(-) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 7070ffac..ab77b3ea 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -13,7 +13,7 @@ import matplotlib.pyplot as plt from icu_benchmarks.models.wrappers import DLPredictionWrapper from torch import Tensor, FloatTensor -from pytorch_forecasting import TemporalFusionTransformer # , RecurrentNetwork +from pytorch_forecasting import TemporalFusionTransformer, RecurrentNetwork from pytorch_forecasting.metrics import QuantileLoss import matplotlib.pyplot as plt @@ -149,7 +149,9 @@ def __init__( **kwargs, ) hidden = hidden if hidden % 2 == 0 else hidden + 1 # Make sure hidden is even - self.input_embedding = nn.Linear(input_size[2], hidden) # This acts as a time-distributed layer by defaults + self.input_embedding = nn.Linear( + input_size[2], hidden + ) # This acts as a time-distributed layer by defaults if pos_encoding: self.pos_encoder = PositionalEncoding(hidden) else: @@ -220,7 +222,9 @@ def __init__( ) hidden = hidden if hidden % 2 == 0 else hidden + 1 # Make sure hidden is even - self.input_embedding = nn.Linear(input_size[2], hidden) # This acts as a time-distributed layer by defaults + self.input_embedding = nn.Linear( + input_size[2], hidden + ) # This acts as a time-distributed layer by defaults if pos_encoding: self.pos_encoder = PositionalEncoding(hidden) else: @@ -286,9 +290,13 @@ def __init__( # We compute automatically the depth based on the desired seq_length. if isinstance(num_channels, Integral) and max_seq_length: - num_channels = [num_channels] * int(np.ceil(np.log(max_seq_length / 2) / np.log(kernel_size))) + num_channels = [num_channels] * int( + np.ceil(np.log(max_seq_length / 2) / np.log(kernel_size)) + ) elif isinstance(num_channels, Integral) and not max_seq_length: - raise Exception("a maximum sequence length needs to be provided if num_channels is int") + raise Exception( + "a maximum sequence length needs to be provided if num_channels is int" + ) num_levels = len(num_channels) for i in range(num_levels): @@ -529,13 +537,17 @@ def forward(self, x): def actual_vs_predictions_plot(self, dataloader): predictions = self.model.predict(dataloader, return_x=True) - predictions_vs_actuals = self.model.calculate_prediction_actual_by_variable(predictions.x, predictions.output) + predictions_vs_actuals = self.model.calculate_prediction_actual_by_variable( + predictions.x, predictions.output + ) self.model.plot_prediction_actual_by_variable(predictions_vs_actuals) return predictions_vs_actuals def interpertations(self, dataloader, log_dir): raw_predictions = self.model.predict(dataloader, return_x=True, mode="raw") - interpretation = self.model.interpret_output(raw_predictions.output, reduction="sum") + interpretation = self.model.interpret_output( + raw_predictions.output, reduction="sum" + ) figs = self.model.plot_interpretation(interpretation) for key, fig in figs.items(): fig.savefig(log_dir / f"interpretation_{key}.png", bbox_inches="tight") @@ -557,6 +569,73 @@ def predict_dependency(self, dataloader, variable, log_dir): q75=lambda x: x.quantile(0.75), ) ax = agg_dependency.plot(y="median") - ax.fill_between(agg_dependency.index, agg_dependency.q25, agg_dependency.q75, alpha=0.3) + ax.fill_between( + agg_dependency.index, agg_dependency.q25, agg_dependency.q75, alpha=0.3 + ) plt.savefig(log_dir / "dependecy.png", bbox_inches="tight") return dependency + + +@gin.configurable +class RNNpytorch(DLPredictionWrapper): + """ + Implementation of RNN from pytorch forecasting + """ + + supported_run_modes = [RunMode.classification, RunMode.regression] + + def __init__( + self, + dataset, + hidden, + dropout, + optimizer, + num_classes, + cell_type, + rnn_layers, + *args, + **kwargs, + ): + super().__init__(optimizer=optimizer, pytorch_forecasting=True, *args, **kwargs) + + self.model = RecurrentNetwork.from_dataset( + cell_type=cell_type, + rnn_layers=rnn_layers, + dataset=dataset, + hidden_size=hidden, + dropout=dropout, + optimizer=optimizer, + ) + + self.logit = nn.Linear(7, num_classes) + + def set_weight(self, weight, dataset): + """ + Set the weight for the loss function + """ + if isinstance(weight, list): + weight = FloatTensor(weight) + elif weight == "balanced": + weight = FloatTensor(dataset.get_balance()) + self.loss_weights = weight + + def forward( + self, + tuple_x: tuple, + ) -> Dict[str, Tensor]: + x_dict = { + "encoder_cat": tuple_x[0], + "encoder_cont": tuple_x[1], + "encoder_target": tuple_x[2], + "encoder_lengths": tuple_x[3], + "decoder_cat": tuple_x[4], + "decoder_cont": tuple_x[5], + "decoder_target": tuple_x[6], + "decoder_lengths": tuple_x[7], + "decoder_time_idx": tuple_x[8], + "groups": tuple_x[9], + "target_scale": tuple_x[10], + } + out = self.model(x_dict) + pred = self.logit(out["prediction"]) + return pred From ec8346d15c8b478c15fc36c1148b96ee75dee30d Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Mon, 2 Oct 2023 13:27:10 +0300 Subject: [PATCH 070/142] added RNN implementation from pytorch forecasting and changed dataloader to fit both --- configs/prediction_models/RNNpytorch.gin | 222 +++++++++++++++++++++++ configs/prediction_models/TFTpytorch.gin | 106 ++++++++++- icu_benchmarks/cross_validation.py | 8 +- icu_benchmarks/data/loader.py | 173 +++++++----------- icu_benchmarks/models/dl_models.py | 5 +- icu_benchmarks/models/train.py | 51 ++++-- icu_benchmarks/models/wrappers.py | 79 ++++++-- 7 files changed, 500 insertions(+), 144 deletions(-) create mode 100644 configs/prediction_models/RNNpytorch.gin diff --git a/configs/prediction_models/RNNpytorch.gin b/configs/prediction_models/RNNpytorch.gin new file mode 100644 index 00000000..0e47111f --- /dev/null +++ b/configs/prediction_models/RNNpytorch.gin @@ -0,0 +1,222 @@ +# Settings for TFT model. + +# Common settings for DL models +include "configs/prediction_models/common/DLCommon.gin" + +# Optimizer params +train_common.model = @RNNpytorch + +optimizer/hyperparameter.class_to_tune = @Adam +optimizer/hyperparameter.weight_decay = 1e-6 + + +# Model params +model/hyperparameter.class_to_tune = @RNNpytorch +model/hyperparameter.hidden = 10 +model/hyperparameter.rnn_layers=2 +model/hyperparameter.num_classes = %NUM_CLASSES +model/hyperparameter.cell_type='LSTM' +model/hyperparameter.dropout = (0.0, 0.4) +model/hyperparameter.lr_scheduler = "exponential" +# Dataset params +PredictionDatasetTFTpytorch.max_encoder_length = 24 +PredictionDatasetTFTpytorch.max_prediction_length = 24 +PredictionDatasetTFTpytorch.time_varying_known_reals=[] +PredictionDatasetTFTpytorch.add_relative_time_idx=False +PredictionDatasetTFTpytorch.target=[ + "alb", + "alp", + "alt", + "ast", + "be", + "bicar", + "bili", + "bili_dir", + "bnd", + "bun", + "ca", + "cai", + "ck", + "ckmb", + "cl", + "crea", + "crp", + "dbp", + "fgn", + "fio2", + "glu", + "hgb", + "hr", + "inr_pt", + "k", + "lact", + "lymph", + "map", + "mch", + "mchc", + "mcv", + "methb", + "mg", + "na", + "neut", + "o2sat", + "pco2", + "ph", + "phos", + "plt", + "po2", + "ptt", + "resp", + "sbp", + "temp", + "tnt", + "urine", + "wbc", + "MissingIndicator_1", + "MissingIndicator_2", + "MissingIndicator_3", + "MissingIndicator_4", + "MissingIndicator_5", + "MissingIndicator_6", + "MissingIndicator_7", + "MissingIndicator_8", + "MissingIndicator_9", + "MissingIndicator_10", + "MissingIndicator_11", + "MissingIndicator_12", + "MissingIndicator_13", + "MissingIndicator_14", + "MissingIndicator_15", + "MissingIndicator_16", + "MissingIndicator_17", + "MissingIndicator_18", + "MissingIndicator_19", + "MissingIndicator_20", + "MissingIndicator_21", + "MissingIndicator_22", + "MissingIndicator_23", + "MissingIndicator_24", + "MissingIndicator_25", + "MissingIndicator_26", + "MissingIndicator_27", + "MissingIndicator_28", + "MissingIndicator_29", + "MissingIndicator_30", + "MissingIndicator_31", + "MissingIndicator_32", + "MissingIndicator_33", + "MissingIndicator_34", + "MissingIndicator_35", + "MissingIndicator_36", + "MissingIndicator_37", + "MissingIndicator_38", + "MissingIndicator_39", + "MissingIndicator_40", + "MissingIndicator_41", + "MissingIndicator_42", + "MissingIndicator_43", + "MissingIndicator_44", + "MissingIndicator_45", + "MissingIndicator_46", + "MissingIndicator_47", + "MissingIndicator_48", + "label", + ] +PredictionDatasetTFTpytorch.time_varying_unknown_reals=["alb", + "alp", + "alt", + "ast", + "be", + "bicar", + "bili", + "bili_dir", + "bnd", + "bun", + "ca", + "cai", + "ck", + "ckmb", + "cl", + "crea", + "crp", + "dbp", + "fgn", + "fio2", + "glu", + "hgb", + "hr", + "inr_pt", + "k", + "lact", + "lymph", + "map", + "mch", + "mchc", + "mcv", + "methb", + "mg", + "na", + "neut", + "o2sat", + "pco2", + "ph", + "phos", + "plt", + "po2", + "ptt", + "resp", + "sbp", + "temp", + "tnt", + "urine", + "wbc", + "MissingIndicator_1", + "MissingIndicator_2", + "MissingIndicator_3", + "MissingIndicator_4", + "MissingIndicator_5", + "MissingIndicator_6", + "MissingIndicator_7", + "MissingIndicator_8", + "MissingIndicator_9", + "MissingIndicator_10", + "MissingIndicator_11", + "MissingIndicator_12", + "MissingIndicator_13", + "MissingIndicator_14", + "MissingIndicator_15", + "MissingIndicator_16", + "MissingIndicator_17", + "MissingIndicator_18", + "MissingIndicator_19", + "MissingIndicator_20", + "MissingIndicator_21", + "MissingIndicator_22", + "MissingIndicator_23", + "MissingIndicator_24", + "MissingIndicator_25", + "MissingIndicator_26", + "MissingIndicator_27", + "MissingIndicator_28", + "MissingIndicator_29", + "MissingIndicator_30", + "MissingIndicator_31", + "MissingIndicator_32", + "MissingIndicator_33", + "MissingIndicator_34", + "MissingIndicator_35", + "MissingIndicator_36", + "MissingIndicator_37", + "MissingIndicator_38", + "MissingIndicator_39", + "MissingIndicator_40", + "MissingIndicator_41", + "MissingIndicator_42", + "MissingIndicator_43", + "MissingIndicator_44", + "MissingIndicator_45", + "MissingIndicator_46", + "MissingIndicator_47", + "MissingIndicator_48", + "label", + ] diff --git a/configs/prediction_models/TFTpytorch.gin b/configs/prediction_models/TFTpytorch.gin index 13c7520a..63bf001d 100644 --- a/configs/prediction_models/TFTpytorch.gin +++ b/configs/prediction_models/TFTpytorch.gin @@ -10,7 +10,7 @@ optimizer/hyperparameter.class_to_tune = @Adam optimizer/hyperparameter.weight_decay = 1e-6 -# Encoder params +# Model params model/hyperparameter.class_to_tune = @TFTpytorch model/hyperparameter.hidden = (16,32,64,128) model/hyperparameter.num_classes = %NUM_CLASSES @@ -18,7 +18,107 @@ model/hyperparameter.dropout = (0.0, 0.4) model/hyperparameter.dropout_att = (0.0, 0.4) model/hyperparameter.n_heads =(1,2,4) model/hyperparameter.lr_scheduler = "exponential" +# Dataset params PredictionDatasetTFTpytorch.max_encoder_length = 24 PredictionDatasetTFTpytorch.max_prediction_length = 24 - - +PredictionDatasetTFTpytorch.target="label" +PredictionDatasetTFTpytorch.time_varying_known_reals=["time_idx"] +PredictionDatasetTFTpytorch.add_relative_time_idx=True +PredictionDatasetTFTpytorch.time_varying_unknown_reals=["alb", + "alp", + "alt", + "ast", + "be", + "bicar", + "bili", + "bili_dir", + "bnd", + "bun", + "ca", + "cai", + "ck", + "ckmb", + "cl", + "crea", + "crp", + "dbp", + "fgn", + "fio2", + "glu", + "hgb", + "hr", + "inr_pt", + "k", + "lact", + "lymph", + "map", + "mch", + "mchc", + "mcv", + "methb", + "mg", + "na", + "neut", + "o2sat", + "pco2", + "ph", + "phos", + "plt", + "po2", + "ptt", + "resp", + "sbp", + "temp", + "tnt", + "urine", + "wbc", + "MissingIndicator_1", + "MissingIndicator_2", + "MissingIndicator_3", + "MissingIndicator_4", + "MissingIndicator_5", + "MissingIndicator_6", + "MissingIndicator_7", + "MissingIndicator_8", + "MissingIndicator_9", + "MissingIndicator_10", + "MissingIndicator_11", + "MissingIndicator_12", + "MissingIndicator_13", + "MissingIndicator_14", + "MissingIndicator_15", + "MissingIndicator_16", + "MissingIndicator_17", + "MissingIndicator_18", + "MissingIndicator_19", + "MissingIndicator_20", + "MissingIndicator_21", + "MissingIndicator_22", + "MissingIndicator_23", + "MissingIndicator_24", + "MissingIndicator_25", + "MissingIndicator_26", + "MissingIndicator_27", + "MissingIndicator_28", + "MissingIndicator_29", + "MissingIndicator_30", + "MissingIndicator_31", + "MissingIndicator_32", + "MissingIndicator_33", + "MissingIndicator_34", + "MissingIndicator_35", + "MissingIndicator_36", + "MissingIndicator_37", + "MissingIndicator_38", + "MissingIndicator_39", + "MissingIndicator_40", + "MissingIndicator_41", + "MissingIndicator_42", + "MissingIndicator_43", + "MissingIndicator_44", + "MissingIndicator_45", + "MissingIndicator_46", + "MissingIndicator_47", + "MissingIndicator_48", + "label", + ] \ No newline at end of file diff --git a/icu_benchmarks/cross_validation.py b/icu_benchmarks/cross_validation.py index 412df3eb..bcf2f5f5 100644 --- a/icu_benchmarks/cross_validation.py +++ b/icu_benchmarks/cross_validation.py @@ -82,11 +82,15 @@ def execute_repeated_cv( cv_folds_to_train = 1 else: - logging.info(f"Starting nested CV with {cv_repetitions_to_train} repetitions of {cv_folds_to_train} folds.") + logging.info( + f"Starting nested CV with {cv_repetitions_to_train} repetitions of {cv_folds_to_train} folds." + ) for repetition in range(cv_repetitions_to_train): for fold_index in range(cv_folds_to_train): - repetition_fold_dir = log_dir / f"repetition_{repetition}" / f"fold_{fold_index}" + repetition_fold_dir = ( + log_dir / f"repetition_{repetition}" / f"fold_{fold_index}" + ) repetition_fold_dir.mkdir(parents=True, exist_ok=True) start_time = datetime.now() diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 82807635..a0c6f39b 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -6,7 +6,7 @@ from torch import Tensor, cat, from_numpy, float32 from torch.utils.data import Dataset import logging -from typing import Dict, Tuple +from typing import Dict, Tuple, Union from icu_benchmarks.imputation.amputations import ampute_data from .constants import DataSegment as Segment from .constants import DataSplit as Split @@ -461,8 +461,12 @@ def __init__( self, data: dict, split: str, + time_varying_unknown_reals: List[str], + target: Union[str, List[str]], + time_varying_known_reals: List[str], *args, ram_cache: bool = False, + add_relative_time_idx: bool = False, name: str = "", max_prediction_length: int = 24, max_encoder_length: int = 24, @@ -481,125 +485,82 @@ def __init__( labels, features, on=["stay_id", "time"] ) # combine labels and features # self.data["sex"].replace([0, 1], ["Female", "Male"], inplace=True) + # List of column names to convert from boolean to float + boolean_columns = [ + "MissingIndicator_1", + "MissingIndicator_2", + "MissingIndicator_3", + "MissingIndicator_4", + "MissingIndicator_5", + "MissingIndicator_6", + "MissingIndicator_7", + "MissingIndicator_8", + "MissingIndicator_9", + "MissingIndicator_10", + "MissingIndicator_11", + "MissingIndicator_12", + "MissingIndicator_13", + "MissingIndicator_14", + "MissingIndicator_15", + "MissingIndicator_16", + "MissingIndicator_17", + "MissingIndicator_18", + "MissingIndicator_19", + "MissingIndicator_20", + "MissingIndicator_21", + "MissingIndicator_22", + "MissingIndicator_23", + "MissingIndicator_24", + "MissingIndicator_25", + "MissingIndicator_26", + "MissingIndicator_27", + "MissingIndicator_28", + "MissingIndicator_29", + "MissingIndicator_30", + "MissingIndicator_31", + "MissingIndicator_32", + "MissingIndicator_33", + "MissingIndicator_34", + "MissingIndicator_35", + "MissingIndicator_36", + "MissingIndicator_37", + "MissingIndicator_38", + "MissingIndicator_39", + "MissingIndicator_40", + "MissingIndicator_41", + "MissingIndicator_42", + "MissingIndicator_43", + "MissingIndicator_44", + "MissingIndicator_45", + "MissingIndicator_46", + "MissingIndicator_47", + "MissingIndicator_48", + ] + + # Convert multiple columns from boolean to float + self.data[boolean_columns] = self.data[boolean_columns].astype(float) self.split = split self.args = args self.ram_cache = ram_cache self.kwargs = kwargs self.column_names = features.columns + super().__init__( data=self.data, time_idx="time_idx", - target="label", + target=target, group_ids=["stay_id"], min_encoder_length=max_encoder_length, max_encoder_length=max_encoder_length, min_prediction_length=max_prediction_length, max_prediction_length=max_prediction_length, static_categoricals=[], - static_reals=["height", "weight", "age", "stay_id", "sex"], - # time_varying_known_categoricals=[], - time_varying_known_reals=["time_idx"], - # time_varying_unknown_categoricals=[], - time_varying_unknown_reals=[ - "alb", - "alp", - "alt", - "ast", - "be", - "bicar", - "bili", - "bili_dir", - "bnd", - "bun", - "ca", - "cai", - "ck", - "ckmb", - "cl", - "crea", - "crp", - "dbp", - "fgn", - "fio2", - "glu", - "hgb", - "hr", - "inr_pt", - "k", - "lact", - "lymph", - "map", - "mch", - "mchc", - "mcv", - "methb", - "mg", - "na", - "neut", - "o2sat", - "pco2", - "ph", - "phos", - "plt", - "po2", - "ptt", - "resp", - "sbp", - "temp", - "tnt", - "urine", - "wbc", - "MissingIndicator_1", - "MissingIndicator_2", - "MissingIndicator_3", - "MissingIndicator_4", - "MissingIndicator_5", - "MissingIndicator_6", - "MissingIndicator_7", - "MissingIndicator_8", - "MissingIndicator_9", - "MissingIndicator_10", - "MissingIndicator_11", - "MissingIndicator_12", - "MissingIndicator_13", - "MissingIndicator_14", - "MissingIndicator_15", - "MissingIndicator_16", - "MissingIndicator_17", - "MissingIndicator_18", - "MissingIndicator_19", - "MissingIndicator_20", - "MissingIndicator_21", - "MissingIndicator_22", - "MissingIndicator_23", - "MissingIndicator_24", - "MissingIndicator_25", - "MissingIndicator_26", - "MissingIndicator_27", - "MissingIndicator_28", - "MissingIndicator_29", - "MissingIndicator_30", - "MissingIndicator_31", - "MissingIndicator_32", - "MissingIndicator_33", - "MissingIndicator_34", - "MissingIndicator_35", - "MissingIndicator_36", - "MissingIndicator_37", - "MissingIndicator_38", - "MissingIndicator_39", - "MissingIndicator_40", - "MissingIndicator_41", - "MissingIndicator_42", - "MissingIndicator_43", - "MissingIndicator_44", - "MissingIndicator_45", - "MissingIndicator_46", - "MissingIndicator_47", - "MissingIndicator_48", - "label", - ], - add_relative_time_idx=True, + static_reals=["height", "weight", "age", "sex"], + time_varying_known_categoricals=[], + time_varying_known_reals=time_varying_known_reals, + time_varying_unknown_categoricals=[], + time_varying_unknown_reals=time_varying_unknown_reals, + add_relative_time_idx=add_relative_time_idx, add_target_scales=True, add_encoder_length=True, predict_mode=True, diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index ab77b3ea..99ce7cb9 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -637,5 +637,8 @@ def forward( "target_scale": tuple_x[10], } out = self.model(x_dict) - pred = self.logit(out["prediction"]) + print(out) + print(out["prediction"][-1].shape) + + pred = self.logit(out["prediction"][-1]) return pred diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 1cca8970..9f7aadf9 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -27,7 +27,9 @@ from collections import OrderedDict from captum.attr import IntegratedGradients -cpu_core_count = len(os.sched_getaffinity(0)) if hasattr(os, "sched_getaffinity") else os.cpu_count() +cpu_core_count = ( + len(os.sched_getaffinity(0)) if hasattr(os, "sched_getaffinity") else os.cpu_count() +) def assure_minimum_length(dataset): @@ -100,15 +102,25 @@ def train_common( dataset_class = ( ImputationDataset if mode == RunMode.imputation - else (PredictionDatasetTFTpytorch if (pytorch_forecasting) else PredictionDataset) + else ( + PredictionDatasetTFTpytorch if (pytorch_forecasting) else PredictionDataset + ) ) logging.info(f"Logging to directory: {log_dir}.") - save_config_file(log_dir) # We save the operative config before and also after training + save_config_file( + log_dir + ) # We save the operative config before and also after training - train_dataset = dataset_class(data, split=Split.train, ram_cache=ram_cache, name=dataset_names["train"]) - val_dataset = dataset_class(data, split=Split.val, ram_cache=ram_cache, name=dataset_names["val"]) - train_dataset, val_dataset = assure_minimum_length(train_dataset), assure_minimum_length(val_dataset) + train_dataset = dataset_class( + data, split=Split.train, ram_cache=ram_cache, name=dataset_names["train"] + ) + val_dataset = dataset_class( + data, split=Split.val, ram_cache=ram_cache, name=dataset_names["val"] + ) + train_dataset, val_dataset = assure_minimum_length( + train_dataset + ), assure_minimum_length(val_dataset) batch_size = min(batch_size, len(train_dataset), len(val_dataset)) test_dataset = dataset_class(data, split=test_on, name=dataset_names["test"]) test_dataset = assure_minimum_length(test_dataset) @@ -119,7 +131,6 @@ def train_common( ) logging.info(f"Using {num_workers} workers for data loading.") - if pytorch_forecasting: train_loader = train_dataset.to_dataloader( train=True, @@ -144,10 +155,14 @@ def train_common( shuffle=False, ) if load_weights: - model = load_model(model, source_dir, pl_model=pl_model, train_dataset=train_dataset) + model = load_model( + model, source_dir, pl_model=pl_model, train_dataset=train_dataset + ) else: - model = model(train_dataset, optimizer=optimizer, epochs=epochs, run_mode=mode) + model = model( + train_dataset, optimizer=optimizer, epochs=epochs, run_mode=mode + ) else: train_loader = DataLoader( @@ -188,7 +203,9 @@ def train_common( if load_weights: model = load_model(model, source_dir, pl_model=pl_model) else: - model = model(optimizer=optimizer, input_size=data_shape, epochs=epochs, run_mode=mode) + model = model( + optimizer=optimizer, input_size=data_shape, epochs=epochs, run_mode=mode + ) model.set_weight(weight, train_dataset) model.set_trained_columns(train_dataset.get_feature_names()) @@ -215,7 +232,7 @@ def train_common( max_epochs=epochs if model.requires_backprop else 1, callbacks=callbacks, precision=precision, - accelerator="auto" if not cpu else "cpu", + accelerator="cpu", devices=max(torch.cuda.device_count(), 1), deterministic="warn" if reproducible else False, benchmark=not reproducible, @@ -228,7 +245,9 @@ def train_common( if not eval_only: if model.requires_backprop: logging.info("Training DL model.") - trainer.fit(model, train_dataloaders=train_loader, val_dataloaders=val_loader) + trainer.fit( + model, train_dataloaders=train_loader, val_dataloaders=val_loader + ) logging.info("Training complete.") else: logging.info("Training ML model.") @@ -263,7 +282,9 @@ def train_common( print(attr) """ model.set_weight("balanced", train_dataset) - test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0]["test/loss"] + test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0][ + "test/loss" + ] save_config_file(log_dir) return test_loss @@ -281,7 +302,9 @@ def load_model(model, source_dir, pl_model=True, train_dataset=None): return Exception(f"No weights to load at path : {source_dir}") if pl_model: if train_dataset is not None: - model = model.load_from_checkpoint(model_path, dataset=train_dataset) + model = model.load_from_checkpoint( + model_path, dataset=train_dataset + ) else: model = model.load_from_checkpoint(model_path) diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 8bffe10f..074bd06a 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -20,7 +20,9 @@ from icu_benchmarks.contants import RunMode gin.config.external_configurable(nn.functional.nll_loss, module="torch.nn.functional") -gin.config.external_configurable(nn.functional.cross_entropy, module="torch.nn.functional") +gin.config.external_configurable( + nn.functional.cross_entropy, module="torch.nn.functional" +) gin.config.external_configurable(nn.functional.mse_loss, module="torch.nn.functional") gin.config.external_configurable(mean_squared_error, module="sklearn.metrics") @@ -85,7 +87,9 @@ def save_model(self, save_path, file_name, file_extension): def check_supported_runmode(self, runmode: RunMode): if runmode not in self._supported_run_modes: - raise ValueError(f"Runmode {runmode} not supported for {self.__class__.__name__}") + raise ValueError( + f"Runmode {runmode} not supported for {self.__class__.__name__}" + ) return True @@ -158,7 +162,9 @@ def finalize_step(self, step_prefix=""): self.log_dict( { f"{step_prefix}/{name}": ( - np.float32(metric.compute()) if isinstance(metric.compute(), np.float64) else metric.compute() + np.float32(metric.compute()) + if isinstance(metric.compute(), np.float64) + else metric.compute() ) for name, metric in self.metrics[step_prefix].items() if "_Curve" not in name @@ -199,7 +205,10 @@ def configure_optimizers(self): def on_test_epoch_start(self) -> None: self.metrics = { - step_name: {metric_name: metric() for metric_name, metric in self.set_metrics().items()} + step_name: { + metric_name: metric() + for metric_name, metric in self.set_metrics().items() + } for step_name in ["train", "val", "test"] } return super().on_test_epoch_start() @@ -328,7 +337,8 @@ def step_fn(self, element, step_prefix=""): isinstance(element[1], list) ): """ - if self.pytorch_forecasting: # check if the data loader is the one for the pytorch forecasring implementation + if self.pytorch_forecasting: + # check if the data loader is the one for the pytorch forecasring implementation dic, labels = element[0], element[1][0] data = ( dic["encoder_cat"], @@ -343,6 +353,8 @@ def step_fn(self, element, step_prefix=""): dic["groups"], dic["target_scale"], ) + if isinstance(labels, list): + labels = labels[0] mask = torch.ones_like(labels).bool() else: data, labels = element[0], (element[1]).to(self.device) @@ -365,7 +377,9 @@ def step_fn(self, element, step_prefix=""): else: data = data.float().to(self.device) else: - raise Exception("Loader should return either (data, label) or (data, label, mask)") + raise Exception( + "Loader should return either (data, label) or (data, label, mask)" + ) out = self(data) # If aux_loss is present, it is returned as a tuple @@ -374,20 +388,31 @@ def step_fn(self, element, step_prefix=""): else: aux_loss = 0 # Get prediction and target - prediction = torch.masked_select(out, mask.unsqueeze(-1)).reshape(-1, out.shape[-1]).to(self.device) + prediction = ( + torch.masked_select(out, mask.unsqueeze(-1)) + .reshape(-1, out.shape[-1]) + .to(self.device) + ) target = torch.masked_select(labels, mask).to(self.device) if prediction.shape[-1] > 1 and self.run_mode == RunMode.classification: # Classification task - loss = self.loss(prediction, target.long(), weight=self.loss_weights.to(self.device)) + aux_loss + loss = ( + self.loss( + prediction, target.long(), weight=self.loss_weights.to(self.device) + ) + + aux_loss + ) # Returns torch.long because negative log likelihood loss elif self.run_mode == RunMode.regression: # Regression task loss = self.loss(prediction[:, 0], target.float()) + aux_loss else: - raise ValueError(f"Run mode {self.run_mode} not yet supported. Please implement it.") + raise ValueError( + f"Run mode {self.run_mode} not yet supported. Please implement it." + ) transformed_output = self.output_transform((prediction, target)) for key, value in self.metrics[step_prefix].items(): @@ -404,7 +429,9 @@ def step_fn(self, element, step_prefix=""): value.update(transformed_output[0], transformed_output[1]) else: value.update(transformed_output) - self.log(f"{step_prefix}/loss", loss, on_step=False, on_epoch=True, sync_dist=True) + self.log( + f"{step_prefix}/loss", loss, on_step=False, on_epoch=True, sync_dist=True + ) return loss @@ -451,9 +478,15 @@ def set_metrics(self, labels): # Regression else: - if self.scaler is not None: # We invert transform the labels and predictions if they were scaled. - self.output_transform = lambda x: self.scaler.inverse_transform(x.reshape(-1, 1)) - self.label_transform = lambda x: self.scaler.inverse_transform(x.reshape(-1, 1)) + if ( + self.scaler is not None + ): # We invert transform the labels and predictions if they were scaled. + self.output_transform = lambda x: self.scaler.inverse_transform( + x.reshape(-1, 1) + ) + self.label_transform = lambda x: self.scaler.inverse_transform( + x.reshape(-1, 1) + ) else: self.output_transform = lambda x: x self.label_transform = lambda x: x @@ -488,7 +521,9 @@ def fit_model(self, train_data, train_labels, val_data, val_labels): def validation_step(self, val_dataset, _): val_rep, val_label = val_dataset.get_data_and_labels() - val_rep, val_label = torch.from_numpy(val_rep).to(self.device), torch.from_numpy(val_label).to(self.device) + val_rep, val_label = torch.from_numpy(val_rep).to( + self.device + ), torch.from_numpy(val_label).to(self.device) self.set_metrics(val_label) val_pred = self.predict(val_rep) @@ -531,7 +566,9 @@ def log_metrics(self, label, pred, metric_type): self.log_dict( { # MPS dependent type casting - f"{metric_type}/{name}": metric(self.label_transform(label), self.output_transform(pred)) + f"{metric_type}/{name}": metric( + self.label_transform(label), self.output_transform(pred) + ) if not self.mps else metric(self.label_transform(label), self.output_transform(pred)) # Fore very metric @@ -569,7 +606,9 @@ def set_model_args(self, model, *args, **kwargs): # Get passed keyword arguments arguments = locals()["kwargs"] # Get valid hyperparameters - hyperparams = {key: value for key, value in arguments.items() if key in possible_hps} + hyperparams = { + key: value for key, value in arguments.items() if key in possible_hps + } logging.debug(f"Creating model with: {hyperparams}.") return model(**hyperparams) @@ -622,7 +661,9 @@ def set_metrics(self): def init_weights(self, init_type="normal", gain=0.02): def init_func(m): classname = m.__class__.__name__ - if hasattr(m, "weight") and (classname.find("Conv") != -1 or classname.find("Linear") != -1): + if hasattr(m, "weight") and ( + classname.find("Conv") != -1 or classname.find("Linear") != -1 + ): if init_type == ImputationInit.NORMAL: nn.init.normal_(m.weight.data, 0.0, gain) elif init_type == ImputationInit.XAVIER: @@ -632,7 +673,9 @@ def init_func(m): elif init_type == ImputationInit.ORTHOGONAL: nn.init.orthogonal_(m.weight.data, gain=gain) else: - raise NotImplementedError(f"Initialization method {init_type} is not implemented") + raise NotImplementedError( + f"Initialization method {init_type} is not implemented" + ) if hasattr(m, "bias") and m.bias is not None: nn.init.constant_(m.bias.data, 0.0) elif classname.find("BatchNorm2d") != -1: From e3f6058d7f6238e9b0860e00c4311c8fbb82419b Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Mon, 2 Oct 2023 13:28:06 +0300 Subject: [PATCH 071/142] added RNN implementation from pytorch forecasting and changed dataloader to fit both --- icu_benchmarks/models/wrappers.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 074bd06a..bdb75f93 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -340,6 +340,8 @@ def step_fn(self, element, step_prefix=""): if self.pytorch_forecasting: # check if the data loader is the one for the pytorch forecasring implementation dic, labels = element[0], element[1][0] + if isinstance(labels, list): + labels = labels[-1] data = ( dic["encoder_cat"], dic["encoder_cont"], @@ -353,8 +355,7 @@ def step_fn(self, element, step_prefix=""): dic["groups"], dic["target_scale"], ) - if isinstance(labels, list): - labels = labels[0] + mask = torch.ones_like(labels).bool() else: data, labels = element[0], (element[1]).to(self.device) From 6c05b225fbaf004687cd2fda3b20cb0dcc29b6fa Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Tue, 3 Oct 2023 15:49:15 +0300 Subject: [PATCH 072/142] changes to get RNN to work --- icu_benchmarks/data/loader.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index a0c6f39b..3eea2698 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -535,10 +535,13 @@ def __init__( "MissingIndicator_46", "MissingIndicator_47", "MissingIndicator_48", + "label", ] # Convert multiple columns from boolean to float - self.data[boolean_columns] = self.data[boolean_columns].astype(float) + self.data[boolean_columns] = self.data[boolean_columns].astype( + float + ) # changing boolean to floats to allow input to models self.split = split self.args = args self.ram_cache = ram_cache @@ -572,8 +575,10 @@ def get_balance(self) -> list: Returns: Weights for each label. """ - - counts = self.data["target"][0].unique(return_counts=True) + if len(self.data["target"]) == 1: + counts = self.data["target"][0].unique(return_counts=True) + else: + counts = self.data["target"][-1].unique(return_counts=True) return list((1 / counts[1]) * counts[1].sum() / counts[0].shape[0]) From 5c3d6a3b4487d2a5d0e1dc2bca2436bcfdd5a22a Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Tue, 3 Oct 2023 15:50:12 +0300 Subject: [PATCH 073/142] changes to get RNN to work --- configs/prediction_models/common/DLCommon.gin | 2 +- icu_benchmarks/models/dl_models.py | 37 ++++++------------- icu_benchmarks/models/train.py | 10 ++++- icu_benchmarks/models/wrappers.py | 5 +-- 4 files changed, 24 insertions(+), 30 deletions(-) diff --git a/configs/prediction_models/common/DLCommon.gin b/configs/prediction_models/common/DLCommon.gin index 7c72b17a..99251ea2 100644 --- a/configs/prediction_models/common/DLCommon.gin +++ b/configs/prediction_models/common/DLCommon.gin @@ -15,7 +15,7 @@ base_regression_preprocessor.generate_features = False # Train params train_common.optimizer = @Adam train_common.epochs = 1000 -train_common.batch_size = 256 +train_common.batch_size = 64 train_common.patience = 10 train_common.min_delta = 1e-4 diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 99ce7cb9..ca491c13 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -12,7 +12,7 @@ ) import matplotlib.pyplot as plt from icu_benchmarks.models.wrappers import DLPredictionWrapper -from torch import Tensor, FloatTensor +from torch import Tensor, FloatTensor, isnan, isfinite from pytorch_forecasting import TemporalFusionTransformer, RecurrentNetwork from pytorch_forecasting.metrics import QuantileLoss import matplotlib.pyplot as plt @@ -497,16 +497,6 @@ def __init__( self.logit = nn.Linear(7, num_classes) - def set_weight(self, weight, dataset): - """ - Set the weight for the loss function - """ - if isinstance(weight, list): - weight = FloatTensor(weight) - elif weight == "balanced": - weight = FloatTensor(dataset.get_balance()) - self.loss_weights = weight - def forward( self, tuple_x: tuple, @@ -593,6 +583,7 @@ def __init__( num_classes, cell_type, rnn_layers, + batch_size, *args, **kwargs, ): @@ -607,17 +598,7 @@ def __init__( optimizer=optimizer, ) - self.logit = nn.Linear(7, num_classes) - - def set_weight(self, weight, dataset): - """ - Set the weight for the loss function - """ - if isinstance(weight, list): - weight = FloatTensor(weight) - elif weight == "balanced": - weight = FloatTensor(dataset.get_balance()) - self.loss_weights = weight + self.logit = nn.Linear(batch_size * 24, num_classes) def forward( self, @@ -637,8 +618,14 @@ def forward( "target_scale": tuple_x[10], } out = self.model(x_dict) - print(out) - print(out["prediction"][-1].shape) + out = out["prediction"][-1].reshape(1, -1) + nan_count = isnan(out).sum().item() + + # Count the number of non-NaN (finite) values in the tensor + non_nan_count = isfinite(out).sum().item() + + print("Number of NaN values in the tensor:", nan_count) + print("Number of non-NaN values in the tensor:", non_nan_count) + pred = self.logit(out) - pred = self.logit(out["prediction"][-1]) return pred diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 9f7aadf9..a7bb5e7e 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -98,6 +98,8 @@ def train_common( num_workers: Number of workers to use for data loading. """ logging.info(f"Training model: {model.__name__}.") + with open("data.pkl", "wb") as f: + pickle.dump(data, f) # choose dataset_class based on the model dataset_class = ( ImputationDataset @@ -161,7 +163,11 @@ def train_common( else: model = model( - train_dataset, optimizer=optimizer, epochs=epochs, run_mode=mode + train_dataset, + optimizer=optimizer, + epochs=epochs, + run_mode=mode, + batch_size=batch_size, # check why validation set is needed here ) else: @@ -241,6 +247,8 @@ def train_common( num_sanity_val_steps=-1, log_every_n_steps=5, check_val_every_n_epoch=1, + gradient_clip_val=0.5, + gradient_clip_algorithm="value", ) if not eval_only: if model.requires_backprop: diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index bdb75f93..163ca900 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -270,6 +270,7 @@ def set_weight(self, weight, dataset): weight = FloatTensor(weight).to(self.device) elif weight == "balanced": weight = FloatTensor(dataset.get_balance()).to(self.device) + self.loss_weights = weight def set_metrics(self, *args): @@ -340,6 +341,7 @@ def step_fn(self, element, step_prefix=""): if self.pytorch_forecasting: # check if the data loader is the one for the pytorch forecasring implementation dic, labels = element[0], element[1][0] + if isinstance(labels, list): labels = labels[-1] data = ( @@ -394,9 +396,7 @@ def step_fn(self, element, step_prefix=""): .reshape(-1, out.shape[-1]) .to(self.device) ) - target = torch.masked_select(labels, mask).to(self.device) - if prediction.shape[-1] > 1 and self.run_mode == RunMode.classification: # Classification task loss = ( @@ -408,7 +408,6 @@ def step_fn(self, element, step_prefix=""): # Returns torch.long because negative log likelihood loss elif self.run_mode == RunMode.regression: # Regression task - loss = self.loss(prediction[:, 0], target.float()) + aux_loss else: raise ValueError( From a5d2a9981c6281858edff2677101d03de3670e3b Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Tue, 3 Oct 2023 16:06:39 +0300 Subject: [PATCH 074/142] changes to get RNN to work --- icu_benchmarks/models/train.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index a7bb5e7e..23744ee5 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -150,7 +150,7 @@ def train_common( ) test_loader = test_dataset.to_dataloader( train=False, - batch_size=min(batch_size * 4, len(test_dataset)), + batch_size=batch_size, num_workers=num_workers, pin_memory=False, drop_last=True, @@ -238,7 +238,7 @@ def train_common( max_epochs=epochs if model.requires_backprop else 1, callbacks=callbacks, precision=precision, - accelerator="cpu", + accelerator="auto" if not cpu else "cpu", devices=max(torch.cuda.device_count(), 1), deterministic="warn" if reproducible else False, benchmark=not reproducible, From 56b0deb465ca90c21d5d05c8c0ef5bcbf9470255 Mon Sep 17 00:00:00 2001 From: "youssef.mecky" Date: Thu, 5 Oct 2023 17:26:04 +0200 Subject: [PATCH 075/142] removed grad clipping --- configs/prediction_models/RNNpytorch.gin | 4 ++-- icu_benchmarks/models/train.py | 12 +++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/configs/prediction_models/RNNpytorch.gin b/configs/prediction_models/RNNpytorch.gin index 0e47111f..74ca1ef3 100644 --- a/configs/prediction_models/RNNpytorch.gin +++ b/configs/prediction_models/RNNpytorch.gin @@ -12,8 +12,8 @@ optimizer/hyperparameter.weight_decay = 1e-6 # Model params model/hyperparameter.class_to_tune = @RNNpytorch -model/hyperparameter.hidden = 10 -model/hyperparameter.rnn_layers=2 +model/hyperparameter.hidden = (32, 256, "log-uniform", 2) +model/hyperparameter.rnn_layers=(1, 3) model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.cell_type='LSTM' model/hyperparameter.dropout = (0.0, 0.4) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 23744ee5..f8cae6af 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -158,7 +158,7 @@ def train_common( ) if load_weights: model = load_model( - model, source_dir, pl_model=pl_model, train_dataset=train_dataset + model, source_dir, pl_model=pl_model, train_dataset=train_dataset, optimizer=optimizer ) else: @@ -247,8 +247,6 @@ def train_common( num_sanity_val_steps=-1, log_every_n_steps=5, check_val_every_n_epoch=1, - gradient_clip_val=0.5, - gradient_clip_algorithm="value", ) if not eval_only: if model.requires_backprop: @@ -272,8 +270,8 @@ def train_common( # print("1", actual_vs_predictions) interperations = model.interpertations(test_loader, log_dir) print("2", interperations) - pd = model.predict_dependency(test_loader, "age", log_dir) - print("3", pd) + # pd = model.predict_dependency(test_loader, "age", log_dir) + # print("3", pd) """ test = next(iter(test_loader)) @@ -297,7 +295,7 @@ def train_common( return test_loss -def load_model(model, source_dir, pl_model=True, train_dataset=None): +def load_model(model, source_dir, pl_model=True, train_dataset=None, optimizer=None): if source_dir.exists(): if model.requires_backprop: if (source_dir / "model.ckpt").exists(): @@ -311,7 +309,7 @@ def load_model(model, source_dir, pl_model=True, train_dataset=None): if pl_model: if train_dataset is not None: model = model.load_from_checkpoint( - model_path, dataset=train_dataset + model_path, dataset=train_dataset, optimizer=optimizer ) else: From 8880adef1c76a9c3a98f14896e51cc0e0388f348 Mon Sep 17 00:00:00 2001 From: "youssef.mecky" Date: Mon, 9 Oct 2023 11:13:59 +0200 Subject: [PATCH 076/142] small edits --- configs/prediction_models/RNNpytorch.gin | 5 ++++- configs/prediction_models/TFTpytorch.gin | 2 ++ configs/prediction_models/common/DLCommon.gin | 4 ++-- configs/prediction_models/common/DLTuning.gin | 2 +- icu_benchmarks/models/dl_models.py | 6 ------ icu_benchmarks/models/train.py | 1 - 6 files changed, 9 insertions(+), 11 deletions(-) diff --git a/configs/prediction_models/RNNpytorch.gin b/configs/prediction_models/RNNpytorch.gin index 74ca1ef3..63c1de4e 100644 --- a/configs/prediction_models/RNNpytorch.gin +++ b/configs/prediction_models/RNNpytorch.gin @@ -11,13 +11,16 @@ optimizer/hyperparameter.weight_decay = 1e-6 # Model params + model/hyperparameter.class_to_tune = @RNNpytorch -model/hyperparameter.hidden = (32, 256, "log-uniform", 2) +model/hyperparameter.hidden = (32, 128, "log-uniform", 2) model/hyperparameter.rnn_layers=(1, 3) model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.cell_type='LSTM' model/hyperparameter.dropout = (0.0, 0.4) model/hyperparameter.lr_scheduler = "exponential" + + # Dataset params PredictionDatasetTFTpytorch.max_encoder_length = 24 PredictionDatasetTFTpytorch.max_prediction_length = 24 diff --git a/configs/prediction_models/TFTpytorch.gin b/configs/prediction_models/TFTpytorch.gin index 63bf001d..a067fc20 100644 --- a/configs/prediction_models/TFTpytorch.gin +++ b/configs/prediction_models/TFTpytorch.gin @@ -18,6 +18,8 @@ model/hyperparameter.dropout = (0.0, 0.4) model/hyperparameter.dropout_att = (0.0, 0.4) model/hyperparameter.n_heads =(1,2,4) model/hyperparameter.lr_scheduler = "exponential" + + # Dataset params PredictionDatasetTFTpytorch.max_encoder_length = 24 PredictionDatasetTFTpytorch.max_prediction_length = 24 diff --git a/configs/prediction_models/common/DLCommon.gin b/configs/prediction_models/common/DLCommon.gin index 99251ea2..2cfadcfe 100644 --- a/configs/prediction_models/common/DLCommon.gin +++ b/configs/prediction_models/common/DLCommon.gin @@ -15,8 +15,8 @@ base_regression_preprocessor.generate_features = False # Train params train_common.optimizer = @Adam train_common.epochs = 1000 -train_common.batch_size = 64 -train_common.patience = 10 +train_common.batch_size = 256 +train_common.patience = 20 train_common.min_delta = 1e-4 # Hyperparameter tuning settings diff --git a/configs/prediction_models/common/DLTuning.gin b/configs/prediction_models/common/DLTuning.gin index 1a2b7bb4..b4d13e12 100644 --- a/configs/prediction_models/common/DLTuning.gin +++ b/configs/prediction_models/common/DLTuning.gin @@ -2,4 +2,4 @@ tune_hyperparameters.scopes = ["model", "optimizer"] tune_hyperparameters.n_initial_points = 5 tune_hyperparameters.n_calls = 30 -tune_hyperparameters.folds_to_tune_on = 1 \ No newline at end of file +tune_hyperparameters.folds_to_tune_on = 2 \ No newline at end of file diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index ca491c13..0646e2f2 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -619,13 +619,7 @@ def forward( } out = self.model(x_dict) out = out["prediction"][-1].reshape(1, -1) - nan_count = isnan(out).sum().item() - # Count the number of non-NaN (finite) values in the tensor - non_nan_count = isfinite(out).sum().item() - - print("Number of NaN values in the tensor:", nan_count) - print("Number of non-NaN values in the tensor:", non_nan_count) pred = self.logit(out) return pred diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index f8cae6af..b7192370 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -246,7 +246,6 @@ def train_common( logger=loggers, num_sanity_val_steps=-1, log_every_n_steps=5, - check_val_every_n_epoch=1, ) if not eval_only: if model.requires_backprop: From 869a6112431cd75f04176215f5ce9c6588e8a14e Mon Sep 17 00:00:00 2001 From: "youssef.mecky" Date: Mon, 9 Oct 2023 12:13:33 +0200 Subject: [PATCH 077/142] changed 0/1 to categorical vars --- configs/prediction_models/RNNpytorch.gin | 99 +++++++++++----------- configs/prediction_models/TFTpytorch.gin | 101 ++++++++++++----------- icu_benchmarks/data/loader.py | 7 +- icu_benchmarks/models/train.py | 3 +- 4 files changed, 107 insertions(+), 103 deletions(-) diff --git a/configs/prediction_models/RNNpytorch.gin b/configs/prediction_models/RNNpytorch.gin index 63c1de4e..ad049c6d 100644 --- a/configs/prediction_models/RNNpytorch.gin +++ b/configs/prediction_models/RNNpytorch.gin @@ -125,55 +125,7 @@ PredictionDatasetTFTpytorch.target=[ "MissingIndicator_48", "label", ] -PredictionDatasetTFTpytorch.time_varying_unknown_reals=["alb", - "alp", - "alt", - "ast", - "be", - "bicar", - "bili", - "bili_dir", - "bnd", - "bun", - "ca", - "cai", - "ck", - "ckmb", - "cl", - "crea", - "crp", - "dbp", - "fgn", - "fio2", - "glu", - "hgb", - "hr", - "inr_pt", - "k", - "lact", - "lymph", - "map", - "mch", - "mchc", - "mcv", - "methb", - "mg", - "na", - "neut", - "o2sat", - "pco2", - "ph", - "phos", - "plt", - "po2", - "ptt", - "resp", - "sbp", - "temp", - "tnt", - "urine", - "wbc", - "MissingIndicator_1", +PredictionDatasetTFTpytorch.time_varying_unknown_categoricals=["MissingIndicator_1", "MissingIndicator_2", "MissingIndicator_3", "MissingIndicator_4", @@ -223,3 +175,52 @@ PredictionDatasetTFTpytorch.time_varying_unknown_reals=["alb", "MissingIndicator_48", "label", ] +PredictionDatasetTFTpytorch.time_varying_unknown_reals=["alb", + "alp", + "alt", + "ast", + "be", + "bicar", + "bili", + "bili_dir", + "bnd", + "bun", + "ca", + "cai", + "ck", + "ckmb", + "cl", + "crea", + "crp", + "dbp", + "fgn", + "fio2", + "glu", + "hgb", + "hr", + "inr_pt", + "k", + "lact", + "lymph", + "map", + "mch", + "mchc", + "mcv", + "methb", + "mg", + "na", + "neut", + "o2sat", + "pco2", + "ph", + "phos", + "plt", + "po2", + "ptt", + "resp", + "sbp", + "temp", + "tnt", + "urine", + "wbc",] + diff --git a/configs/prediction_models/TFTpytorch.gin b/configs/prediction_models/TFTpytorch.gin index a067fc20..442d44e8 100644 --- a/configs/prediction_models/TFTpytorch.gin +++ b/configs/prediction_models/TFTpytorch.gin @@ -26,55 +26,7 @@ PredictionDatasetTFTpytorch.max_prediction_length = 24 PredictionDatasetTFTpytorch.target="label" PredictionDatasetTFTpytorch.time_varying_known_reals=["time_idx"] PredictionDatasetTFTpytorch.add_relative_time_idx=True -PredictionDatasetTFTpytorch.time_varying_unknown_reals=["alb", - "alp", - "alt", - "ast", - "be", - "bicar", - "bili", - "bili_dir", - "bnd", - "bun", - "ca", - "cai", - "ck", - "ckmb", - "cl", - "crea", - "crp", - "dbp", - "fgn", - "fio2", - "glu", - "hgb", - "hr", - "inr_pt", - "k", - "lact", - "lymph", - "map", - "mch", - "mchc", - "mcv", - "methb", - "mg", - "na", - "neut", - "o2sat", - "pco2", - "ph", - "phos", - "plt", - "po2", - "ptt", - "resp", - "sbp", - "temp", - "tnt", - "urine", - "wbc", - "MissingIndicator_1", +PredictionDatasetTFTpytorch.time_varying_unknown_categoricals=["MissingIndicator_1", "MissingIndicator_2", "MissingIndicator_3", "MissingIndicator_4", @@ -123,4 +75,53 @@ PredictionDatasetTFTpytorch.time_varying_unknown_reals=["alb", "MissingIndicator_47", "MissingIndicator_48", "label", - ] \ No newline at end of file + ] +PredictionDatasetTFTpytorch.time_varying_unknown_reals=["alb", + "alp", + "alt", + "ast", + "be", + "bicar", + "bili", + "bili_dir", + "bnd", + "bun", + "ca", + "cai", + "ck", + "ckmb", + "cl", + "crea", + "crp", + "dbp", + "fgn", + "fio2", + "glu", + "hgb", + "hr", + "inr_pt", + "k", + "lact", + "lymph", + "map", + "mch", + "mchc", + "mcv", + "methb", + "mg", + "na", + "neut", + "o2sat", + "pco2", + "ph", + "phos", + "plt", + "po2", + "ptt", + "resp", + "sbp", + "temp", + "tnt", + "urine", + "wbc",] + \ No newline at end of file diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 3eea2698..e8e8aaa4 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -464,6 +464,7 @@ def __init__( time_varying_unknown_reals: List[str], target: Union[str, List[str]], time_varying_known_reals: List[str], + time_varying_unknown_categoricals: List[str], *args, ram_cache: bool = False, add_relative_time_idx: bool = False, @@ -486,6 +487,7 @@ def __init__( ) # combine labels and features # self.data["sex"].replace([0, 1], ["Female", "Male"], inplace=True) # List of column names to convert from boolean to float + boolean_columns = [ "MissingIndicator_1", "MissingIndicator_2", @@ -540,8 +542,9 @@ def __init__( # Convert multiple columns from boolean to float self.data[boolean_columns] = self.data[boolean_columns].astype( - float + str ) # changing boolean to floats to allow input to models + self.split = split self.args = args self.ram_cache = ram_cache @@ -561,7 +564,7 @@ def __init__( static_reals=["height", "weight", "age", "sex"], time_varying_known_categoricals=[], time_varying_known_reals=time_varying_known_reals, - time_varying_unknown_categoricals=[], + time_varying_unknown_categoricals=time_varying_unknown_categoricals, time_varying_unknown_reals=time_varying_unknown_reals, add_relative_time_idx=add_relative_time_idx, add_target_scales=True, diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index b7192370..c1d96240 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -98,8 +98,7 @@ def train_common( num_workers: Number of workers to use for data loading. """ logging.info(f"Training model: {model.__name__}.") - with open("data.pkl", "wb") as f: - pickle.dump(data, f) + # choose dataset_class based on the model dataset_class = ( ImputationDataset From ce0831cb53229fffab73c97f5e6ad3997f78036a Mon Sep 17 00:00:00 2001 From: "youssef.mecky" Date: Mon, 9 Oct 2023 12:19:53 +0200 Subject: [PATCH 078/142] changed input of rnn --- configs/prediction_models/RNNpytorch.gin | 99 ++++++++++++------------ 1 file changed, 49 insertions(+), 50 deletions(-) diff --git a/configs/prediction_models/RNNpytorch.gin b/configs/prediction_models/RNNpytorch.gin index ad049c6d..63c1de4e 100644 --- a/configs/prediction_models/RNNpytorch.gin +++ b/configs/prediction_models/RNNpytorch.gin @@ -125,7 +125,55 @@ PredictionDatasetTFTpytorch.target=[ "MissingIndicator_48", "label", ] -PredictionDatasetTFTpytorch.time_varying_unknown_categoricals=["MissingIndicator_1", +PredictionDatasetTFTpytorch.time_varying_unknown_reals=["alb", + "alp", + "alt", + "ast", + "be", + "bicar", + "bili", + "bili_dir", + "bnd", + "bun", + "ca", + "cai", + "ck", + "ckmb", + "cl", + "crea", + "crp", + "dbp", + "fgn", + "fio2", + "glu", + "hgb", + "hr", + "inr_pt", + "k", + "lact", + "lymph", + "map", + "mch", + "mchc", + "mcv", + "methb", + "mg", + "na", + "neut", + "o2sat", + "pco2", + "ph", + "phos", + "plt", + "po2", + "ptt", + "resp", + "sbp", + "temp", + "tnt", + "urine", + "wbc", + "MissingIndicator_1", "MissingIndicator_2", "MissingIndicator_3", "MissingIndicator_4", @@ -175,52 +223,3 @@ PredictionDatasetTFTpytorch.time_varying_unknown_categoricals=["MissingIndicator "MissingIndicator_48", "label", ] -PredictionDatasetTFTpytorch.time_varying_unknown_reals=["alb", - "alp", - "alt", - "ast", - "be", - "bicar", - "bili", - "bili_dir", - "bnd", - "bun", - "ca", - "cai", - "ck", - "ckmb", - "cl", - "crea", - "crp", - "dbp", - "fgn", - "fio2", - "glu", - "hgb", - "hr", - "inr_pt", - "k", - "lact", - "lymph", - "map", - "mch", - "mchc", - "mcv", - "methb", - "mg", - "na", - "neut", - "o2sat", - "pco2", - "ph", - "phos", - "plt", - "po2", - "ptt", - "resp", - "sbp", - "temp", - "tnt", - "urine", - "wbc",] - From 2828a3ac359b81a8e98061d9f1954a81bb8a998b Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Tue, 10 Oct 2023 16:06:50 +0300 Subject: [PATCH 079/142] latest changes --- configs/prediction_models/TFTpytorch.gin | 54 +----- configs/prediction_models/common/DLCommon.gin | 4 +- icu_benchmarks/data/loader.py | 155 ++++++++++++------ icu_benchmarks/models/dl_models.py | 29 ++++ icu_benchmarks/models/train.py | 44 +++-- 5 files changed, 168 insertions(+), 118 deletions(-) diff --git a/configs/prediction_models/TFTpytorch.gin b/configs/prediction_models/TFTpytorch.gin index 442d44e8..68290c1d 100644 --- a/configs/prediction_models/TFTpytorch.gin +++ b/configs/prediction_models/TFTpytorch.gin @@ -26,56 +26,7 @@ PredictionDatasetTFTpytorch.max_prediction_length = 24 PredictionDatasetTFTpytorch.target="label" PredictionDatasetTFTpytorch.time_varying_known_reals=["time_idx"] PredictionDatasetTFTpytorch.add_relative_time_idx=True -PredictionDatasetTFTpytorch.time_varying_unknown_categoricals=["MissingIndicator_1", - "MissingIndicator_2", - "MissingIndicator_3", - "MissingIndicator_4", - "MissingIndicator_5", - "MissingIndicator_6", - "MissingIndicator_7", - "MissingIndicator_8", - "MissingIndicator_9", - "MissingIndicator_10", - "MissingIndicator_11", - "MissingIndicator_12", - "MissingIndicator_13", - "MissingIndicator_14", - "MissingIndicator_15", - "MissingIndicator_16", - "MissingIndicator_17", - "MissingIndicator_18", - "MissingIndicator_19", - "MissingIndicator_20", - "MissingIndicator_21", - "MissingIndicator_22", - "MissingIndicator_23", - "MissingIndicator_24", - "MissingIndicator_25", - "MissingIndicator_26", - "MissingIndicator_27", - "MissingIndicator_28", - "MissingIndicator_29", - "MissingIndicator_30", - "MissingIndicator_31", - "MissingIndicator_32", - "MissingIndicator_33", - "MissingIndicator_34", - "MissingIndicator_35", - "MissingIndicator_36", - "MissingIndicator_37", - "MissingIndicator_38", - "MissingIndicator_39", - "MissingIndicator_40", - "MissingIndicator_41", - "MissingIndicator_42", - "MissingIndicator_43", - "MissingIndicator_44", - "MissingIndicator_45", - "MissingIndicator_46", - "MissingIndicator_47", - "MissingIndicator_48", - "label", - ] +PredictionDatasetTFTpytorch.time_varying_unknown_categoricals=[] PredictionDatasetTFTpytorch.time_varying_unknown_reals=["alb", "alp", "alt", @@ -123,5 +74,6 @@ PredictionDatasetTFTpytorch.time_varying_unknown_reals=["alb", "temp", "tnt", "urine", - "wbc",] + "wbc", + "label",] \ No newline at end of file diff --git a/configs/prediction_models/common/DLCommon.gin b/configs/prediction_models/common/DLCommon.gin index 2cfadcfe..c1600089 100644 --- a/configs/prediction_models/common/DLCommon.gin +++ b/configs/prediction_models/common/DLCommon.gin @@ -14,8 +14,8 @@ base_regression_preprocessor.generate_features = False # Train params train_common.optimizer = @Adam -train_common.epochs = 1000 -train_common.batch_size = 256 +train_common.epochs = 1 +train_common.batch_size = 64 train_common.patience = 20 train_common.min_delta = 1e-4 diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index e8e8aaa4..1de09cd2 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -487,58 +487,109 @@ def __init__( ) # combine labels and features # self.data["sex"].replace([0, 1], ["Female", "Male"], inplace=True) # List of column names to convert from boolean to float - - boolean_columns = [ - "MissingIndicator_1", - "MissingIndicator_2", - "MissingIndicator_3", - "MissingIndicator_4", - "MissingIndicator_5", - "MissingIndicator_6", - "MissingIndicator_7", - "MissingIndicator_8", - "MissingIndicator_9", - "MissingIndicator_10", - "MissingIndicator_11", - "MissingIndicator_12", - "MissingIndicator_13", - "MissingIndicator_14", - "MissingIndicator_15", - "MissingIndicator_16", - "MissingIndicator_17", - "MissingIndicator_18", - "MissingIndicator_19", - "MissingIndicator_20", - "MissingIndicator_21", - "MissingIndicator_22", - "MissingIndicator_23", - "MissingIndicator_24", - "MissingIndicator_25", - "MissingIndicator_26", - "MissingIndicator_27", - "MissingIndicator_28", - "MissingIndicator_29", - "MissingIndicator_30", - "MissingIndicator_31", - "MissingIndicator_32", - "MissingIndicator_33", - "MissingIndicator_34", - "MissingIndicator_35", - "MissingIndicator_36", - "MissingIndicator_37", - "MissingIndicator_38", - "MissingIndicator_39", - "MissingIndicator_40", - "MissingIndicator_41", - "MissingIndicator_42", - "MissingIndicator_43", - "MissingIndicator_44", - "MissingIndicator_45", - "MissingIndicator_46", - "MissingIndicator_47", - "MissingIndicator_48", - "label", - ] + if self.data["label"].dtype == "bool": + boolean_columns = [ + "MissingIndicator_1", + "MissingIndicator_2", + "MissingIndicator_3", + "MissingIndicator_4", + "MissingIndicator_5", + "MissingIndicator_6", + "MissingIndicator_7", + "MissingIndicator_8", + "MissingIndicator_9", + "MissingIndicator_10", + "MissingIndicator_11", + "MissingIndicator_12", + "MissingIndicator_13", + "MissingIndicator_14", + "MissingIndicator_15", + "MissingIndicator_16", + "MissingIndicator_17", + "MissingIndicator_18", + "MissingIndicator_19", + "MissingIndicator_20", + "MissingIndicator_21", + "MissingIndicator_22", + "MissingIndicator_23", + "MissingIndicator_24", + "MissingIndicator_25", + "MissingIndicator_26", + "MissingIndicator_27", + "MissingIndicator_28", + "MissingIndicator_29", + "MissingIndicator_30", + "MissingIndicator_31", + "MissingIndicator_32", + "MissingIndicator_33", + "MissingIndicator_34", + "MissingIndicator_35", + "MissingIndicator_36", + "MissingIndicator_37", + "MissingIndicator_38", + "MissingIndicator_39", + "MissingIndicator_40", + "MissingIndicator_41", + "MissingIndicator_42", + "MissingIndicator_43", + "MissingIndicator_44", + "MissingIndicator_45", + "MissingIndicator_46", + "MissingIndicator_47", + "MissingIndicator_48", + "label", + ] + else: + boolean_columns = [ + "MissingIndicator_1", + "MissingIndicator_2", + "MissingIndicator_3", + "MissingIndicator_4", + "MissingIndicator_5", + "MissingIndicator_6", + "MissingIndicator_7", + "MissingIndicator_8", + "MissingIndicator_9", + "MissingIndicator_10", + "MissingIndicator_11", + "MissingIndicator_12", + "MissingIndicator_13", + "MissingIndicator_14", + "MissingIndicator_15", + "MissingIndicator_16", + "MissingIndicator_17", + "MissingIndicator_18", + "MissingIndicator_19", + "MissingIndicator_20", + "MissingIndicator_21", + "MissingIndicator_22", + "MissingIndicator_23", + "MissingIndicator_24", + "MissingIndicator_25", + "MissingIndicator_26", + "MissingIndicator_27", + "MissingIndicator_28", + "MissingIndicator_29", + "MissingIndicator_30", + "MissingIndicator_31", + "MissingIndicator_32", + "MissingIndicator_33", + "MissingIndicator_34", + "MissingIndicator_35", + "MissingIndicator_36", + "MissingIndicator_37", + "MissingIndicator_38", + "MissingIndicator_39", + "MissingIndicator_40", + "MissingIndicator_41", + "MissingIndicator_42", + "MissingIndicator_43", + "MissingIndicator_44", + "MissingIndicator_45", + "MissingIndicator_46", + "MissingIndicator_47", + "MissingIndicator_48", + ] # Convert multiple columns from boolean to float self.data[boolean_columns] = self.data[boolean_columns].astype( diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 0646e2f2..c492f6a3 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -565,6 +565,35 @@ def predict_dependency(self, dataloader, variable, log_dir): plt.savefig(log_dir / "dependecy.png", bbox_inches="tight") return dependency + def forward_captum( + self, + encoder_cat: Tensor, + encoder_cont: Tensor, + encoder_target: Tensor, + encoder_lengths: Tensor, + decoder_cat: Tensor, + decoder_cont: Tensor, + decoder_target: Tensor, + decoder_lengths: Tensor, + decoder_time_idx: Tensor, + groups: Tensor, + target_scale: Tensor, + ): + tuple_x = ( + encoder_cat.float().requires_grad_(), + encoder_cont.requires_grad_(), + encoder_target.requires_grad_(), + encoder_lengths.float().requires_grad_(), + decoder_cat.float().requires_grad_(), + decoder_cont.requires_grad_(), + decoder_target.requires_grad_(), + decoder_lengths.float().requires_grad_(), + decoder_time_idx.float().requires_grad_(), + groups.float().requires_grad_(), + target_scale.requires_grad_(), + ) + return self.forward(tuple_x) + @gin.configurable class RNNpytorch(DLPredictionWrapper): diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index c1d96240..11ca6a57 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -25,7 +25,8 @@ from icu_benchmarks.contants import RunMode from icu_benchmarks.data.constants import DataSplit as Split from collections import OrderedDict -from captum.attr import IntegratedGradients +from captum.attr import IntegratedGradients, Saliency + cpu_core_count = ( len(os.sched_getaffinity(0)) if hasattr(os, "sched_getaffinity") else os.cpu_count() @@ -149,7 +150,7 @@ def train_common( ) test_loader = test_dataset.to_dataloader( train=False, - batch_size=batch_size, + batch_size=1, num_workers=num_workers, pin_memory=False, drop_last=True, @@ -157,7 +158,11 @@ def train_common( ) if load_weights: model = load_model( - model, source_dir, pl_model=pl_model, train_dataset=train_dataset, optimizer=optimizer + model, + source_dir, + pl_model=pl_model, + train_dataset=train_dataset, + optimizer=optimizer, ) else: @@ -237,7 +242,7 @@ def train_common( max_epochs=epochs if model.requires_backprop else 1, callbacks=callbacks, precision=precision, - accelerator="auto" if not cpu else "cpu", + accelerator="cpu", devices=max(torch.cuda.device_count(), 1), deterministic="warn" if reproducible else False, benchmark=not reproducible, @@ -266,25 +271,38 @@ def train_common( if explain: # actual_vs_predictions = model.actual_vs_predictions_plot(test_loader) # print("1", actual_vs_predictions) - interperations = model.interpertations(test_loader, log_dir) - print("2", interperations) + # interperations = model.interpertations(test_loader, log_dir) + # print("2", interperations) # pd = model.predict_dependency(test_loader, "age", log_dir) # print("3", pd) - """ + test = next(iter(test_loader)) for key, value in test[0].items(): test[0][key] = test[0][key].to(model.device) - - pred = model(test[0]) - - ig = IntegratedGradients(model) + data = ( + test[0]["encoder_cat"], + test[0]["encoder_cont"], + test[0]["encoder_target"], + test[0]["encoder_lengths"], + test[0]["decoder_cat"], + test[0]["decoder_cont"], + test[0]["decoder_target"], + test[0]["decoder_lengths"], + test[0]["decoder_time_idx"], + test[0]["groups"], + test[0]["target_scale"], + ) + # pred = model(data) + saliency = Saliency(model.forward_captum) + attr = saliency.attribute(data, target=0) + # ig = IntegratedGradients(model.forward_captum) # Reformat attributions. - attr, delta = ig.attribute(test[0], target=0, return_convergence_delta=True) + # attr, delta = ig.attribute(data, target=0, return_convergence_delta=True) attr = attr.detach().numpy() print(attr) - """ + model.set_weight("balanced", train_dataset) test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0][ "test/loss" From fec90cfc1d966dd593ce8781c96b1e6bc83be012 Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Wed, 11 Oct 2023 16:01:19 +0300 Subject: [PATCH 080/142] added quantus explantation and plot --- icu_benchmarks/models/dl_models.py | 100 +++++++++++++++++++++++++---- icu_benchmarks/models/train.py | 40 ++---------- 2 files changed, 95 insertions(+), 45 deletions(-) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index c492f6a3..a5f7f555 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -12,7 +12,7 @@ ) import matplotlib.pyplot as plt from icu_benchmarks.models.wrappers import DLPredictionWrapper -from torch import Tensor, FloatTensor, isnan, isfinite +from torch import Tensor, FloatTensor, zeros_like from pytorch_forecasting import TemporalFusionTransformer, RecurrentNetwork from pytorch_forecasting.metrics import QuantileLoss import matplotlib.pyplot as plt @@ -580,20 +580,96 @@ def forward_captum( target_scale: Tensor, ): tuple_x = ( - encoder_cat.float().requires_grad_(), - encoder_cont.requires_grad_(), - encoder_target.requires_grad_(), - encoder_lengths.float().requires_grad_(), - decoder_cat.float().requires_grad_(), - decoder_cont.requires_grad_(), - decoder_target.requires_grad_(), - decoder_lengths.float().requires_grad_(), - decoder_time_idx.float().requires_grad_(), - groups.float().requires_grad_(), - target_scale.requires_grad_(), + encoder_cat, + encoder_cont, + encoder_target, + encoder_lengths, + decoder_cat, + decoder_cont, + decoder_target, + decoder_lengths, + decoder_time_idx, + groups, + target_scale, ) + return self.forward(tuple_x) + def explantation_captum(self, test_loader, log_dir, method): + # Initialize lists to store attribution values for all instances + all_attrs = [] + + # Loop through the test_loader to compute attributions for all instances + for batch in test_loader: + for key, value in batch[0].items(): + batch[0][key] = batch[0][key].to(self.device) + + data = ( + batch[0]["encoder_cat"].float().requires_grad_(), + batch[0]["encoder_cont"].requires_grad_(), + batch[0]["encoder_target"].requires_grad_(), + batch[0]["encoder_lengths"].float().requires_grad_(), + batch[0]["decoder_cat"].float().requires_grad_(), + batch[0]["decoder_cont"].requires_grad_(), + batch[0]["decoder_target"].requires_grad_(), + batch[0]["decoder_lengths"].float().requires_grad_(), + batch[0]["decoder_time_idx"].float().requires_grad_(), + batch[0]["groups"].float().requires_grad_(), + batch[0]["target_scale"].requires_grad_(), + ) + baselines = ( + zeros_like(data[0]), # encoder_cat, set to zero + zeros_like(data[1]), # encoder_cont, set to zero + zeros_like(data[2]), # encoder_target, set to zero + data[3], # encoder_lengths, leave unchanged + zeros_like(data[4]), # decoder_cat, set to zero + zeros_like(data[5]), # decoder_cont, set to zero + zeros_like(data[6]), # decoder_target, set to zero + data[7], # decoder_lengths, leave unchanged + zeros_like(data[8]), # decoder_time_idx, set to zero + data[9], # groups, leave unchanged + data[10], # target_scale, leave unchanged + ) + + explantation = method(self.forward_captum, self.model.multihead_attn) + # Reformat attributions. + attr, delta = explantation.attribute( + data, target=0, return_convergence_delta=True, baselines=baselines + ) + + # Convert attributions to numpy array and append to the list + all_attrs.append(attr.detach().numpy()) + + # Concatenate attribution values for all instances along the batch dimension + all_attrs = np.concatenate(all_attrs, axis=0) + + # Compute mean along the batch dimension + means = all_attrs.mean(axis=(0, 2)) + # Normalize the means values to range [0, 1] + normalized_means = (means - means.min()) / (means.max() - means.min()) + + # Create x values (assuming you want a simple sequential x-axis) + x_values = np.arange(1, 25) # Assuming you have 24 values + + # Plotting the normalized means + plt.figure(figsize=(8, 6)) + plt.plot( + x_values, + normalized_means, + marker="o", + color="skyblue", + linestyle="-", + linewidth=2, + markersize=8, + ) + plt.xlabel("Feature Index") + plt.ylabel("Normalized Means") + plt.title("Normalized Means of Attribution Values") + plt.xticks(x_values) # Set x-ticks to match the number of features + plt.tight_layout() + plt.savefig(log_dir / "attribution_plot.png", bbox_inches="tight") + return means + @gin.configurable class RNNpytorch(DLPredictionWrapper): diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 11ca6a57..5f707551 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -25,7 +25,7 @@ from icu_benchmarks.contants import RunMode from icu_benchmarks.data.constants import DataSplit as Split from collections import OrderedDict -from captum.attr import IntegratedGradients, Saliency +from captum.attr import IntegratedGradients, Saliency, LayerIntegratedGradients cpu_core_count = ( @@ -150,7 +150,7 @@ def train_common( ) test_loader = test_dataset.to_dataloader( train=False, - batch_size=1, + batch_size=batch_size, num_workers=num_workers, pin_memory=False, drop_last=True, @@ -269,39 +269,13 @@ def train_common( return 0 if explain: - # actual_vs_predictions = model.actual_vs_predictions_plot(test_loader) - # print("1", actual_vs_predictions) - # interperations = model.interpertations(test_loader, log_dir) - # print("2", interperations) - # pd = model.predict_dependency(test_loader, "age", log_dir) - # print("3", pd) - - test = next(iter(test_loader)) + interperations = model.interpertations(test_loader, log_dir) + print("attention", interperations) - for key, value in test[0].items(): - test[0][key] = test[0][key].to(model.device) - data = ( - test[0]["encoder_cat"], - test[0]["encoder_cont"], - test[0]["encoder_target"], - test[0]["encoder_lengths"], - test[0]["decoder_cat"], - test[0]["decoder_cont"], - test[0]["decoder_target"], - test[0]["decoder_lengths"], - test[0]["decoder_time_idx"], - test[0]["groups"], - test[0]["target_scale"], + explanintations = model.explantation_captum( + test_loader, log_dir, LayerIntegratedGradients ) - # pred = model(data) - saliency = Saliency(model.forward_captum) - attr = saliency.attribute(data, target=0) - # ig = IntegratedGradients(model.forward_captum) - # Reformat attributions. - - # attr, delta = ig.attribute(data, target=0, return_convergence_delta=True) - attr = attr.detach().numpy() - print(attr) + print("IG", explanintations) model.set_weight("balanced", train_dataset) test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0][ From 391c7f5a10d18402f97fd12ca467688f5d098a4d Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Wed, 11 Oct 2023 16:07:19 +0300 Subject: [PATCH 081/142] added back gpu line --- icu_benchmarks/models/train.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 5f707551..0b54a4b5 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -242,7 +242,7 @@ def train_common( max_epochs=epochs if model.requires_backprop else 1, callbacks=callbacks, precision=precision, - accelerator="cpu", + accelerator="auto" if not cpu else "cpu", devices=max(torch.cuda.device_count(), 1), deterministic="warn" if reproducible else False, benchmark=not reproducible, From 8746996d8c5217bb51b579672fa69256eb61ab2e Mon Sep 17 00:00:00 2001 From: "youssef.mecky" Date: Tue, 17 Oct 2023 17:13:21 +0200 Subject: [PATCH 082/142] quantus looks like it is working --- configs/prediction_models/RNNpytorch.gin | 99 +------------------ configs/prediction_models/common/DLCommon.gin | 2 +- icu_benchmarks/data/loader.py | 7 +- icu_benchmarks/models/dl_models.py | 45 ++++----- icu_benchmarks/models/train.py | 10 +- 5 files changed, 34 insertions(+), 129 deletions(-) diff --git a/configs/prediction_models/RNNpytorch.gin b/configs/prediction_models/RNNpytorch.gin index 63c1de4e..91ea89f1 100644 --- a/configs/prediction_models/RNNpytorch.gin +++ b/configs/prediction_models/RNNpytorch.gin @@ -13,7 +13,7 @@ optimizer/hyperparameter.weight_decay = 1e-6 # Model params model/hyperparameter.class_to_tune = @RNNpytorch -model/hyperparameter.hidden = (32, 128, "log-uniform", 2) +model/hyperparameter.hidden = (10, 32, "log-uniform", 2) model/hyperparameter.rnn_layers=(1, 3) model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.cell_type='LSTM' @@ -75,54 +75,6 @@ PredictionDatasetTFTpytorch.target=[ "tnt", "urine", "wbc", - "MissingIndicator_1", - "MissingIndicator_2", - "MissingIndicator_3", - "MissingIndicator_4", - "MissingIndicator_5", - "MissingIndicator_6", - "MissingIndicator_7", - "MissingIndicator_8", - "MissingIndicator_9", - "MissingIndicator_10", - "MissingIndicator_11", - "MissingIndicator_12", - "MissingIndicator_13", - "MissingIndicator_14", - "MissingIndicator_15", - "MissingIndicator_16", - "MissingIndicator_17", - "MissingIndicator_18", - "MissingIndicator_19", - "MissingIndicator_20", - "MissingIndicator_21", - "MissingIndicator_22", - "MissingIndicator_23", - "MissingIndicator_24", - "MissingIndicator_25", - "MissingIndicator_26", - "MissingIndicator_27", - "MissingIndicator_28", - "MissingIndicator_29", - "MissingIndicator_30", - "MissingIndicator_31", - "MissingIndicator_32", - "MissingIndicator_33", - "MissingIndicator_34", - "MissingIndicator_35", - "MissingIndicator_36", - "MissingIndicator_37", - "MissingIndicator_38", - "MissingIndicator_39", - "MissingIndicator_40", - "MissingIndicator_41", - "MissingIndicator_42", - "MissingIndicator_43", - "MissingIndicator_44", - "MissingIndicator_45", - "MissingIndicator_46", - "MissingIndicator_47", - "MissingIndicator_48", "label", ] PredictionDatasetTFTpytorch.time_varying_unknown_reals=["alb", @@ -173,53 +125,6 @@ PredictionDatasetTFTpytorch.time_varying_unknown_reals=["alb", "tnt", "urine", "wbc", - "MissingIndicator_1", - "MissingIndicator_2", - "MissingIndicator_3", - "MissingIndicator_4", - "MissingIndicator_5", - "MissingIndicator_6", - "MissingIndicator_7", - "MissingIndicator_8", - "MissingIndicator_9", - "MissingIndicator_10", - "MissingIndicator_11", - "MissingIndicator_12", - "MissingIndicator_13", - "MissingIndicator_14", - "MissingIndicator_15", - "MissingIndicator_16", - "MissingIndicator_17", - "MissingIndicator_18", - "MissingIndicator_19", - "MissingIndicator_20", - "MissingIndicator_21", - "MissingIndicator_22", - "MissingIndicator_23", - "MissingIndicator_24", - "MissingIndicator_25", - "MissingIndicator_26", - "MissingIndicator_27", - "MissingIndicator_28", - "MissingIndicator_29", - "MissingIndicator_30", - "MissingIndicator_31", - "MissingIndicator_32", - "MissingIndicator_33", - "MissingIndicator_34", - "MissingIndicator_35", - "MissingIndicator_36", - "MissingIndicator_37", - "MissingIndicator_38", - "MissingIndicator_39", - "MissingIndicator_40", - "MissingIndicator_41", - "MissingIndicator_42", - "MissingIndicator_43", - "MissingIndicator_44", - "MissingIndicator_45", - "MissingIndicator_46", - "MissingIndicator_47", - "MissingIndicator_48", "label", ] +PredictionDatasetTFTpytorch.time_varying_unknown_categoricals=[] \ No newline at end of file diff --git a/configs/prediction_models/common/DLCommon.gin b/configs/prediction_models/common/DLCommon.gin index c1600089..fa4b440f 100644 --- a/configs/prediction_models/common/DLCommon.gin +++ b/configs/prediction_models/common/DLCommon.gin @@ -14,7 +14,7 @@ base_regression_preprocessor.generate_features = False # Train params train_common.optimizer = @Adam -train_common.epochs = 1 +train_common.epochs = 1000 train_common.batch_size = 64 train_common.patience = 20 train_common.min_delta = 1e-4 diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 1de09cd2..d25174ce 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -591,10 +591,9 @@ def __init__( "MissingIndicator_48", ] - # Convert multiple columns from boolean to float - self.data[boolean_columns] = self.data[boolean_columns].astype( - str - ) # changing boolean to floats to allow input to models + # self.data[boolean_columns] = self.data[boolean_columns].astype( + # float + # ) self.split = split self.args = args diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index a5f7f555..7e4d7c8b 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -516,6 +516,7 @@ def forward( } out = self.model(x_dict) pred = self.logit(out["prediction"]) + return pred """ @@ -595,7 +596,7 @@ def forward_captum( return self.forward(tuple_x) - def explantation_captum(self, test_loader, log_dir, method): + def explantation_captum(self, test_loader, log_dir, method, target): # Initialize lists to store attribution values for all instances all_attrs = [] @@ -607,40 +608,40 @@ def explantation_captum(self, test_loader, log_dir, method): data = ( batch[0]["encoder_cat"].float().requires_grad_(), batch[0]["encoder_cont"].requires_grad_(), - batch[0]["encoder_target"].requires_grad_(), + batch[0]["encoder_target"].float().requires_grad_(), batch[0]["encoder_lengths"].float().requires_grad_(), batch[0]["decoder_cat"].float().requires_grad_(), batch[0]["decoder_cont"].requires_grad_(), - batch[0]["decoder_target"].requires_grad_(), + batch[0]["decoder_target"].float().requires_grad_(), batch[0]["decoder_lengths"].float().requires_grad_(), batch[0]["decoder_time_idx"].float().requires_grad_(), batch[0]["groups"].float().requires_grad_(), batch[0]["target_scale"].requires_grad_(), ) + baselines = ( - zeros_like(data[0]), # encoder_cat, set to zero - zeros_like(data[1]), # encoder_cont, set to zero - zeros_like(data[2]), # encoder_target, set to zero - data[3], # encoder_lengths, leave unchanged - zeros_like(data[4]), # decoder_cat, set to zero - zeros_like(data[5]), # decoder_cont, set to zero - zeros_like(data[6]), # decoder_target, set to zero - data[7], # decoder_lengths, leave unchanged - zeros_like(data[8]), # decoder_time_idx, set to zero - data[9], # groups, leave unchanged - data[10], # target_scale, leave unchanged + zeros_like(data[0]).to(self.device), # encoder_cat, set to zero + zeros_like(data[1]).to(self.device), # encoder_cont, set to zero + zeros_like(data[2]).to(self.device), # encoder_target, set to zero + data[3].to(self.device), # encoder_lengths, leave unchanged + zeros_like(data[4]).to(self.device), # decoder_cat, set to zero + zeros_like(data[5]).to(self.device), # decoder_cont, set to zero + zeros_like(data[6]).to(self.device), # decoder_target, set to zero + data[7].to(self.device), # decoder_lengths, leave unchanged + zeros_like(data[8]).to(self.device), # decoder_time_idx, set to zero + data[9].to(self.device), # groups, leave unchanged + data[10].to(self.device), # target_scale, leave unchanged ) - explantation = method(self.forward_captum, self.model.multihead_attn) + explantation = method(self.forward_captum) # Reformat attributions. attr, delta = explantation.attribute( - data, target=0, return_convergence_delta=True, baselines=baselines + data, target=target, return_convergence_delta=True, baselines=baselines, n_steps=20 ) - # Convert attributions to numpy array and append to the list - all_attrs.append(attr.detach().numpy()) + all_attrs.append(attr[0].cpu().detach().numpy()) - # Concatenate attribution values for all instances along the batch dimension + # Concatenate a‚ttribution values for all instances along the batch dimension all_attrs = np.concatenate(all_attrs, axis=0) # Compute mean along the batch dimension @@ -662,9 +663,9 @@ def explantation_captum(self, test_loader, log_dir, method): linewidth=2, markersize=8, ) - plt.xlabel("Feature Index") - plt.ylabel("Normalized Means") - plt.title("Normalized Means of Attribution Values") + plt.xlabel("Time Step") + plt.ylabel("Normalized Attribution") + plt.title("Attribution Values") plt.xticks(x_values) # Set x-ticks to match the number of features plt.tight_layout() plt.savefig(log_dir / "attribution_plot.png", bbox_inches="tight") diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 0b54a4b5..cabdb8ba 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -25,7 +25,7 @@ from icu_benchmarks.contants import RunMode from icu_benchmarks.data.constants import DataSplit as Split from collections import OrderedDict -from captum.attr import IntegratedGradients, Saliency, LayerIntegratedGradients +from captum.attr import IntegratedGradients, LayerLRP cpu_core_count = ( @@ -269,13 +269,13 @@ def train_common( return 0 if explain: - interperations = model.interpertations(test_loader, log_dir) - print("attention", interperations) explanintations = model.explantation_captum( - test_loader, log_dir, LayerIntegratedGradients - ) + test_loader, log_dir, IntegratedGradients, target=(0, 1)) + print("IG", explanintations) + interperations = model.interpertations(test_loader, log_dir) + print("attention", interperations) model.set_weight("balanced", train_dataset) test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0][ From b7be9f6f201e77f06b1b4c4a98a8e5db696cdd19 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Tue, 24 Oct 2023 11:36:28 +0200 Subject: [PATCH 083/142] added deeparmodel --- configs/prediction_models/DeepARpytorch.gin | 80 +++++++++++++++++++++ configs/prediction_models/RNNpytorch.gin | 6 +- icu_benchmarks/models/dl_models.py | 27 +++++-- icu_benchmarks/models/wrappers.py | 2 + 4 files changed, 108 insertions(+), 7 deletions(-) create mode 100644 configs/prediction_models/DeepARpytorch.gin diff --git a/configs/prediction_models/DeepARpytorch.gin b/configs/prediction_models/DeepARpytorch.gin new file mode 100644 index 00000000..9c7b8116 --- /dev/null +++ b/configs/prediction_models/DeepARpytorch.gin @@ -0,0 +1,80 @@ +# Settings for TFT model. + +# Common settings for DL models +include "configs/prediction_models/common/DLCommon.gin" + +# Optimizer params +train_common.model = @RNNpytorch + +optimizer/hyperparameter.class_to_tune = @Adam +optimizer/hyperparameter.weight_decay = 1e-6 + + +# Model params + +model/hyperparameter.class_to_tune = @DeepARpytorch +model/hyperparameter.hidden = 2 +model/hyperparameter.rnn_layers=2 +model/hyperparameter.num_classes = %NUM_CLASSES +model/hyperparameter.cell_type='LSTM' +model/hyperparameter.dropout = (0.0, 0.4) +model/hyperparameter.lr_scheduler = "exponential" + + +# Dataset params +PredictionDatasetTFTpytorch.max_encoder_length = 24 +PredictionDatasetTFTpytorch.max_prediction_length = 24 +PredictionDatasetTFTpytorch.target="label" +PredictionDatasetTFTpytorch.time_varying_known_reals=["time_idx"] +PredictionDatasetTFTpytorch.add_relative_time_idx=True +PredictionDatasetTFTpytorch.time_varying_unknown_categoricals=[] +PredictionDatasetTFTpytorch.time_varying_unknown_reals=["alb", + "alp", + "alt", + "ast", + "be", + "bicar", + "bili", + "bili_dir", + "bnd", + "bun", + "ca", + "cai", + "ck", + "ckmb", + "cl", + "crea", + "crp", + "dbp", + "fgn", + "fio2", + "glu", + "hgb", + "hr", + "inr_pt", + "k", + "lact", + "lymph", + "map", + "mch", + "mchc", + "mcv", + "methb", + "mg", + "na", + "neut", + "o2sat", + "pco2", + "ph", + "phos", + "plt", + "po2", + "ptt", + "resp", + "sbp", + "temp", + "tnt", + "urine", + "wbc", + "label",] + \ No newline at end of file diff --git a/configs/prediction_models/RNNpytorch.gin b/configs/prediction_models/RNNpytorch.gin index 91ea89f1..84eb6dd9 100644 --- a/configs/prediction_models/RNNpytorch.gin +++ b/configs/prediction_models/RNNpytorch.gin @@ -13,10 +13,10 @@ optimizer/hyperparameter.weight_decay = 1e-6 # Model params model/hyperparameter.class_to_tune = @RNNpytorch -model/hyperparameter.hidden = (10, 32, "log-uniform", 2) -model/hyperparameter.rnn_layers=(1, 3) +model/hyperparameter.hidden = 2 +model/hyperparameter.rnn_layers=2 model/hyperparameter.num_classes = %NUM_CLASSES -model/hyperparameter.cell_type='LSTM' +model/hyperparameter.cell_type='GRU' model/hyperparameter.dropout = (0.0, 0.4) model/hyperparameter.lr_scheduler = "exponential" diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 7e4d7c8b..f5052b0c 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -643,6 +643,8 @@ def explantation_captum(self, test_loader, log_dir, method, target): # Concatenate a‚ttribution values for all instances along the batch dimension all_attrs = np.concatenate(all_attrs, axis=0) + print(all_attrs.shape) + means_feature = all_attrs.mean(axis=(0, 1)) # Compute mean along the batch dimension means = all_attrs.mean(axis=(0, 2)) @@ -650,10 +652,27 @@ def explantation_captum(self, test_loader, log_dir, method, target): normalized_means = (means - means.min()) / (means.max() - means.min()) # Create x values (assuming you want a simple sequential x-axis) - x_values = np.arange(1, 25) # Assuming you have 24 values - - # Plotting the normalized means + # Assuming you have 24 values + x_values = np.arange(1, 57) + # Plotting the featrue means + plt.figure(figsize=(8, 6)) + plt.plot( + x_values, + means_feature, + marker="o", + color="skyblue", + linestyle="-", + linewidth=2, + markersize=8, + ) + plt.xlabel("Time Step") + plt.ylabel("Normalized Attribution") + plt.title("Attribution Values") + plt.xticks(x_values) # Set x-ticks to match the number of features + plt.tight_layout() + plt.savefig(log_dir / "attribution_features_plot.png", bbox_inches="tight") plt.figure(figsize=(8, 6)) + x_values = np.arange(1, 25) plt.plot( x_values, normalized_means, @@ -727,5 +746,5 @@ def forward( out = out["prediction"][-1].reshape(1, -1) pred = self.logit(out) - + print(pred) return pred diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 163ca900..f8dcbb42 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -397,6 +397,8 @@ def step_fn(self, element, step_prefix=""): .to(self.device) ) target = torch.masked_select(labels, mask).to(self.device) + print('target', target) + print('pred', prediction) if prediction.shape[-1] > 1 and self.run_mode == RunMode.classification: # Classification task loss = ( From 89943455ea959030b9e0998771b9684612ac5e2f Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Tue, 24 Oct 2023 12:17:43 +0200 Subject: [PATCH 084/142] deepar fixes --- configs/prediction_models/DeepARpytorch.gin | 63 +++++++++++++++++++-- icu_benchmarks/data/loader.py | 6 +- icu_benchmarks/models/dl_models.py | 62 +++++++++++++++++++- icu_benchmarks/models/wrappers.py | 3 +- 4 files changed, 122 insertions(+), 12 deletions(-) diff --git a/configs/prediction_models/DeepARpytorch.gin b/configs/prediction_models/DeepARpytorch.gin index 9c7b8116..43c6e914 100644 --- a/configs/prediction_models/DeepARpytorch.gin +++ b/configs/prediction_models/DeepARpytorch.gin @@ -4,7 +4,7 @@ include "configs/prediction_models/common/DLCommon.gin" # Optimizer params -train_common.model = @RNNpytorch +train_common.model = @DeepARpytorch optimizer/hyperparameter.class_to_tune = @Adam optimizer/hyperparameter.weight_decay = 1e-6 @@ -24,10 +24,59 @@ model/hyperparameter.lr_scheduler = "exponential" # Dataset params PredictionDatasetTFTpytorch.max_encoder_length = 24 PredictionDatasetTFTpytorch.max_prediction_length = 24 -PredictionDatasetTFTpytorch.target="label" -PredictionDatasetTFTpytorch.time_varying_known_reals=["time_idx"] -PredictionDatasetTFTpytorch.add_relative_time_idx=True -PredictionDatasetTFTpytorch.time_varying_unknown_categoricals=[] +PredictionDatasetTFTpytorch.time_varying_known_reals=[] +PredictionDatasetTFTpytorch.add_relative_time_idx=False +PredictionDatasetTFTpytorch.target=[ + "alb", + "alp", + "alt", + "ast", + "be", + "bicar", + "bili", + "bili_dir", + "bnd", + "bun", + "ca", + "cai", + "ck", + "ckmb", + "cl", + "crea", + "crp", + "dbp", + "fgn", + "fio2", + "glu", + "hgb", + "hr", + "inr_pt", + "k", + "lact", + "lymph", + "map", + "mch", + "mchc", + "mcv", + "methb", + "mg", + "na", + "neut", + "o2sat", + "pco2", + "ph", + "phos", + "plt", + "po2", + "ptt", + "resp", + "sbp", + "temp", + "tnt", + "urine", + "wbc", + "label", + ] PredictionDatasetTFTpytorch.time_varying_unknown_reals=["alb", "alp", "alt", @@ -76,5 +125,7 @@ PredictionDatasetTFTpytorch.time_varying_unknown_reals=["alb", "tnt", "urine", "wbc", - "label",] + "label", + ] +PredictionDatasetTFTpytorch.time_varying_unknown_categoricals=[] \ No newline at end of file diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index d25174ce..642d678a 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -591,9 +591,9 @@ def __init__( "MissingIndicator_48", ] - # self.data[boolean_columns] = self.data[boolean_columns].astype( - # float - # ) + self.data[boolean_columns] = self.data[boolean_columns].astype( + float + ) self.split = split self.args = args diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index f5052b0c..b66fe3c4 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -13,7 +13,7 @@ import matplotlib.pyplot as plt from icu_benchmarks.models.wrappers import DLPredictionWrapper from torch import Tensor, FloatTensor, zeros_like -from pytorch_forecasting import TemporalFusionTransformer, RecurrentNetwork +from pytorch_forecasting import TemporalFusionTransformer, RecurrentNetwork, DeepAR from pytorch_forecasting.metrics import QuantileLoss import matplotlib.pyplot as plt @@ -748,3 +748,63 @@ def forward( pred = self.logit(out) print(pred) return pred + + +@gin.configurable +class DeepARpytorch(DLPredictionWrapper): + """ + Implementation of RNN from pytorch forecasting + """ + + supported_run_modes = [RunMode.classification, RunMode.regression] + + def __init__( + self, + dataset, + hidden, + dropout, + optimizer, + num_classes, + cell_type, + rnn_layers, + batch_size, + *args, + **kwargs, + ): + super().__init__(optimizer=optimizer, pytorch_forecasting=True, *args, **kwargs) + + self.model = DeepAR.from_dataset( + cell_type=cell_type, + rnn_layers=rnn_layers, + dataset=dataset, + hidden_size=hidden, + dropout=dropout, + optimizer=optimizer, + ) + + self.logit = nn.Linear(batch_size * 24*4, num_classes) + + def forward( + self, + tuple_x: tuple, + ) -> Dict[str, Tensor]: + x_dict = { + "encoder_cat": tuple_x[0], + "encoder_cont": tuple_x[1], + "encoder_target": tuple_x[2], + "encoder_lengths": tuple_x[3], + "decoder_cat": tuple_x[4], + "decoder_cont": tuple_x[5], + "decoder_target": tuple_x[6], + "decoder_lengths": tuple_x[7], + "decoder_time_idx": tuple_x[8], + "groups": tuple_x[9], + "target_scale": tuple_x[10], + } + out = self.model(x_dict) + print(out["prediction"]) + out = out["prediction"][-1].reshape(1, -1) + + pred = self.logit(out) + print(pred) + return pred diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index f8dcbb42..f78792a2 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -397,8 +397,7 @@ def step_fn(self, element, step_prefix=""): .to(self.device) ) target = torch.masked_select(labels, mask).to(self.device) - print('target', target) - print('pred', prediction) + if prediction.shape[-1] > 1 and self.run_mode == RunMode.classification: # Classification task loss = ( From 986e11fe915b568e264140d74670b97d957eb658 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Wed, 25 Oct 2023 13:36:25 +0200 Subject: [PATCH 085/142] minor changes --- configs/prediction_models/DeepARpytorch.gin | 68 ++--------- configs/prediction_models/RNNpytorch.gin | 65 ++--------- configs/prediction_models/TFTpytorch.gin | 2 +- icu_benchmarks/data/loader.py | 121 +++----------------- icu_benchmarks/models/dl_models.py | 16 ++- icu_benchmarks/models/wrappers.py | 8 +- 6 files changed, 50 insertions(+), 230 deletions(-) diff --git a/configs/prediction_models/DeepARpytorch.gin b/configs/prediction_models/DeepARpytorch.gin index 43c6e914..94ae90f0 100644 --- a/configs/prediction_models/DeepARpytorch.gin +++ b/configs/prediction_models/DeepARpytorch.gin @@ -13,8 +13,8 @@ optimizer/hyperparameter.weight_decay = 1e-6 # Model params model/hyperparameter.class_to_tune = @DeepARpytorch -model/hyperparameter.hidden = 2 -model/hyperparameter.rnn_layers=2 +model/hyperparameter.hidden = (32, 256, "log-uniform", 2) +model/hyperparameter.rnn_layers=(1, 3) model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.cell_type='LSTM' model/hyperparameter.dropout = (0.0, 0.4) @@ -24,60 +24,18 @@ model/hyperparameter.lr_scheduler = "exponential" # Dataset params PredictionDatasetTFTpytorch.max_encoder_length = 24 PredictionDatasetTFTpytorch.max_prediction_length = 24 -PredictionDatasetTFTpytorch.time_varying_known_reals=[] +PredictionDatasetTFTpytorch.time_varying_known_reals=['alb_lag_24', 'alp_lag_24', 'alt_lag_24', 'ast_lag_24', 'be_lag_24', 'bicar_lag_24', 'bili_lag_24', 'bili_dir_lag_24', 'bnd_lag_24', 'bun_lag_24', 'ca_lag_24', 'cai_lag_24', 'ck_lag_24', 'ckmb_lag_24', 'cl_lag_24', 'crea_lag_24', 'crp_lag_24', 'dbp_lag_24', 'fgn_lag_24', 'fio2_lag_24', 'glu_lag_24', 'hgb_lag_24', 'hr_lag_24', 'inr_pt_lag_24', 'k_lag_24', 'lact_lag_24', 'lymph_lag_24', 'map_lag_24', 'mch_lag_24', 'mchc_lag_24', 'mcv_lag_24', 'methb_lag_24', 'mg_lag_24', 'na_lag_24', 'neut_lag_24', 'o2sat_lag_24', 'pco2_lag_24', 'ph_lag_24', 'phos_lag_24', 'plt_lag_24', 'po2_lag_24', 'ptt_lag_24', 'resp_lag_24', 'sbp_lag_24', 'temp_lag_24', 'tnt_lag_24', 'urine_lag_24', 'wbc_lag_24', 'alb_lag_25', 'alp_lag_25', 'alt_lag_25', 'ast_lag_25', 'be_lag_25', 'bicar_lag_25', 'bili_lag_25', 'bili_dir_lag_25', 'bnd_lag_25', 'bun_lag_25', 'ca_lag_25', 'cai_lag_25', 'ck_lag_25', 'ckmb_lag_25', 'cl_lag_25', 'crea_lag_25', 'crp_lag_25', 'dbp_lag_25', 'fgn_lag_25', 'fio2_lag_25', 'glu_lag_25', 'hgb_lag_25', 'hr_lag_25', 'inr_pt_lag_25', 'k_lag_25', 'lact_lag_25', 'lymph_lag_25', 'map_lag_25', 'mch_lag_25', 'mchc_lag_25', 'mcv_lag_25', 'methb_lag_25', 'mg_lag_25', 'na_lag_25', 'neut_lag_25', 'o2sat_lag_25', 'pco2_lag_25', 'ph_lag_25', 'phos_lag_25', 'plt_lag_25', 'po2_lag_25', 'ptt_lag_25', 'resp_lag_25', 'sbp_lag_25', 'temp_lag_25', 'tnt_lag_25', 'urine_lag_25', 'wbc_lag_25', 'alb_lag_26', 'alp_lag_26', 'alt_lag_26', 'ast_lag_26', 'be_lag_26', 'bicar_lag_26', 'bili_lag_26', 'bili_dir_lag_26', 'bnd_lag_26', 'bun_lag_26', 'ca_lag_26', 'cai_lag_26', 'ck_lag_26', 'ckmb_lag_26', 'cl_lag_26', 'crea_lag_26', 'crp_lag_26', 'dbp_lag_26', 'fgn_lag_26', 'fio2_lag_26', 'glu_lag_26', 'hgb_lag_26', 'hr_lag_26', 'inr_pt_lag_26', 'k_lag_26', 'lact_lag_26', 'lymph_lag_26', 'map_lag_26', 'mch_lag_26', 'mchc_lag_26', 'mcv_lag_26', 'methb_lag_26', 'mg_lag_26', 'na_lag_26', 'neut_lag_26', 'o2sat_lag_26', 'pco2_lag_26', 'ph_lag_26', 'phos_lag_26', 'plt_lag_26', 'po2_lag_26', 'ptt_lag_26', 'resp_lag_26', 'sbp_lag_26', 'temp_lag_26', 'tnt_lag_26', 'urine_lag_26', 'wbc_lag_26', 'alb_lag_27', 'alp_lag_27', 'alt_lag_27', 'ast_lag_27', 'be_lag_27', 'bicar_lag_27', 'bili_lag_27', 'bili_dir_lag_27', 'bnd_lag_27', 'bun_lag_27', 'ca_lag_27', 'cai_lag_27', 'ck_lag_27', 'ckmb_lag_27', 'cl_lag_27', 'crea_lag_27', 'crp_lag_27', 'dbp_lag_27', 'fgn_lag_27', 'fio2_lag_27', 'glu_lag_27', 'hgb_lag_27', 'hr_lag_27', 'inr_pt_lag_27', 'k_lag_27', 'lact_lag_27', 'lymph_lag_27', 'map_lag_27', 'mch_lag_27', 'mchc_lag_27', 'mcv_lag_27', 'methb_lag_27', 'mg_lag_27', 'na_lag_27', 'neut_lag_27', 'o2sat_lag_27', 'pco2_lag_27', 'ph_lag_27', 'phos_lag_27', 'plt_lag_27', 'po2_lag_27', 'ptt_lag_27', 'resp_lag_27', 'sbp_lag_27', 'temp_lag_27', 'tnt_lag_27', 'urine_lag_27', 'wbc_lag_27', 'alb_lag_28', 'alp_lag_28', 'alt_lag_28', 'ast_lag_28', 'be_lag_28', 'bicar_lag_28', 'bili_lag_28', 'bili_dir_lag_28', 'bnd_lag_28', 'bun_lag_28', 'ca_lag_28', 'cai_lag_28', 'ck_lag_28', 'ckmb_lag_28', 'cl_lag_28', 'crea_lag_28', 'crp_lag_28', 'dbp_lag_28', 'fgn_lag_28', 'fio2_lag_28', 'glu_lag_28', 'hgb_lag_28', 'hr_lag_28', 'inr_pt_lag_28', 'k_lag_28', 'lact_lag_28', 'lymph_lag_28', 'map_lag_28', 'mch_lag_28', 'mchc_lag_28', 'mcv_lag_28', 'methb_lag_28', 'mg_lag_28', 'na_lag_28', 'neut_lag_28', 'o2sat_lag_28', 'pco2_lag_28', 'ph_lag_28', 'phos_lag_28', 'plt_lag_28', 'po2_lag_28', 'ptt_lag_28', 'resp_lag_28', 'sbp_lag_28', 'temp_lag_28', 'tnt_lag_28', 'urine_lag_28', 'wbc_lag_28', 'alb_lag_29', 'alp_lag_29', 'alt_lag_29', 'ast_lag_29', 'be_lag_29', 'bicar_lag_29', 'bili_lag_29', 'bili_dir_lag_29', 'bnd_lag_29', 'bun_lag_29', 'ca_lag_29', 'cai_lag_29', 'ck_lag_29', 'ckmb_lag_29', 'cl_lag_29', 'crea_lag_29', 'crp_lag_29', 'dbp_lag_29', 'fgn_lag_29', 'fio2_lag_29', 'glu_lag_29', 'hgb_lag_29', 'hr_lag_29', 'inr_pt_lag_29', 'k_lag_29', 'lact_lag_29', 'lymph_lag_29', 'map_lag_29', 'mch_lag_29', 'mchc_lag_29', 'mcv_lag_29', 'methb_lag_29', 'mg_lag_29', 'na_lag_29', 'neut_lag_29', 'o2sat_lag_29', 'pco2_lag_29', 'ph_lag_29', 'phos_lag_29', 'plt_lag_29', 'po2_lag_29', 'ptt_lag_29', 'resp_lag_29', 'sbp_lag_29', 'temp_lag_29', 'tnt_lag_29', 'urine_lag_29', 'wbc_lag_29', 'alb_lag_30', 'alp_lag_30', 'alt_lag_30', 'ast_lag_30', 'be_lag_30', 'bicar_lag_30', 'bili_lag_30', 'bili_dir_lag_30', 'bnd_lag_30', 'bun_lag_30', 'ca_lag_30', 'cai_lag_30', 'ck_lag_30', 'ckmb_lag_30', 'cl_lag_30', 'crea_lag_30', 'crp_lag_30', 'dbp_lag_30', 'fgn_lag_30', 'fio2_lag_30', 'glu_lag_30', 'hgb_lag_30', 'hr_lag_30', 'inr_pt_lag_30', 'k_lag_30', 'lact_lag_30', 'lymph_lag_30', 'map_lag_30', 'mch_lag_30', 'mchc_lag_30', 'mcv_lag_30', 'methb_lag_30', 'mg_lag_30', 'na_lag_30', 'neut_lag_30', 'o2sat_lag_30', 'pco2_lag_30', 'ph_lag_30', 'phos_lag_30', 'plt_lag_30', 'po2_lag_30', 'ptt_lag_30', 'resp_lag_30', 'sbp_lag_30', 'temp_lag_30', 'tnt_lag_30', 'urine_lag_30', 'wbc_lag_30', 'alb_lag_31', 'alp_lag_31', 'alt_lag_31', 'ast_lag_31', 'be_lag_31', 'bicar_lag_31', 'bili_lag_31', 'bili_dir_lag_31', 'bnd_lag_31', 'bun_lag_31', 'ca_lag_31', 'cai_lag_31', 'ck_lag_31', 'ckmb_lag_31', 'cl_lag_31', 'crea_lag_31', 'crp_lag_31', 'dbp_lag_31', 'fgn_lag_31', 'fio2_lag_31', 'glu_lag_31', 'hgb_lag_31', 'hr_lag_31', 'inr_pt_lag_31', 'k_lag_31', 'lact_lag_31', 'lymph_lag_31', 'map_lag_31', 'mch_lag_31', 'mchc_lag_31', 'mcv_lag_31', 'methb_lag_31', 'mg_lag_31', 'na_lag_31', 'neut_lag_31', 'o2sat_lag_31', 'pco2_lag_31', 'ph_lag_31', 'phos_lag_31', 'plt_lag_31', 'po2_lag_31', 'ptt_lag_31', 'resp_lag_31', 'sbp_lag_31', 'temp_lag_31', 'tnt_lag_31', 'urine_lag_31', 'wbc_lag_31', 'alb_lag_32', 'alp_lag_32', 'alt_lag_32', 'ast_lag_32', 'be_lag_32', 'bicar_lag_32', 'bili_lag_32', 'bili_dir_lag_32', 'bnd_lag_32', 'bun_lag_32', 'ca_lag_32', 'cai_lag_32', 'ck_lag_32', 'ckmb_lag_32', 'cl_lag_32', 'crea_lag_32', 'crp_lag_32', 'dbp_lag_32', 'fgn_lag_32', 'fio2_lag_32', 'glu_lag_32', 'hgb_lag_32', 'hr_lag_32', 'inr_pt_lag_32', 'k_lag_32', 'lact_lag_32', 'lymph_lag_32', 'map_lag_32', 'mch_lag_32', 'mchc_lag_32', 'mcv_lag_32', 'methb_lag_32', 'mg_lag_32', 'na_lag_32', 'neut_lag_32', 'o2sat_lag_32', 'pco2_lag_32', 'ph_lag_32', 'phos_lag_32', 'plt_lag_32', 'po2_lag_32', 'ptt_lag_32', 'resp_lag_32', 'sbp_lag_32', 'temp_lag_32', 'tnt_lag_32', 'urine_lag_32', 'wbc_lag_32', 'alb_lag_33', 'alp_lag_33', 'alt_lag_33', 'ast_lag_33', 'be_lag_33', 'bicar_lag_33', 'bili_lag_33', 'bili_dir_lag_33', 'bnd_lag_33', 'bun_lag_33', 'ca_lag_33', 'cai_lag_33', 'ck_lag_33', 'ckmb_lag_33', 'cl_lag_33', 'crea_lag_33', 'crp_lag_33', 'dbp_lag_33', 'fgn_lag_33', 'fio2_lag_33', 'glu_lag_33', 'hgb_lag_33', 'hr_lag_33', 'inr_pt_lag_33', 'k_lag_33', 'lact_lag_33', 'lymph_lag_33', 'map_lag_33', 'mch_lag_33', 'mchc_lag_33', 'mcv_lag_33', 'methb_lag_33', 'mg_lag_33', 'na_lag_33', 'neut_lag_33', 'o2sat_lag_33', 'pco2_lag_33', 'ph_lag_33', 'phos_lag_33', 'plt_lag_33', 'po2_lag_33', 'ptt_lag_33', 'resp_lag_33', 'sbp_lag_33', 'temp_lag_33', 'tnt_lag_33', 'urine_lag_33', 'wbc_lag_33', 'alb_lag_34', 'alp_lag_34', 'alt_lag_34', 'ast_lag_34', 'be_lag_34', 'bicar_lag_34', 'bili_lag_34', 'bili_dir_lag_34', 'bnd_lag_34', 'bun_lag_34', 'ca_lag_34', 'cai_lag_34', 'ck_lag_34', 'ckmb_lag_34', 'cl_lag_34', 'crea_lag_34', 'crp_lag_34', 'dbp_lag_34', 'fgn_lag_34', 'fio2_lag_34', 'glu_lag_34', 'hgb_lag_34', 'hr_lag_34', 'inr_pt_lag_34', 'k_lag_34', 'lact_lag_34', 'lymph_lag_34', 'map_lag_34', 'mch_lag_34', 'mchc_lag_34', 'mcv_lag_34', 'methb_lag_34', 'mg_lag_34', 'na_lag_34', 'neut_lag_34', 'o2sat_lag_34', 'pco2_lag_34', 'ph_lag_34', 'phos_lag_34', 'plt_lag_34', 'po2_lag_34', 'ptt_lag_34', 'resp_lag_34', 'sbp_lag_34', 'temp_lag_34', 'tnt_lag_34', 'urine_lag_34', 'wbc_lag_34', 'alb_lag_35', 'alp_lag_35', 'alt_lag_35', 'ast_lag_35', 'be_lag_35', 'bicar_lag_35', 'bili_lag_35', 'bili_dir_lag_35', 'bnd_lag_35', 'bun_lag_35', 'ca_lag_35', 'cai_lag_35', 'ck_lag_35', 'ckmb_lag_35', 'cl_lag_35', 'crea_lag_35', 'crp_lag_35', 'dbp_lag_35', 'fgn_lag_35', 'fio2_lag_35', 'glu_lag_35', 'hgb_lag_35', 'hr_lag_35', 'inr_pt_lag_35', 'k_lag_35', 'lact_lag_35', 'lymph_lag_35', 'map_lag_35', 'mch_lag_35', 'mchc_lag_35', 'mcv_lag_35', 'methb_lag_35', 'mg_lag_35', 'na_lag_35', 'neut_lag_35', 'o2sat_lag_35', 'pco2_lag_35', 'ph_lag_35', 'phos_lag_35', 'plt_lag_35', 'po2_lag_35', 'ptt_lag_35', 'resp_lag_35', 'sbp_lag_35', 'temp_lag_35', 'tnt_lag_35', 'urine_lag_35', 'wbc_lag_35', 'alb_lag_36', 'alp_lag_36', 'alt_lag_36', 'ast_lag_36', 'be_lag_36', 'bicar_lag_36', 'bili_lag_36', 'bili_dir_lag_36', 'bnd_lag_36', 'bun_lag_36', 'ca_lag_36', 'cai_lag_36', 'ck_lag_36', 'ckmb_lag_36', 'cl_lag_36', 'crea_lag_36', 'crp_lag_36', 'dbp_lag_36', 'fgn_lag_36', 'fio2_lag_36', 'glu_lag_36', 'hgb_lag_36', 'hr_lag_36', 'inr_pt_lag_36', 'k_lag_36', 'lact_lag_36', 'lymph_lag_36', 'map_lag_36', 'mch_lag_36', 'mchc_lag_36', 'mcv_lag_36', 'methb_lag_36', 'mg_lag_36', 'na_lag_36', 'neut_lag_36', 'o2sat_lag_36', 'pco2_lag_36', 'ph_lag_36', 'phos_lag_36', 'plt_lag_36', 'po2_lag_36', 'ptt_lag_36', 'resp_lag_36', 'sbp_lag_36', 'temp_lag_36', 'tnt_lag_36', 'urine_lag_36', 'wbc_lag_36', 'alb_lag_37', 'alp_lag_37', 'alt_lag_37', 'ast_lag_37', 'be_lag_37', 'bicar_lag_37', 'bili_lag_37', 'bili_dir_lag_37', 'bnd_lag_37', 'bun_lag_37', 'ca_lag_37', 'cai_lag_37', 'ck_lag_37', 'ckmb_lag_37', 'cl_lag_37', 'crea_lag_37', 'crp_lag_37', 'dbp_lag_37', 'fgn_lag_37', 'fio2_lag_37', 'glu_lag_37', 'hgb_lag_37', 'hr_lag_37', 'inr_pt_lag_37', 'k_lag_37', 'lact_lag_37', 'lymph_lag_37', 'map_lag_37', 'mch_lag_37', 'mchc_lag_37', 'mcv_lag_37', 'methb_lag_37', 'mg_lag_37', 'na_lag_37', 'neut_lag_37', 'o2sat_lag_37', 'pco2_lag_37', 'ph_lag_37', 'phos_lag_37', 'plt_lag_37', 'po2_lag_37', 'ptt_lag_37', 'resp_lag_37', 'sbp_lag_37', 'temp_lag_37', 'tnt_lag_37', 'urine_lag_37', 'wbc_lag_37', 'alb_lag_38', 'alp_lag_38', 'alt_lag_38', 'ast_lag_38', 'be_lag_38', 'bicar_lag_38', 'bili_lag_38', 'bili_dir_lag_38', 'bnd_lag_38', 'bun_lag_38', 'ca_lag_38', 'cai_lag_38', 'ck_lag_38', 'ckmb_lag_38', 'cl_lag_38', 'crea_lag_38', 'crp_lag_38', 'dbp_lag_38', 'fgn_lag_38', 'fio2_lag_38', 'glu_lag_38', 'hgb_lag_38', 'hr_lag_38', 'inr_pt_lag_38', 'k_lag_38', 'lact_lag_38', 'lymph_lag_38', 'map_lag_38', 'mch_lag_38', 'mchc_lag_38', 'mcv_lag_38', 'methb_lag_38', 'mg_lag_38', 'na_lag_38', 'neut_lag_38', 'o2sat_lag_38', 'pco2_lag_38', 'ph_lag_38', 'phos_lag_38', 'plt_lag_38', 'po2_lag_38', 'ptt_lag_38', 'resp_lag_38', 'sbp_lag_38', 'temp_lag_38', 'tnt_lag_38', 'urine_lag_38', 'wbc_lag_38', 'alb_lag_39', 'alp_lag_39', 'alt_lag_39', 'ast_lag_39', 'be_lag_39', 'bicar_lag_39', 'bili_lag_39', 'bili_dir_lag_39', 'bnd_lag_39', 'bun_lag_39', 'ca_lag_39', 'cai_lag_39', 'ck_lag_39', 'ckmb_lag_39', 'cl_lag_39', 'crea_lag_39', 'crp_lag_39', 'dbp_lag_39', 'fgn_lag_39', 'fio2_lag_39', 'glu_lag_39', 'hgb_lag_39', 'hr_lag_39', 'inr_pt_lag_39', 'k_lag_39', 'lact_lag_39', 'lymph_lag_39', 'map_lag_39', 'mch_lag_39', 'mchc_lag_39', 'mcv_lag_39', 'methb_lag_39', 'mg_lag_39', 'na_lag_39', 'neut_lag_39', 'o2sat_lag_39', 'pco2_lag_39', 'ph_lag_39', 'phos_lag_39', 'plt_lag_39', 'po2_lag_39', 'ptt_lag_39', 'resp_lag_39', 'sbp_lag_39', 'temp_lag_39', 'tnt_lag_39', 'urine_lag_39', 'wbc_lag_39', 'alb_lag_40', 'alp_lag_40', 'alt_lag_40', 'ast_lag_40', 'be_lag_40', 'bicar_lag_40', 'bili_lag_40', 'bili_dir_lag_40', 'bnd_lag_40', 'bun_lag_40', 'ca_lag_40', 'cai_lag_40', 'ck_lag_40', 'ckmb_lag_40', 'cl_lag_40', 'crea_lag_40', 'crp_lag_40', 'dbp_lag_40', 'fgn_lag_40', 'fio2_lag_40', 'glu_lag_40', 'hgb_lag_40', 'hr_lag_40', 'inr_pt_lag_40', 'k_lag_40', 'lact_lag_40', 'lymph_lag_40', 'map_lag_40', 'mch_lag_40', 'mchc_lag_40', 'mcv_lag_40', 'methb_lag_40', 'mg_lag_40', 'na_lag_40', 'neut_lag_40', 'o2sat_lag_40', 'pco2_lag_40', 'ph_lag_40', 'phos_lag_40', 'plt_lag_40', 'po2_lag_40', 'ptt_lag_40', 'resp_lag_40', 'sbp_lag_40', 'temp_lag_40', 'tnt_lag_40', 'urine_lag_40', 'wbc_lag_40', 'alb_lag_41', 'alp_lag_41', 'alt_lag_41', 'ast_lag_41', 'be_lag_41', 'bicar_lag_41', 'bili_lag_41', 'bili_dir_lag_41', 'bnd_lag_41', 'bun_lag_41', 'ca_lag_41', 'cai_lag_41', 'ck_lag_41', 'ckmb_lag_41', 'cl_lag_41', 'crea_lag_41', 'crp_lag_41', 'dbp_lag_41', 'fgn_lag_41', 'fio2_lag_41', 'glu_lag_41', 'hgb_lag_41', 'hr_lag_41', 'inr_pt_lag_41', 'k_lag_41', 'lact_lag_41', 'lymph_lag_41', 'map_lag_41', 'mch_lag_41', 'mchc_lag_41', 'mcv_lag_41', 'methb_lag_41', 'mg_lag_41', 'na_lag_41', 'neut_lag_41', 'o2sat_lag_41', 'pco2_lag_41', 'ph_lag_41', 'phos_lag_41', 'plt_lag_41', 'po2_lag_41', 'ptt_lag_41', 'resp_lag_41', 'sbp_lag_41', 'temp_lag_41', 'tnt_lag_41', 'urine_lag_41', 'wbc_lag_41', 'alb_lag_42', 'alp_lag_42', 'alt_lag_42', 'ast_lag_42', 'be_lag_42', 'bicar_lag_42', 'bili_lag_42', 'bili_dir_lag_42', 'bnd_lag_42', 'bun_lag_42', 'ca_lag_42', 'cai_lag_42', 'ck_lag_42', 'ckmb_lag_42', 'cl_lag_42', 'crea_lag_42', 'crp_lag_42', 'dbp_lag_42', 'fgn_lag_42', 'fio2_lag_42', 'glu_lag_42', 'hgb_lag_42', 'hr_lag_42', 'inr_pt_lag_42', 'k_lag_42', 'lact_lag_42', 'lymph_lag_42', 'map_lag_42', 'mch_lag_42', 'mchc_lag_42', 'mcv_lag_42', 'methb_lag_42', 'mg_lag_42', 'na_lag_42', 'neut_lag_42', 'o2sat_lag_42', 'pco2_lag_42', 'ph_lag_42', 'phos_lag_42', 'plt_lag_42', 'po2_lag_42', 'ptt_lag_42', 'resp_lag_42', 'sbp_lag_42', 'temp_lag_42', 'tnt_lag_42', 'urine_lag_42', 'wbc_lag_42', 'alb_lag_43', 'alp_lag_43', 'alt_lag_43', 'ast_lag_43', 'be_lag_43', 'bicar_lag_43', 'bili_lag_43', 'bili_dir_lag_43', 'bnd_lag_43', 'bun_lag_43', 'ca_lag_43', 'cai_lag_43', 'ck_lag_43', 'ckmb_lag_43', 'cl_lag_43', 'crea_lag_43', 'crp_lag_43', 'dbp_lag_43', 'fgn_lag_43', 'fio2_lag_43', 'glu_lag_43', 'hgb_lag_43', 'hr_lag_43', 'inr_pt_lag_43', 'k_lag_43', 'lact_lag_43', 'lymph_lag_43', 'map_lag_43', 'mch_lag_43', 'mchc_lag_43', 'mcv_lag_43', 'methb_lag_43', 'mg_lag_43', 'na_lag_43', 'neut_lag_43', 'o2sat_lag_43', 'pco2_lag_43', 'ph_lag_43', 'phos_lag_43', 'plt_lag_43', 'po2_lag_43', 'ptt_lag_43', 'resp_lag_43', 'sbp_lag_43', 'temp_lag_43', 'tnt_lag_43', 'urine_lag_43', 'wbc_lag_43', 'alb_lag_44', 'alp_lag_44', 'alt_lag_44', 'ast_lag_44', 'be_lag_44', 'bicar_lag_44', 'bili_lag_44', 'bili_dir_lag_44', 'bnd_lag_44', 'bun_lag_44', 'ca_lag_44', 'cai_lag_44', 'ck_lag_44', 'ckmb_lag_44', 'cl_lag_44', 'crea_lag_44', 'crp_lag_44', 'dbp_lag_44', 'fgn_lag_44', 'fio2_lag_44', 'glu_lag_44', 'hgb_lag_44', 'hr_lag_44', 'inr_pt_lag_44', 'k_lag_44', 'lact_lag_44', 'lymph_lag_44', 'map_lag_44', 'mch_lag_44', 'mchc_lag_44', 'mcv_lag_44', 'methb_lag_44', 'mg_lag_44', 'na_lag_44', 'neut_lag_44', 'o2sat_lag_44', 'pco2_lag_44', 'ph_lag_44', 'phos_lag_44', 'plt_lag_44', 'po2_lag_44', 'ptt_lag_44', 'resp_lag_44', 'sbp_lag_44', 'temp_lag_44', 'tnt_lag_44', 'urine_lag_44', 'wbc_lag_44', 'alb_lag_45', 'alp_lag_45', 'alt_lag_45', 'ast_lag_45', 'be_lag_45', 'bicar_lag_45', 'bili_lag_45', 'bili_dir_lag_45', 'bnd_lag_45', 'bun_lag_45', 'ca_lag_45', 'cai_lag_45', 'ck_lag_45', 'ckmb_lag_45', 'cl_lag_45', 'crea_lag_45', 'crp_lag_45', 'dbp_lag_45', 'fgn_lag_45', 'fio2_lag_45', 'glu_lag_45', 'hgb_lag_45', 'hr_lag_45', 'inr_pt_lag_45', 'k_lag_45', 'lact_lag_45', 'lymph_lag_45', 'map_lag_45', 'mch_lag_45', 'mchc_lag_45', 'mcv_lag_45', 'methb_lag_45', 'mg_lag_45', 'na_lag_45', 'neut_lag_45', 'o2sat_lag_45', 'pco2_lag_45', 'ph_lag_45', 'phos_lag_45', 'plt_lag_45', 'po2_lag_45', 'ptt_lag_45', 'resp_lag_45', 'sbp_lag_45', 'temp_lag_45', 'tnt_lag_45', 'urine_lag_45', 'wbc_lag_45', 'alb_lag_46', 'alp_lag_46', 'alt_lag_46', 'ast_lag_46', 'be_lag_46', 'bicar_lag_46', 'bili_lag_46', 'bili_dir_lag_46', 'bnd_lag_46', 'bun_lag_46', 'ca_lag_46', 'cai_lag_46', 'ck_lag_46', 'ckmb_lag_46', 'cl_lag_46', 'crea_lag_46', 'crp_lag_46', 'dbp_lag_46', 'fgn_lag_46', 'fio2_lag_46', 'glu_lag_46', 'hgb_lag_46', 'hr_lag_46', 'inr_pt_lag_46', 'k_lag_46', 'lact_lag_46', 'lymph_lag_46', 'map_lag_46', 'mch_lag_46', 'mchc_lag_46', 'mcv_lag_46', 'methb_lag_46', 'mg_lag_46', 'na_lag_46', 'neut_lag_46', 'o2sat_lag_46', 'pco2_lag_46', 'ph_lag_46', 'phos_lag_46', 'plt_lag_46', 'po2_lag_46', 'ptt_lag_46', 'resp_lag_46', 'sbp_lag_46', 'temp_lag_46', 'tnt_lag_46', 'urine_lag_46', 'wbc_lag_46', 'alb_lag_47', 'alp_lag_47', 'alt_lag_47', 'ast_lag_47', 'be_lag_47', 'bicar_lag_47', 'bili_lag_47', 'bili_dir_lag_47', 'bnd_lag_47', 'bun_lag_47', 'ca_lag_47', 'cai_lag_47', 'ck_lag_47', 'ckmb_lag_47', 'cl_lag_47', 'crea_lag_47', 'crp_lag_47', 'dbp_lag_47', 'fgn_lag_47', 'fio2_lag_47', 'glu_lag_47', 'hgb_lag_47', 'hr_lag_47', 'inr_pt_lag_47', 'k_lag_47', 'lact_lag_47', 'lymph_lag_47', 'map_lag_47', 'mch_lag_47', 'mchc_lag_47', 'mcv_lag_47', 'methb_lag_47', 'mg_lag_47', 'na_lag_47', 'neut_lag_47', 'o2sat_lag_47', 'pco2_lag_47', 'ph_lag_47', 'phos_lag_47', 'plt_lag_47', 'po2_lag_47', 'ptt_lag_47', 'resp_lag_47', 'sbp_lag_47', 'temp_lag_47', 'tnt_lag_47', 'urine_lag_47', 'wbc_lag_47', 'alb_lag_48', 'alp_lag_48', 'alt_lag_48', 'ast_lag_48', 'be_lag_48', 'bicar_lag_48', 'bili_lag_48', 'bili_dir_lag_48', 'bnd_lag_48', 'bun_lag_48', 'ca_lag_48', 'cai_lag_48', 'ck_lag_48', 'ckmb_lag_48', 'cl_lag_48', 'crea_lag_48', 'crp_lag_48', 'dbp_lag_48', 'fgn_lag_48', 'fio2_lag_48', 'glu_lag_48', 'hgb_lag_48', 'hr_lag_48', 'inr_pt_lag_48', 'k_lag_48', 'lact_lag_48', 'lymph_lag_48', 'map_lag_48', 'mch_lag_48', 'mchc_lag_48', 'mcv_lag_48', 'methb_lag_48', 'mg_lag_48', 'na_lag_48', 'neut_lag_48', 'o2sat_lag_48', 'pco2_lag_48', 'ph_lag_48', 'phos_lag_48', 'plt_lag_48', 'po2_lag_48', 'ptt_lag_48', 'resp_lag_48', 'sbp_lag_48', 'temp_lag_48', 'tnt_lag_48', 'urine_lag_48', 'wbc_lag_48'] PredictionDatasetTFTpytorch.add_relative_time_idx=False PredictionDatasetTFTpytorch.target=[ - "alb", - "alp", - "alt", - "ast", - "be", - "bicar", - "bili", - "bili_dir", - "bnd", - "bun", - "ca", - "cai", - "ck", - "ckmb", - "cl", - "crea", - "crp", - "dbp", - "fgn", - "fio2", - "glu", - "hgb", - "hr", - "inr_pt", - "k", - "lact", - "lymph", - "map", - "mch", - "mchc", - "mcv", - "methb", - "mg", - "na", - "neut", - "o2sat", - "pco2", - "ph", - "phos", - "plt", - "po2", - "ptt", - "resp", - "sbp", - "temp", - "tnt", - "urine", - "wbc", "label", ] -PredictionDatasetTFTpytorch.time_varying_unknown_reals=["alb", +PredictionDatasetTFTpytorch.time_varying_unknown_reals=[ + "label", + ] + + +PredictionDatasetTFTpytorch.time_varying_unknown_categoricals=[] +PredictionDatasetTFTpytorch.lagged_variables=["alb", "alp", "alt", "ast", @@ -124,8 +82,6 @@ PredictionDatasetTFTpytorch.time_varying_unknown_reals=["alb", "temp", "tnt", "urine", - "wbc", - "label", - ] -PredictionDatasetTFTpytorch.time_varying_unknown_categoricals=[] + "wbc",] + \ No newline at end of file diff --git a/configs/prediction_models/RNNpytorch.gin b/configs/prediction_models/RNNpytorch.gin index 84eb6dd9..ab42884f 100644 --- a/configs/prediction_models/RNNpytorch.gin +++ b/configs/prediction_models/RNNpytorch.gin @@ -16,7 +16,7 @@ model/hyperparameter.class_to_tune = @RNNpytorch model/hyperparameter.hidden = 2 model/hyperparameter.rnn_layers=2 model/hyperparameter.num_classes = %NUM_CLASSES -model/hyperparameter.cell_type='GRU' +model/hyperparameter.cell_type='LSTM' model/hyperparameter.dropout = (0.0, 0.4) model/hyperparameter.lr_scheduler = "exponential" @@ -24,60 +24,16 @@ model/hyperparameter.lr_scheduler = "exponential" # Dataset params PredictionDatasetTFTpytorch.max_encoder_length = 24 PredictionDatasetTFTpytorch.max_prediction_length = 24 -PredictionDatasetTFTpytorch.time_varying_known_reals=[] +PredictionDatasetTFTpytorch.time_varying_known_reals=['alb_lag_24', 'alp_lag_24', 'alt_lag_24', 'ast_lag_24', 'be_lag_24', 'bicar_lag_24', 'bili_lag_24', 'bili_dir_lag_24', 'bnd_lag_24', 'bun_lag_24', 'ca_lag_24', 'cai_lag_24', 'ck_lag_24', 'ckmb_lag_24', 'cl_lag_24', 'crea_lag_24', 'crp_lag_24', 'dbp_lag_24', 'fgn_lag_24', 'fio2_lag_24', 'glu_lag_24', 'hgb_lag_24', 'hr_lag_24', 'inr_pt_lag_24', 'k_lag_24', 'lact_lag_24', 'lymph_lag_24', 'map_lag_24', 'mch_lag_24', 'mchc_lag_24', 'mcv_lag_24', 'methb_lag_24', 'mg_lag_24', 'na_lag_24', 'neut_lag_24', 'o2sat_lag_24', 'pco2_lag_24', 'ph_lag_24', 'phos_lag_24', 'plt_lag_24', 'po2_lag_24', 'ptt_lag_24', 'resp_lag_24', 'sbp_lag_24', 'temp_lag_24', 'tnt_lag_24', 'urine_lag_24', 'wbc_lag_24', 'alb_lag_25', 'alp_lag_25', 'alt_lag_25', 'ast_lag_25', 'be_lag_25', 'bicar_lag_25', 'bili_lag_25', 'bili_dir_lag_25', 'bnd_lag_25', 'bun_lag_25', 'ca_lag_25', 'cai_lag_25', 'ck_lag_25', 'ckmb_lag_25', 'cl_lag_25', 'crea_lag_25', 'crp_lag_25', 'dbp_lag_25', 'fgn_lag_25', 'fio2_lag_25', 'glu_lag_25', 'hgb_lag_25', 'hr_lag_25', 'inr_pt_lag_25', 'k_lag_25', 'lact_lag_25', 'lymph_lag_25', 'map_lag_25', 'mch_lag_25', 'mchc_lag_25', 'mcv_lag_25', 'methb_lag_25', 'mg_lag_25', 'na_lag_25', 'neut_lag_25', 'o2sat_lag_25', 'pco2_lag_25', 'ph_lag_25', 'phos_lag_25', 'plt_lag_25', 'po2_lag_25', 'ptt_lag_25', 'resp_lag_25', 'sbp_lag_25', 'temp_lag_25', 'tnt_lag_25', 'urine_lag_25', 'wbc_lag_25', 'alb_lag_26', 'alp_lag_26', 'alt_lag_26', 'ast_lag_26', 'be_lag_26', 'bicar_lag_26', 'bili_lag_26', 'bili_dir_lag_26', 'bnd_lag_26', 'bun_lag_26', 'ca_lag_26', 'cai_lag_26', 'ck_lag_26', 'ckmb_lag_26', 'cl_lag_26', 'crea_lag_26', 'crp_lag_26', 'dbp_lag_26', 'fgn_lag_26', 'fio2_lag_26', 'glu_lag_26', 'hgb_lag_26', 'hr_lag_26', 'inr_pt_lag_26', 'k_lag_26', 'lact_lag_26', 'lymph_lag_26', 'map_lag_26', 'mch_lag_26', 'mchc_lag_26', 'mcv_lag_26', 'methb_lag_26', 'mg_lag_26', 'na_lag_26', 'neut_lag_26', 'o2sat_lag_26', 'pco2_lag_26', 'ph_lag_26', 'phos_lag_26', 'plt_lag_26', 'po2_lag_26', 'ptt_lag_26', 'resp_lag_26', 'sbp_lag_26', 'temp_lag_26', 'tnt_lag_26', 'urine_lag_26', 'wbc_lag_26', 'alb_lag_27', 'alp_lag_27', 'alt_lag_27', 'ast_lag_27', 'be_lag_27', 'bicar_lag_27', 'bili_lag_27', 'bili_dir_lag_27', 'bnd_lag_27', 'bun_lag_27', 'ca_lag_27', 'cai_lag_27', 'ck_lag_27', 'ckmb_lag_27', 'cl_lag_27', 'crea_lag_27', 'crp_lag_27', 'dbp_lag_27', 'fgn_lag_27', 'fio2_lag_27', 'glu_lag_27', 'hgb_lag_27', 'hr_lag_27', 'inr_pt_lag_27', 'k_lag_27', 'lact_lag_27', 'lymph_lag_27', 'map_lag_27', 'mch_lag_27', 'mchc_lag_27', 'mcv_lag_27', 'methb_lag_27', 'mg_lag_27', 'na_lag_27', 'neut_lag_27', 'o2sat_lag_27', 'pco2_lag_27', 'ph_lag_27', 'phos_lag_27', 'plt_lag_27', 'po2_lag_27', 'ptt_lag_27', 'resp_lag_27', 'sbp_lag_27', 'temp_lag_27', 'tnt_lag_27', 'urine_lag_27', 'wbc_lag_27', 'alb_lag_28', 'alp_lag_28', 'alt_lag_28', 'ast_lag_28', 'be_lag_28', 'bicar_lag_28', 'bili_lag_28', 'bili_dir_lag_28', 'bnd_lag_28', 'bun_lag_28', 'ca_lag_28', 'cai_lag_28', 'ck_lag_28', 'ckmb_lag_28', 'cl_lag_28', 'crea_lag_28', 'crp_lag_28', 'dbp_lag_28', 'fgn_lag_28', 'fio2_lag_28', 'glu_lag_28', 'hgb_lag_28', 'hr_lag_28', 'inr_pt_lag_28', 'k_lag_28', 'lact_lag_28', 'lymph_lag_28', 'map_lag_28', 'mch_lag_28', 'mchc_lag_28', 'mcv_lag_28', 'methb_lag_28', 'mg_lag_28', 'na_lag_28', 'neut_lag_28', 'o2sat_lag_28', 'pco2_lag_28', 'ph_lag_28', 'phos_lag_28', 'plt_lag_28', 'po2_lag_28', 'ptt_lag_28', 'resp_lag_28', 'sbp_lag_28', 'temp_lag_28', 'tnt_lag_28', 'urine_lag_28', 'wbc_lag_28', 'alb_lag_29', 'alp_lag_29', 'alt_lag_29', 'ast_lag_29', 'be_lag_29', 'bicar_lag_29', 'bili_lag_29', 'bili_dir_lag_29', 'bnd_lag_29', 'bun_lag_29', 'ca_lag_29', 'cai_lag_29', 'ck_lag_29', 'ckmb_lag_29', 'cl_lag_29', 'crea_lag_29', 'crp_lag_29', 'dbp_lag_29', 'fgn_lag_29', 'fio2_lag_29', 'glu_lag_29', 'hgb_lag_29', 'hr_lag_29', 'inr_pt_lag_29', 'k_lag_29', 'lact_lag_29', 'lymph_lag_29', 'map_lag_29', 'mch_lag_29', 'mchc_lag_29', 'mcv_lag_29', 'methb_lag_29', 'mg_lag_29', 'na_lag_29', 'neut_lag_29', 'o2sat_lag_29', 'pco2_lag_29', 'ph_lag_29', 'phos_lag_29', 'plt_lag_29', 'po2_lag_29', 'ptt_lag_29', 'resp_lag_29', 'sbp_lag_29', 'temp_lag_29', 'tnt_lag_29', 'urine_lag_29', 'wbc_lag_29', 'alb_lag_30', 'alp_lag_30', 'alt_lag_30', 'ast_lag_30', 'be_lag_30', 'bicar_lag_30', 'bili_lag_30', 'bili_dir_lag_30', 'bnd_lag_30', 'bun_lag_30', 'ca_lag_30', 'cai_lag_30', 'ck_lag_30', 'ckmb_lag_30', 'cl_lag_30', 'crea_lag_30', 'crp_lag_30', 'dbp_lag_30', 'fgn_lag_30', 'fio2_lag_30', 'glu_lag_30', 'hgb_lag_30', 'hr_lag_30', 'inr_pt_lag_30', 'k_lag_30', 'lact_lag_30', 'lymph_lag_30', 'map_lag_30', 'mch_lag_30', 'mchc_lag_30', 'mcv_lag_30', 'methb_lag_30', 'mg_lag_30', 'na_lag_30', 'neut_lag_30', 'o2sat_lag_30', 'pco2_lag_30', 'ph_lag_30', 'phos_lag_30', 'plt_lag_30', 'po2_lag_30', 'ptt_lag_30', 'resp_lag_30', 'sbp_lag_30', 'temp_lag_30', 'tnt_lag_30', 'urine_lag_30', 'wbc_lag_30', 'alb_lag_31', 'alp_lag_31', 'alt_lag_31', 'ast_lag_31', 'be_lag_31', 'bicar_lag_31', 'bili_lag_31', 'bili_dir_lag_31', 'bnd_lag_31', 'bun_lag_31', 'ca_lag_31', 'cai_lag_31', 'ck_lag_31', 'ckmb_lag_31', 'cl_lag_31', 'crea_lag_31', 'crp_lag_31', 'dbp_lag_31', 'fgn_lag_31', 'fio2_lag_31', 'glu_lag_31', 'hgb_lag_31', 'hr_lag_31', 'inr_pt_lag_31', 'k_lag_31', 'lact_lag_31', 'lymph_lag_31', 'map_lag_31', 'mch_lag_31', 'mchc_lag_31', 'mcv_lag_31', 'methb_lag_31', 'mg_lag_31', 'na_lag_31', 'neut_lag_31', 'o2sat_lag_31', 'pco2_lag_31', 'ph_lag_31', 'phos_lag_31', 'plt_lag_31', 'po2_lag_31', 'ptt_lag_31', 'resp_lag_31', 'sbp_lag_31', 'temp_lag_31', 'tnt_lag_31', 'urine_lag_31', 'wbc_lag_31', 'alb_lag_32', 'alp_lag_32', 'alt_lag_32', 'ast_lag_32', 'be_lag_32', 'bicar_lag_32', 'bili_lag_32', 'bili_dir_lag_32', 'bnd_lag_32', 'bun_lag_32', 'ca_lag_32', 'cai_lag_32', 'ck_lag_32', 'ckmb_lag_32', 'cl_lag_32', 'crea_lag_32', 'crp_lag_32', 'dbp_lag_32', 'fgn_lag_32', 'fio2_lag_32', 'glu_lag_32', 'hgb_lag_32', 'hr_lag_32', 'inr_pt_lag_32', 'k_lag_32', 'lact_lag_32', 'lymph_lag_32', 'map_lag_32', 'mch_lag_32', 'mchc_lag_32', 'mcv_lag_32', 'methb_lag_32', 'mg_lag_32', 'na_lag_32', 'neut_lag_32', 'o2sat_lag_32', 'pco2_lag_32', 'ph_lag_32', 'phos_lag_32', 'plt_lag_32', 'po2_lag_32', 'ptt_lag_32', 'resp_lag_32', 'sbp_lag_32', 'temp_lag_32', 'tnt_lag_32', 'urine_lag_32', 'wbc_lag_32', 'alb_lag_33', 'alp_lag_33', 'alt_lag_33', 'ast_lag_33', 'be_lag_33', 'bicar_lag_33', 'bili_lag_33', 'bili_dir_lag_33', 'bnd_lag_33', 'bun_lag_33', 'ca_lag_33', 'cai_lag_33', 'ck_lag_33', 'ckmb_lag_33', 'cl_lag_33', 'crea_lag_33', 'crp_lag_33', 'dbp_lag_33', 'fgn_lag_33', 'fio2_lag_33', 'glu_lag_33', 'hgb_lag_33', 'hr_lag_33', 'inr_pt_lag_33', 'k_lag_33', 'lact_lag_33', 'lymph_lag_33', 'map_lag_33', 'mch_lag_33', 'mchc_lag_33', 'mcv_lag_33', 'methb_lag_33', 'mg_lag_33', 'na_lag_33', 'neut_lag_33', 'o2sat_lag_33', 'pco2_lag_33', 'ph_lag_33', 'phos_lag_33', 'plt_lag_33', 'po2_lag_33', 'ptt_lag_33', 'resp_lag_33', 'sbp_lag_33', 'temp_lag_33', 'tnt_lag_33', 'urine_lag_33', 'wbc_lag_33', 'alb_lag_34', 'alp_lag_34', 'alt_lag_34', 'ast_lag_34', 'be_lag_34', 'bicar_lag_34', 'bili_lag_34', 'bili_dir_lag_34', 'bnd_lag_34', 'bun_lag_34', 'ca_lag_34', 'cai_lag_34', 'ck_lag_34', 'ckmb_lag_34', 'cl_lag_34', 'crea_lag_34', 'crp_lag_34', 'dbp_lag_34', 'fgn_lag_34', 'fio2_lag_34', 'glu_lag_34', 'hgb_lag_34', 'hr_lag_34', 'inr_pt_lag_34', 'k_lag_34', 'lact_lag_34', 'lymph_lag_34', 'map_lag_34', 'mch_lag_34', 'mchc_lag_34', 'mcv_lag_34', 'methb_lag_34', 'mg_lag_34', 'na_lag_34', 'neut_lag_34', 'o2sat_lag_34', 'pco2_lag_34', 'ph_lag_34', 'phos_lag_34', 'plt_lag_34', 'po2_lag_34', 'ptt_lag_34', 'resp_lag_34', 'sbp_lag_34', 'temp_lag_34', 'tnt_lag_34', 'urine_lag_34', 'wbc_lag_34', 'alb_lag_35', 'alp_lag_35', 'alt_lag_35', 'ast_lag_35', 'be_lag_35', 'bicar_lag_35', 'bili_lag_35', 'bili_dir_lag_35', 'bnd_lag_35', 'bun_lag_35', 'ca_lag_35', 'cai_lag_35', 'ck_lag_35', 'ckmb_lag_35', 'cl_lag_35', 'crea_lag_35', 'crp_lag_35', 'dbp_lag_35', 'fgn_lag_35', 'fio2_lag_35', 'glu_lag_35', 'hgb_lag_35', 'hr_lag_35', 'inr_pt_lag_35', 'k_lag_35', 'lact_lag_35', 'lymph_lag_35', 'map_lag_35', 'mch_lag_35', 'mchc_lag_35', 'mcv_lag_35', 'methb_lag_35', 'mg_lag_35', 'na_lag_35', 'neut_lag_35', 'o2sat_lag_35', 'pco2_lag_35', 'ph_lag_35', 'phos_lag_35', 'plt_lag_35', 'po2_lag_35', 'ptt_lag_35', 'resp_lag_35', 'sbp_lag_35', 'temp_lag_35', 'tnt_lag_35', 'urine_lag_35', 'wbc_lag_35', 'alb_lag_36', 'alp_lag_36', 'alt_lag_36', 'ast_lag_36', 'be_lag_36', 'bicar_lag_36', 'bili_lag_36', 'bili_dir_lag_36', 'bnd_lag_36', 'bun_lag_36', 'ca_lag_36', 'cai_lag_36', 'ck_lag_36', 'ckmb_lag_36', 'cl_lag_36', 'crea_lag_36', 'crp_lag_36', 'dbp_lag_36', 'fgn_lag_36', 'fio2_lag_36', 'glu_lag_36', 'hgb_lag_36', 'hr_lag_36', 'inr_pt_lag_36', 'k_lag_36', 'lact_lag_36', 'lymph_lag_36', 'map_lag_36', 'mch_lag_36', 'mchc_lag_36', 'mcv_lag_36', 'methb_lag_36', 'mg_lag_36', 'na_lag_36', 'neut_lag_36', 'o2sat_lag_36', 'pco2_lag_36', 'ph_lag_36', 'phos_lag_36', 'plt_lag_36', 'po2_lag_36', 'ptt_lag_36', 'resp_lag_36', 'sbp_lag_36', 'temp_lag_36', 'tnt_lag_36', 'urine_lag_36', 'wbc_lag_36', 'alb_lag_37', 'alp_lag_37', 'alt_lag_37', 'ast_lag_37', 'be_lag_37', 'bicar_lag_37', 'bili_lag_37', 'bili_dir_lag_37', 'bnd_lag_37', 'bun_lag_37', 'ca_lag_37', 'cai_lag_37', 'ck_lag_37', 'ckmb_lag_37', 'cl_lag_37', 'crea_lag_37', 'crp_lag_37', 'dbp_lag_37', 'fgn_lag_37', 'fio2_lag_37', 'glu_lag_37', 'hgb_lag_37', 'hr_lag_37', 'inr_pt_lag_37', 'k_lag_37', 'lact_lag_37', 'lymph_lag_37', 'map_lag_37', 'mch_lag_37', 'mchc_lag_37', 'mcv_lag_37', 'methb_lag_37', 'mg_lag_37', 'na_lag_37', 'neut_lag_37', 'o2sat_lag_37', 'pco2_lag_37', 'ph_lag_37', 'phos_lag_37', 'plt_lag_37', 'po2_lag_37', 'ptt_lag_37', 'resp_lag_37', 'sbp_lag_37', 'temp_lag_37', 'tnt_lag_37', 'urine_lag_37', 'wbc_lag_37', 'alb_lag_38', 'alp_lag_38', 'alt_lag_38', 'ast_lag_38', 'be_lag_38', 'bicar_lag_38', 'bili_lag_38', 'bili_dir_lag_38', 'bnd_lag_38', 'bun_lag_38', 'ca_lag_38', 'cai_lag_38', 'ck_lag_38', 'ckmb_lag_38', 'cl_lag_38', 'crea_lag_38', 'crp_lag_38', 'dbp_lag_38', 'fgn_lag_38', 'fio2_lag_38', 'glu_lag_38', 'hgb_lag_38', 'hr_lag_38', 'inr_pt_lag_38', 'k_lag_38', 'lact_lag_38', 'lymph_lag_38', 'map_lag_38', 'mch_lag_38', 'mchc_lag_38', 'mcv_lag_38', 'methb_lag_38', 'mg_lag_38', 'na_lag_38', 'neut_lag_38', 'o2sat_lag_38', 'pco2_lag_38', 'ph_lag_38', 'phos_lag_38', 'plt_lag_38', 'po2_lag_38', 'ptt_lag_38', 'resp_lag_38', 'sbp_lag_38', 'temp_lag_38', 'tnt_lag_38', 'urine_lag_38', 'wbc_lag_38', 'alb_lag_39', 'alp_lag_39', 'alt_lag_39', 'ast_lag_39', 'be_lag_39', 'bicar_lag_39', 'bili_lag_39', 'bili_dir_lag_39', 'bnd_lag_39', 'bun_lag_39', 'ca_lag_39', 'cai_lag_39', 'ck_lag_39', 'ckmb_lag_39', 'cl_lag_39', 'crea_lag_39', 'crp_lag_39', 'dbp_lag_39', 'fgn_lag_39', 'fio2_lag_39', 'glu_lag_39', 'hgb_lag_39', 'hr_lag_39', 'inr_pt_lag_39', 'k_lag_39', 'lact_lag_39', 'lymph_lag_39', 'map_lag_39', 'mch_lag_39', 'mchc_lag_39', 'mcv_lag_39', 'methb_lag_39', 'mg_lag_39', 'na_lag_39', 'neut_lag_39', 'o2sat_lag_39', 'pco2_lag_39', 'ph_lag_39', 'phos_lag_39', 'plt_lag_39', 'po2_lag_39', 'ptt_lag_39', 'resp_lag_39', 'sbp_lag_39', 'temp_lag_39', 'tnt_lag_39', 'urine_lag_39', 'wbc_lag_39', 'alb_lag_40', 'alp_lag_40', 'alt_lag_40', 'ast_lag_40', 'be_lag_40', 'bicar_lag_40', 'bili_lag_40', 'bili_dir_lag_40', 'bnd_lag_40', 'bun_lag_40', 'ca_lag_40', 'cai_lag_40', 'ck_lag_40', 'ckmb_lag_40', 'cl_lag_40', 'crea_lag_40', 'crp_lag_40', 'dbp_lag_40', 'fgn_lag_40', 'fio2_lag_40', 'glu_lag_40', 'hgb_lag_40', 'hr_lag_40', 'inr_pt_lag_40', 'k_lag_40', 'lact_lag_40', 'lymph_lag_40', 'map_lag_40', 'mch_lag_40', 'mchc_lag_40', 'mcv_lag_40', 'methb_lag_40', 'mg_lag_40', 'na_lag_40', 'neut_lag_40', 'o2sat_lag_40', 'pco2_lag_40', 'ph_lag_40', 'phos_lag_40', 'plt_lag_40', 'po2_lag_40', 'ptt_lag_40', 'resp_lag_40', 'sbp_lag_40', 'temp_lag_40', 'tnt_lag_40', 'urine_lag_40', 'wbc_lag_40', 'alb_lag_41', 'alp_lag_41', 'alt_lag_41', 'ast_lag_41', 'be_lag_41', 'bicar_lag_41', 'bili_lag_41', 'bili_dir_lag_41', 'bnd_lag_41', 'bun_lag_41', 'ca_lag_41', 'cai_lag_41', 'ck_lag_41', 'ckmb_lag_41', 'cl_lag_41', 'crea_lag_41', 'crp_lag_41', 'dbp_lag_41', 'fgn_lag_41', 'fio2_lag_41', 'glu_lag_41', 'hgb_lag_41', 'hr_lag_41', 'inr_pt_lag_41', 'k_lag_41', 'lact_lag_41', 'lymph_lag_41', 'map_lag_41', 'mch_lag_41', 'mchc_lag_41', 'mcv_lag_41', 'methb_lag_41', 'mg_lag_41', 'na_lag_41', 'neut_lag_41', 'o2sat_lag_41', 'pco2_lag_41', 'ph_lag_41', 'phos_lag_41', 'plt_lag_41', 'po2_lag_41', 'ptt_lag_41', 'resp_lag_41', 'sbp_lag_41', 'temp_lag_41', 'tnt_lag_41', 'urine_lag_41', 'wbc_lag_41', 'alb_lag_42', 'alp_lag_42', 'alt_lag_42', 'ast_lag_42', 'be_lag_42', 'bicar_lag_42', 'bili_lag_42', 'bili_dir_lag_42', 'bnd_lag_42', 'bun_lag_42', 'ca_lag_42', 'cai_lag_42', 'ck_lag_42', 'ckmb_lag_42', 'cl_lag_42', 'crea_lag_42', 'crp_lag_42', 'dbp_lag_42', 'fgn_lag_42', 'fio2_lag_42', 'glu_lag_42', 'hgb_lag_42', 'hr_lag_42', 'inr_pt_lag_42', 'k_lag_42', 'lact_lag_42', 'lymph_lag_42', 'map_lag_42', 'mch_lag_42', 'mchc_lag_42', 'mcv_lag_42', 'methb_lag_42', 'mg_lag_42', 'na_lag_42', 'neut_lag_42', 'o2sat_lag_42', 'pco2_lag_42', 'ph_lag_42', 'phos_lag_42', 'plt_lag_42', 'po2_lag_42', 'ptt_lag_42', 'resp_lag_42', 'sbp_lag_42', 'temp_lag_42', 'tnt_lag_42', 'urine_lag_42', 'wbc_lag_42', 'alb_lag_43', 'alp_lag_43', 'alt_lag_43', 'ast_lag_43', 'be_lag_43', 'bicar_lag_43', 'bili_lag_43', 'bili_dir_lag_43', 'bnd_lag_43', 'bun_lag_43', 'ca_lag_43', 'cai_lag_43', 'ck_lag_43', 'ckmb_lag_43', 'cl_lag_43', 'crea_lag_43', 'crp_lag_43', 'dbp_lag_43', 'fgn_lag_43', 'fio2_lag_43', 'glu_lag_43', 'hgb_lag_43', 'hr_lag_43', 'inr_pt_lag_43', 'k_lag_43', 'lact_lag_43', 'lymph_lag_43', 'map_lag_43', 'mch_lag_43', 'mchc_lag_43', 'mcv_lag_43', 'methb_lag_43', 'mg_lag_43', 'na_lag_43', 'neut_lag_43', 'o2sat_lag_43', 'pco2_lag_43', 'ph_lag_43', 'phos_lag_43', 'plt_lag_43', 'po2_lag_43', 'ptt_lag_43', 'resp_lag_43', 'sbp_lag_43', 'temp_lag_43', 'tnt_lag_43', 'urine_lag_43', 'wbc_lag_43', 'alb_lag_44', 'alp_lag_44', 'alt_lag_44', 'ast_lag_44', 'be_lag_44', 'bicar_lag_44', 'bili_lag_44', 'bili_dir_lag_44', 'bnd_lag_44', 'bun_lag_44', 'ca_lag_44', 'cai_lag_44', 'ck_lag_44', 'ckmb_lag_44', 'cl_lag_44', 'crea_lag_44', 'crp_lag_44', 'dbp_lag_44', 'fgn_lag_44', 'fio2_lag_44', 'glu_lag_44', 'hgb_lag_44', 'hr_lag_44', 'inr_pt_lag_44', 'k_lag_44', 'lact_lag_44', 'lymph_lag_44', 'map_lag_44', 'mch_lag_44', 'mchc_lag_44', 'mcv_lag_44', 'methb_lag_44', 'mg_lag_44', 'na_lag_44', 'neut_lag_44', 'o2sat_lag_44', 'pco2_lag_44', 'ph_lag_44', 'phos_lag_44', 'plt_lag_44', 'po2_lag_44', 'ptt_lag_44', 'resp_lag_44', 'sbp_lag_44', 'temp_lag_44', 'tnt_lag_44', 'urine_lag_44', 'wbc_lag_44', 'alb_lag_45', 'alp_lag_45', 'alt_lag_45', 'ast_lag_45', 'be_lag_45', 'bicar_lag_45', 'bili_lag_45', 'bili_dir_lag_45', 'bnd_lag_45', 'bun_lag_45', 'ca_lag_45', 'cai_lag_45', 'ck_lag_45', 'ckmb_lag_45', 'cl_lag_45', 'crea_lag_45', 'crp_lag_45', 'dbp_lag_45', 'fgn_lag_45', 'fio2_lag_45', 'glu_lag_45', 'hgb_lag_45', 'hr_lag_45', 'inr_pt_lag_45', 'k_lag_45', 'lact_lag_45', 'lymph_lag_45', 'map_lag_45', 'mch_lag_45', 'mchc_lag_45', 'mcv_lag_45', 'methb_lag_45', 'mg_lag_45', 'na_lag_45', 'neut_lag_45', 'o2sat_lag_45', 'pco2_lag_45', 'ph_lag_45', 'phos_lag_45', 'plt_lag_45', 'po2_lag_45', 'ptt_lag_45', 'resp_lag_45', 'sbp_lag_45', 'temp_lag_45', 'tnt_lag_45', 'urine_lag_45', 'wbc_lag_45', 'alb_lag_46', 'alp_lag_46', 'alt_lag_46', 'ast_lag_46', 'be_lag_46', 'bicar_lag_46', 'bili_lag_46', 'bili_dir_lag_46', 'bnd_lag_46', 'bun_lag_46', 'ca_lag_46', 'cai_lag_46', 'ck_lag_46', 'ckmb_lag_46', 'cl_lag_46', 'crea_lag_46', 'crp_lag_46', 'dbp_lag_46', 'fgn_lag_46', 'fio2_lag_46', 'glu_lag_46', 'hgb_lag_46', 'hr_lag_46', 'inr_pt_lag_46', 'k_lag_46', 'lact_lag_46', 'lymph_lag_46', 'map_lag_46', 'mch_lag_46', 'mchc_lag_46', 'mcv_lag_46', 'methb_lag_46', 'mg_lag_46', 'na_lag_46', 'neut_lag_46', 'o2sat_lag_46', 'pco2_lag_46', 'ph_lag_46', 'phos_lag_46', 'plt_lag_46', 'po2_lag_46', 'ptt_lag_46', 'resp_lag_46', 'sbp_lag_46', 'temp_lag_46', 'tnt_lag_46', 'urine_lag_46', 'wbc_lag_46', 'alb_lag_47', 'alp_lag_47', 'alt_lag_47', 'ast_lag_47', 'be_lag_47', 'bicar_lag_47', 'bili_lag_47', 'bili_dir_lag_47', 'bnd_lag_47', 'bun_lag_47', 'ca_lag_47', 'cai_lag_47', 'ck_lag_47', 'ckmb_lag_47', 'cl_lag_47', 'crea_lag_47', 'crp_lag_47', 'dbp_lag_47', 'fgn_lag_47', 'fio2_lag_47', 'glu_lag_47', 'hgb_lag_47', 'hr_lag_47', 'inr_pt_lag_47', 'k_lag_47', 'lact_lag_47', 'lymph_lag_47', 'map_lag_47', 'mch_lag_47', 'mchc_lag_47', 'mcv_lag_47', 'methb_lag_47', 'mg_lag_47', 'na_lag_47', 'neut_lag_47', 'o2sat_lag_47', 'pco2_lag_47', 'ph_lag_47', 'phos_lag_47', 'plt_lag_47', 'po2_lag_47', 'ptt_lag_47', 'resp_lag_47', 'sbp_lag_47', 'temp_lag_47', 'tnt_lag_47', 'urine_lag_47', 'wbc_lag_47', 'alb_lag_48', 'alp_lag_48', 'alt_lag_48', 'ast_lag_48', 'be_lag_48', 'bicar_lag_48', 'bili_lag_48', 'bili_dir_lag_48', 'bnd_lag_48', 'bun_lag_48', 'ca_lag_48', 'cai_lag_48', 'ck_lag_48', 'ckmb_lag_48', 'cl_lag_48', 'crea_lag_48', 'crp_lag_48', 'dbp_lag_48', 'fgn_lag_48', 'fio2_lag_48', 'glu_lag_48', 'hgb_lag_48', 'hr_lag_48', 'inr_pt_lag_48', 'k_lag_48', 'lact_lag_48', 'lymph_lag_48', 'map_lag_48', 'mch_lag_48', 'mchc_lag_48', 'mcv_lag_48', 'methb_lag_48', 'mg_lag_48', 'na_lag_48', 'neut_lag_48', 'o2sat_lag_48', 'pco2_lag_48', 'ph_lag_48', 'phos_lag_48', 'plt_lag_48', 'po2_lag_48', 'ptt_lag_48', 'resp_lag_48', 'sbp_lag_48', 'temp_lag_48', 'tnt_lag_48', 'urine_lag_48', 'wbc_lag_48'] + PredictionDatasetTFTpytorch.add_relative_time_idx=False -PredictionDatasetTFTpytorch.target=[ - "alb", - "alp", - "alt", - "ast", - "be", - "bicar", - "bili", - "bili_dir", - "bnd", - "bun", - "ca", - "cai", - "ck", - "ckmb", - "cl", - "crea", - "crp", - "dbp", - "fgn", - "fio2", - "glu", - "hgb", - "hr", - "inr_pt", - "k", - "lact", - "lymph", - "map", - "mch", - "mchc", - "mcv", - "methb", - "mg", - "na", - "neut", - "o2sat", - "pco2", - "ph", - "phos", - "plt", - "po2", - "ptt", - "resp", - "sbp", - "temp", - "tnt", - "urine", - "wbc", +PredictionDatasetTFTpytorch.target="label" + +PredictionDatasetTFTpytorch.time_varying_unknown_reals=[ "label", ] -PredictionDatasetTFTpytorch.time_varying_unknown_reals=["alb", +PredictionDatasetTFTpytorch.time_varying_unknown_categoricals=[] +PredictionDatasetTFTpytorch.lagged_variables=["alb", "alp", "alt", "ast", @@ -124,7 +80,4 @@ PredictionDatasetTFTpytorch.time_varying_unknown_reals=["alb", "temp", "tnt", "urine", - "wbc", - "label", - ] -PredictionDatasetTFTpytorch.time_varying_unknown_categoricals=[] \ No newline at end of file + "wbc",] \ No newline at end of file diff --git a/configs/prediction_models/TFTpytorch.gin b/configs/prediction_models/TFTpytorch.gin index 68290c1d..a6523c6b 100644 --- a/configs/prediction_models/TFTpytorch.gin +++ b/configs/prediction_models/TFTpytorch.gin @@ -76,4 +76,4 @@ PredictionDatasetTFTpytorch.time_varying_unknown_reals=["alb", "urine", "wbc", "label",] - \ No newline at end of file +PredictionDatasetTFTpytorch.lagged_variables=[] \ No newline at end of file diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 642d678a..e02b2e76 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -465,14 +465,17 @@ def __init__( target: Union[str, List[str]], time_varying_known_reals: List[str], time_varying_unknown_categoricals: List[str], + lagged_variables: List[str], *args, ram_cache: bool = False, add_relative_time_idx: bool = False, name: str = "", max_prediction_length: int = 24, max_encoder_length: int = 24, + **kwargs, ): + data[split]["FEATURES"]["time_idx"] = ( (data[split]["FEATURES"]["time"] / pd.Timedelta(seconds=3600)) ).astype( @@ -487,113 +490,17 @@ def __init__( ) # combine labels and features # self.data["sex"].replace([0, 1], ["Female", "Male"], inplace=True) # List of column names to convert from boolean to float - if self.data["label"].dtype == "bool": - boolean_columns = [ - "MissingIndicator_1", - "MissingIndicator_2", - "MissingIndicator_3", - "MissingIndicator_4", - "MissingIndicator_5", - "MissingIndicator_6", - "MissingIndicator_7", - "MissingIndicator_8", - "MissingIndicator_9", - "MissingIndicator_10", - "MissingIndicator_11", - "MissingIndicator_12", - "MissingIndicator_13", - "MissingIndicator_14", - "MissingIndicator_15", - "MissingIndicator_16", - "MissingIndicator_17", - "MissingIndicator_18", - "MissingIndicator_19", - "MissingIndicator_20", - "MissingIndicator_21", - "MissingIndicator_22", - "MissingIndicator_23", - "MissingIndicator_24", - "MissingIndicator_25", - "MissingIndicator_26", - "MissingIndicator_27", - "MissingIndicator_28", - "MissingIndicator_29", - "MissingIndicator_30", - "MissingIndicator_31", - "MissingIndicator_32", - "MissingIndicator_33", - "MissingIndicator_34", - "MissingIndicator_35", - "MissingIndicator_36", - "MissingIndicator_37", - "MissingIndicator_38", - "MissingIndicator_39", - "MissingIndicator_40", - "MissingIndicator_41", - "MissingIndicator_42", - "MissingIndicator_43", - "MissingIndicator_44", - "MissingIndicator_45", - "MissingIndicator_46", - "MissingIndicator_47", - "MissingIndicator_48", - "label", - ] - else: - boolean_columns = [ - "MissingIndicator_1", - "MissingIndicator_2", - "MissingIndicator_3", - "MissingIndicator_4", - "MissingIndicator_5", - "MissingIndicator_6", - "MissingIndicator_7", - "MissingIndicator_8", - "MissingIndicator_9", - "MissingIndicator_10", - "MissingIndicator_11", - "MissingIndicator_12", - "MissingIndicator_13", - "MissingIndicator_14", - "MissingIndicator_15", - "MissingIndicator_16", - "MissingIndicator_17", - "MissingIndicator_18", - "MissingIndicator_19", - "MissingIndicator_20", - "MissingIndicator_21", - "MissingIndicator_22", - "MissingIndicator_23", - "MissingIndicator_24", - "MissingIndicator_25", - "MissingIndicator_26", - "MissingIndicator_27", - "MissingIndicator_28", - "MissingIndicator_29", - "MissingIndicator_30", - "MissingIndicator_31", - "MissingIndicator_32", - "MissingIndicator_33", - "MissingIndicator_34", - "MissingIndicator_35", - "MissingIndicator_36", - "MissingIndicator_37", - "MissingIndicator_38", - "MissingIndicator_39", - "MissingIndicator_40", - "MissingIndicator_41", - "MissingIndicator_42", - "MissingIndicator_43", - "MissingIndicator_44", - "MissingIndicator_45", - "MissingIndicator_46", - "MissingIndicator_47", - "MissingIndicator_48", - ] - - self.data[boolean_columns] = self.data[boolean_columns].astype( - float - ) + if (len(lagged_variables) > 0): + if self.data["label"].dtype == "bool": + self.data["label"] = self.data["label"].astype( + float + ) + columns_to_lag = lagged_variables + grouped = self.data.sort_values("time_idx").groupby("stay_id") + for lag in range(max_encoder_length, max_encoder_length+max_prediction_length + 1): + for column in columns_to_lag: + # Create a new column with lagged values + self.data[f"{column}_lag_{lag}"] = grouped[column].shift(lag, fill_value=0) self.split = split self.args = args diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index b66fe3c4..12a7213a 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -516,7 +516,6 @@ def forward( } out = self.model(x_dict) pred = self.logit(out["prediction"]) - return pred """ @@ -723,7 +722,7 @@ def __init__( optimizer=optimizer, ) - self.logit = nn.Linear(batch_size * 24, num_classes) + self.logit = nn.Linear(1, num_classes) def forward( self, @@ -743,10 +742,11 @@ def forward( "target_scale": tuple_x[10], } out = self.model(x_dict) - out = out["prediction"][-1].reshape(1, -1) - pred = self.logit(out) - print(pred) + # out = out["prediction"].reshape(1, -1) + + pred = self.logit(out["prediction"]) + return pred @@ -782,7 +782,7 @@ def __init__( optimizer=optimizer, ) - self.logit = nn.Linear(batch_size * 24*4, num_classes) + self.logit = nn.Linear(4, num_classes) def forward( self, @@ -802,9 +802,7 @@ def forward( "target_scale": tuple_x[10], } out = self.model(x_dict) - print(out["prediction"]) - out = out["prediction"][-1].reshape(1, -1) + out = out["prediction"][0] pred = self.logit(out) - print(pred) return pred diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index f78792a2..7a72c4ca 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -391,13 +391,19 @@ def step_fn(self, element, step_prefix=""): else: aux_loss = 0 # Get prediction and target + prediction = ( torch.masked_select(out, mask.unsqueeze(-1)) .reshape(-1, out.shape[-1]) .to(self.device) ) + print('output', out) + print('output', out.shape) + print('pred', prediction) + print('pred shape', prediction.shape) target = torch.masked_select(labels, mask).to(self.device) - + print('target', target) + print('target shape', target.shape) if prediction.shape[-1] > 1 and self.run_mode == RunMode.classification: # Classification task loss = ( From 0ed2fbc6cd78ce1cbdd855919f722435b395567a Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Wed, 25 Oct 2023 13:40:16 +0200 Subject: [PATCH 086/142] added XAI metric flag --- icu_benchmarks/cross_validation.py | 2 + icu_benchmarks/models/train.py | 3 + icu_benchmarks/run.py | 2 + icu_benchmarks/run_utils.py | 128 ++++++++++++++++++++++------- 4 files changed, 105 insertions(+), 30 deletions(-) diff --git a/icu_benchmarks/cross_validation.py b/icu_benchmarks/cross_validation.py index bcf2f5f5..8c0c02b1 100644 --- a/icu_benchmarks/cross_validation.py +++ b/icu_benchmarks/cross_validation.py @@ -40,6 +40,7 @@ def execute_repeated_cv( complete_train: bool = False, explain: bool = False, pytorch_forecasting: bool = False, + XAI_metric: bool = False, ) -> float: """Preprocesses data and trains a model for each fold. @@ -127,6 +128,7 @@ def execute_repeated_cv( train_only=complete_train, explain=explain, pytorch_forecasting=pytorch_forecasting, + XAI_metric=XAI_metric, ) train_time = datetime.now() - start_time diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index cabdb8ba..806b0c20 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -71,6 +71,7 @@ def train_common( ), explain: bool = False, pytorch_forecasting: bool = False, + XAI_metric: bool = False, ): """Common wrapper to train all benchmarked models. @@ -276,6 +277,8 @@ def train_common( print("IG", explanintations) interperations = model.interpertations(test_loader, log_dir) print("attention", interperations) + if XAI_metric: + model.set_weight("balanced", train_dataset) test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0][ diff --git a/icu_benchmarks/run.py b/icu_benchmarks/run.py index 7302aec8..4d9fb2f4 100644 --- a/icu_benchmarks/run.py +++ b/icu_benchmarks/run.py @@ -54,6 +54,7 @@ def main(my_args=tuple(sys.argv[1:])): source_dir = args.source_dir explain = args.explain pytorch_forecasting = args.pytorch_forecasting + XAI_metric = args.XAI_metric # Load task config gin.parse_config_file(f"configs/tasks/{task}.gin") mode = get_mode() @@ -209,6 +210,7 @@ def main(my_args=tuple(sys.argv[1:])): complete_train=args.complete_train, explain=explain, pytorch_forecasting=pytorch_forecasting, + XAI_metric=XAI_metric, ) log_full_line("FINISHED TRAINING", level=logging.INFO, char="=", num_newlines=3) diff --git a/icu_benchmarks/run_utils.py b/icu_benchmarks/run_utils.py index b23aae68..cc4b7960 100644 --- a/icu_benchmarks/run_utils.py +++ b/icu_benchmarks/run_utils.py @@ -23,7 +23,9 @@ def build_parser() -> ArgumentParser: Returns: The configured ArgumentParser. """ - parser = ArgumentParser(description="Framework for benchmarking ML/DL models on ICU data") + parser = ArgumentParser( + description="Framework for benchmarking ML/DL models on ICU data" + ) parser.add_argument( "-d", @@ -40,8 +42,12 @@ def build_parser() -> ArgumentParser: help="Name of the task gin.", ) parser.add_argument("-n", "--name", help="Name of the (target) dataset.") - parser.add_argument("-tn", "--task-name", help="Name of the task, used for naming experiments.") - parser.add_argument("-m", "--model", default="LGBMClassifier", help="Name of the model gin.") + parser.add_argument( + "-tn", "--task-name", help="Name of the task, used for naming experiments." + ) + parser.add_argument( + "-m", "--model", default="LGBMClassifier", help="Name of the model gin." + ) parser.add_argument("-e", "--experiment", help="Name of the experiment gin.") parser.add_argument( "-l", @@ -65,8 +71,12 @@ def build_parser() -> ArgumentParser: help="Set to log verbosly. Disable for clean logs.", ) parser.add_argument("--cpu", default=False, action=BOA, help="Set to use CPU.") - parser.add_argument("-db", "--debug", default=False, action=BOA, help="Set to load less data.") - parser.add_argument("--reproducible", default=True, action=BOA, help="Make torch reproducible.") + parser.add_argument( + "-db", "--debug", default=False, action=BOA, help="Set to load less data." + ) + parser.add_argument( + "--reproducible", default=True, action=BOA, help="Make torch reproducible." + ) parser.add_argument( "-lc", "--load_cache", @@ -81,7 +91,9 @@ def build_parser() -> ArgumentParser: action=BOA, help="Set to generate data cache.", ) - parser.add_argument("-p", "--preprocessor", type=Path, help="Load custom preprocessor from file.") + parser.add_argument( + "-p", "--preprocessor", type=Path, help="Load custom preprocessor from file." + ) parser.add_argument("-pl", "--plot", action=BOA, help="Generate common plots.") parser.add_argument( "-wd", @@ -95,10 +107,18 @@ def build_parser() -> ArgumentParser: type=str, help="Path to pretrained imputation model.", ) - parser.add_argument("-hp", "--hyperparams", nargs="+", help="Hyperparameters for model.") - parser.add_argument("--tune", default=False, action=BOA, help="Find best hyperparameters.") - parser.add_argument("--hp-checkpoint", type=Path, help="Use previous hyperparameter checkpoint.") - parser.add_argument("--eval", default=False, action=BOA, help="Only evaluate model, skip training.") + parser.add_argument( + "-hp", "--hyperparams", nargs="+", help="Hyperparameters for model." + ) + parser.add_argument( + "--tune", default=False, action=BOA, help="Find best hyperparameters." + ) + parser.add_argument( + "--hp-checkpoint", type=Path, help="Use previous hyperparameter checkpoint." + ) + parser.add_argument( + "--eval", default=False, action=BOA, help="Only evaluate model, skip training." + ) parser.add_argument( "--complete-train", default=False, @@ -112,8 +132,12 @@ def build_parser() -> ArgumentParser: type=int, help="Finetune model with amount of train data.", ) - parser.add_argument("-sn", "--source-name", type=Path, help="Name of the source dataset.") - parser.add_argument("--source-dir", type=Path, help="Directory containing gin and model weights.") + parser.add_argument( + "-sn", "--source-name", type=Path, help="Name of the source dataset." + ) + parser.add_argument( + "--source-dir", type=Path, help="Directory containing gin and model weights." + ) parser.add_argument( "-sa", "--samples", @@ -133,6 +157,13 @@ def build_parser() -> ArgumentParser: action=BOA, help="use pytorch forecasting library ", ) + parser.add_argument( + "--XAI_metric", + default=False, + action=BOA, + help="Compare explantations ", + ) + return parser @@ -161,15 +192,21 @@ def create_run_dir(log_dir: Path, randomly_searched_params: str = None) -> Path: def import_preprocessor(preprocessor_path: str): # Import custom supplied preprocessor - log_full_line(f"Importing custom preprocessor from {preprocessor_path}.", logging.INFO) + log_full_line( + f"Importing custom preprocessor from {preprocessor_path}.", logging.INFO + ) try: - spec = importlib.util.spec_from_file_location("CustomPreprocessor", preprocessor_path) + spec = importlib.util.spec_from_file_location( + "CustomPreprocessor", preprocessor_path + ) module = importlib.util.module_from_spec(spec) sys.modules["preprocessor"] = module spec.loader.exec_module(module) gin.bind_parameter("preprocess.preprocessor", module.CustomPreprocessor) except Exception as e: - logging.error(f"Could not import custom preprocessor from {preprocessor_path}: {e}") + logging.error( + f"Could not import custom preprocessor from {preprocessor_path}: {e}" + ) def aggregate_results(log_dir: Path, execution_time: timedelta = None): @@ -213,10 +250,14 @@ def aggregate_results(log_dir: Path, execution_time: timedelta = None): # Calculate the population standard deviation over aggregated results over folds/iterations # Divide by sqrt(n) to get standard deviation. - std_scores = {metric: (pstdev(list) / sqrt(len(list))) for metric, list in list_scores.items()} + std_scores = { + metric: (pstdev(list) / sqrt(len(list))) for metric, list in list_scores.items() + } confidence_interval = { - metric: (stats.t.interval(0.95, len(list) - 1, loc=mean(list), scale=stats.sem(list))) + metric: ( + stats.t.interval(0.95, len(list) - 1, loc=mean(list), scale=stats.sem(list)) + ) for metric, list in list_scores.items() } @@ -224,7 +265,9 @@ def aggregate_results(log_dir: Path, execution_time: timedelta = None): "avg": averaged_scores, "std": std_scores, "CI_0.95": confidence_interval, - "execution_time": execution_time.total_seconds() if execution_time is not None else 0.0, + "execution_time": execution_time.total_seconds() + if execution_time is not None + else 0.0, } with open(log_dir / "aggregated_test_metrics.json", "w") as f: @@ -240,10 +283,14 @@ def aggregate_results(log_dir: Path, execution_time: timedelta = None): def name_datasets(train="default", val="default", test="default"): """Names the datasets for logging (optional).""" - gin.bind_parameter("train_common.dataset_names", {"train": train, "val": val, "test": test}) + gin.bind_parameter( + "train_common.dataset_names", {"train": train, "val": val, "test": test} + ) -def log_full_line(msg: str, level: int = logging.INFO, char: str = "-", num_newlines: int = 0): +def log_full_line( + msg: str, level: int = logging.INFO, char: str = "-", num_newlines: int = 0 +): """Logs a full line of a given character with a message centered. Args: @@ -271,24 +318,43 @@ def load_pretrained_imputation_model(use_pretrained_imputation): Args: use_pretrained_imputation: Path to the pretrained imputation model. """ - if use_pretrained_imputation is not None and not Path(use_pretrained_imputation).exists(): + if ( + use_pretrained_imputation is not None + and not Path(use_pretrained_imputation).exists() + ): logging.warning("The specified pretrained imputation model does not exist.") use_pretrained_imputation = None if use_pretrained_imputation is not None: - logging.info("Using pretrained imputation from" + str(use_pretrained_imputation)) - pretrained_imputation_model_checkpoint = torch.load(use_pretrained_imputation, map_location=torch.device("cpu")) + logging.info( + "Using pretrained imputation from" + str(use_pretrained_imputation) + ) + pretrained_imputation_model_checkpoint = torch.load( + use_pretrained_imputation, map_location=torch.device("cpu") + ) if isinstance(pretrained_imputation_model_checkpoint, dict): imputation_model_class = pretrained_imputation_model_checkpoint["class"] - pretrained_imputation_model = imputation_model_class(**pretrained_imputation_model_checkpoint["hyper_parameters"]) - pretrained_imputation_model.set_trained_columns(pretrained_imputation_model_checkpoint["trained_columns"]) - pretrained_imputation_model.load_state_dict(pretrained_imputation_model_checkpoint["state_dict"]) + pretrained_imputation_model = imputation_model_class( + **pretrained_imputation_model_checkpoint["hyper_parameters"] + ) + pretrained_imputation_model.set_trained_columns( + pretrained_imputation_model_checkpoint["trained_columns"] + ) + pretrained_imputation_model.load_state_dict( + pretrained_imputation_model_checkpoint["state_dict"] + ) else: pretrained_imputation_model = pretrained_imputation_model_checkpoint - pretrained_imputation_model = pretrained_imputation_model.to("cuda" if torch.cuda.is_available() else "cpu") + pretrained_imputation_model = pretrained_imputation_model.to( + "cuda" if torch.cuda.is_available() else "cpu" + ) try: - logging.info(f"imputation model device: {next(pretrained_imputation_model.parameters()).device}") - pretrained_imputation_model.device = next(pretrained_imputation_model.parameters()).device + logging.info( + f"imputation model device: {next(pretrained_imputation_model.parameters()).device}" + ) + pretrained_imputation_model.device = next( + pretrained_imputation_model.parameters() + ).device except Exception as e: logging.debug(f"Could not set device of imputation model: {e}") else: @@ -309,7 +375,9 @@ def setup_logging(date_format, log_format, verbose): logging.basicConfig(format=log_format, datefmt=date_format) loggers = ["pytorch_lightning", "lightning_fabric"] for logger in loggers: - logging.getLogger(logger).handlers[0].setFormatter(logging.Formatter(log_format, datefmt=date_format)) + logging.getLogger(logger).handlers[0].setFormatter( + logging.Formatter(log_format, datefmt=date_format) + ) if not verbose: logging.getLogger().setLevel(logging.INFO) From d42bd7338d948dce40708dd5a5e05cf768bbe3d4 Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Wed, 25 Oct 2023 14:40:30 +0200 Subject: [PATCH 087/142] added XAI metric part --- icu_benchmarks/models/train.py | 68 +++++++++++++++++++++++++++++-- icu_benchmarks/models/wrappers.py | 8 +--- 2 files changed, 67 insertions(+), 9 deletions(-) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 806b0c20..1caaf9c6 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -6,6 +6,7 @@ import pandas as pd from joblib import load from torch.optim import Adam +import quantus from torch.utils.data import DataLoader from pytorch_lightning.loggers import TensorBoardLogger, WandbLogger from pytorch_lightning import Trainer @@ -270,15 +271,76 @@ def train_common( return 0 if explain: - explanintations = model.explantation_captum( - test_loader, log_dir, IntegratedGradients, target=(0, 1)) + test_loader, log_dir, IntegratedGradients, target=(0, 1) + ) print("IG", explanintations) interperations = model.interpertations(test_loader, log_dir) print("attention", interperations) - if XAI_metric: + if XAI_metric: + batch = iter(test_loader).next() + + for key, value in batch[0].items(): + batch[0][key] = batch[0][key].to(model.device) + + data = ( + batch[0]["encoder_cat"].float().requires_grad_(), + batch[0]["encoder_cont"].requires_grad_(), + batch[0]["encoder_target"].float().requires_grad_(), + batch[0]["encoder_lengths"].float().requires_grad_(), + batch[0]["decoder_cat"].float().requires_grad_(), + batch[0]["decoder_cont"].requires_grad_(), + batch[0]["decoder_target"].float().requires_grad_(), + batch[0]["decoder_lengths"].float().requires_grad_(), + batch[0]["decoder_time_idx"].float().requires_grad_(), + batch[0]["groups"].float().requires_grad_(), + batch[0]["target_scale"].requires_grad_(), + ) + baselines = ( + torch.zeros_like(data[0]).to(model.device), # encoder_cat, set to zero + torch.zeros_like(data[1]).to(model.device), # encoder_cont, set to zero + torch.zeros_like(data[2]).to(model.device), # encoder_target, set to zero + data[3].to(model.device), # encoder_lengths, leave unchanged + torch.zeros_like(data[4]).to(model.device), # decoder_cat, set to zero + torch.zeros_like(data[5]).to(model.device), # decoder_cont, set to zero + torch.zeros_like(data[6]).to(model.device), # decoder_target, set to zero + data[7].to(model.device), # decoder_lengths, leave unchanged + torch.zeros_like(data[8]).to(model.device), # decoder_time_idx, set to zero + data[9].to(model.device), # groups, leave unchanged + data[10].to(model.device), # target_scale, leave unchanged + ) + + explantation = IntegratedGradients(model.forward_captum) + # Reformat attributions. + attr, delta = explantation.attribute( + data, + target=(0, 1), + return_convergence_delta=True, + baselines=baselines, + n_steps=20, + ) + a_batch_IG = attr.detach().numpy() + + x_batch, y_batch = batch[0], batch[1][0] + for key, value in x_batch.items(): + x_batch[key] = x_batch[key].cpu().numpy() + y_batch = y_batch.cpu().numpy() + metric = quantus.FaithfulnessCorrelation() + scores = metric( + model=model, + x_batch=x_batch, + y_batch=y_batch, + a_batch=a_batch_IG, + device=model.device, + explain_func=quantus.explain, + explain_func_kwargs={ + "method": "IntegratedGradients", + "baselines": baselines, + "n_steps": 20, + }, + ) model.set_weight("balanced", train_dataset) test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0][ diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 7a72c4ca..ec467b75 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -397,13 +397,9 @@ def step_fn(self, element, step_prefix=""): .reshape(-1, out.shape[-1]) .to(self.device) ) - print('output', out) - print('output', out.shape) - print('pred', prediction) - print('pred shape', prediction.shape) + target = torch.masked_select(labels, mask).to(self.device) - print('target', target) - print('target shape', target.shape) + if prediction.shape[-1] > 1 and self.run_mode == RunMode.classification: # Classification task loss = ( From 46eea23667ace3a69138245e11f25f41386ea4c7 Mon Sep 17 00:00:00 2001 From: "Youssef.mecky" Date: Wed, 25 Oct 2023 15:09:19 +0200 Subject: [PATCH 088/142] changes for quantus --- icu_benchmarks/models/train.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 1caaf9c6..bf6ab4f9 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -272,15 +272,15 @@ def train_common( if explain: explanintations = model.explantation_captum( - test_loader, log_dir, IntegratedGradients, target=(0, 1) + test_loader, log_dir, IntegratedGradients, target=0 ) print("IG", explanintations) interperations = model.interpertations(test_loader, log_dir) print("attention", interperations) if XAI_metric: - batch = iter(test_loader).next() - + batch = next(iter(test_loader)) + model = model.cpu() for key, value in batch[0].items(): batch[0][key] = batch[0][key].to(model.device) @@ -316,7 +316,7 @@ def train_common( # Reformat attributions. attr, delta = explantation.attribute( data, - target=(0, 1), + target=0, return_convergence_delta=True, baselines=baselines, n_steps=20, From 16cc5c81b46623a94603fcdb1f8f5f2944203647 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Tue, 31 Oct 2023 14:34:17 +0100 Subject: [PATCH 089/142] added faithfulness correlation manuelly --- icu_benchmarks/models/dl_models.py | 108 +++++++++++++++++++++++++---- icu_benchmarks/models/train.py | 67 ++---------------- 2 files changed, 99 insertions(+), 76 deletions(-) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 12a7213a..6f321709 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -12,7 +12,7 @@ ) import matplotlib.pyplot as plt from icu_benchmarks.models.wrappers import DLPredictionWrapper -from torch import Tensor, FloatTensor, zeros_like +from torch import Tensor, FloatTensor, zeros_like, ones_like, randn_like from pytorch_forecasting import TemporalFusionTransformer, RecurrentNetwork, DeepAR from pytorch_forecasting.metrics import QuantileLoss import matplotlib.pyplot as plt @@ -603,19 +603,19 @@ def explantation_captum(self, test_loader, log_dir, method, target): for batch in test_loader: for key, value in batch[0].items(): batch[0][key] = batch[0][key].to(self.device) - + x = batch[0] data = ( - batch[0]["encoder_cat"].float().requires_grad_(), - batch[0]["encoder_cont"].requires_grad_(), - batch[0]["encoder_target"].float().requires_grad_(), - batch[0]["encoder_lengths"].float().requires_grad_(), - batch[0]["decoder_cat"].float().requires_grad_(), - batch[0]["decoder_cont"].requires_grad_(), - batch[0]["decoder_target"].float().requires_grad_(), - batch[0]["decoder_lengths"].float().requires_grad_(), - batch[0]["decoder_time_idx"].float().requires_grad_(), - batch[0]["groups"].float().requires_grad_(), - batch[0]["target_scale"].requires_grad_(), + x["encoder_cat"].float().requires_grad_(), + x["encoder_cont"].requires_grad_(), + x["encoder_target"].float().requires_grad_(), + x["encoder_lengths"].float().requires_grad_(), + x["decoder_cat"].float().requires_grad_(), + x["decoder_cont"].requires_grad_(), + x["decoder_target"].float().requires_grad_(), + x["decoder_lengths"].float().requires_grad_(), + x["decoder_time_idx"].float().requires_grad_(), + x["groups"].float().requires_grad_(), + x["target_scale"].requires_grad_(), ) baselines = ( @@ -642,7 +642,6 @@ def explantation_captum(self, test_loader, log_dir, method, target): # Concatenate a‚ttribution values for all instances along the batch dimension all_attrs = np.concatenate(all_attrs, axis=0) - print(all_attrs.shape) means_feature = all_attrs.mean(axis=(0, 1)) # Compute mean along the batch dimension @@ -689,6 +688,87 @@ def explantation_captum(self, test_loader, log_dir, method, target): plt.savefig(log_dir / "attribution_plot.png", bbox_inches="tight") return means + def faithfulness_correlation(self, test_loader, attribution, nr_runs=100, pertrub=None, subset_size=4): + """ + Implementation of faithfulness correlation by Bhatt et al., 2020. + + The Faithfulness Correlation metric intend to capture an explanation's relative faithfulness + (or 'fidelity') with respect to the model behaviour. + + Faithfulness correlation scores shows to what extent the predicted logits of each modified test point and + the average explanation attribution for only the subset of features are (linearly) correlated, taking the + average over multiple runs and test samples. The metric returns one float per input-attribution pair that + ranges between -1 and 1, where higher scores are better. + + For each test sample, |S| features are randomly selected and replace them with baseline values (zero baseline + or average of set). Thereafter, Pearson’s correlation coefficient between the predicted logits of each modified + test point and the average explanation attribution for only the subset of features is calculated. Results is + average over multiple runs and several test samples. + + References: + 1) Umang Bhatt et al.: "Evaluating and aggregating feature-based model + explanations." IJCAI (2020): 3016-3022. + """ + if pertrub == None: + pertrub = "baseline" + similarities = [] + for batch in test_loader: + + for key, value in batch[0].items(): + + batch[0][key] = batch[0][key].to(self.device) + x = batch[0] + data = ( + x["encoder_cat"], + x["encoder_cont"], + x["encoder_target"], + x["encoder_lengths"], + x["decoder_cat"], + x["decoder_cont"], + x["decoder_target"], + x["decoder_lengths"], + x["decoder_time_idx"], + x["groups"], + x["target_scale"], + ) + + y_pred = self(data).detach().cpu().numpy() + pred_deltas = [] + att_sums = [] + for i_ix in range(nr_runs): + # Randomly mask by subset size. + a_ix = np.random.choice(x["encoder_cont"].shape[1], subset_size, replace=False) + print(a_ix) + print(x["encoder_cont"][:, a_ix, :]) + + if pertrub == "Noise": + noise = randn_like(x["encoder_cont"]) + x["encoder_cont"][:, a_ix, :] += noise[:, a_ix, :] + elif pertrub == "baseline": + # Create a mask tensor with zeros at specified time steps and ones everywhere else + mask = ones_like(x["encoder_cont"]) + print("Shape of mask:", mask.shape, mask.device) + print("Shape of a_ix:", a_ix.shape, a_ix.device) + + mask[:, a_ix, :] = 0 + x["encoder_cont"] = x["encoder_cont"] * mask + + # Predict on perturbed input x. + print(x["encoder_cont"][:, a_ix, :]) + y_pred_perturb = self(data).detach().cpu().numpy() + pred_deltas.append((y_pred - y_pred_perturb).mean(axis=1)) + + # Sum attributions of the random subset. + att_sums.append(np.sum(attribution[a_ix])) + print(np.asarray(pred_deltas).shape) + print(np.asarray(att_sums).shape) + correlation_matrix = np.corrcoef(pred_deltas, att_sums, rowvar=False) + + # Get the correlation coefficient from the correlation matrix + pearson_correlation = correlation_matrix[0, 1] + similarities.append(pearson_correlation) + return similarities.mean() + @gin.configurable class RNNpytorch(DLPredictionWrapper): diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index bf6ab4f9..d8858250 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -272,75 +272,18 @@ def train_common( if explain: explanintations = model.explantation_captum( - test_loader, log_dir, IntegratedGradients, target=0 + test_loader, log_dir, IntegratedGradients, target=(0, 1) ) print("IG", explanintations) interperations = model.interpertations(test_loader, log_dir) print("attention", interperations) if XAI_metric: - batch = next(iter(test_loader)) - model = model.cpu() - for key, value in batch[0].items(): - batch[0][key] = batch[0][key].to(model.device) - - data = ( - batch[0]["encoder_cat"].float().requires_grad_(), - batch[0]["encoder_cont"].requires_grad_(), - batch[0]["encoder_target"].float().requires_grad_(), - batch[0]["encoder_lengths"].float().requires_grad_(), - batch[0]["decoder_cat"].float().requires_grad_(), - batch[0]["decoder_cont"].requires_grad_(), - batch[0]["decoder_target"].float().requires_grad_(), - batch[0]["decoder_lengths"].float().requires_grad_(), - batch[0]["decoder_time_idx"].float().requires_grad_(), - batch[0]["groups"].float().requires_grad_(), - batch[0]["target_scale"].requires_grad_(), - ) - - baselines = ( - torch.zeros_like(data[0]).to(model.device), # encoder_cat, set to zero - torch.zeros_like(data[1]).to(model.device), # encoder_cont, set to zero - torch.zeros_like(data[2]).to(model.device), # encoder_target, set to zero - data[3].to(model.device), # encoder_lengths, leave unchanged - torch.zeros_like(data[4]).to(model.device), # decoder_cat, set to zero - torch.zeros_like(data[5]).to(model.device), # decoder_cont, set to zero - torch.zeros_like(data[6]).to(model.device), # decoder_target, set to zero - data[7].to(model.device), # decoder_lengths, leave unchanged - torch.zeros_like(data[8]).to(model.device), # decoder_time_idx, set to zero - data[9].to(model.device), # groups, leave unchanged - data[10].to(model.device), # target_scale, leave unchanged - ) - - explantation = IntegratedGradients(model.forward_captum) - # Reformat attributions. - attr, delta = explantation.attribute( - data, - target=0, - return_convergence_delta=True, - baselines=baselines, - n_steps=20, - ) - a_batch_IG = attr.detach().numpy() - - x_batch, y_batch = batch[0], batch[1][0] - for key, value in x_batch.items(): - x_batch[key] = x_batch[key].cpu().numpy() - y_batch = y_batch.cpu().numpy() - metric = quantus.FaithfulnessCorrelation() - scores = metric( - model=model, - x_batch=x_batch, - y_batch=y_batch, - a_batch=a_batch_IG, - device=model.device, - explain_func=quantus.explain, - explain_func_kwargs={ - "method": "IntegratedGradients", - "baselines": baselines, - "n_steps": 20, - }, + explanintations = model.explantation_captum( + test_loader, log_dir, IntegratedGradients, target=(0, 1) ) + scores = model.faithfulness_correlation(test_loader, explanintations) + print(scores) model.set_weight("balanced", train_dataset) test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0][ From 85216b77ce7c1250fdbd82cba9487b1f2db62df8 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Tue, 31 Oct 2023 14:54:46 +0100 Subject: [PATCH 090/142] fixed a few bugs to get the faithfulness correlation to work --- icu_benchmarks/models/dl_models.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 6f321709..1db5a8a1 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -12,7 +12,7 @@ ) import matplotlib.pyplot as plt from icu_benchmarks.models.wrappers import DLPredictionWrapper -from torch import Tensor, FloatTensor, zeros_like, ones_like, randn_like +from torch import Tensor, FloatTensor, zeros_like, ones_like, randn_like, from_numpy from pytorch_forecasting import TemporalFusionTransformer, RecurrentNetwork, DeepAR from pytorch_forecasting.metrics import QuantileLoss import matplotlib.pyplot as plt @@ -704,10 +704,12 @@ def faithfulness_correlation(self, test_loader, attribution, nr_runs=100, pertru or average of set). Thereafter, Pearson’s correlation coefficient between the predicted logits of each modified test point and the average explanation attribution for only the subset of features is calculated. Results is average over multiple runs and several test samples. + This code is adapted from the quantus libray to suit our use case References: 1) Umang Bhatt et al.: "Evaluating and aggregating feature-based model explanations." IJCAI (2020): 3016-3022. + 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. """ if pertrub == None: pertrub = "baseline" @@ -738,25 +740,27 @@ def faithfulness_correlation(self, test_loader, attribution, nr_runs=100, pertru for i_ix in range(nr_runs): # Randomly mask by subset size. a_ix = np.random.choice(x["encoder_cont"].shape[1], subset_size, replace=False) - print(a_ix) - print(x["encoder_cont"][:, a_ix, :]) + + # Move a_ix_tensor to the same device as mask if pertrub == "Noise": + # add normal noise to input noise = randn_like(x["encoder_cont"]) + x["encoder_cont"][:, a_ix, :] += noise[:, a_ix, :] elif pertrub == "baseline": # Create a mask tensor with zeros at specified time steps and ones everywhere else - mask = ones_like(x["encoder_cont"]) - print("Shape of mask:", mask.shape, mask.device) - print("Shape of a_ix:", a_ix.shape, a_ix.device) + # pytorch bug need to change to cpu for next step and then revert + mask = ones_like(x["encoder_cont"]).cpu() mask[:, a_ix, :] = 0 + mask = mask.to(x["encoder_cont"].device) + x["encoder_cont"] = x["encoder_cont"] * mask # Predict on perturbed input x. - print(x["encoder_cont"][:, a_ix, :]) y_pred_perturb = self(data).detach().cpu().numpy() - pred_deltas.append((y_pred - y_pred_perturb).mean(axis=1)) + pred_deltas.append((y_pred - y_pred_perturb).mean(axis=(0, 2))) # Sum attributions of the random subset. att_sums.append(np.sum(attribution[a_ix])) @@ -767,7 +771,7 @@ def faithfulness_correlation(self, test_loader, attribution, nr_runs=100, pertru # Get the correlation coefficient from the correlation matrix pearson_correlation = correlation_matrix[0, 1] similarities.append(pearson_correlation) - return similarities.mean() + return np.mean(similarities) @gin.configurable From 119dbcf20a92e525732e26aa2f7be1faf89cc99f Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Tue, 31 Oct 2023 15:49:03 +0100 Subject: [PATCH 091/142] added pytorch forecasting wrapper to make it easier --- icu_benchmarks/models/dl_models.py | 98 ++----------- icu_benchmarks/models/train.py | 19 ++- icu_benchmarks/models/wrappers.py | 214 +++++++++++++++++++++++++++++ 3 files changed, 231 insertions(+), 100 deletions(-) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 1db5a8a1..17dd9260 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -11,8 +11,8 @@ PositionalEncoding, ) import matplotlib.pyplot as plt -from icu_benchmarks.models.wrappers import DLPredictionWrapper -from torch import Tensor, FloatTensor, zeros_like, ones_like, randn_like, from_numpy +from icu_benchmarks.models.wrappers import DLPredictionWrapper, DLPredictionPytorchForecastingWrapper +from torch import Tensor, FloatTensor, zeros_like, ones_like, randn_like, is_tensor from pytorch_forecasting import TemporalFusionTransformer, RecurrentNetwork, DeepAR from pytorch_forecasting.metrics import QuantileLoss import matplotlib.pyplot as plt @@ -464,7 +464,7 @@ def forward(self, x: Dict[str, Tensor]) -> Tensor: @gin.configurable -class TFTpytorch(DLPredictionWrapper): +class TFTpytorch(DLPredictionPytorchForecastingWrapper): """ Implementation of https://arxiv.org/abs/1912.09363 from pytorch forecasting """ @@ -534,6 +534,7 @@ def actual_vs_predictions_plot(self, dataloader): return predictions_vs_actuals def interpertations(self, dataloader, log_dir): + raw_predictions = self.model.predict(dataloader, return_x=True, mode="raw") interpretation = self.model.interpret_output( raw_predictions.output, reduction="sum" @@ -542,6 +543,8 @@ def interpertations(self, dataloader, log_dir): for key, fig in figs.items(): fig.savefig(log_dir / f"interpretation_{key}.png", bbox_inches="tight") + self.model = self.model.to(self.device) + return interpretation def predict_dependency(self, dataloader, variable, log_dir): @@ -688,94 +691,9 @@ def explantation_captum(self, test_loader, log_dir, method, target): plt.savefig(log_dir / "attribution_plot.png", bbox_inches="tight") return means - def faithfulness_correlation(self, test_loader, attribution, nr_runs=100, pertrub=None, subset_size=4): - """ - Implementation of faithfulness correlation by Bhatt et al., 2020. - - The Faithfulness Correlation metric intend to capture an explanation's relative faithfulness - (or 'fidelity') with respect to the model behaviour. - - Faithfulness correlation scores shows to what extent the predicted logits of each modified test point and - the average explanation attribution for only the subset of features are (linearly) correlated, taking the - average over multiple runs and test samples. The metric returns one float per input-attribution pair that - ranges between -1 and 1, where higher scores are better. - - For each test sample, |S| features are randomly selected and replace them with baseline values (zero baseline - or average of set). Thereafter, Pearson’s correlation coefficient between the predicted logits of each modified - test point and the average explanation attribution for only the subset of features is calculated. Results is - average over multiple runs and several test samples. - This code is adapted from the quantus libray to suit our use case - - References: - 1) Umang Bhatt et al.: "Evaluating and aggregating feature-based model - explanations." IJCAI (2020): 3016-3022. - 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. - """ - if pertrub == None: - pertrub = "baseline" - similarities = [] - for batch in test_loader: - - for key, value in batch[0].items(): - - batch[0][key] = batch[0][key].to(self.device) - x = batch[0] - data = ( - x["encoder_cat"], - x["encoder_cont"], - x["encoder_target"], - x["encoder_lengths"], - x["decoder_cat"], - x["decoder_cont"], - x["decoder_target"], - x["decoder_lengths"], - x["decoder_time_idx"], - x["groups"], - x["target_scale"], - ) - - y_pred = self(data).detach().cpu().numpy() - pred_deltas = [] - att_sums = [] - for i_ix in range(nr_runs): - # Randomly mask by subset size. - a_ix = np.random.choice(x["encoder_cont"].shape[1], subset_size, replace=False) - - # Move a_ix_tensor to the same device as mask - - if pertrub == "Noise": - # add normal noise to input - noise = randn_like(x["encoder_cont"]) - - x["encoder_cont"][:, a_ix, :] += noise[:, a_ix, :] - elif pertrub == "baseline": - # Create a mask tensor with zeros at specified time steps and ones everywhere else - # pytorch bug need to change to cpu for next step and then revert - mask = ones_like(x["encoder_cont"]).cpu() - - mask[:, a_ix, :] = 0 - mask = mask.to(x["encoder_cont"].device) - - x["encoder_cont"] = x["encoder_cont"] * mask - - # Predict on perturbed input x. - y_pred_perturb = self(data).detach().cpu().numpy() - pred_deltas.append((y_pred - y_pred_perturb).mean(axis=(0, 2))) - - # Sum attributions of the random subset. - att_sums.append(np.sum(attribution[a_ix])) - print(np.asarray(pred_deltas).shape) - print(np.asarray(att_sums).shape) - correlation_matrix = np.corrcoef(pred_deltas, att_sums, rowvar=False) - - # Get the correlation coefficient from the correlation matrix - pearson_correlation = correlation_matrix[0, 1] - similarities.append(pearson_correlation) - return np.mean(similarities) - @gin.configurable -class RNNpytorch(DLPredictionWrapper): +class RNNpytorch(DLPredictionPytorchForecastingWrapper): """ Implementation of RNN from pytorch forecasting """ @@ -835,7 +753,7 @@ def forward( @gin.configurable -class DeepARpytorch(DLPredictionWrapper): +class DeepARpytorch(DLPredictionPytorchForecastingWrapper): """ Implementation of RNN from pytorch forecasting """ diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index d8858250..e9f563da 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -271,19 +271,18 @@ def train_common( return 0 if explain: - explanintations = model.explantation_captum( + attributions_IG = model.explantation_captum( test_loader, log_dir, IntegratedGradients, target=(0, 1) ) - print("IG", explanintations) - interperations = model.interpertations(test_loader, log_dir) - print("attention", interperations) - if XAI_metric: - explanintations = model.explantation_captum( - test_loader, log_dir, IntegratedGradients, target=(0, 1) - ) - scores = model.faithfulness_correlation(test_loader, explanintations) - print(scores) + # print("IG", attributions_IG) + # Attention_weights = model.interpertations(test_loader, log_dir) + # print("attention", Attention_weights) + if XAI_metric: + scores = model.faithfulness_correlation(test_loader, attributions_IG) + print('Attributions faithfulness correlation', scores) + # scores = model.faithfulness_correlation(test_loader, Attention_weights["attention"]) + print('Attention faithfulness correlation', scores) model.set_weight("balanced", train_dataset) test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0][ diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index ec467b75..892477d5 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -438,6 +438,220 @@ def step_fn(self, element, step_prefix=""): return loss +@gin.configurable("DLPredictionPytorchForecastingWrapper") +class DLPredictionPytorchForecastingWrapper(DLPredictionWrapper): + """Interface for Deep Learning models.""" + + _supported_run_modes = [RunMode.classification, RunMode.regression] + + def __init__( + self, + loss=CrossEntropyLoss(), + optimizer=torch.optim.Adam, + run_mode: RunMode = RunMode.classification, + input_shape=None, + lr: float = 0.002, + momentum: float = 0.9, + lr_scheduler: Optional[str] = None, + lr_factor: float = 0.99, + lr_steps: Optional[List[int]] = None, + epochs: int = 100, + input_size: Tensor = None, + initialization_method: str = "normal", + pytorch_forecasting: bool = False, + **kwargs, + ): + super().__init__( + loss=loss, + optimizer=optimizer, + run_mode=run_mode, + input_shape=input_shape, + lr=lr, + momentum=momentum, + lr_scheduler=lr_scheduler, + lr_factor=lr_factor, + lr_steps=lr_steps, + epochs=epochs, + input_size=input_size, + initialization_method=initialization_method, + kwargs=kwargs, + ) + + def step_fn(self, element, step_prefix=""): + """Perform a step in the DL prediction model training loop. + + Args: + element (object): + step_prefix (str): Step type, by default: test, train, val. + """ + + dic, labels = element[0], element[1][0] + + if isinstance(labels, list): + labels = labels[-1] + data = ( + dic["encoder_cat"], + dic["encoder_cont"], + dic["encoder_target"], + dic["encoder_lengths"], + dic["decoder_cat"], + dic["decoder_cont"], + dic["decoder_target"], + dic["decoder_lengths"], + dic["decoder_time_idx"], + dic["groups"], + dic["target_scale"], + ) + + mask = torch.ones_like(labels).bool() + + out = self(data) + + # If aux_loss is present, it is returned as a tuple + if len(out) == 2 and isinstance(out, tuple): + out, aux_loss = out + else: + aux_loss = 0 + # Get prediction and target + + prediction = ( + torch.masked_select(out, mask.unsqueeze(-1)) + .reshape(-1, out.shape[-1]) + .to(self.device) + ) + + target = torch.masked_select(labels, mask).to(self.device) + + if prediction.shape[-1] > 1 and self.run_mode == RunMode.classification: + # Classification task + loss = ( + self.loss( + prediction, target.long(), weight=self.loss_weights.to(self.device) + ) + + aux_loss + ) + # Returns torch.long because negative log likelihood loss + elif self.run_mode == RunMode.regression: + # Regression task + loss = self.loss(prediction[:, 0], target.float()) + aux_loss + else: + raise ValueError( + f"Run mode {self.run_mode} not yet supported. Please implement it." + ) + transformed_output = self.output_transform((prediction, target)) + + for key, value in self.metrics[step_prefix].items(): + if isinstance(value, torchmetrics.Metric): + if key == "Binary_Fairness": + feature_names = key.feature_helper(self.trainer) + value.update( + transformed_output[0], + transformed_output[1], + data, + feature_names, + ) + else: + value.update(transformed_output[0], transformed_output[1]) + else: + value.update(transformed_output) + self.log( + f"{step_prefix}/loss", loss, on_step=False, on_epoch=True, sync_dist=True + ) + return loss + + def faithfulness_correlation(self, test_loader, attribution, nr_runs=100, pertrub=None, subset_size=4): + """ + Implementation of faithfulness correlation by Bhatt et al., 2020. + + The Faithfulness Correlation metric intend to capture an explanation's relative faithfulness + (or 'fidelity') with respect to the model behaviour. + + Faithfulness correlation scores shows to what extent the predicted logits of each modified test point and + the average explanation attribution for only the subset of features are (linearly) correlated, taking the + average over multiple runs and test samples. The metric returns one float per input-attribution pair that + ranges between -1 and 1, where higher scores are better. + + For each test sample, |S| features are randomly selected and replace them with baseline values (zero baseline + or average of set). Thereafter, Pearson’s correlation coefficient between the predicted logits of each modified + test point and the average explanation attribution for only the subset of features is calculated. Results is + average over multiple runs and several test samples. + This code is adapted from the quantus libray to suit our use case + + References: + 1) Umang Bhatt et al.: "Evaluating and aggregating feature-based model + explanations." IJCAI (2020): 3016-3022. + 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. + """ + + if torch.is_tensor(attribution): + # Convert the tensor to a NumPy array + example_numpy_array = attribution.cpu().detach().numpy() + if pertrub == None: + pertrub = "baseline" + similarities = [] + for batch in test_loader: + + for key, value in batch[0].items(): + + batch[0][key] = batch[0][key].to(self.device) + x = batch[0] + data = ( + x["encoder_cat"], + x["encoder_cont"], + x["encoder_target"], + x["encoder_lengths"], + x["decoder_cat"], + x["decoder_cont"], + x["decoder_target"], + x["decoder_lengths"], + x["decoder_time_idx"], + x["groups"], + x["target_scale"], + ) + + y_pred = self(data).detach().cpu().numpy() + pred_deltas = [] + att_sums = [] + for i_ix in range(nr_runs): + # Randomly mask by subset size. + a_ix = np.random.choice(x["encoder_cont"].shape[1], subset_size, replace=False) + + # Move a_ix_tensor to the same device as mask + + if pertrub == "Noise": + # add normal noise to input + noise = torch.randn_like(x["encoder_cont"]) + + x["encoder_cont"][:, a_ix, :] += noise[:, a_ix, :] + elif pertrub == "baseline": + # Create a mask tensor with zeros at specified time steps and ones everywhere else + # pytorch bug need to change to cpu for next step and then revert + mask = torch.ones_like(x["encoder_cont"]).cpu() + + mask[:, a_ix, :] = 0 + mask = mask.to(x["encoder_cont"].device) + + x["encoder_cont"] = x["encoder_cont"] * mask + + # Predict on perturbed input x. + y_pred_perturb = self(data).detach().cpu().numpy() + print(y_pred - y_pred_perturb) + break + pred_deltas.append((y_pred - y_pred_perturb).mean(axis=(0, 2))) + + # Sum attributions of the random subset. + + att_sums.append(np.sum(attribution[a_ix])) + print(pred_deltas, att_sums) + correlation_matrix = np.corrcoef(pred_deltas, att_sums, rowvar=False) + + # Get the correlation coefficient from the correlation matrix + pearson_correlation = correlation_matrix[0, 1] + similarities.append(pearson_correlation) + print(similarities) + return np.nanmean(similarities) + + @gin.configurable("MLWrapper") class MLWrapper(BaseModule, ABC): """Interface for prediction with traditional Scikit-learn-like Machine Learning models.""" From d03deedc2e2d58c6844ccdf19e88345e1c3d4760 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Tue, 31 Oct 2023 15:54:16 +0100 Subject: [PATCH 092/142] added pytorch forecasting wrapper to make it easier --- configs/tasks/BinaryClassification.gin | 2 +- configs/tasks/Regression.gin | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/configs/tasks/BinaryClassification.gin b/configs/tasks/BinaryClassification.gin index 492a12eb..f3a7790d 100644 --- a/configs/tasks/BinaryClassification.gin +++ b/configs/tasks/BinaryClassification.gin @@ -16,7 +16,7 @@ train_common.ram_cache = True # DEEP LEARNING DLPredictionWrapper.loss = @cross_entropy - +DLPredictionPytorchForecastingWrapper.loss= @cross_entropy # SELECTING PREPROCESSOR preprocess.preprocessor = @base_classification_preprocessor preprocess.vars = %vars diff --git a/configs/tasks/Regression.gin b/configs/tasks/Regression.gin index 5cf3f8d9..6b6ac607 100644 --- a/configs/tasks/Regression.gin +++ b/configs/tasks/Regression.gin @@ -16,6 +16,7 @@ train_common.ram_cache = True # LOSS FUNCTION DLPredictionWrapper.loss = @mse_loss +DLPredictionPytorchForecastingWrapper.loss = @mse_loss MLWrapper.loss = @mean_squared_error # SELECTING PREPROCESSOR From 3c78359a3d3a059d44827f371dbb411e55640afe Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Tue, 31 Oct 2023 16:29:24 +0100 Subject: [PATCH 093/142] added data randomization test and fixed few issues with wrapper --- icu_benchmarks/models/dl_models.py | 93 --------------- icu_benchmarks/models/train.py | 4 +- icu_benchmarks/models/wrappers.py | 179 ++++++++++++++++++++++++++++- 3 files changed, 175 insertions(+), 101 deletions(-) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 17dd9260..08a1e75f 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -598,99 +598,6 @@ def forward_captum( return self.forward(tuple_x) - def explantation_captum(self, test_loader, log_dir, method, target): - # Initialize lists to store attribution values for all instances - all_attrs = [] - - # Loop through the test_loader to compute attributions for all instances - for batch in test_loader: - for key, value in batch[0].items(): - batch[0][key] = batch[0][key].to(self.device) - x = batch[0] - data = ( - x["encoder_cat"].float().requires_grad_(), - x["encoder_cont"].requires_grad_(), - x["encoder_target"].float().requires_grad_(), - x["encoder_lengths"].float().requires_grad_(), - x["decoder_cat"].float().requires_grad_(), - x["decoder_cont"].requires_grad_(), - x["decoder_target"].float().requires_grad_(), - x["decoder_lengths"].float().requires_grad_(), - x["decoder_time_idx"].float().requires_grad_(), - x["groups"].float().requires_grad_(), - x["target_scale"].requires_grad_(), - ) - - baselines = ( - zeros_like(data[0]).to(self.device), # encoder_cat, set to zero - zeros_like(data[1]).to(self.device), # encoder_cont, set to zero - zeros_like(data[2]).to(self.device), # encoder_target, set to zero - data[3].to(self.device), # encoder_lengths, leave unchanged - zeros_like(data[4]).to(self.device), # decoder_cat, set to zero - zeros_like(data[5]).to(self.device), # decoder_cont, set to zero - zeros_like(data[6]).to(self.device), # decoder_target, set to zero - data[7].to(self.device), # decoder_lengths, leave unchanged - zeros_like(data[8]).to(self.device), # decoder_time_idx, set to zero - data[9].to(self.device), # groups, leave unchanged - data[10].to(self.device), # target_scale, leave unchanged - ) - - explantation = method(self.forward_captum) - # Reformat attributions. - attr, delta = explantation.attribute( - data, target=target, return_convergence_delta=True, baselines=baselines, n_steps=20 - ) - # Convert attributions to numpy array and append to the list - all_attrs.append(attr[0].cpu().detach().numpy()) - - # Concatenate a‚ttribution values for all instances along the batch dimension - all_attrs = np.concatenate(all_attrs, axis=0) - means_feature = all_attrs.mean(axis=(0, 1)) - - # Compute mean along the batch dimension - means = all_attrs.mean(axis=(0, 2)) - # Normalize the means values to range [0, 1] - normalized_means = (means - means.min()) / (means.max() - means.min()) - - # Create x values (assuming you want a simple sequential x-axis) - # Assuming you have 24 values - x_values = np.arange(1, 57) - # Plotting the featrue means - plt.figure(figsize=(8, 6)) - plt.plot( - x_values, - means_feature, - marker="o", - color="skyblue", - linestyle="-", - linewidth=2, - markersize=8, - ) - plt.xlabel("Time Step") - plt.ylabel("Normalized Attribution") - plt.title("Attribution Values") - plt.xticks(x_values) # Set x-ticks to match the number of features - plt.tight_layout() - plt.savefig(log_dir / "attribution_features_plot.png", bbox_inches="tight") - plt.figure(figsize=(8, 6)) - x_values = np.arange(1, 25) - plt.plot( - x_values, - normalized_means, - marker="o", - color="skyblue", - linestyle="-", - linewidth=2, - markersize=8, - ) - plt.xlabel("Time Step") - plt.ylabel("Normalized Attribution") - plt.title("Attribution Values") - plt.xticks(x_values) # Set x-ticks to match the number of features - plt.tight_layout() - plt.savefig(log_dir / "attribution_plot.png", bbox_inches="tight") - return means - @gin.configurable class RNNpytorch(DLPredictionPytorchForecastingWrapper): diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index e9f563da..eadb252f 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -272,14 +272,14 @@ def train_common( if explain: attributions_IG = model.explantation_captum( - test_loader, log_dir, IntegratedGradients, target=(0, 1) + test_loader, log_dir, IntegratedGradients ) # print("IG", attributions_IG) # Attention_weights = model.interpertations(test_loader, log_dir) # print("attention", Attention_weights) if XAI_metric: - scores = model.faithfulness_correlation(test_loader, attributions_IG) + scores = model.Faithfulness_Correlation(test_loader, attributions_IG) print('Attributions faithfulness correlation', scores) # scores = model.faithfulness_correlation(test_loader, Attention_weights["attention"]) print('Attention faithfulness correlation', scores) diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 892477d5..c22d9bcb 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -18,7 +18,7 @@ from pytorch_lightning import LightningModule from icu_benchmarks.models.constants import MLMetrics, DLMetrics from icu_benchmarks.contants import RunMode - +import matplotlib.pyplot as plt gin.config.external_configurable(nn.functional.nll_loss, module="torch.nn.functional") gin.config.external_configurable( nn.functional.cross_entropy, module="torch.nn.functional" @@ -559,7 +559,101 @@ def step_fn(self, element, step_prefix=""): ) return loss - def faithfulness_correlation(self, test_loader, attribution, nr_runs=100, pertrub=None, subset_size=4): + def explantation_captum(self, test_loader, log_dir, method): + # Initialize lists to store attribution values for all instances + all_attrs = [] + + # Loop through the test_loader to compute attributions for all instances + for batch in test_loader: + for key, value in batch[0].items(): + batch[0][key] = batch[0][key].to(self.device) + x = batch[0] + data = ( + x["encoder_cat"].float().requires_grad_(), + x["encoder_cont"].requires_grad_(), + x["encoder_target"].float().requires_grad_(), + x["encoder_lengths"].float().requires_grad_(), + x["decoder_cat"].float().requires_grad_(), + x["decoder_cont"].requires_grad_(), + x["decoder_target"].float().requires_grad_(), + x["decoder_lengths"].float().requires_grad_(), + x["decoder_time_idx"].float().requires_grad_(), + x["groups"].float().requires_grad_(), + x["target_scale"].requires_grad_(), + ) + target = self(data) + print(target.shape) + baselines = ( + torch.zeros_like(data[0]).to(self.device), # encoder_cat, set to zero + torch.zeros_like(data[1]).to(self.device), # encoder_cont, set to zero + torch.zeros_like(data[2]).to(self.device), # encoder_target, set to zero + data[3].to(self.device), # encoder_lengths, leave unchanged + torch.zeros_like(data[4]).to(self.device), # decoder_cat, set to zero + torch.zeros_like(data[5]).to(self.device), # decoder_cont, set to zero + torch.zeros_like(data[6]).to(self.device), # decoder_target, set to zero + data[7].to(self.device), # decoder_lengths, leave unchanged + torch.zeros_like(data[8]).to(self.device), # decoder_time_idx, set to zero + data[9].to(self.device), # groups, leave unchanged + data[10].to(self.device), # target_scale, leave unchanged + ) + + explantation = method(self.forward_captum) + # Reformat attributions. + attr, delta = explantation.attribute( + data, target=target, return_convergence_delta=True, baselines=baselines, n_steps=20 + ) + # Convert attributions to numpy array and append to the list + all_attrs.append(attr[0].cpu().detach().numpy()) + + # Concatenate a‚ttribution values for all instances along the batch dimension + all_attrs = np.concatenate(all_attrs, axis=0) + means_feature = all_attrs.mean(axis=(0, 1)) + + # Compute mean along the batch dimension + means = all_attrs.mean(axis=(0, 2)) + # Normalize the means values to range [0, 1] + normalized_means = (means - means.min()) / (means.max() - means.min()) + + # Create x values (assuming you want a simple sequential x-axis) + # Assuming you have 24 values + x_values = np.arange(1, 57) + # Plotting the featrue means + plt.figure(figsize=(8, 6)) + plt.plot( + x_values, + means_feature, + marker="o", + color="skyblue", + linestyle="-", + linewidth=2, + markersize=8, + ) + plt.xlabel("Time Step") + plt.ylabel("Normalized Attribution") + plt.title("Attribution Values") + plt.xticks(x_values) # Set x-ticks to match the number of features + plt.tight_layout() + plt.savefig(log_dir / "attribution_features_plot.png", bbox_inches="tight") + plt.figure(figsize=(8, 6)) + x_values = np.arange(1, 25) + plt.plot( + x_values, + normalized_means, + marker="o", + color="skyblue", + linestyle="-", + linewidth=2, + markersize=8, + ) + plt.xlabel("Time Step") + plt.ylabel("Normalized Attribution") + plt.title("Attribution Values") + plt.xticks(x_values) # Set x-ticks to match the number of features + plt.tight_layout() + plt.savefig(log_dir / "attribution_plot.png", bbox_inches="tight") + return means + + def Faithfulness_Correlation(self, test_loader, attribution, nr_runs=100, pertrub=None, subset_size=4): """ Implementation of faithfulness correlation by Bhatt et al., 2020. @@ -635,22 +729,95 @@ def faithfulness_correlation(self, test_loader, attribution, nr_runs=100, pertru # Predict on perturbed input x. y_pred_perturb = self(data).detach().cpu().numpy() - print(y_pred - y_pred_perturb) - break + pred_deltas.append((y_pred - y_pred_perturb).mean(axis=(0, 2))) # Sum attributions of the random subset. att_sums.append(np.sum(attribution[a_ix])) - print(pred_deltas, att_sums) correlation_matrix = np.corrcoef(pred_deltas, att_sums, rowvar=False) # Get the correlation coefficient from the correlation matrix pearson_correlation = correlation_matrix[0, 1] similarities.append(pearson_correlation) - print(similarities) return np.nanmean(similarities) + def Data_Randomization(self, test_loader, attribution, explain_method, nr_runs=100, similarity=None): + """ + Implementation of the Random Logit Metric by Sixt et al., 2020. + + The Random Logit Metric computes the distance between the original explanation and a reference explanation of + a randomly chosen non-target class. + This code is adapted from the quantus libray to suit our use case + + References: + 1) Leon Sixt et al.: "When Explanations Lie: Why Many Modified BP + Attributions Fail." ICML (2020): 9046-9057. + 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. + + """ + a_perturbed = [] + for batch in test_loader: + + for key, value in batch[0].items(): + + batch[0][key] = batch[0][key].to(self.device) + x = batch[0] + y = batch[1][0] + data = ( + x["encoder_cat"], + x["encoder_cont"], + x["encoder_target"], + x["encoder_lengths"], + x["decoder_cat"], + x["decoder_cont"], + x["decoder_target"], + x["decoder_lengths"], + x["decoder_time_idx"], + x["groups"], + x["target_scale"], + ) + y_off = np.array( + [ + np.random.choice( + [y_ for y_ in list(np.arange(0, self.num_classes)) if y_ != y] + ) + ] + ) + baselines = ( + torch.zeros_like(data[0]).to(self.device), # encoder_cat, set to zero + torch.zeros_like(data[1]).to(self.device), # encoder_cont, set to zero + torch.zeros_like(data[2]).to(self.device), # encoder_target, set to zero + data[3].to(self.device), # encoder_lengths, leave unchanged + torch.zeros_like(data[4]).to(self.device), # decoder_cat, set to zero + torch.zeros_like(data[5]).to(self.device), # decoder_cont, set to zero + torch.zeros_like(data[6]).to(self.device), # decoder_target, set to zero + data[7].to(self.device), # decoder_lengths, leave unchanged + torch.zeros_like(data[8]).to(self.device), # decoder_time_idx, set to zero + data[9].to(self.device), # groups, leave unchanged + data[10].to(self.device), # target_scale, leave unchanged + ) + + explantation = explain_method(self.forward_captum) + # Reformat attributions. + attr, delta = explantation.attribute( + data, target=y_off, return_convergence_delta=True, baselines=baselines, n_steps=20 + ) + # Convert attributions to numpy array and append to the list + a_perturbed.append(attr[0].cpu().detach().numpy()) + a_perturbed = np.concatenate(a_perturbed, axis=0) + a_perturbed = a_perturbed.mean(axis=(0, 1)) + + # Compute mean along the batch dimension + a_perturbed = a_perturbed.mean(axis=(0, 2)) + # Normalize the means values to range [0, 1] + normalized_a_perturbed = (a_perturbed - a_perturbed.min()) / (a_perturbed.max() - a_perturbed.min()) + correlation_matrix = np.corrcoef(normalized_a_perturbed, attribution, rowvar=False) + + # Get the correlation coefficient from the correlation matrix + pearson_correlation = correlation_matrix[0, 1] + return pearson_correlation + @gin.configurable("MLWrapper") class MLWrapper(BaseModule, ABC): From 8972c12c817032c52b9cd4ee3da2d4a44cdc6e19 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Tue, 31 Oct 2023 16:32:07 +0100 Subject: [PATCH 094/142] removed check for pytorch forecasting from DLwrapper --- icu_benchmarks/models/wrappers.py | 38 +++++-------------------------- 1 file changed, 6 insertions(+), 32 deletions(-) diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index c22d9bcb..a89ed8d8 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -333,40 +333,14 @@ def step_fn(self, element, step_prefix=""): data[key] = value """ if len(element) == 2: - """ - if (isinstance(element[1], tuple)) or ( - isinstance(element[1], list) - ): - """ - if self.pytorch_forecasting: - # check if the data loader is the one for the pytorch forecasring implementation - dic, labels = element[0], element[1][0] - - if isinstance(labels, list): - labels = labels[-1] - data = ( - dic["encoder_cat"], - dic["encoder_cont"], - dic["encoder_target"], - dic["encoder_lengths"], - dic["decoder_cat"], - dic["decoder_cont"], - dic["decoder_target"], - dic["decoder_lengths"], - dic["decoder_time_idx"], - dic["groups"], - dic["target_scale"], - ) - mask = torch.ones_like(labels).bool() + data, labels = element[0], (element[1]).to(self.device) + if isinstance(data, list): + for i in range(len(data)): + data[i] = data[i].float().to(self.device) else: - data, labels = element[0], (element[1]).to(self.device) - if isinstance(data, list): - for i in range(len(data)): - data[i] = data[i].float().to(self.device) - else: - data = data.float().to(self.device) - mask = torch.ones_like(labels).bool() + data = data.float().to(self.device) + mask = torch.ones_like(labels).bool() elif len(element) == 3: data, labels, mask = ( From bd8c2e5f8520d836be64770d689fa648e094ceaa Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Tue, 31 Oct 2023 16:37:51 +0100 Subject: [PATCH 095/142] similiary function from captum --- icu_benchmarks/models/similarity_func.py | 292 +++++++++++++++++++++++ 1 file changed, 292 insertions(+) create mode 100644 icu_benchmarks/models/similarity_func.py diff --git a/icu_benchmarks/models/similarity_func.py b/icu_benchmarks/models/similarity_func.py new file mode 100644 index 00000000..88d19a9a --- /dev/null +++ b/icu_benchmarks/models/similarity_func.py @@ -0,0 +1,292 @@ +"""This module holds a collection of similarity functions i.e., ways to measure the distance between two inputs (or explanations).""" + +# This file is part of Quantus. +# Quantus is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. +# Quantus is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +# You should have received a copy of the GNU Lesser General Public License along with Quantus. If not, see . +# Quantus project URL: . +# Quantus project URL: https://github.com/understandable-machine-intelligence-lab/Quantus + +from typing import Union + +import numpy as np +import scipy +import skimage + + +def correlation_spearman(a: np.array, b: np.array, **kwargs) -> float: + """ + Calculate Spearman rank of two images (or explanations). + + Parameters + ---------- + a: np.ndarray + The first array to use for similarity scoring. + b: np.ndarray + The second array to use for similarity scoring. + kwargs: optional + Keyword arguments. + + Returns + ------- + float + The similarity score. + """ + return scipy.stats.spearmanr(a, b)[0] + + +def correlation_pearson(a: np.array, b: np.array, **kwargs) -> float: + """ + Calculate Pearson correlation of two images (or explanations). + + Parameters + ---------- + a: np.ndarray + The first array to use for similarity scoring. + b: np.ndarray + The second array to use for similarity scoring. + kwargs: optional + Keyword arguments. + + Returns + ------- + float + The similarity score. + """ + return scipy.stats.pearsonr(a, b)[0] + + +def correlation_kendall_tau(a: np.array, b: np.array, **kwargs) -> float: + """ + Calculate Kendall Tau correlation of two images (or explanations). + + Parameters + ---------- + a: np.ndarray + The first array to use for similarity scoring. + b: np.ndarray + The second array to use for similarity scoring. + kwargs: optional + Keyword arguments. + + Returns + ------- + float + The similarity score. + """ + return scipy.stats.kendalltau(a, b)[0] + + +def distance_euclidean(a: np.array, b: np.array, **kwargs) -> float: + """ + Calculate Euclidean distance of two images (or explanations). + + Parameters + ---------- + a: np.ndarray + The first array to use for similarity scoring. + b: np.ndarray + The second array to use for similarity scoring. + kwargs: optional + Keyword arguments. + + Returns + ------- + float + The similarity score. + """ + return scipy.spatial.distance.euclidean(u=a, v=b) + + +def distance_manhattan(a: np.array, b: np.array, **kwargs) -> float: + """ + Calculate Manhattan distance of two images (or explanations). + + Parameters + ---------- + a: np.ndarray + The first array to use for similarity scoring. + b: np.ndarray + The second array to use for similarity scoring. + kwargs: optional + Keyword arguments. + + Returns + ------- + float + The similarity score. + """ + return scipy.spatial.distance.cityblock(u=a, v=b) + + +def distance_chebyshev(a: np.array, b: np.array, **kwargs) -> float: + """ + Calculate Chebyshev distance of two images (or explanations). + + Parameters + ---------- + a: np.ndarray + The first array to use for similarity scoring. + b: np.ndarray + The second array to use for similarity scoring. + kwargs: optional + Keyword arguments. + + Returns + ------- + float + The similarity score. + """ + return scipy.spatial.distance.chebyshev(u=a, v=b) + + +def lipschitz_constant( + a: np.array, + b: np.array, + c: Union[np.array, None], + d: Union[np.array, None], + **kwargs +) -> float: + """ + Calculate non-negative local Lipschitz abs(||a-b||/||c-d||), where a,b can be f(x) or a(x) and c,d is x. + + For numerical stability, a small value is added to division. + + Parameters + ---------- + a: np.ndarray + The first array to use for similarity scoring. + b: np.ndarray + The second array to use for similarity scoring. + c: np.ndarray + The third array to use for similarity scoring. + d: np.ndarray + The fourth array to use for similarity scoring. + kwargs: optional + Keyword arguments. + + Returns + ------- + float + The similarity score. + """ + eps = 1e-10 + + d1 = kwargs.get("norm_numerator", distance_manhattan) + d2 = kwargs.get("norm_denominator", distance_euclidean) + + if np.shape(a) == (): + return float(abs(a - b) / (d2(c, d) + eps)) + else: + return float(d1(a, b) / (d2(a=c, b=d) + eps)) + + +def abs_difference(a: np.array, b: np.array, **kwargs) -> float: + """ + Calculate the mean of the absolute differences between two images (or explanations). + + Parameters + ---------- + a: np.ndarray + The first array to use for similarity scoring. + b: np.ndarray + The second array to use for similarity scoring. + kwargs: optional + Keyword arguments. + + Returns + ------- + float + The similarity score. + """ + return np.mean(abs(a - b)) + + +def squared_difference(a: np.array, b: np.array, **kwargs) -> float: + """ + Calculate the sqaured differences between two images (or explanations). + + Parameters + ---------- + a: np.ndarray + The first array to use for similarity scoring. + b: np.ndarray + The second array to use for similarity scoring. + kwargs: optional + Keyword arguments. + + Returns + ------- + float + The similarity score. + """ + return np.sum((a - b) ** 2) + + +def cosine(a: np.array, b: np.array, **kwargs) -> float: + """ + Calculate Cosine of two images (or explanations). + + Parameters + ---------- + a: np.ndarray + The first array to use for similarity scoring. + b: np.ndarray + The second array to use for similarity scoring. + kwargs: optional + Keyword arguments. + + Returns + ------- + float + The similarity score. + """ + return scipy.spatial.distance.cosine(u=a, v=b) + + +def ssim(a: np.array, b: np.array, **kwargs) -> float: + """ + Calculate Structural Similarity Index Measure of two images (or explanations). + + Parameters + ---------- + a: np.ndarray + The first array to use for similarity scoring. + b: np.ndarray + The second array to use for similarity scoring. + kwargs: optional + Keyword arguments. + + Returns + ------- + float + The similarity score. + """ + max_point, min_point = np.max(np.concatenate([a, b])), np.min( + np.concatenate([a, b]) + ) + data_range = float(np.abs(max_point - min_point)) + return skimage.metrics.structural_similarity( + im1=a, im2=b, win_size=kwargs.get("win_size", None), data_range=data_range + ) + + +def difference(a: np.array, b: np.array, **kwargs) -> np.array: + """ + Calculate the difference between two images (or explanations). + + Parameters + ---------- + a: np.ndarray + The first array to use for similarity scoring. + b: np.ndarray + The second array to use for similarity scoring. + kwargs: optional + Keyword arguments. + + Returns + ------- + np.array + The difference in each element. + """ + return a - b From d4a55ff2074851ec4b4b07f11e5546da527f56f4 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Tue, 31 Oct 2023 16:42:34 +0100 Subject: [PATCH 096/142] used similarty functions already defined --- icu_benchmarks/models/wrappers.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index a89ed8d8..9640dcd9 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -19,6 +19,7 @@ from icu_benchmarks.models.constants import MLMetrics, DLMetrics from icu_benchmarks.contants import RunMode import matplotlib.pyplot as plt +from icu_benchmarks.models.similarity_func import correlation_spearman,distance_euclidean gin.config.external_configurable(nn.functional.nll_loss, module="torch.nn.functional") gin.config.external_configurable( nn.functional.cross_entropy, module="torch.nn.functional" @@ -627,7 +628,7 @@ def explantation_captum(self, test_loader, log_dir, method): plt.savefig(log_dir / "attribution_plot.png", bbox_inches="tight") return means - def Faithfulness_Correlation(self, test_loader, attribution, nr_runs=100, pertrub=None, subset_size=4): + def Faithfulness_Correlation(self, test_loader, attribution,similarity_func=None nr_runs=100, pertrub=None, subset_size=4): """ Implementation of faithfulness correlation by Bhatt et al., 2020. @@ -654,6 +655,8 @@ def Faithfulness_Correlation(self, test_loader, attribution, nr_runs=100, pertru if torch.is_tensor(attribution): # Convert the tensor to a NumPy array example_numpy_array = attribution.cpu().detach().numpy() + if similarity_func== None: + similarity_func=correlation_spearman if pertrub == None: pertrub = "baseline" similarities = [] @@ -709,11 +712,8 @@ def Faithfulness_Correlation(self, test_loader, attribution, nr_runs=100, pertru # Sum attributions of the random subset. att_sums.append(np.sum(attribution[a_ix])) - correlation_matrix = np.corrcoef(pred_deltas, att_sums, rowvar=False) - - # Get the correlation coefficient from the correlation matrix - pearson_correlation = correlation_matrix[0, 1] - similarities.append(pearson_correlation) + + similarities.append(similarity_func(pred_deltas,att_sums)) return np.nanmean(similarities) def Data_Randomization(self, test_loader, attribution, explain_method, nr_runs=100, similarity=None): @@ -731,6 +731,8 @@ def Data_Randomization(self, test_loader, attribution, explain_method, nr_runs=1 """ a_perturbed = [] + if similarity_func== None: + similarity_func=distance_euclidean for batch in test_loader: for key, value in batch[0].items(): @@ -786,11 +788,8 @@ def Data_Randomization(self, test_loader, attribution, explain_method, nr_runs=1 a_perturbed = a_perturbed.mean(axis=(0, 2)) # Normalize the means values to range [0, 1] normalized_a_perturbed = (a_perturbed - a_perturbed.min()) / (a_perturbed.max() - a_perturbed.min()) - correlation_matrix = np.corrcoef(normalized_a_perturbed, attribution, rowvar=False) - - # Get the correlation coefficient from the correlation matrix - pearson_correlation = correlation_matrix[0, 1] - return pearson_correlation + + return similarity_func(normalized_a_perturbed,attribution) @gin.configurable("MLWrapper") From edd4147a554121d7ce0915e5b05c4eddf3024e99 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Thu, 2 Nov 2023 14:10:58 +0100 Subject: [PATCH 097/142] latest changes --- icu_benchmarks/models/dl_models.py | 15 +- icu_benchmarks/models/train.py | 17 ++- icu_benchmarks/models/wrappers.py | 227 +++++++++++++++-------------- 3 files changed, 136 insertions(+), 123 deletions(-) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 08a1e75f..7fb7fd27 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -494,7 +494,7 @@ def __init__( loss=QuantileLoss(), hidden_continuous_size=hidden, ) - + self.num_classes = num_classes self.logit = nn.Linear(7, num_classes) def forward( @@ -533,15 +533,16 @@ def actual_vs_predictions_plot(self, dataloader): self.model.plot_prediction_actual_by_variable(predictions_vs_actuals) return predictions_vs_actuals - def interpertations(self, dataloader, log_dir): + def interpertations(self, dataloader, log_dir='.', plot=False): raw_predictions = self.model.predict(dataloader, return_x=True, mode="raw") interpretation = self.model.interpret_output( raw_predictions.output, reduction="sum" ) - figs = self.model.plot_interpretation(interpretation) - for key, fig in figs.items(): - fig.savefig(log_dir / f"interpretation_{key}.png", bbox_inches="tight") + if (plot): + figs = self.model.plot_interpretation(interpretation) + for key, fig in figs.items(): + fig.savefig(log_dir / f"interpretation_{key}.png", bbox_inches="tight") self.model = self.model.to(self.device) @@ -630,7 +631,7 @@ def __init__( dropout=dropout, optimizer=optimizer, ) - + self.num_classes = num_classes self.logit = nn.Linear(1, num_classes) def forward( @@ -690,7 +691,7 @@ def __init__( dropout=dropout, optimizer=optimizer, ) - + self.num_classes = num_classes self.logit = nn.Linear(4, num_classes) def forward( diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index eadb252f..b7c966c2 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -275,14 +275,19 @@ def train_common( test_loader, log_dir, IntegratedGradients ) - # print("IG", attributions_IG) - # Attention_weights = model.interpertations(test_loader, log_dir) - # print("attention", Attention_weights) + print("IG", attributions_IG) + Attention_weights = model.interpertations(test_loader, log_dir) + print("attention", Attention_weights) if XAI_metric: - scores = model.Faithfulness_Correlation(test_loader, attributions_IG) - print('Attributions faithfulness correlation', scores) + scores1 = model.Faithfulness_Correlation(test_loader, attributions_IG) + print('Attributions faithfulness correlation', scores1) + scores2 = model.Faithfulness_Correlation(test_loader, Attention_weights["attention"]) + print('Attention faithfulness correlation', scores2) # scores = model.faithfulness_correlation(test_loader, Attention_weights["attention"]) - print('Attention faithfulness correlation', scores) + + scores_2 = model.Data_Randomization( + test_loader, attributions_IG, IntegratedGradients) + print('Distance Data randmoization score', scores_2) model.set_weight("balanced", train_dataset) test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0][ diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 9640dcd9..45c44771 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -19,7 +19,7 @@ from icu_benchmarks.models.constants import MLMetrics, DLMetrics from icu_benchmarks.contants import RunMode import matplotlib.pyplot as plt -from icu_benchmarks.models.similarity_func import correlation_spearman,distance_euclidean +from icu_benchmarks.models.similarity_func import correlation_spearman, distance_euclidean gin.config.external_configurable(nn.functional.nll_loss, module="torch.nn.functional") gin.config.external_configurable( nn.functional.cross_entropy, module="torch.nn.functional" @@ -534,7 +534,7 @@ def step_fn(self, element, step_prefix=""): ) return loss - def explantation_captum(self, test_loader, log_dir, method): + def explantation_captum(self, test_loader, method, log_dir='.', plot=False): # Initialize lists to store attribution values for all instances all_attrs = [] @@ -556,8 +556,6 @@ def explantation_captum(self, test_loader, log_dir, method): x["groups"].float().requires_grad_(), x["target_scale"].requires_grad_(), ) - target = self(data) - print(target.shape) baselines = ( torch.zeros_like(data[0]).to(self.device), # encoder_cat, set to zero torch.zeros_like(data[1]).to(self.device), # encoder_cont, set to zero @@ -575,60 +573,60 @@ def explantation_captum(self, test_loader, log_dir, method): explantation = method(self.forward_captum) # Reformat attributions. attr, delta = explantation.attribute( - data, target=target, return_convergence_delta=True, baselines=baselines, n_steps=20 + data, target=(0, 1), return_convergence_delta=True, baselines=baselines, n_steps=35 ) # Convert attributions to numpy array and append to the list all_attrs.append(attr[0].cpu().detach().numpy()) # Concatenate a‚ttribution values for all instances along the batch dimension - all_attrs = np.concatenate(all_attrs, axis=0) + all_attrs = np.mean(all_attrs, axis=0) means_feature = all_attrs.mean(axis=(0, 1)) # Compute mean along the batch dimension means = all_attrs.mean(axis=(0, 2)) # Normalize the means values to range [0, 1] normalized_means = (means - means.min()) / (means.max() - means.min()) - - # Create x values (assuming you want a simple sequential x-axis) - # Assuming you have 24 values - x_values = np.arange(1, 57) - # Plotting the featrue means - plt.figure(figsize=(8, 6)) - plt.plot( - x_values, - means_feature, - marker="o", - color="skyblue", - linestyle="-", - linewidth=2, - markersize=8, - ) - plt.xlabel("Time Step") - plt.ylabel("Normalized Attribution") - plt.title("Attribution Values") - plt.xticks(x_values) # Set x-ticks to match the number of features - plt.tight_layout() - plt.savefig(log_dir / "attribution_features_plot.png", bbox_inches="tight") - plt.figure(figsize=(8, 6)) - x_values = np.arange(1, 25) - plt.plot( - x_values, - normalized_means, - marker="o", - color="skyblue", - linestyle="-", - linewidth=2, - markersize=8, - ) - plt.xlabel("Time Step") - plt.ylabel("Normalized Attribution") - plt.title("Attribution Values") - plt.xticks(x_values) # Set x-ticks to match the number of features - plt.tight_layout() - plt.savefig(log_dir / "attribution_plot.png", bbox_inches="tight") + if plot: + # Create x values (assuming you want a simple sequential x-axis) + # Assuming you have 24 values + x_values = np.arange(1, 57) + # Plotting the featrue means + plt.figure(figsize=(8, 6)) + plt.plot( + x_values, + means_feature, + marker="o", + color="skyblue", + linestyle="-", + linewidth=2, + markersize=8, + ) + plt.xlabel("Time Step") + plt.ylabel("Normalized Attribution") + plt.title("Attribution Values") + plt.xticks(x_values) # Set x-ticks to match the number of features + plt.tight_layout() + plt.savefig(log_dir / "attribution_features_plot.png", bbox_inches="tight") + plt.figure(figsize=(8, 6)) + x_values = np.arange(1, 25) + plt.plot( + x_values, + normalized_means, + marker="o", + color="skyblue", + linestyle="-", + linewidth=2, + markersize=8, + ) + plt.xlabel("Time Step") + plt.ylabel("Normalized Attribution") + plt.title("Attribution Values") + plt.xticks(x_values) # Set x-ticks to match the number of features + plt.tight_layout() + plt.savefig(log_dir / "attribution_plot.png", bbox_inches="tight") return means - def Faithfulness_Correlation(self, test_loader, attribution,similarity_func=None nr_runs=100, pertrub=None, subset_size=4): + def Faithfulness_Correlation(self, test_loader, attribution, similarity_func=None, nr_runs=100, pertrub=None, subset_size=4): """ Implementation of faithfulness correlation by Bhatt et al., 2020. @@ -654,10 +652,10 @@ def Faithfulness_Correlation(self, test_loader, attribution,similarity_func=None if torch.is_tensor(attribution): # Convert the tensor to a NumPy array - example_numpy_array = attribution.cpu().detach().numpy() - if similarity_func== None: - similarity_func=correlation_spearman - if pertrub == None: + attribution = attribution.cpu().detach().numpy() + if similarity_func is None: + similarity_func = correlation_spearman + if pertrub is None: pertrub = "baseline" similarities = [] for batch in test_loader: @@ -679,7 +677,6 @@ def Faithfulness_Correlation(self, test_loader, attribution,similarity_func=None x["groups"], x["target_scale"], ) - y_pred = self(data).detach().cpu().numpy() pred_deltas = [] att_sums = [] @@ -701,9 +698,20 @@ def Faithfulness_Correlation(self, test_loader, attribution,similarity_func=None mask[:, a_ix, :] = 0 mask = mask.to(x["encoder_cont"].device) - x["encoder_cont"] = x["encoder_cont"] * mask - + data = ( + x["encoder_cat"], + x["encoder_cont"], + x["encoder_target"], + x["encoder_lengths"], + x["decoder_cat"], + x["decoder_cont"], + x["decoder_target"], + x["decoder_lengths"], + x["decoder_time_idx"], + x["groups"], + x["target_scale"], + ) # Predict on perturbed input x. y_pred_perturb = self(data).detach().cpu().numpy() @@ -712,11 +720,11 @@ def Faithfulness_Correlation(self, test_loader, attribution,similarity_func=None # Sum attributions of the random subset. att_sums.append(np.sum(attribution[a_ix])) - - similarities.append(similarity_func(pred_deltas,att_sums)) + + similarities.append(similarity_func(pred_deltas, att_sums)) return np.nanmean(similarities) - def Data_Randomization(self, test_loader, attribution, explain_method, nr_runs=100, similarity=None): + def Data_Randomization(self, test_loader, attribution, explain_method, similarity_func=None): """ Implementation of the Random Logit Metric by Sixt et al., 2020. @@ -731,65 +739,64 @@ def Data_Randomization(self, test_loader, attribution, explain_method, nr_runs=1 """ a_perturbed = [] - if similarity_func== None: - similarity_func=distance_euclidean - for batch in test_loader: + if similarity_func is None: + similarity_func = distance_euclidean + if (explain_method == 'attention'): + Attention_weights = self.interpertations(test_loader) - for key, value in batch[0].items(): + else: + for batch in test_loader: + + for key, value in batch[0].items(): + + batch[0][key] = batch[0][key].to(self.device) + x = batch[0] + y = batch[1][0] + data = ( + x["encoder_cat"], + x["encoder_cont"], + x["encoder_target"], + x["encoder_lengths"], + x["decoder_cat"], + x["decoder_cont"], + x["decoder_target"], + x["decoder_lengths"], + x["decoder_time_idx"], + x["groups"], + x["target_scale"], + ) - batch[0][key] = batch[0][key].to(self.device) - x = batch[0] - y = batch[1][0] - data = ( - x["encoder_cat"], - x["encoder_cont"], - x["encoder_target"], - x["encoder_lengths"], - x["decoder_cat"], - x["decoder_cont"], - x["decoder_target"], - x["decoder_lengths"], - x["decoder_time_idx"], - x["groups"], - x["target_scale"], - ) - y_off = np.array( - [ - np.random.choice( - [y_ for y_ in list(np.arange(0, self.num_classes)) if y_ != y] - ) - ] - ) - baselines = ( - torch.zeros_like(data[0]).to(self.device), # encoder_cat, set to zero - torch.zeros_like(data[1]).to(self.device), # encoder_cont, set to zero - torch.zeros_like(data[2]).to(self.device), # encoder_target, set to zero - data[3].to(self.device), # encoder_lengths, leave unchanged - torch.zeros_like(data[4]).to(self.device), # decoder_cat, set to zero - torch.zeros_like(data[5]).to(self.device), # decoder_cont, set to zero - torch.zeros_like(data[6]).to(self.device), # decoder_target, set to zero - data[7].to(self.device), # decoder_lengths, leave unchanged - torch.zeros_like(data[8]).to(self.device), # decoder_time_idx, set to zero - data[9].to(self.device), # groups, leave unchanged - data[10].to(self.device), # target_scale, leave unchanged - ) + y_off = np.random.randint(low=0, high=self.num_classes, size=y.shape) + + baselines = ( + torch.zeros_like(data[0]).to(self.device), # encoder_cat, set to zero + torch.zeros_like(data[1]).to(self.device), # encoder_cont, set to zero + torch.zeros_like(data[2]).to(self.device), # encoder_target, set to zero + data[3].to(self.device), # encoder_lengths, leave unchanged + torch.zeros_like(data[4]).to(self.device), # decoder_cat, set to zero + torch.zeros_like(data[5]).to(self.device), # decoder_cont, set to zero + torch.zeros_like(data[6]).to(self.device), # decoder_target, set to zero + data[7].to(self.device), # decoder_lengths, leave unchanged + torch.zeros_like(data[8]).to(self.device), # decoder_time_idx, set to zero + data[9].to(self.device), # groups, leave unchanged + data[10].to(self.device), # target_scale, leave unchanged + ) + self.train() + explantation = explain_method(self.forward_captum) + # Reformat attributions. + attr, delta = explantation.attribute( + data, target=(1, 1), return_convergence_delta=True, baselines=baselines, n_steps=35 + ) + # Convert attributions to numpy array and append to the list + a_perturbed.append(attr[0].cpu().detach().numpy()) - explantation = explain_method(self.forward_captum) - # Reformat attributions. - attr, delta = explantation.attribute( - data, target=y_off, return_convergence_delta=True, baselines=baselines, n_steps=20 - ) - # Convert attributions to numpy array and append to the list - a_perturbed.append(attr[0].cpu().detach().numpy()) - a_perturbed = np.concatenate(a_perturbed, axis=0) - a_perturbed = a_perturbed.mean(axis=(0, 1)) + a_perturbed = np.mean(a_perturbed, axis=(0, 1, 3)) - # Compute mean along the batch dimension - a_perturbed = a_perturbed.mean(axis=(0, 2)) - # Normalize the means values to range [0, 1] - normalized_a_perturbed = (a_perturbed - a_perturbed.min()) / (a_perturbed.max() - a_perturbed.min()) - - return similarity_func(normalized_a_perturbed,attribution) + # Normalize the means values to range [0, 1] + normalized_a_perturbed = (a_perturbed - a_perturbed.min()) / (a_perturbed.max() - a_perturbed.min()) + score = similarity_func(normalized_a_perturbed, attribution) + + return score @gin.configurable("MLWrapper") From 176120ff66424326f374d7d2aed4d706ccd4c350 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Thu, 2 Nov 2023 14:31:46 +0100 Subject: [PATCH 098/142] changed attribution to take into account all timesteps --- icu_benchmarks/models/train.py | 14 ++++++++------ icu_benchmarks/models/wrappers.py | 17 +++++++++++------ 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index b7c966c2..4037f480 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -272,22 +272,24 @@ def train_common( if explain: attributions_IG = model.explantation_captum( - test_loader, log_dir, IntegratedGradients + test_loader=test_loader, + method=IntegratedGradients, log_dir=log_dir, plot=True ) print("IG", attributions_IG) - Attention_weights = model.interpertations(test_loader, log_dir) + Attention_weights = model.interpertations(test_loader, log_dir, plot=True) print("attention", Attention_weights) if XAI_metric: scores1 = model.Faithfulness_Correlation(test_loader, attributions_IG) print('Attributions faithfulness correlation', scores1) scores2 = model.Faithfulness_Correlation(test_loader, Attention_weights["attention"]) print('Attention faithfulness correlation', scores2) - # scores = model.faithfulness_correlation(test_loader, Attention_weights["attention"]) - - scores_2 = model.Data_Randomization( + scores_1 = model.Data_Randomization( test_loader, attributions_IG, IntegratedGradients) - print('Distance Data randmoization score', scores_2) + """ + scores_2 = model.Data_Randomization( + test_loader, attributions_IG, Attention_weights["attention"])""" + print('Distance Data randmoization score', scores_1) model.set_weight("balanced", train_dataset) test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0][ diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 45c44771..0f1db2bb 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -572,14 +572,19 @@ def explantation_captum(self, test_loader, method, log_dir='.', plot=False): explantation = method(self.forward_captum) # Reformat attributions. - attr, delta = explantation.attribute( - data, target=(0, 1), return_convergence_delta=True, baselines=baselines, n_steps=35 - ) - # Convert attributions to numpy array and append to the list - all_attrs.append(attr[0].cpu().detach().numpy()) - + attr_all_timesteps = [] + for time_step in range(0, 24): + attr, delta = explantation.attribute( + data, target=(time_step, 1), return_convergence_delta=True, baselines=baselines, n_steps=35 + ) + # Convert attributions to numpy array and append to the list + attr_all_timesteps.append(attr[0].cpu().detach().numpy()) + print(np.shape((attr_all_timesteps))) + all_attrs.append(np.mean(attr_all_timesteps, axis=0)) + print(np.shape(all_attrs)) # Concatenate a‚ttribution values for all instances along the batch dimension all_attrs = np.mean(all_attrs, axis=0) + print(np.shape(all_attrs)) means_feature = all_attrs.mean(axis=(0, 1)) # Compute mean along the batch dimension From edc4ad2f0778fe74ba9f957012a1834e329edfa9 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Thu, 2 Nov 2023 16:13:32 +0100 Subject: [PATCH 099/142] added attention method for randomization --- icu_benchmarks/data/loader.py | 11 ++++++++--- icu_benchmarks/models/train.py | 30 +++++++++++++++--------------- icu_benchmarks/models/wrappers.py | 29 ++++++++++++++++++----------- 3 files changed, 41 insertions(+), 29 deletions(-) diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index e02b2e76..cb2db4be 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -487,9 +487,7 @@ def __init__( self.name = name self.data = pd.merge( labels, features, on=["stay_id", "time"] - ) # combine labels and features - # self.data["sex"].replace([0, 1], ["Female", "Male"], inplace=True) - # List of column names to convert from boolean to float + ) if (len(lagged_variables) > 0): if self.data["label"].dtype == "bool": self.data["label"] = self.data["label"].astype( @@ -544,3 +542,10 @@ def get_balance(self) -> list: def get_feature_names(self): return self.column_names + + def randomize_labels(self, num_classes=None, min=None, max=None): + if num_classes is None: + random_target = np.random.uniform(min, max, size=len(self.data['target'][0])) + else: + random_target = np.random.randint(num_classes, size=len(self.data['target'][0])) + self.data['target'][0] = Tensor(random_target) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 4037f480..e1e70da1 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -271,25 +271,25 @@ def train_common( return 0 if explain: - attributions_IG = model.explantation_captum( - test_loader=test_loader, - method=IntegratedGradients, log_dir=log_dir, plot=True - ) + # attributions_IG = model.explantation_captum( + # test_loader=test_loader, + # method=IntegratedGradients, log_dir=log_dir, plot=True + # ) - print("IG", attributions_IG) + # print("IG", attributions_IG) Attention_weights = model.interpertations(test_loader, log_dir, plot=True) print("attention", Attention_weights) if XAI_metric: - scores1 = model.Faithfulness_Correlation(test_loader, attributions_IG) - print('Attributions faithfulness correlation', scores1) - scores2 = model.Faithfulness_Correlation(test_loader, Attention_weights["attention"]) - print('Attention faithfulness correlation', scores2) - scores_1 = model.Data_Randomization( - test_loader, attributions_IG, IntegratedGradients) - """ - scores_2 = model.Data_Randomization( - test_loader, attributions_IG, Attention_weights["attention"])""" - print('Distance Data randmoization score', scores_1) + # F_attribution = model.Faithfulness_Correlation(test_loader, attributions_IG) + # print('Attributions faithfulness correlation', F_attribution) + F_attention = model.Faithfulness_Correlation(test_loader, Attention_weights["attention"]) + print('Attention faithfulness correlation', F_attention) + # R_attribution = model.Data_Randomization( + # test_loader, attributions_IG, IntegratedGradients) + # print('Distance Data randmoization score attribution', R_attribution) + R_attention = model.Data_Randomization( + test_loader, Attention_weights["attention"], "Attention", test_dataset=test_dataset) + print('Distance Data randmoization score attention', R_attention) model.set_weight("balanced", train_dataset) test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0][ diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 0f1db2bb..71cb6f40 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -579,12 +579,9 @@ def explantation_captum(self, test_loader, method, log_dir='.', plot=False): ) # Convert attributions to numpy array and append to the list attr_all_timesteps.append(attr[0].cpu().detach().numpy()) - print(np.shape((attr_all_timesteps))) all_attrs.append(np.mean(attr_all_timesteps, axis=0)) - print(np.shape(all_attrs)) # Concatenate a‚ttribution values for all instances along the batch dimension all_attrs = np.mean(all_attrs, axis=0) - print(np.shape(all_attrs)) means_feature = all_attrs.mean(axis=(0, 1)) # Compute mean along the batch dimension @@ -729,7 +726,7 @@ def Faithfulness_Correlation(self, test_loader, attribution, similarity_func=Non similarities.append(similarity_func(pred_deltas, att_sums)) return np.nanmean(similarities) - def Data_Randomization(self, test_loader, attribution, explain_method, similarity_func=None): + def Data_Randomization(self, test_loader, attribution, explain_method, similarity_func=None, test_dataset=None): """ Implementation of the Random Logit Metric by Sixt et al., 2020. @@ -746,9 +743,16 @@ def Data_Randomization(self, test_loader, attribution, explain_method, similarit a_perturbed = [] if similarity_func is None: similarity_func = distance_euclidean - if (explain_method == 'attention'): - Attention_weights = self.interpertations(test_loader) + if (explain_method == 'Attention'): + for batch in test_loader: + print(batch[1][0]) + + test_dataset.randomize_labels(num_classes=self.num_classes) + for batch in test_loader: + print(batch[1][0]) # generate random labels + Attention_weights = self.interpertations(test_loader) # test_loader works by reference + score = similarity_func(Attention_weights["attention"].cpu().numpy(), attribution.cpu().numpy()) else: for batch in test_loader: @@ -789,11 +793,14 @@ def Data_Randomization(self, test_loader, attribution, explain_method, similarit self.train() explantation = explain_method(self.forward_captum) # Reformat attributions. - attr, delta = explantation.attribute( - data, target=(1, 1), return_convergence_delta=True, baselines=baselines, n_steps=35 - ) - # Convert attributions to numpy array and append to the list - a_perturbed.append(attr[0].cpu().detach().numpy()) + attr_all_timesteps = [] + for time_step in range(0, 24): + attr, delta = explantation.attribute( + data, target=(time_step, 1), return_convergence_delta=True, baselines=baselines, n_steps=35 + ) + # Convert attributions to numpy array and append to the list + attr_all_timesteps.append(attr[0].cpu().detach().numpy()) + a_perturbed.append(np.mean(attr_all_timesteps, axis=0)) a_perturbed = np.mean(a_perturbed, axis=(0, 1, 3)) From 2be507674b1d17fbfbf090a61c5401001974d548 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Tue, 7 Nov 2023 13:34:21 +0100 Subject: [PATCH 100/142] added baseline for faithfulness attribution --- icu_benchmarks/cross_validation.py | 13 +- icu_benchmarks/data/loader.py | 56 ++----- icu_benchmarks/models/dl_models.py | 33 ++-- icu_benchmarks/models/similarity_func.py | 16 +- icu_benchmarks/models/train.py | 56 ++++--- icu_benchmarks/models/wrappers.py | 192 ++++++++--------------- icu_benchmarks/run.py | 49 ++---- icu_benchmarks/run_utils.py | 133 +++++----------- icu_benchmarks/tuning/hyperparameters.py | 26 +-- 9 files changed, 199 insertions(+), 375 deletions(-) diff --git a/icu_benchmarks/cross_validation.py b/icu_benchmarks/cross_validation.py index 8c0c02b1..7d3f217e 100644 --- a/icu_benchmarks/cross_validation.py +++ b/icu_benchmarks/cross_validation.py @@ -41,6 +41,8 @@ def execute_repeated_cv( explain: bool = False, pytorch_forecasting: bool = False, XAI_metric: bool = False, + random_labels: bool = False, + random_model_dir: str = None ) -> float: """Preprocesses data and trains a model for each fold. @@ -83,15 +85,11 @@ def execute_repeated_cv( cv_folds_to_train = 1 else: - logging.info( - f"Starting nested CV with {cv_repetitions_to_train} repetitions of {cv_folds_to_train} folds." - ) + logging.info(f"Starting nested CV with {cv_repetitions_to_train} repetitions of {cv_folds_to_train} folds.") for repetition in range(cv_repetitions_to_train): for fold_index in range(cv_folds_to_train): - repetition_fold_dir = ( - log_dir / f"repetition_{repetition}" / f"fold_{fold_index}" - ) + repetition_fold_dir = log_dir / f"repetition_{repetition}" / f"fold_{fold_index}" repetition_fold_dir.mkdir(parents=True, exist_ok=True) start_time = datetime.now() @@ -109,6 +107,7 @@ def execute_repeated_cv( pretrained_imputation_model=pretrained_imputation_model, runmode=mode, complete_train=complete_train, + ) preprocess_time = datetime.now() - start_time @@ -129,6 +128,8 @@ def execute_repeated_cv( explain=explain, pytorch_forecasting=pytorch_forecasting, XAI_metric=XAI_metric, + random_labels=random_labels, + random_model_dir=random_model_dir ) train_time = datetime.now() - start_time diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index cb2db4be..fdf4dc68 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -103,15 +103,11 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: # slice to make sure to always return a DF window = self.features_df.loc[stay_id:stay_id].to_numpy() - labels = self.outcome_df.loc[stay_id:stay_id][self.vars["LABEL"]].to_numpy( - dtype=float - ) + labels = self.outcome_df.loc[stay_id:stay_id][self.vars["LABEL"]].to_numpy(dtype=float) if len(labels) == 1: # only one label per stay, align with window - labels = np.concatenate( - [np.empty(window.shape[0] - 1) * np.nan, labels], axis=0 - ) + labels = np.concatenate([np.empty(window.shape[0] - 1) * np.nan, labels], axis=0) length_diff = self.maxlen - window.shape[0] pad_mask = np.ones(window.shape[0]) @@ -119,9 +115,7 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: # Padding the array to fulfill size requirement if length_diff > 0: # window shorter than the longest window in dataset, pad to same length - window = np.concatenate( - [window, np.ones((length_diff, window.shape[1])) * pad_value], axis=0 - ) + window = np.concatenate([window, np.ones((length_diff, window.shape[1])) * pad_value], axis=0) labels = np.concatenate([labels, np.ones(length_diff) * pad_value], axis=0) pad_mask = np.concatenate([pad_mask, np.zeros(length_diff)], axis=0) @@ -331,12 +325,8 @@ def __init__( self.amputated_values, self.amputation_mask = ampute_data( self.features_df, mask_method, mask_proportion, mask_observation_proportion ) - self.amputation_mask = ( - self.amputation_mask + self.features_df.isna().values - ).bool() - self.amputation_mask = DataFrame( - self.amputation_mask, columns=self.vars[Segment.dynamic] - ) + self.amputation_mask = (self.amputation_mask + self.features_df.isna().values).bool() + self.amputation_mask = DataFrame(self.amputation_mask, columns=self.vars[Segment.dynamic]) self.amputation_mask[self.vars["GROUP"]] = self.features_df.index self.amputation_mask.set_index(self.vars["GROUP"], inplace=True) @@ -361,15 +351,9 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: # slice to make sure to always return a DF window = self.features_df.loc[stay_id:stay_id, self.vars[Segment.dynamic]] - window_missingness_mask = self.target_missingness_mask.loc[ - stay_id:stay_id, self.vars[Segment.dynamic] - ] - amputated_window = self.amputated_values.loc[ - stay_id:stay_id, self.vars[Segment.dynamic] - ] - amputation_mask = self.amputation_mask.loc[ - stay_id:stay_id, self.vars[Segment.dynamic] - ] + window_missingness_mask = self.target_missingness_mask.loc[stay_id:stay_id, self.vars[Segment.dynamic]] + amputated_window = self.amputated_values.loc[stay_id:stay_id, self.vars[Segment.dynamic]] + amputation_mask = self.amputation_mask.loc[stay_id:stay_id, self.vars[Segment.dynamic]] return ( from_numpy(amputated_window.values).to(float32), @@ -472,30 +456,22 @@ def __init__( name: str = "", max_prediction_length: int = 24, max_encoder_length: int = 24, - **kwargs, ): - - data[split]["FEATURES"]["time_idx"] = ( - (data[split]["FEATURES"]["time"] / pd.Timedelta(seconds=3600)) - ).astype( + data[split]["FEATURES"]["time_idx"] = ((data[split]["FEATURES"]["time"] / pd.Timedelta(seconds=3600))).astype( int ) # create an incremental column indicating the time step(required by constructor) data = data.get(split) # get split labels = data["OUTCOME"] features = data["FEATURES"] self.name = name - self.data = pd.merge( - labels, features, on=["stay_id", "time"] - ) - if (len(lagged_variables) > 0): + self.data = pd.merge(labels, features, on=["stay_id", "time"]) + if len(lagged_variables) > 0: if self.data["label"].dtype == "bool": - self.data["label"] = self.data["label"].astype( - float - ) + self.data["label"] = self.data["label"].astype(float) columns_to_lag = lagged_variables grouped = self.data.sort_values("time_idx").groupby("stay_id") - for lag in range(max_encoder_length, max_encoder_length+max_prediction_length + 1): + for lag in range(max_encoder_length, max_encoder_length + max_prediction_length + 1): for column in columns_to_lag: # Create a new column with lagged values self.data[f"{column}_lag_{lag}"] = grouped[column].shift(lag, fill_value=0) @@ -545,7 +521,7 @@ def get_feature_names(self): def randomize_labels(self, num_classes=None, min=None, max=None): if num_classes is None: - random_target = np.random.uniform(min, max, size=len(self.data['target'][0])) + random_target = np.random.uniform(min, max, size=len(self.data["target"][0])) else: - random_target = np.random.randint(num_classes, size=len(self.data['target'][0])) - self.data['target'][0] = Tensor(random_target) + random_target = np.random.randint(num_classes, size=len(self.data["target"][0])) + self.data["target"][0] = Tensor(random_target) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 7fb7fd27..b26bd6b0 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -149,9 +149,7 @@ def __init__( **kwargs, ) hidden = hidden if hidden % 2 == 0 else hidden + 1 # Make sure hidden is even - self.input_embedding = nn.Linear( - input_size[2], hidden - ) # This acts as a time-distributed layer by defaults + self.input_embedding = nn.Linear(input_size[2], hidden) # This acts as a time-distributed layer by defaults if pos_encoding: self.pos_encoder = PositionalEncoding(hidden) else: @@ -222,9 +220,7 @@ def __init__( ) hidden = hidden if hidden % 2 == 0 else hidden + 1 # Make sure hidden is even - self.input_embedding = nn.Linear( - input_size[2], hidden - ) # This acts as a time-distributed layer by defaults + self.input_embedding = nn.Linear(input_size[2], hidden) # This acts as a time-distributed layer by defaults if pos_encoding: self.pos_encoder = PositionalEncoding(hidden) else: @@ -290,13 +286,9 @@ def __init__( # We compute automatically the depth based on the desired seq_length. if isinstance(num_channels, Integral) and max_seq_length: - num_channels = [num_channels] * int( - np.ceil(np.log(max_seq_length / 2) / np.log(kernel_size)) - ) + num_channels = [num_channels] * int(np.ceil(np.log(max_seq_length / 2) / np.log(kernel_size))) elif isinstance(num_channels, Integral) and not max_seq_length: - raise Exception( - "a maximum sequence length needs to be provided if num_channels is int" - ) + raise Exception("a maximum sequence length needs to be provided if num_channels is int") num_levels = len(num_channels) for i in range(num_levels): @@ -527,19 +519,14 @@ def forward(self, x): def actual_vs_predictions_plot(self, dataloader): predictions = self.model.predict(dataloader, return_x=True) - predictions_vs_actuals = self.model.calculate_prediction_actual_by_variable( - predictions.x, predictions.output - ) + predictions_vs_actuals = self.model.calculate_prediction_actual_by_variable(predictions.x, predictions.output) self.model.plot_prediction_actual_by_variable(predictions_vs_actuals) return predictions_vs_actuals - def interpertations(self, dataloader, log_dir='.', plot=False): - + def interpertations(self, dataloader, log_dir=".", plot=False): raw_predictions = self.model.predict(dataloader, return_x=True, mode="raw") - interpretation = self.model.interpret_output( - raw_predictions.output, reduction="sum" - ) - if (plot): + interpretation = self.model.interpret_output(raw_predictions.output, reduction="sum") + if plot: figs = self.model.plot_interpretation(interpretation) for key, fig in figs.items(): fig.savefig(log_dir / f"interpretation_{key}.png", bbox_inches="tight") @@ -563,9 +550,7 @@ def predict_dependency(self, dataloader, variable, log_dir): q75=lambda x: x.quantile(0.75), ) ax = agg_dependency.plot(y="median") - ax.fill_between( - agg_dependency.index, agg_dependency.q25, agg_dependency.q75, alpha=0.3 - ) + ax.fill_between(agg_dependency.index, agg_dependency.q25, agg_dependency.q75, alpha=0.3) plt.savefig(log_dir / "dependecy.png", bbox_inches="tight") return dependency diff --git a/icu_benchmarks/models/similarity_func.py b/icu_benchmarks/models/similarity_func.py index 88d19a9a..3ff96474 100644 --- a/icu_benchmarks/models/similarity_func.py +++ b/icu_benchmarks/models/similarity_func.py @@ -140,13 +140,7 @@ def distance_chebyshev(a: np.array, b: np.array, **kwargs) -> float: return scipy.spatial.distance.chebyshev(u=a, v=b) -def lipschitz_constant( - a: np.array, - b: np.array, - c: Union[np.array, None], - d: Union[np.array, None], - **kwargs -) -> float: +def lipschitz_constant(a: np.array, b: np.array, c: Union[np.array, None], d: Union[np.array, None], **kwargs) -> float: """ Calculate non-negative local Lipschitz abs(||a-b||/||c-d||), where a,b can be f(x) or a(x) and c,d is x. @@ -262,13 +256,9 @@ def ssim(a: np.array, b: np.array, **kwargs) -> float: float The similarity score. """ - max_point, min_point = np.max(np.concatenate([a, b])), np.min( - np.concatenate([a, b]) - ) + max_point, min_point = np.max(np.concatenate([a, b])), np.min(np.concatenate([a, b])) data_range = float(np.abs(max_point - min_point)) - return skimage.metrics.structural_similarity( - im1=a, im2=b, win_size=kwargs.get("win_size", None), data_range=data_range - ) + return skimage.metrics.structural_similarity(im1=a, im2=b, win_size=kwargs.get("win_size", None), data_range=data_range) def difference(a: np.array, b: np.array, **kwargs) -> np.array: diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index e1e70da1..f7a38790 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -6,6 +6,7 @@ import pandas as pd from joblib import load from torch.optim import Adam +import numpy as np import quantus from torch.utils.data import DataLoader from pytorch_lightning.loggers import TensorBoardLogger, WandbLogger @@ -54,7 +55,7 @@ def train_common( optimizer: type = Adam, precision=32, batch_size=64, - epochs=100, + epochs=1000, patience=20, min_delta=1e-5, test_on: str = Split.test, @@ -73,6 +74,8 @@ def train_common( explain: bool = False, pytorch_forecasting: bool = False, XAI_metric: bool = False, + random_labels: bool = False, + random_model_dir: str =None ): """Common wrapper to train all benchmarked models. @@ -173,8 +176,12 @@ def train_common( optimizer=optimizer, epochs=epochs, run_mode=mode, - batch_size=batch_size, # check why validation set is needed here + batch_size=batch_size, ) + if random_labels: + train_dataset.randomize_labels(num_classes=model.num_classes) + val_dataset.randomize_labels(num_classes=model.num_classes) + test_dataset.randomize_labels(num_classes=model.num_classes) else: train_loader = DataLoader( @@ -206,10 +213,6 @@ def train_common( if model.requires_backprop else DataLoader([test_dataset.to_tensor()], batch_size=1) ) - """ - if isinstance(next(iter(train_loader))[0], OrderedDict): - model = model(optimizer=optimizer, epochs=epochs, run_mode=mode) - """ data_shape = next(iter(train_loader))[0].shape if load_weights: @@ -271,25 +274,32 @@ def train_common( return 0 if explain: - # attributions_IG = model.explantation_captum( - # test_loader=test_loader, - # method=IntegratedGradients, log_dir=log_dir, plot=True - # ) + attributions_IG = model.explantation_captum( + test_loader=test_loader, + method=IntegratedGradients, log_dir=log_dir, plot=True + ) - # print("IG", attributions_IG) - Attention_weights = model.interpertations(test_loader, log_dir, plot=True) - print("attention", Attention_weights) + print("IG", attributions_IG) + # Attention_weights = model.interpertations(test_loader, log_dir, plot=True) + # print("attention", Attention_weights) if XAI_metric: - # F_attribution = model.Faithfulness_Correlation(test_loader, attributions_IG) - # print('Attributions faithfulness correlation', F_attribution) - F_attention = model.Faithfulness_Correlation(test_loader, Attention_weights["attention"]) - print('Attention faithfulness correlation', F_attention) - # R_attribution = model.Data_Randomization( - # test_loader, attributions_IG, IntegratedGradients) - # print('Distance Data randmoization score attribution', R_attribution) - R_attention = model.Data_Randomization( - test_loader, Attention_weights["attention"], "Attention", test_dataset=test_dataset) - print('Distance Data randmoization score attention', R_attention) + # ra2 = np.random.randint(low=0, high=101, size=24) + # random_attributions = np.random.normal(size=24) + # F_baseline = model.Faithfulness_Correlation(test_loader, random_attributions, pertrub='Noise') + # F_baseline2 = model.Faithfulness_Correlation(test_loader, random_attributions, pertrub='Noise') + # print('Random noraml faithfulness correlation', F_baseline) + # print('random unifrom ', F_baseline2) + # F_attribution = model.Faithfulness_Correlation(test_loader, attributions_IG, pertrub='Noise') + # print('Attributions faithfulness correlation', F_attribution) + # F_attention = model.Faithfulness_Correlation(test_loader, Attention_weights["attention"], pertrub='Noise') + # print('Attention faithfulness correlation', F_attention) + + #R_attribution = model.Data_Randomization( + # test_loader, attributions_IG, IntegratedGradients) + #print('Distance Data randmoization score attribution', R_attribution) + # R_attention = model.Data_Randomization( + # test_loader, Attention_weights["attention"], "Attention", test_dataset=test_dataset) + # print('Distance Data randmoization score attention', R_attention) model.set_weight("balanced", train_dataset) test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0][ diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 71cb6f40..90177122 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -20,10 +20,9 @@ from icu_benchmarks.contants import RunMode import matplotlib.pyplot as plt from icu_benchmarks.models.similarity_func import correlation_spearman, distance_euclidean + gin.config.external_configurable(nn.functional.nll_loss, module="torch.nn.functional") -gin.config.external_configurable( - nn.functional.cross_entropy, module="torch.nn.functional" -) +gin.config.external_configurable(nn.functional.cross_entropy, module="torch.nn.functional") gin.config.external_configurable(nn.functional.mse_loss, module="torch.nn.functional") gin.config.external_configurable(mean_squared_error, module="sklearn.metrics") @@ -88,9 +87,7 @@ def save_model(self, save_path, file_name, file_extension): def check_supported_runmode(self, runmode: RunMode): if runmode not in self._supported_run_modes: - raise ValueError( - f"Runmode {runmode} not supported for {self.__class__.__name__}" - ) + raise ValueError(f"Runmode {runmode} not supported for {self.__class__.__name__}") return True @@ -163,9 +160,7 @@ def finalize_step(self, step_prefix=""): self.log_dict( { f"{step_prefix}/{name}": ( - np.float32(metric.compute()) - if isinstance(metric.compute(), np.float64) - else metric.compute() + np.float32(metric.compute()) if isinstance(metric.compute(), np.float64) else metric.compute() ) for name, metric in self.metrics[step_prefix].items() if "_Curve" not in name @@ -206,10 +201,7 @@ def configure_optimizers(self): def on_test_epoch_start(self) -> None: self.metrics = { - step_name: { - metric_name: metric() - for metric_name, metric in self.set_metrics().items() - } + step_name: {metric_name: metric() for metric_name, metric in self.set_metrics().items()} for step_name in ["train", "val", "test"] } return super().on_test_epoch_start() @@ -334,7 +326,6 @@ def step_fn(self, element, step_prefix=""): data[key] = value """ if len(element) == 2: - data, labels = element[0], (element[1]).to(self.device) if isinstance(data, list): for i in range(len(data)): @@ -355,9 +346,7 @@ def step_fn(self, element, step_prefix=""): else: data = data.float().to(self.device) else: - raise Exception( - "Loader should return either (data, label) or (data, label, mask)" - ) + raise Exception("Loader should return either (data, label) or (data, label, mask)") out = self(data) # If aux_loss is present, it is returned as a tuple @@ -367,30 +356,19 @@ def step_fn(self, element, step_prefix=""): aux_loss = 0 # Get prediction and target - prediction = ( - torch.masked_select(out, mask.unsqueeze(-1)) - .reshape(-1, out.shape[-1]) - .to(self.device) - ) + prediction = torch.masked_select(out, mask.unsqueeze(-1)).reshape(-1, out.shape[-1]).to(self.device) target = torch.masked_select(labels, mask).to(self.device) if prediction.shape[-1] > 1 and self.run_mode == RunMode.classification: # Classification task - loss = ( - self.loss( - prediction, target.long(), weight=self.loss_weights.to(self.device) - ) - + aux_loss - ) + loss = self.loss(prediction, target.long(), weight=self.loss_weights.to(self.device)) + aux_loss # Returns torch.long because negative log likelihood loss elif self.run_mode == RunMode.regression: # Regression task loss = self.loss(prediction[:, 0], target.float()) + aux_loss else: - raise ValueError( - f"Run mode {self.run_mode} not yet supported. Please implement it." - ) + raise ValueError(f"Run mode {self.run_mode} not yet supported. Please implement it.") transformed_output = self.output_transform((prediction, target)) for key, value in self.metrics[step_prefix].items(): @@ -407,9 +385,7 @@ def step_fn(self, element, step_prefix=""): value.update(transformed_output[0], transformed_output[1]) else: value.update(transformed_output) - self.log( - f"{step_prefix}/loss", loss, on_step=False, on_epoch=True, sync_dist=True - ) + self.log(f"{step_prefix}/loss", loss, on_step=False, on_epoch=True, sync_dist=True) return loss @@ -489,30 +465,19 @@ def step_fn(self, element, step_prefix=""): aux_loss = 0 # Get prediction and target - prediction = ( - torch.masked_select(out, mask.unsqueeze(-1)) - .reshape(-1, out.shape[-1]) - .to(self.device) - ) + prediction = torch.masked_select(out, mask.unsqueeze(-1)).reshape(-1, out.shape[-1]).to(self.device) target = torch.masked_select(labels, mask).to(self.device) if prediction.shape[-1] > 1 and self.run_mode == RunMode.classification: # Classification task - loss = ( - self.loss( - prediction, target.long(), weight=self.loss_weights.to(self.device) - ) - + aux_loss - ) + loss = self.loss(prediction, target.long(), weight=self.loss_weights.to(self.device)) + aux_loss # Returns torch.long because negative log likelihood loss elif self.run_mode == RunMode.regression: # Regression task loss = self.loss(prediction[:, 0], target.float()) + aux_loss else: - raise ValueError( - f"Run mode {self.run_mode} not yet supported. Please implement it." - ) + raise ValueError(f"Run mode {self.run_mode} not yet supported. Please implement it.") transformed_output = self.output_transform((prediction, target)) for key, value in self.metrics[step_prefix].items(): @@ -529,12 +494,10 @@ def step_fn(self, element, step_prefix=""): value.update(transformed_output[0], transformed_output[1]) else: value.update(transformed_output) - self.log( - f"{step_prefix}/loss", loss, on_step=False, on_epoch=True, sync_dist=True - ) + self.log(f"{step_prefix}/loss", loss, on_step=False, on_epoch=True, sync_dist=True) return loss - def explantation_captum(self, test_loader, method, log_dir='.', plot=False): + def explantation_captum(self, test_loader, method, log_dir=".", plot=False): # Initialize lists to store attribution values for all instances all_attrs = [] @@ -587,7 +550,7 @@ def explantation_captum(self, test_loader, method, log_dir='.', plot=False): # Compute mean along the batch dimension means = all_attrs.mean(axis=(0, 2)) # Normalize the means values to range [0, 1] - normalized_means = (means - means.min()) / (means.max() - means.min()) + # normalized_means = (means - means.min()) / (means.max() - means.min()) if plot: # Create x values (assuming you want a simple sequential x-axis) # Assuming you have 24 values @@ -604,7 +567,7 @@ def explantation_captum(self, test_loader, method, log_dir='.', plot=False): markersize=8, ) plt.xlabel("Time Step") - plt.ylabel("Normalized Attribution") + plt.ylabel(" Attribution") plt.title("Attribution Values") plt.xticks(x_values) # Set x-ticks to match the number of features plt.tight_layout() @@ -613,7 +576,7 @@ def explantation_captum(self, test_loader, method, log_dir='.', plot=False): x_values = np.arange(1, 25) plt.plot( x_values, - normalized_means, + means, marker="o", color="skyblue", linestyle="-", @@ -628,29 +591,31 @@ def explantation_captum(self, test_loader, method, log_dir='.', plot=False): plt.savefig(log_dir / "attribution_plot.png", bbox_inches="tight") return means - def Faithfulness_Correlation(self, test_loader, attribution, similarity_func=None, nr_runs=100, pertrub=None, subset_size=4): + def Faithfulness_Correlation( + self, test_loader, attribution, similarity_func=None, nr_runs=100, pertrub=None, subset_size=2 + ): """ - Implementation of faithfulness correlation by Bhatt et al., 2020. + Implementation of faithfulness correlation by Bhatt et al., 2020. - The Faithfulness Correlation metric intend to capture an explanation's relative faithfulness - (or 'fidelity') with respect to the model behaviour. + The Faithfulness Correlation metric intend to capture an explanation's relative faithfulness + (or 'fidelity') with respect to the model behaviour. - Faithfulness correlation scores shows to what extent the predicted logits of each modified test point and - the average explanation attribution for only the subset of features are (linearly) correlated, taking the - average over multiple runs and test samples. The metric returns one float per input-attribution pair that - ranges between -1 and 1, where higher scores are better. + Faithfulness correlation scores shows to what extent the predicted logits of each modified test point and + the average explanation attribution for only the subset of features are (linearly) correlated, taking the + average over multiple runs and test samples. The metric returns one float per input-attribution pair that + ranges between -1 and 1, where higher scores are better. - For each test sample, |S| features are randomly selected and replace them with baseline values (zero baseline - or average of set). Thereafter, Pearson’s correlation coefficient between the predicted logits of each modified - test point and the average explanation attribution for only the subset of features is calculated. Results is - average over multiple runs and several test samples. - This code is adapted from the quantus libray to suit our use case + For each test sample, |S| features are randomly selected and replace them with baseline values (zero baseline + or average of set). Thereafter, Pearson’s correlation coefficient between the predicted logits of each modified + test point and the average explanation attribution for only the subset of features is calculated. Results is + average over multiple runs and several test samples. + This code is adapted from the quantus libray to suit our use case - References: - 1) Umang Bhatt et al.: "Evaluating and aggregating feature-based model - explanations." IJCAI (2020): 3016-3022. - 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. - """ + References: + 1) Umang Bhatt et al.: "Evaluating and aggregating feature-based model + explanations." IJCAI (2020): 3016-3022. + 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. + """ if torch.is_tensor(attribution): # Convert the tensor to a NumPy array @@ -661,9 +626,7 @@ def Faithfulness_Correlation(self, test_loader, attribution, similarity_func=Non pertrub = "baseline" similarities = [] for batch in test_loader: - for key, value in batch[0].items(): - batch[0][key] = batch[0][key].to(self.device) x = batch[0] data = ( @@ -726,7 +689,7 @@ def Faithfulness_Correlation(self, test_loader, attribution, similarity_func=Non similarities.append(similarity_func(pred_deltas, att_sums)) return np.nanmean(similarities) - def Data_Randomization(self, test_loader, attribution, explain_method, similarity_func=None, test_dataset=None): + def Data_Randomization(self, test_loader, attribution, explain_method, random_model, similarity_func=None, test_dataset=None): """ Implementation of the Random Logit Metric by Sixt et al., 2020. @@ -743,22 +706,15 @@ def Data_Randomization(self, test_loader, attribution, explain_method, similarit a_perturbed = [] if similarity_func is None: similarity_func = distance_euclidean - if (explain_method == 'Attention'): - for batch in test_loader: - print(batch[1][0]) + if explain_method == "Attention": - test_dataset.randomize_labels(num_classes=self.num_classes) - for batch in test_loader: - print(batch[1][0]) # generate random labels - Attention_weights = self.interpertations(test_loader) # test_loader works by reference + Attention_weights = random_model.interpertations(test_loader) # test_loader works by reference score = similarity_func(Attention_weights["attention"].cpu().numpy(), attribution.cpu().numpy()) else: for batch in test_loader: - for key, value in batch[0].items(): - - batch[0][key] = batch[0][key].to(self.device) + batch[0][key] = batch[0][key].to(random_model.device) x = batch[0] y = batch[1][0] data = ( @@ -775,23 +731,21 @@ def Data_Randomization(self, test_loader, attribution, explain_method, similarit x["target_scale"], ) - y_off = np.random.randint(low=0, high=self.num_classes, size=y.shape) - baselines = ( - torch.zeros_like(data[0]).to(self.device), # encoder_cat, set to zero - torch.zeros_like(data[1]).to(self.device), # encoder_cont, set to zero - torch.zeros_like(data[2]).to(self.device), # encoder_target, set to zero - data[3].to(self.device), # encoder_lengths, leave unchanged - torch.zeros_like(data[4]).to(self.device), # decoder_cat, set to zero - torch.zeros_like(data[5]).to(self.device), # decoder_cont, set to zero - torch.zeros_like(data[6]).to(self.device), # decoder_target, set to zero - data[7].to(self.device), # decoder_lengths, leave unchanged - torch.zeros_like(data[8]).to(self.device), # decoder_time_idx, set to zero - data[9].to(self.device), # groups, leave unchanged - data[10].to(self.device), # target_scale, leave unchanged + torch.zeros_like(data[0]).to(random_model.device), # encoder_cat, set to zero + torch.zeros_like(data[1]).to(random_model.device), # encoder_cont, set to zero + torch.zeros_like(data[2]).to(random_model.device), # encoder_target, set to zero + data[3].to(random_model.device), # encoder_lengths, leave unchanged + torch.zeros_like(data[4]).to(random_model.device), # decoder_cat, set to zero + torch.zeros_like(data[5]).to(random_model.device), # decoder_cont, set to zero + torch.zeros_like(data[6]).to(random_model.device), # decoder_target, set to zero + data[7].to(random_model.device), # decoder_lengths, leave unchanged + torch.zeros_like(data[8]).to(random_model.device), # decoder_time_idx, set to zero + data[9].to(random_model.device), # groups, leave unchanged + data[10].to(random_model.device), # target_scale, leave unchanged ) - self.train() - explantation = explain_method(self.forward_captum) + + explantation = explain_method(random_model.forward_captum) # Reformat attributions. attr_all_timesteps = [] for time_step in range(0, 24): @@ -805,8 +759,8 @@ def Data_Randomization(self, test_loader, attribution, explain_method, similarit a_perturbed = np.mean(a_perturbed, axis=(0, 1, 3)) # Normalize the means values to range [0, 1] - normalized_a_perturbed = (a_perturbed - a_perturbed.min()) / (a_perturbed.max() - a_perturbed.min()) - score = similarity_func(normalized_a_perturbed, attribution) + # normalized_a_perturbed = (a_perturbed - a_perturbed.min()) / (a_perturbed.max() - a_perturbed.min()) + score = similarity_func(a_perturbed, attribution) return score @@ -854,15 +808,9 @@ def set_metrics(self, labels): # Regression else: - if ( - self.scaler is not None - ): # We invert transform the labels and predictions if they were scaled. - self.output_transform = lambda x: self.scaler.inverse_transform( - x.reshape(-1, 1) - ) - self.label_transform = lambda x: self.scaler.inverse_transform( - x.reshape(-1, 1) - ) + if self.scaler is not None: # We invert transform the labels and predictions if they were scaled. + self.output_transform = lambda x: self.scaler.inverse_transform(x.reshape(-1, 1)) + self.label_transform = lambda x: self.scaler.inverse_transform(x.reshape(-1, 1)) else: self.output_transform = lambda x: x self.label_transform = lambda x: x @@ -897,9 +845,7 @@ def fit_model(self, train_data, train_labels, val_data, val_labels): def validation_step(self, val_dataset, _): val_rep, val_label = val_dataset.get_data_and_labels() - val_rep, val_label = torch.from_numpy(val_rep).to( - self.device - ), torch.from_numpy(val_label).to(self.device) + val_rep, val_label = torch.from_numpy(val_rep).to(self.device), torch.from_numpy(val_label).to(self.device) self.set_metrics(val_label) val_pred = self.predict(val_rep) @@ -942,9 +888,7 @@ def log_metrics(self, label, pred, metric_type): self.log_dict( { # MPS dependent type casting - f"{metric_type}/{name}": metric( - self.label_transform(label), self.output_transform(pred) - ) + f"{metric_type}/{name}": metric(self.label_transform(label), self.output_transform(pred)) if not self.mps else metric(self.label_transform(label), self.output_transform(pred)) # Fore very metric @@ -982,9 +926,7 @@ def set_model_args(self, model, *args, **kwargs): # Get passed keyword arguments arguments = locals()["kwargs"] # Get valid hyperparameters - hyperparams = { - key: value for key, value in arguments.items() if key in possible_hps - } + hyperparams = {key: value for key, value in arguments.items() if key in possible_hps} logging.debug(f"Creating model with: {hyperparams}.") return model(**hyperparams) @@ -1037,9 +979,7 @@ def set_metrics(self): def init_weights(self, init_type="normal", gain=0.02): def init_func(m): classname = m.__class__.__name__ - if hasattr(m, "weight") and ( - classname.find("Conv") != -1 or classname.find("Linear") != -1 - ): + if hasattr(m, "weight") and (classname.find("Conv") != -1 or classname.find("Linear") != -1): if init_type == ImputationInit.NORMAL: nn.init.normal_(m.weight.data, 0.0, gain) elif init_type == ImputationInit.XAVIER: @@ -1049,9 +989,7 @@ def init_func(m): elif init_type == ImputationInit.ORTHOGONAL: nn.init.orthogonal_(m.weight.data, gain=gain) else: - raise NotImplementedError( - f"Initialization method {init_type} is not implemented" - ) + raise NotImplementedError(f"Initialization method {init_type} is not implemented") if hasattr(m, "bias") and m.bias is not None: nn.init.constant_(m.bias.data, 0.0) elif classname.find("BatchNorm2d") != -1: diff --git a/icu_benchmarks/run.py b/icu_benchmarks/run.py index 4d9fb2f4..70fbfa43 100644 --- a/icu_benchmarks/run.py +++ b/icu_benchmarks/run.py @@ -55,6 +55,7 @@ def main(my_args=tuple(sys.argv[1:])): explain = args.explain pytorch_forecasting = args.pytorch_forecasting XAI_metric = args.XAI_metric + random_labels = args.random_labels # Load task config gin.parse_config_file(f"configs/tasks/{task}.gin") mode = get_mode() @@ -67,19 +68,11 @@ def main(my_args=tuple(sys.argv[1:])): logging.info(f"Task mode: {mode}.") # Set train size to fine tune size if fine tune is set, else use custom train size - train_size = ( - args.fine_tune - if args.fine_tune is not None - else args.samples - if args.samples is not None - else None - ) + train_size = args.fine_tune if args.fine_tune is not None else args.samples if args.samples is not None else None # Whether to load weights from a previous run load_weights = evaluate or args.fine_tune is not None - pretrained_imputation_model = load_pretrained_imputation_model( - args.pretrained_imputation - ) + pretrained_imputation_model = load_pretrained_imputation_model(args.pretrained_imputation) # Log imputation model to wandb update_wandb_config( { @@ -88,16 +81,12 @@ def main(my_args=tuple(sys.argv[1:])): else "None" } ) - + random_model_dir = args.random_model log_dir_name = args.log_dir / name log_dir = ( (log_dir_name / experiment) if experiment - else ( - log_dir_name - / (args.task_name if args.task_name is not None else args.task) - / model - ) + else (log_dir_name / (args.task_name if args.task_name is not None else args.task) / model) ) log_full_line(f"Logging to {log_dir}.", logging.INFO) @@ -120,9 +109,7 @@ def main(my_args=tuple(sys.argv[1:])): # Load pretrained model in evaluate mode or when finetuning if load_weights: if args.source_dir is None: - raise ValueError( - "Please specify a source directory when evaluating or fine-tuning." - ) + raise ValueError("Please specify a source directory when evaluating or fine-tuning.") log_dir /= f"_from_{args.source_name}" name_datasets(args.source_name, args.source_name, args.name) if args.fine_tune: @@ -131,12 +118,9 @@ def main(my_args=tuple(sys.argv[1:])): run_dir = create_run_dir(log_dir) source_dir = args.source_dir logging.info( - f"Will load weights from {source_dir} and bind train gin-config. Note: this might override your config." - ) + f"Will load weights from {source_dir} and bind train gin-config. Note: this might override your config.") gin.parse_config_file(source_dir / "train_config.gin") - elif ( - args.samples and args.source_dir is not None - ): # Train model with limited samples and bind existing config + elif args.samples and args.source_dir is not None: # Train model with limited samples and bind existing config logging.info("Binding train gin-config. Note: this might override your config.") gin.parse_config_file(args.source_dir / "train_config.gin") log_dir /= f"samples_{args.fine_tune}" @@ -147,22 +131,15 @@ def main(my_args=tuple(sys.argv[1:])): name_datasets(args.name, args.name, args.name) hp_checkpoint = log_dir / args.hp_checkpoint if args.hp_checkpoint else None model_path = ( - Path("configs") - / ( - "imputation_models" - if mode == RunMode.imputation - else "prediction_models" - ) - / f"{model}.gin" + Path("configs") / ("imputation_models" if mode == + RunMode.imputation else "prediction_models") / f"{model}.gin" ) gin_config_files = ( [Path(f"configs/experiments/{args.experiment}.gin")] if args.experiment else [model_path, Path(f"configs/tasks/{task}.gin")] ) - gin.parse_config_files_and_bindings( - gin_config_files, args.hyperparams, finalize_config=False - ) + gin.parse_config_files_and_bindings(gin_config_files, args.hyperparams, finalize_config=False) log_full_line(f"Data directory: {data_dir.resolve()}", level=logging.INFO) run_dir = create_run_dir(log_dir) choose_and_bind_hyperparameters( @@ -177,6 +154,9 @@ def main(my_args=tuple(sys.argv[1:])): load_cache=args.load_cache, verbose=verbose, pytorch_forecasting=pytorch_forecasting, + random_labels=random_labels, + + ) log_full_line(f"Logging to {run_dir.resolve()}", level=logging.INFO) @@ -211,6 +191,7 @@ def main(my_args=tuple(sys.argv[1:])): explain=explain, pytorch_forecasting=pytorch_forecasting, XAI_metric=XAI_metric, + random_model_dir=random_model_dir ) log_full_line("FINISHED TRAINING", level=logging.INFO, char="=", num_newlines=3) diff --git a/icu_benchmarks/run_utils.py b/icu_benchmarks/run_utils.py index cc4b7960..6caf4bee 100644 --- a/icu_benchmarks/run_utils.py +++ b/icu_benchmarks/run_utils.py @@ -23,9 +23,7 @@ def build_parser() -> ArgumentParser: Returns: The configured ArgumentParser. """ - parser = ArgumentParser( - description="Framework for benchmarking ML/DL models on ICU data" - ) + parser = ArgumentParser(description="Framework for benchmarking ML/DL models on ICU data") parser.add_argument( "-d", @@ -42,12 +40,8 @@ def build_parser() -> ArgumentParser: help="Name of the task gin.", ) parser.add_argument("-n", "--name", help="Name of the (target) dataset.") - parser.add_argument( - "-tn", "--task-name", help="Name of the task, used for naming experiments." - ) - parser.add_argument( - "-m", "--model", default="LGBMClassifier", help="Name of the model gin." - ) + parser.add_argument("-tn", "--task-name", help="Name of the task, used for naming experiments.") + parser.add_argument("-m", "--model", default="LGBMClassifier", help="Name of the model gin.") parser.add_argument("-e", "--experiment", help="Name of the experiment gin.") parser.add_argument( "-l", @@ -71,12 +65,8 @@ def build_parser() -> ArgumentParser: help="Set to log verbosly. Disable for clean logs.", ) parser.add_argument("--cpu", default=False, action=BOA, help="Set to use CPU.") - parser.add_argument( - "-db", "--debug", default=False, action=BOA, help="Set to load less data." - ) - parser.add_argument( - "--reproducible", default=True, action=BOA, help="Make torch reproducible." - ) + parser.add_argument("-db", "--debug", default=False, action=BOA, help="Set to load less data.") + parser.add_argument("--reproducible", default=True, action=BOA, help="Make torch reproducible.") parser.add_argument( "-lc", "--load_cache", @@ -91,9 +81,7 @@ def build_parser() -> ArgumentParser: action=BOA, help="Set to generate data cache.", ) - parser.add_argument( - "-p", "--preprocessor", type=Path, help="Load custom preprocessor from file." - ) + parser.add_argument("-p", "--preprocessor", type=Path, help="Load custom preprocessor from file.") parser.add_argument("-pl", "--plot", action=BOA, help="Generate common plots.") parser.add_argument( "-wd", @@ -107,18 +95,10 @@ def build_parser() -> ArgumentParser: type=str, help="Path to pretrained imputation model.", ) - parser.add_argument( - "-hp", "--hyperparams", nargs="+", help="Hyperparameters for model." - ) - parser.add_argument( - "--tune", default=False, action=BOA, help="Find best hyperparameters." - ) - parser.add_argument( - "--hp-checkpoint", type=Path, help="Use previous hyperparameter checkpoint." - ) - parser.add_argument( - "--eval", default=False, action=BOA, help="Only evaluate model, skip training." - ) + parser.add_argument("-hp", "--hyperparams", nargs="+", help="Hyperparameters for model.") + parser.add_argument("--tune", default=False, action=BOA, help="Find best hyperparameters.") + parser.add_argument("--hp-checkpoint", type=Path, help="Use previous hyperparameter checkpoint.") + parser.add_argument("--eval", default=False, action=BOA, help="Only evaluate model, skip training.") parser.add_argument( "--complete-train", default=False, @@ -132,12 +112,8 @@ def build_parser() -> ArgumentParser: type=int, help="Finetune model with amount of train data.", ) - parser.add_argument( - "-sn", "--source-name", type=Path, help="Name of the source dataset." - ) - parser.add_argument( - "--source-dir", type=Path, help="Directory containing gin and model weights." - ) + parser.add_argument("-sn", "--source-name", type=Path, help="Name of the source dataset.") + parser.add_argument("--source-dir", type=Path, help="Directory containing gin and model weights.") parser.add_argument( "-sa", "--samples", @@ -163,6 +139,19 @@ def build_parser() -> ArgumentParser: action=BOA, help="Compare explantations ", ) + parser.add_argument( + "--random_labels", + default=False, + action=BOA, + help="train model on random label dataset", + ) + + parser.add_argument( + "-random_model", + default=Path("."), + type=Path, + help="Log directory for model weights that is trained on random labels", + ) return parser @@ -192,21 +181,15 @@ def create_run_dir(log_dir: Path, randomly_searched_params: str = None) -> Path: def import_preprocessor(preprocessor_path: str): # Import custom supplied preprocessor - log_full_line( - f"Importing custom preprocessor from {preprocessor_path}.", logging.INFO - ) + log_full_line(f"Importing custom preprocessor from {preprocessor_path}.", logging.INFO) try: - spec = importlib.util.spec_from_file_location( - "CustomPreprocessor", preprocessor_path - ) + spec = importlib.util.spec_from_file_location("CustomPreprocessor", preprocessor_path) module = importlib.util.module_from_spec(spec) sys.modules["preprocessor"] = module spec.loader.exec_module(module) gin.bind_parameter("preprocess.preprocessor", module.CustomPreprocessor) except Exception as e: - logging.error( - f"Could not import custom preprocessor from {preprocessor_path}: {e}" - ) + logging.error(f"Could not import custom preprocessor from {preprocessor_path}: {e}") def aggregate_results(log_dir: Path, execution_time: timedelta = None): @@ -250,14 +233,10 @@ def aggregate_results(log_dir: Path, execution_time: timedelta = None): # Calculate the population standard deviation over aggregated results over folds/iterations # Divide by sqrt(n) to get standard deviation. - std_scores = { - metric: (pstdev(list) / sqrt(len(list))) for metric, list in list_scores.items() - } + std_scores = {metric: (pstdev(list) / sqrt(len(list))) for metric, list in list_scores.items()} confidence_interval = { - metric: ( - stats.t.interval(0.95, len(list) - 1, loc=mean(list), scale=stats.sem(list)) - ) + metric: (stats.t.interval(0.95, len(list) - 1, loc=mean(list), scale=stats.sem(list))) for metric, list in list_scores.items() } @@ -265,9 +244,7 @@ def aggregate_results(log_dir: Path, execution_time: timedelta = None): "avg": averaged_scores, "std": std_scores, "CI_0.95": confidence_interval, - "execution_time": execution_time.total_seconds() - if execution_time is not None - else 0.0, + "execution_time": execution_time.total_seconds() if execution_time is not None else 0.0, } with open(log_dir / "aggregated_test_metrics.json", "w") as f: @@ -283,14 +260,10 @@ def aggregate_results(log_dir: Path, execution_time: timedelta = None): def name_datasets(train="default", val="default", test="default"): """Names the datasets for logging (optional).""" - gin.bind_parameter( - "train_common.dataset_names", {"train": train, "val": val, "test": test} - ) + gin.bind_parameter("train_common.dataset_names", {"train": train, "val": val, "test": test}) -def log_full_line( - msg: str, level: int = logging.INFO, char: str = "-", num_newlines: int = 0 -): +def log_full_line(msg: str, level: int = logging.INFO, char: str = "-", num_newlines: int = 0): """Logs a full line of a given character with a message centered. Args: @@ -318,43 +291,25 @@ def load_pretrained_imputation_model(use_pretrained_imputation): Args: use_pretrained_imputation: Path to the pretrained imputation model. """ - if ( - use_pretrained_imputation is not None - and not Path(use_pretrained_imputation).exists() - ): + if use_pretrained_imputation is not None and not Path(use_pretrained_imputation).exists(): logging.warning("The specified pretrained imputation model does not exist.") use_pretrained_imputation = None if use_pretrained_imputation is not None: - logging.info( - "Using pretrained imputation from" + str(use_pretrained_imputation) - ) - pretrained_imputation_model_checkpoint = torch.load( - use_pretrained_imputation, map_location=torch.device("cpu") - ) + logging.info("Using pretrained imputation from" + str(use_pretrained_imputation)) + pretrained_imputation_model_checkpoint = torch.load(use_pretrained_imputation, map_location=torch.device("cpu")) if isinstance(pretrained_imputation_model_checkpoint, dict): imputation_model_class = pretrained_imputation_model_checkpoint["class"] pretrained_imputation_model = imputation_model_class( - **pretrained_imputation_model_checkpoint["hyper_parameters"] - ) - pretrained_imputation_model.set_trained_columns( - pretrained_imputation_model_checkpoint["trained_columns"] - ) - pretrained_imputation_model.load_state_dict( - pretrained_imputation_model_checkpoint["state_dict"] - ) + **pretrained_imputation_model_checkpoint["hyper_parameters"]) + pretrained_imputation_model.set_trained_columns(pretrained_imputation_model_checkpoint["trained_columns"]) + pretrained_imputation_model.load_state_dict(pretrained_imputation_model_checkpoint["state_dict"]) else: pretrained_imputation_model = pretrained_imputation_model_checkpoint - pretrained_imputation_model = pretrained_imputation_model.to( - "cuda" if torch.cuda.is_available() else "cpu" - ) + pretrained_imputation_model = pretrained_imputation_model.to("cuda" if torch.cuda.is_available() else "cpu") try: - logging.info( - f"imputation model device: {next(pretrained_imputation_model.parameters()).device}" - ) - pretrained_imputation_model.device = next( - pretrained_imputation_model.parameters() - ).device + logging.info(f"imputation model device: {next(pretrained_imputation_model.parameters()).device}") + pretrained_imputation_model.device = next(pretrained_imputation_model.parameters()).device except Exception as e: logging.debug(f"Could not set device of imputation model: {e}") else: @@ -375,9 +330,7 @@ def setup_logging(date_format, log_format, verbose): logging.basicConfig(format=log_format, datefmt=date_format) loggers = ["pytorch_lightning", "lightning_fabric"] for logger in loggers: - logging.getLogger(logger).handlers[0].setFormatter( - logging.Formatter(log_format, datefmt=date_format) - ) + logging.getLogger(logger).handlers[0].setFormatter(logging.Formatter(log_format, datefmt=date_format)) if not verbose: logging.getLogger().setLevel(logging.INFO) diff --git a/icu_benchmarks/tuning/hyperparameters.py b/icu_benchmarks/tuning/hyperparameters.py index cf3d0c17..7c02735e 100644 --- a/icu_benchmarks/tuning/hyperparameters.py +++ b/icu_benchmarks/tuning/hyperparameters.py @@ -37,6 +37,7 @@ def choose_and_bind_hyperparameters( verbose: bool = False, wandb: bool = False, pytorch_forecasting: bool = False, + random_labels: bool = False, ): """Choose hyperparameters to tune and bind them to gin. @@ -68,9 +69,7 @@ def choose_and_bind_hyperparameters( return # Collect hyperparameters. - hyperparams_bounds, hyperparams_names = collect_bound_hyperparameters( - hyperparams, scopes - ) + hyperparams_bounds, hyperparams_names = collect_bound_hyperparameters(hyperparams, scopes) if do_tune and not hyperparams_bounds: logging.info("No hyperparameters to tune, skipping tuning.") @@ -81,16 +80,12 @@ def choose_and_bind_hyperparameters( if checkpoint: checkpoint_path = checkpoint / checkpoint_file if not checkpoint_path.exists(): - logging.warning( - f"Hyperparameter checkpoint {checkpoint_path} does not exist." - ) + logging.warning(f"Hyperparameter checkpoint {checkpoint_path} does not exist.") logging.info("Attempting to find latest checkpoint file.") checkpoint_path = find_checkpoint(log_dir.parent, checkpoint_file) # Check if we found a checkpoint file if checkpoint_path: - n_calls, configuration, evaluation = load_checkpoint( - checkpoint_path, n_calls - ) + n_calls, configuration, evaluation = load_checkpoint(checkpoint_path, n_calls) # Check if we surpassed maximum tuning iterations if n_calls <= 0: logging.log( @@ -98,9 +93,7 @@ def choose_and_bind_hyperparameters( "No more hyperparameter tuning iterations left, skipping tuning.", ) logging.info("Training with these hyperparameters:") - bind_gin_params( - hyperparams_names, configuration[np.argmin(evaluation)] - ) # bind best hyperparameters + bind_gin_params(hyperparams_names, configuration[np.argmin(evaluation)]) # bind best hyperparameters return else: logging.warning("No checkpoint file found, starting from scratch.") @@ -125,6 +118,7 @@ def bind_params_and_train(hyperparams): verbose=verbose, wandb=wandb, pytorch_forecasting=pytorch_forecasting, + random_labels=random_labels ) header = ["ITERATION"] + hyperparams_names + ["LOSS AT ITERATION"] @@ -139,9 +133,7 @@ def tune_step_callback(res): table_cells = [len(res.x_iters)] + res.x_iters[-1] + [res.func_vals[-1]] highlight = res.x_iters[-1] == res.x # highlight if best so far log_table_row(header, TUNE) - log_table_row( - table_cells, TUNE, align=Align.RIGHT, header=header, highlight=highlight - ) + log_table_row(table_cells, TUNE, align=Align.RIGHT, header=header, highlight=highlight) wandb_log({"hp-iteration": len(res.x_iters)}) if do_tune: @@ -156,9 +148,7 @@ def tune_step_callback(res): logging.log(TUNE, "Hyperparameter tuning disabled") if configuration: # We have loaded a checkpoint, use the best hyperparameters. - logging.info( - "Training with the best hyperparameters from loaded checkpoint:" - ) + logging.info("Training with the best hyperparameters from loaded checkpoint:") bind_gin_params(hyperparams_names, configuration[np.argmin(evaluation)]) else: logging.log(TUNE, "Choosing hyperparameters randomly from bounds.") From f5e8a3d66cd4c72e157aaf9c190e79e91e024809 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Tue, 7 Nov 2023 13:54:20 +0100 Subject: [PATCH 101/142] added option to load model trained with random labels --- icu_benchmarks/models/train.py | 28 ++++++++++++++++++++-------- icu_benchmarks/models/wrappers.py | 1 + icu_benchmarks/run.py | 1 + icu_benchmarks/run_utils.py | 2 +- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index f7a38790..68511625 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -75,7 +75,7 @@ def train_common( pytorch_forecasting: bool = False, XAI_metric: bool = False, random_labels: bool = False, - random_model_dir: str =None + random_model_dir: str = None ): """Common wrapper to train all benchmarked models. @@ -259,6 +259,9 @@ def train_common( if not eval_only: if model.requires_backprop: logging.info("Training DL model.") + print(next(train_loader)[1][0]) + print(next(val_loader)[1][0]) + print(next(test_loader)[1][0]) trainer.fit( model, train_dataloaders=train_loader, val_dataloaders=val_loader ) @@ -293,13 +296,22 @@ def train_common( # print('Attributions faithfulness correlation', F_attribution) # F_attention = model.Faithfulness_Correlation(test_loader, Attention_weights["attention"], pertrub='Noise') # print('Attention faithfulness correlation', F_attention) - - #R_attribution = model.Data_Randomization( - # test_loader, attributions_IG, IntegratedGradients) - #print('Distance Data randmoization score attribution', R_attribution) - # R_attention = model.Data_Randomization( - # test_loader, Attention_weights["attention"], "Attention", test_dataset=test_dataset) - # print('Distance Data randmoization score attention', R_attention) + print(random_model_dir) + path = Path(random_model_dir) + print(path) + random_model = load_model( + model, + source_dir=path, + pl_model=pl_model, + train_dataset=train_dataset, + optimizer=optimizer, + ) + R_attribution = model.Data_Randomization( + test_loader, attributions_IG, IntegratedGradients, random_model) + print('Distance Data randmoization score attribution', R_attribution) + # R_attention = model.Data_Randomization( + # test_loader, Attention_weights["attention"], "Attention", test_dataset=test_dataset) + # print('Distance Data randmoization score attention', R_attention) model.set_weight("balanced", train_dataset) test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0][ diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 90177122..20d13a25 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -703,6 +703,7 @@ def Data_Randomization(self, test_loader, attribution, explain_method, random_mo 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. """ + print(random_model) a_perturbed = [] if similarity_func is None: similarity_func = distance_euclidean diff --git a/icu_benchmarks/run.py b/icu_benchmarks/run.py index 70fbfa43..b8d8a98d 100644 --- a/icu_benchmarks/run.py +++ b/icu_benchmarks/run.py @@ -81,6 +81,7 @@ def main(my_args=tuple(sys.argv[1:])): else "None" } ) + print(args.random_model) random_model_dir = args.random_model log_dir_name = args.log_dir / name log_dir = ( diff --git a/icu_benchmarks/run_utils.py b/icu_benchmarks/run_utils.py index 6caf4bee..4bc3985c 100644 --- a/icu_benchmarks/run_utils.py +++ b/icu_benchmarks/run_utils.py @@ -147,7 +147,7 @@ def build_parser() -> ArgumentParser: ) parser.add_argument( - "-random_model", + "--random_model", default=Path("."), type=Path, help="Log directory for model weights that is trained on random labels", From 78146cc077d82e7289b25f088e5fc6d632aeec36 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Tue, 7 Nov 2023 14:44:04 +0100 Subject: [PATCH 102/142] added shapley explantations --- configs/prediction_models/common/DLCommon.gin | 2 +- icu_benchmarks/models/train.py | 74 ++++++++++--------- icu_benchmarks/models/wrappers.py | 6 +- icu_benchmarks/run.py | 1 - 4 files changed, 44 insertions(+), 39 deletions(-) diff --git a/configs/prediction_models/common/DLCommon.gin b/configs/prediction_models/common/DLCommon.gin index fa4b440f..8f5d648e 100644 --- a/configs/prediction_models/common/DLCommon.gin +++ b/configs/prediction_models/common/DLCommon.gin @@ -15,7 +15,7 @@ base_regression_preprocessor.generate_features = False # Train params train_common.optimizer = @Adam train_common.epochs = 1000 -train_common.batch_size = 64 +train_common.batch_size = 128 train_common.patience = 20 train_common.min_delta = 1e-4 diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 68511625..3b35dd73 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -27,7 +27,7 @@ from icu_benchmarks.contants import RunMode from icu_benchmarks.data.constants import DataSplit as Split from collections import OrderedDict -from captum.attr import IntegratedGradients, LayerLRP +from captum.attr import IntegratedGradients, ShapleyValueSampling cpu_core_count = ( @@ -259,9 +259,9 @@ def train_common( if not eval_only: if model.requires_backprop: logging.info("Training DL model.") - print(next(train_loader)[1][0]) - print(next(val_loader)[1][0]) - print(next(test_loader)[1][0]) + print(next(iter(train_loader))[1][0]) + print(next(iter(val_loader))[1][0]) + print(next(iter(test_loader))[1][0]) trainer.fit( model, train_dataloaders=train_loader, val_dataloaders=val_loader ) @@ -277,41 +277,47 @@ def train_common( return 0 if explain: + # + attributions_SHAP = model.explantation_captum( + test_loader=test_loader, + method=ShapleyValueSampling, log_dir=log_dir, plot=True + ) + print("shap", attributions_SHAP) attributions_IG = model.explantation_captum( test_loader=test_loader, - method=IntegratedGradients, log_dir=log_dir, plot=True + method=IntegratedGradients, log_dir=log_dir, plot=True, n_steps=35, return_convergence_delta=True, ) - print("IG", attributions_IG) - # Attention_weights = model.interpertations(test_loader, log_dir, plot=True) - # print("attention", Attention_weights) + """ + Attention_weights = model.interpertations(test_loader, log_dir, plot=True) + print("attention", Attention_weights) if XAI_metric: - # ra2 = np.random.randint(low=0, high=101, size=24) - # random_attributions = np.random.normal(size=24) - # F_baseline = model.Faithfulness_Correlation(test_loader, random_attributions, pertrub='Noise') - # F_baseline2 = model.Faithfulness_Correlation(test_loader, random_attributions, pertrub='Noise') - # print('Random noraml faithfulness correlation', F_baseline) - # print('random unifrom ', F_baseline2) - # F_attribution = model.Faithfulness_Correlation(test_loader, attributions_IG, pertrub='Noise') - # print('Attributions faithfulness correlation', F_attribution) - # F_attention = model.Faithfulness_Correlation(test_loader, Attention_weights["attention"], pertrub='Noise') - # print('Attention faithfulness correlation', F_attention) - print(random_model_dir) - path = Path(random_model_dir) - print(path) - random_model = load_model( - model, - source_dir=path, - pl_model=pl_model, - train_dataset=train_dataset, - optimizer=optimizer, - ) - R_attribution = model.Data_Randomization( - test_loader, attributions_IG, IntegratedGradients, random_model) - print('Distance Data randmoization score attribution', R_attribution) - # R_attention = model.Data_Randomization( - # test_loader, Attention_weights["attention"], "Attention", test_dataset=test_dataset) - # print('Distance Data randmoization score attention', R_attention) + ra2 = np.random.randint(low=0, high=101, size=24) + random_attributions = np.random.normal(size=24) + F_baseline = model.Faithfulness_Correlation(test_loader, random_attributions, pertrub='Noise') + F_baseline2 = model.Faithfulness_Correlation(test_loader, random_attributions, pertrub='Noise') + print('Random noraml faithfulness correlation', F_baseline) + print('random unifrom ', F_baseline2) + F_attribution = model.Faithfulness_Correlation(test_loader, attributions_IG, pertrub='Noise') + print('Attributions faithfulness correlation', F_attribution) + F_attention = model.Faithfulness_Correlation(test_loader, Attention_weights["attention"], pertrub='Noise') + print('Attention faithfulness correlation', F_attention) + """ + # path = Path(random_model_dir) + + # random_model = load_model( + # model, + # source_dir=path, + # pl_model=pl_model, + # train_dataset=train_dataset, + # optimizer=optimizer, + # ) + # R_attribution = model.Data_Randomization( + # test_loader, attributions_IG, IntegratedGradients, random_model) + # print('Distance Data randmoization score attribution', R_attribution) + # R_attention = model.Data_Randomization( + # test_loader, Attention_weights["attention"], "Attention", test_dataset=test_dataset) + # print('Distance Data randmoization score attention', R_attention) model.set_weight("balanced", train_dataset) test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0][ diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 20d13a25..6220c875 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -497,7 +497,7 @@ def step_fn(self, element, step_prefix=""): self.log(f"{step_prefix}/loss", loss, on_step=False, on_epoch=True, sync_dist=True) return loss - def explantation_captum(self, test_loader, method, log_dir=".", plot=False): + def explantation_captum(self, test_loader, method, log_dir=".", plot=False, **kwargs): # Initialize lists to store attribution values for all instances all_attrs = [] @@ -538,7 +538,7 @@ def explantation_captum(self, test_loader, method, log_dir=".", plot=False): attr_all_timesteps = [] for time_step in range(0, 24): attr, delta = explantation.attribute( - data, target=(time_step, 1), return_convergence_delta=True, baselines=baselines, n_steps=35 + data, target=(time_step, 1), baselines=baselines, **kwargs ) # Convert attributions to numpy array and append to the list attr_all_timesteps.append(attr[0].cpu().detach().numpy()) @@ -592,7 +592,7 @@ def explantation_captum(self, test_loader, method, log_dir=".", plot=False): return means def Faithfulness_Correlation( - self, test_loader, attribution, similarity_func=None, nr_runs=100, pertrub=None, subset_size=2 + self, test_loader, attribution, similarity_func=None, nr_runs=100, pertrub=None, subset_size=6 ): """ Implementation of faithfulness correlation by Bhatt et al., 2020. diff --git a/icu_benchmarks/run.py b/icu_benchmarks/run.py index b8d8a98d..70fbfa43 100644 --- a/icu_benchmarks/run.py +++ b/icu_benchmarks/run.py @@ -81,7 +81,6 @@ def main(my_args=tuple(sys.argv[1:])): else "None" } ) - print(args.random_model) random_model_dir = args.random_model log_dir_name = args.log_dir / name log_dir = ( From 48dc500d833795a28f1102f8d76157168e6f5205 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Wed, 8 Nov 2023 15:29:50 +0100 Subject: [PATCH 103/142] latest changes --- icu_benchmarks/models/dl_models.py | 2 +- icu_benchmarks/models/wrappers.py | 11 +++++++---- icu_benchmarks/run.py | 3 ++- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index b26bd6b0..455e333c 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -641,7 +641,7 @@ def forward( # out = out["prediction"].reshape(1, -1) pred = self.logit(out["prediction"]) - + print(pred) return pred diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 6220c875..beebdb36 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -537,7 +537,7 @@ def explantation_captum(self, test_loader, method, log_dir=".", plot=False, **kw # Reformat attributions. attr_all_timesteps = [] for time_step in range(0, 24): - attr, delta = explantation.attribute( + attr = explantation.attribute( data, target=(time_step, 1), baselines=baselines, **kwargs ) # Convert attributions to numpy array and append to the list @@ -546,9 +546,10 @@ def explantation_captum(self, test_loader, method, log_dir=".", plot=False, **kw # Concatenate a‚ttribution values for all instances along the batch dimension all_attrs = np.mean(all_attrs, axis=0) means_feature = all_attrs.mean(axis=(0, 1)) - + self.log('Attribution Featues', means_feature) # Compute mean along the batch dimension means = all_attrs.mean(axis=(0, 2)) + self.log('Attribution Time steps', means) # Normalize the means values to range [0, 1] # normalized_means = (means - means.min()) / (means.max() - means.min()) if plot: @@ -687,7 +688,9 @@ def Faithfulness_Correlation( att_sums.append(np.sum(attribution[a_ix])) similarities.append(similarity_func(pred_deltas, att_sums)) - return np.nanmean(similarities) + score = np.nanmean(similarities) + self.log('Faithfulness correlation', score) + return score def Data_Randomization(self, test_loader, attribution, explain_method, random_model, similarity_func=None, test_dataset=None): """ @@ -762,7 +765,7 @@ def Data_Randomization(self, test_loader, attribution, explain_method, random_mo # Normalize the means values to range [0, 1] # normalized_a_perturbed = (a_perturbed - a_perturbed.min()) / (a_perturbed.max() - a_perturbed.min()) score = similarity_func(a_perturbed, attribution) - + self.log('data_randomization', score) return score diff --git a/icu_benchmarks/run.py b/icu_benchmarks/run.py index 70fbfa43..182a1814 100644 --- a/icu_benchmarks/run.py +++ b/icu_benchmarks/run.py @@ -191,7 +191,8 @@ def main(my_args=tuple(sys.argv[1:])): explain=explain, pytorch_forecasting=pytorch_forecasting, XAI_metric=XAI_metric, - random_model_dir=random_model_dir + random_model_dir=random_model_dir, + random_labels=random_labels, ) log_full_line("FINISHED TRAINING", level=logging.INFO, char="=", num_newlines=3) From 446ceaaaabc77d38f7b3e4aae9bdb164223a48e9 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Sat, 11 Nov 2023 02:02:21 +0100 Subject: [PATCH 104/142] latest --- icu_benchmarks/data/loader.py | 1 + icu_benchmarks/models/constants.py | 4 +-- icu_benchmarks/models/dl_models.py | 3 +- icu_benchmarks/models/metrics.py | 21 ++---------- icu_benchmarks/models/train.py | 26 +++++++-------- icu_benchmarks/models/wrappers.py | 53 ++++++++++++++++-------------- 6 files changed, 48 insertions(+), 60 deletions(-) diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index fdf4dc68..6287810b 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -501,6 +501,7 @@ def __init__( add_target_scales=True, add_encoder_length=True, predict_mode=True, + target_normalizer=None, ) def get_balance(self) -> list: diff --git a/icu_benchmarks/models/constants.py b/icu_benchmarks/models/constants.py index 45af8271..49555d42 100644 --- a/icu_benchmarks/models/constants.py +++ b/icu_benchmarks/models/constants.py @@ -65,10 +65,10 @@ class DLMetrics: BINARY_CLASSIFICATION_TORCHMETRICS = { "AUC": AUROC(task="binary"), "PR": TorchMetricsAveragePrecision(task="binary"), - "PrecisionRecallCurve": TorchMetricsPrecisionRecallCurve(task="binary"), + # "PrecisionRecallCurve": TorchMetricsPrecisionRecallCurve(task="binary"), "Calibration_Error": CalibrationError(task="binary", n_bins=10), "F1": F1Score(task="binary", num_classes=2), - "Binary_Fairness": BinaryFairnessWrapper(num_groups=2, task="demographic_parity", group_name="sex"), + # "Binary_Fairness": BinaryFairnessWrapper(num_groups=2, task="demographic_parity", group_name="sex"), } MULTICLASS_CLASSIFICATION = { diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 455e333c..4e2a9a42 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -641,7 +641,8 @@ def forward( # out = out["prediction"].reshape(1, -1) pred = self.logit(out["prediction"]) - print(pred) + # print(pred + return pred diff --git a/icu_benchmarks/models/metrics.py b/icu_benchmarks/models/metrics.py index 4c2ffd2b..5428db61 100644 --- a/icu_benchmarks/models/metrics.py +++ b/icu_benchmarks/models/metrics.py @@ -1,37 +1,20 @@ import torch from typing import Callable import numpy as np -from ignite.metrics import EpochMetric +from ignite.metrics import EpochMetric, Precision, Recall from sklearn.metrics import balanced_accuracy_score, mean_absolute_error from sklearn.calibration import calibration_curve from scipy.spatial.distance import jensenshannon import gin import torch.nn.functional as F + """" This file contains metrics that are not available in ignite.metrics. Specifically, it adds transformation capabilities to some metrics. """ -class QuantileLossClass(torch.nn.Module): - def __init__(self, quantiles): - super().__init__() - self.register_buffer("q", torch.tensor(quantiles)) - - def forward(self, predictions, targets): - diff = predictions - targets - ql = (1 - self.q) * F.relu(diff) + self.q * F.relu(-diff) - losses = ql.view(-1, ql.shape[-1]).mean(0) - return losses - - -@gin.configurable -def Quantileloss(self, predictions, targets, quantiles=[0.1, 0.5, 0.9]): - loss = QuantileLossClass(quantiles=quantiles) - return loss(predictions, targets) - - def accuracy(output, target, topk=(1,)): """Computes the accuracy over the k top predictions for the specified values of k""" with torch.no_grad(): diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 3b35dd73..3da76809 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -259,9 +259,7 @@ def train_common( if not eval_only: if model.requires_backprop: logging.info("Training DL model.") - print(next(iter(train_loader))[1][0]) - print(next(iter(val_loader))[1][0]) - print(next(iter(test_loader))[1][0]) + trainer.fit( model, train_dataloaders=train_loader, val_dataloaders=val_loader ) @@ -278,31 +276,31 @@ def train_common( if explain: # - attributions_SHAP = model.explantation_captum( - test_loader=test_loader, - method=ShapleyValueSampling, log_dir=log_dir, plot=True - ) - print("shap", attributions_SHAP) + # attributions_SHAP = model.explantation_captum( + # test_loader=test_loader, + # method=ShapleyValueSampling, log_dir=log_dir, plot=True + # ) + # print("shap", attributions_SHAP) attributions_IG = model.explantation_captum( test_loader=test_loader, - method=IntegratedGradients, log_dir=log_dir, plot=True, n_steps=35, return_convergence_delta=True, + method=IntegratedGradients, log_dir=log_dir, plot=True, n_steps=35 ) - """ Attention_weights = model.interpertations(test_loader, log_dir, plot=True) print("attention", Attention_weights) if XAI_metric: + ra2 = np.random.randint(low=0, high=101, size=24) random_attributions = np.random.normal(size=24) - F_baseline = model.Faithfulness_Correlation(test_loader, random_attributions, pertrub='Noise') - F_baseline2 = model.Faithfulness_Correlation(test_loader, random_attributions, pertrub='Noise') + F_baseline = model.Faithfulness_Correlation(test_loader, random_attributions) + F_baseline2 = model.Faithfulness_Correlation(test_loader, random_attributions) print('Random noraml faithfulness correlation', F_baseline) print('random unifrom ', F_baseline2) F_attribution = model.Faithfulness_Correlation(test_loader, attributions_IG, pertrub='Noise') print('Attributions faithfulness correlation', F_attribution) F_attention = model.Faithfulness_Correlation(test_loader, Attention_weights["attention"], pertrub='Noise') print('Attention faithfulness correlation', F_attention) - """ + # path = Path(random_model_dir) # random_model = load_model( @@ -316,7 +314,7 @@ def train_common( # test_loader, attributions_IG, IntegratedGradients, random_model) # print('Distance Data randmoization score attribution', R_attribution) # R_attention = model.Data_Randomization( - # test_loader, Attention_weights["attention"], "Attention", test_dataset=test_dataset) + # test_loader, Attention_weights["attention"], "Attention", random_model) # print('Distance Data randmoization score attention', R_attention) model.set_weight("balanced", train_dataset) diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index beebdb36..c87bf161 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -156,23 +156,27 @@ def on_train_start(self): return super().on_train_start() def finalize_step(self, step_prefix=""): + try: - self.log_dict( - { - f"{step_prefix}/{name}": ( - np.float32(metric.compute()) if isinstance(metric.compute(), np.float64) else metric.compute() - ) - for name, metric in self.metrics[step_prefix].items() - if "_Curve" not in name - }, - sync_dist=True, - ) + for name, metric in self.metrics[step_prefix].items(): + try: + value = np.float32(metric.compute()) if isinstance( + metric.compute(), np.float64) else metric.compute() + self.log_dict({f"{step_prefix}/{name}": value}, sync_dist=True) + + except (NotComputableError, ValueError) as e: + if step_prefix not in self._metrics_warning_printed: + self._metrics_warning_printed.add(step_prefix) + logging.warning(f"Metric for {step_prefix}/{name} not computable: {e}") + for metric in self.metrics[step_prefix].values(): metric.reset() - except (NotComputableError, ValueError): + except (NotComputableError, ValueError) as e: if step_prefix not in self._metrics_warning_printed: self._metrics_warning_printed.add(step_prefix) logging.warning(f"Metrics for {step_prefix} not computable") + print(e) + pass def configure_optimizers(self): @@ -284,9 +288,11 @@ def softmax_multi_output_transform(output): # Output transform is not applied for contrib metrics, so we do our own. if self.run_mode == RunMode.classification: # Binary classification + if self.logit.out_features == 2: self.output_transform = softmax_binary_output_transform - metrics = DLMetrics.BINARY_CLASSIFICATION + metrics = DLMetrics.BINARY_CLASSIFICATION_TORCHMETRICS + else: # Multiclass classification self.output_transform = softmax_multi_output_transform @@ -483,15 +489,15 @@ def step_fn(self, element, step_prefix=""): for key, value in self.metrics[step_prefix].items(): if isinstance(value, torchmetrics.Metric): if key == "Binary_Fairness": - feature_names = key.feature_helper(self.trainer) + feature_names = self.metrics[step_prefix][key].feature_helper(self.trainer, step_prefix) value.update( transformed_output[0], - transformed_output[1], + transformed_output[1].int(), data, feature_names, ) else: - value.update(transformed_output[0], transformed_output[1]) + value.update(transformed_output[0], transformed_output[1].int()) else: value.update(transformed_output) self.log(f"{step_prefix}/loss", loss, on_step=False, on_epoch=True, sync_dist=True) @@ -538,18 +544,20 @@ def explantation_captum(self, test_loader, method, log_dir=".", plot=False, **kw attr_all_timesteps = [] for time_step in range(0, 24): attr = explantation.attribute( - data, target=(time_step, 1), baselines=baselines, **kwargs + data, target=(time_step), baselines=baselines, **kwargs ) # Convert attributions to numpy array and append to the list attr_all_timesteps.append(attr[0].cpu().detach().numpy()) all_attrs.append(np.mean(attr_all_timesteps, axis=0)) # Concatenate a‚ttribution values for all instances along the batch dimension + print(np.shape(all_attrs)) all_attrs = np.mean(all_attrs, axis=0) - means_feature = all_attrs.mean(axis=(0, 1)) - self.log('Attribution Featues', means_feature) + print(np.shape(all_attrs)) + means_feature = all_attrs.mean(axis=(1)) + # self.log('Attribution Featues', means_feature) # Compute mean along the batch dimension - means = all_attrs.mean(axis=(0, 2)) - self.log('Attribution Time steps', means) + means = all_attrs.mean(axis=(0)) + # self.log('Attribution Time steps', means) # Normalize the means values to range [0, 1] # normalized_means = (means - means.min()) / (means.max() - means.min()) if plot: @@ -689,7 +697,6 @@ def Faithfulness_Correlation( similarities.append(similarity_func(pred_deltas, att_sums)) score = np.nanmean(similarities) - self.log('Faithfulness correlation', score) return score def Data_Randomization(self, test_loader, attribution, explain_method, random_model, similarity_func=None, test_dataset=None): @@ -706,13 +713,12 @@ def Data_Randomization(self, test_loader, attribution, explain_method, random_mo 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. """ - print(random_model) a_perturbed = [] if similarity_func is None: similarity_func = distance_euclidean if explain_method == "Attention": - Attention_weights = random_model.interpertations(test_loader) # test_loader works by reference + Attention_weights = random_model.interpertations(test_loader) score = similarity_func(Attention_weights["attention"].cpu().numpy(), attribution.cpu().numpy()) else: @@ -765,7 +771,6 @@ def Data_Randomization(self, test_loader, attribution, explain_method, random_mo # Normalize the means values to range [0, 1] # normalized_a_perturbed = (a_perturbed - a_perturbed.min()) / (a_perturbed.max() - a_perturbed.min()) score = similarity_func(a_perturbed, attribution) - self.log('data_randomization', score) return score From 73d39095cdee74e123adad5229cb6d29ddd26c5c Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Mon, 13 Nov 2023 13:36:13 +0100 Subject: [PATCH 105/142] removed label from input for tft --- configs/prediction_models/RNNpytorch.gin | 4 ++-- configs/prediction_models/TFTpytorch.gin | 3 +-- icu_benchmarks/models/dl_models.py | 4 ++-- icu_benchmarks/models/train.py | 9 +++++---- icu_benchmarks/models/wrappers.py | 18 +++++++++--------- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/configs/prediction_models/RNNpytorch.gin b/configs/prediction_models/RNNpytorch.gin index ab42884f..334def2d 100644 --- a/configs/prediction_models/RNNpytorch.gin +++ b/configs/prediction_models/RNNpytorch.gin @@ -13,8 +13,8 @@ optimizer/hyperparameter.weight_decay = 1e-6 # Model params model/hyperparameter.class_to_tune = @RNNpytorch -model/hyperparameter.hidden = 2 -model/hyperparameter.rnn_layers=2 +model/hyperparameter.hidden = (32, 256, "log-uniform", 2) +model/hyperparameter.rnn_layers=(1, 3) model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.cell_type='LSTM' model/hyperparameter.dropout = (0.0, 0.4) diff --git a/configs/prediction_models/TFTpytorch.gin b/configs/prediction_models/TFTpytorch.gin index a6523c6b..abb81f6f 100644 --- a/configs/prediction_models/TFTpytorch.gin +++ b/configs/prediction_models/TFTpytorch.gin @@ -74,6 +74,5 @@ PredictionDatasetTFTpytorch.time_varying_unknown_reals=["alb", "temp", "tnt", "urine", - "wbc", - "label",] + "wbc",] PredictionDatasetTFTpytorch.lagged_variables=[] \ No newline at end of file diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 4e2a9a42..5f1d287e 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -640,10 +640,10 @@ def forward( # out = out["prediction"].reshape(1, -1) - pred = self.logit(out["prediction"]) + # pred = self.logit(out["prediction"]) # print(pred - return pred + return out["prediction"] @gin.configurable diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 3da76809..018c8bc3 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -287,18 +287,19 @@ def train_common( ) Attention_weights = model.interpertations(test_loader, log_dir, plot=True) - print("attention", Attention_weights) + # print("attention", Attention_weights) if XAI_metric: - ra2 = np.random.randint(low=0, high=101, size=24) + ra2 = np.random.randint(low=0, high=100, size=24) random_attributions = np.random.normal(size=24) + F_baseline = model.Faithfulness_Correlation(test_loader, random_attributions) F_baseline2 = model.Faithfulness_Correlation(test_loader, random_attributions) print('Random noraml faithfulness correlation', F_baseline) print('random unifrom ', F_baseline2) - F_attribution = model.Faithfulness_Correlation(test_loader, attributions_IG, pertrub='Noise') + F_attribution = model.Faithfulness_Correlation(test_loader, attributions_IG) print('Attributions faithfulness correlation', F_attribution) - F_attention = model.Faithfulness_Correlation(test_loader, Attention_weights["attention"], pertrub='Noise') + F_attention = model.Faithfulness_Correlation(test_loader, Attention_weights["attention"]) print('Attention faithfulness correlation', F_attention) # path = Path(random_model_dir) diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index c87bf161..41eee23b 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -509,9 +509,11 @@ def explantation_captum(self, test_loader, method, log_dir=".", plot=False, **kw # Loop through the test_loader to compute attributions for all instances for batch in test_loader: + for key, value in batch[0].items(): batch[0][key] = batch[0][key].to(self.device) x = batch[0] + data = ( x["encoder_cat"].float().requires_grad_(), x["encoder_cont"].requires_grad_(), @@ -542,28 +544,28 @@ def explantation_captum(self, test_loader, method, log_dir=".", plot=False, **kw explantation = method(self.forward_captum) # Reformat attributions. attr_all_timesteps = [] - for time_step in range(0, 24): + for time_step in range(23, 24): attr = explantation.attribute( data, target=(time_step), baselines=baselines, **kwargs ) # Convert attributions to numpy array and append to the list + attr_all_timesteps.append(attr[0].cpu().detach().numpy()) all_attrs.append(np.mean(attr_all_timesteps, axis=0)) # Concatenate a‚ttribution values for all instances along the batch dimension - print(np.shape(all_attrs)) all_attrs = np.mean(all_attrs, axis=0) - print(np.shape(all_attrs)) - means_feature = all_attrs.mean(axis=(1)) + + means_feature = all_attrs.mean(axis=(0)) # self.log('Attribution Featues', means_feature) # Compute mean along the batch dimension - means = all_attrs.mean(axis=(0)) + means = all_attrs.mean(axis=(1)) # self.log('Attribution Time steps', means) # Normalize the means values to range [0, 1] # normalized_means = (means - means.min()) / (means.max() - means.min()) if plot: # Create x values (assuming you want a simple sequential x-axis) # Assuming you have 24 values - x_values = np.arange(1, 57) + x_values = np.arange(1, 58) # Plotting the featrue means plt.figure(figsize=(8, 6)) plt.plot( @@ -601,7 +603,7 @@ def explantation_captum(self, test_loader, method, log_dir=".", plot=False, **kw return means def Faithfulness_Correlation( - self, test_loader, attribution, similarity_func=None, nr_runs=100, pertrub=None, subset_size=6 + self, test_loader, attribution, similarity_func=None, nr_runs=100, pertrub=None, subset_size=3 ): """ Implementation of faithfulness correlation by Bhatt et al., 2020. @@ -688,13 +690,11 @@ def Faithfulness_Correlation( ) # Predict on perturbed input x. y_pred_perturb = self(data).detach().cpu().numpy() - pred_deltas.append((y_pred - y_pred_perturb).mean(axis=(0, 2))) # Sum attributions of the random subset. att_sums.append(np.sum(attribution[a_ix])) - similarities.append(similarity_func(pred_deltas, att_sums)) score = np.nanmean(similarities) return score From 25446047f514a36bbe69f6a0a2d3397e53c05989 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Tue, 14 Nov 2023 14:20:35 +0100 Subject: [PATCH 106/142] labels for los not shown to RNN and deepAR --- configs/prediction_models/DeepARpytorch.gin | 9 ++++---- configs/prediction_models/RNNpytorch.gin | 4 ++-- configs/prediction_models/TFTpytorch.gin | 6 ++--- configs/prediction_models/common/DLCommon.gin | 2 +- icu_benchmarks/data/loader.py | 10 ++++---- icu_benchmarks/models/constants.py | 2 +- icu_benchmarks/models/dl_models.py | 12 ++++++---- icu_benchmarks/models/train.py | 13 ++++++----- icu_benchmarks/models/wrappers.py | 23 ++++--------------- 9 files changed, 35 insertions(+), 46 deletions(-) diff --git a/configs/prediction_models/DeepARpytorch.gin b/configs/prediction_models/DeepARpytorch.gin index 94ae90f0..d4962081 100644 --- a/configs/prediction_models/DeepARpytorch.gin +++ b/configs/prediction_models/DeepARpytorch.gin @@ -13,7 +13,7 @@ optimizer/hyperparameter.weight_decay = 1e-6 # Model params model/hyperparameter.class_to_tune = @DeepARpytorch -model/hyperparameter.hidden = (32, 256, "log-uniform", 2) +model/hyperparameter.hidden = (4, 64, "log-uniform", 2) model/hyperparameter.rnn_layers=(1, 3) model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.cell_type='LSTM' @@ -26,11 +26,10 @@ PredictionDatasetTFTpytorch.max_encoder_length = 24 PredictionDatasetTFTpytorch.max_prediction_length = 24 PredictionDatasetTFTpytorch.time_varying_known_reals=['alb_lag_24', 'alp_lag_24', 'alt_lag_24', 'ast_lag_24', 'be_lag_24', 'bicar_lag_24', 'bili_lag_24', 'bili_dir_lag_24', 'bnd_lag_24', 'bun_lag_24', 'ca_lag_24', 'cai_lag_24', 'ck_lag_24', 'ckmb_lag_24', 'cl_lag_24', 'crea_lag_24', 'crp_lag_24', 'dbp_lag_24', 'fgn_lag_24', 'fio2_lag_24', 'glu_lag_24', 'hgb_lag_24', 'hr_lag_24', 'inr_pt_lag_24', 'k_lag_24', 'lact_lag_24', 'lymph_lag_24', 'map_lag_24', 'mch_lag_24', 'mchc_lag_24', 'mcv_lag_24', 'methb_lag_24', 'mg_lag_24', 'na_lag_24', 'neut_lag_24', 'o2sat_lag_24', 'pco2_lag_24', 'ph_lag_24', 'phos_lag_24', 'plt_lag_24', 'po2_lag_24', 'ptt_lag_24', 'resp_lag_24', 'sbp_lag_24', 'temp_lag_24', 'tnt_lag_24', 'urine_lag_24', 'wbc_lag_24', 'alb_lag_25', 'alp_lag_25', 'alt_lag_25', 'ast_lag_25', 'be_lag_25', 'bicar_lag_25', 'bili_lag_25', 'bili_dir_lag_25', 'bnd_lag_25', 'bun_lag_25', 'ca_lag_25', 'cai_lag_25', 'ck_lag_25', 'ckmb_lag_25', 'cl_lag_25', 'crea_lag_25', 'crp_lag_25', 'dbp_lag_25', 'fgn_lag_25', 'fio2_lag_25', 'glu_lag_25', 'hgb_lag_25', 'hr_lag_25', 'inr_pt_lag_25', 'k_lag_25', 'lact_lag_25', 'lymph_lag_25', 'map_lag_25', 'mch_lag_25', 'mchc_lag_25', 'mcv_lag_25', 'methb_lag_25', 'mg_lag_25', 'na_lag_25', 'neut_lag_25', 'o2sat_lag_25', 'pco2_lag_25', 'ph_lag_25', 'phos_lag_25', 'plt_lag_25', 'po2_lag_25', 'ptt_lag_25', 'resp_lag_25', 'sbp_lag_25', 'temp_lag_25', 'tnt_lag_25', 'urine_lag_25', 'wbc_lag_25', 'alb_lag_26', 'alp_lag_26', 'alt_lag_26', 'ast_lag_26', 'be_lag_26', 'bicar_lag_26', 'bili_lag_26', 'bili_dir_lag_26', 'bnd_lag_26', 'bun_lag_26', 'ca_lag_26', 'cai_lag_26', 'ck_lag_26', 'ckmb_lag_26', 'cl_lag_26', 'crea_lag_26', 'crp_lag_26', 'dbp_lag_26', 'fgn_lag_26', 'fio2_lag_26', 'glu_lag_26', 'hgb_lag_26', 'hr_lag_26', 'inr_pt_lag_26', 'k_lag_26', 'lact_lag_26', 'lymph_lag_26', 'map_lag_26', 'mch_lag_26', 'mchc_lag_26', 'mcv_lag_26', 'methb_lag_26', 'mg_lag_26', 'na_lag_26', 'neut_lag_26', 'o2sat_lag_26', 'pco2_lag_26', 'ph_lag_26', 'phos_lag_26', 'plt_lag_26', 'po2_lag_26', 'ptt_lag_26', 'resp_lag_26', 'sbp_lag_26', 'temp_lag_26', 'tnt_lag_26', 'urine_lag_26', 'wbc_lag_26', 'alb_lag_27', 'alp_lag_27', 'alt_lag_27', 'ast_lag_27', 'be_lag_27', 'bicar_lag_27', 'bili_lag_27', 'bili_dir_lag_27', 'bnd_lag_27', 'bun_lag_27', 'ca_lag_27', 'cai_lag_27', 'ck_lag_27', 'ckmb_lag_27', 'cl_lag_27', 'crea_lag_27', 'crp_lag_27', 'dbp_lag_27', 'fgn_lag_27', 'fio2_lag_27', 'glu_lag_27', 'hgb_lag_27', 'hr_lag_27', 'inr_pt_lag_27', 'k_lag_27', 'lact_lag_27', 'lymph_lag_27', 'map_lag_27', 'mch_lag_27', 'mchc_lag_27', 'mcv_lag_27', 'methb_lag_27', 'mg_lag_27', 'na_lag_27', 'neut_lag_27', 'o2sat_lag_27', 'pco2_lag_27', 'ph_lag_27', 'phos_lag_27', 'plt_lag_27', 'po2_lag_27', 'ptt_lag_27', 'resp_lag_27', 'sbp_lag_27', 'temp_lag_27', 'tnt_lag_27', 'urine_lag_27', 'wbc_lag_27', 'alb_lag_28', 'alp_lag_28', 'alt_lag_28', 'ast_lag_28', 'be_lag_28', 'bicar_lag_28', 'bili_lag_28', 'bili_dir_lag_28', 'bnd_lag_28', 'bun_lag_28', 'ca_lag_28', 'cai_lag_28', 'ck_lag_28', 'ckmb_lag_28', 'cl_lag_28', 'crea_lag_28', 'crp_lag_28', 'dbp_lag_28', 'fgn_lag_28', 'fio2_lag_28', 'glu_lag_28', 'hgb_lag_28', 'hr_lag_28', 'inr_pt_lag_28', 'k_lag_28', 'lact_lag_28', 'lymph_lag_28', 'map_lag_28', 'mch_lag_28', 'mchc_lag_28', 'mcv_lag_28', 'methb_lag_28', 'mg_lag_28', 'na_lag_28', 'neut_lag_28', 'o2sat_lag_28', 'pco2_lag_28', 'ph_lag_28', 'phos_lag_28', 'plt_lag_28', 'po2_lag_28', 'ptt_lag_28', 'resp_lag_28', 'sbp_lag_28', 'temp_lag_28', 'tnt_lag_28', 'urine_lag_28', 'wbc_lag_28', 'alb_lag_29', 'alp_lag_29', 'alt_lag_29', 'ast_lag_29', 'be_lag_29', 'bicar_lag_29', 'bili_lag_29', 'bili_dir_lag_29', 'bnd_lag_29', 'bun_lag_29', 'ca_lag_29', 'cai_lag_29', 'ck_lag_29', 'ckmb_lag_29', 'cl_lag_29', 'crea_lag_29', 'crp_lag_29', 'dbp_lag_29', 'fgn_lag_29', 'fio2_lag_29', 'glu_lag_29', 'hgb_lag_29', 'hr_lag_29', 'inr_pt_lag_29', 'k_lag_29', 'lact_lag_29', 'lymph_lag_29', 'map_lag_29', 'mch_lag_29', 'mchc_lag_29', 'mcv_lag_29', 'methb_lag_29', 'mg_lag_29', 'na_lag_29', 'neut_lag_29', 'o2sat_lag_29', 'pco2_lag_29', 'ph_lag_29', 'phos_lag_29', 'plt_lag_29', 'po2_lag_29', 'ptt_lag_29', 'resp_lag_29', 'sbp_lag_29', 'temp_lag_29', 'tnt_lag_29', 'urine_lag_29', 'wbc_lag_29', 'alb_lag_30', 'alp_lag_30', 'alt_lag_30', 'ast_lag_30', 'be_lag_30', 'bicar_lag_30', 'bili_lag_30', 'bili_dir_lag_30', 'bnd_lag_30', 'bun_lag_30', 'ca_lag_30', 'cai_lag_30', 'ck_lag_30', 'ckmb_lag_30', 'cl_lag_30', 'crea_lag_30', 'crp_lag_30', 'dbp_lag_30', 'fgn_lag_30', 'fio2_lag_30', 'glu_lag_30', 'hgb_lag_30', 'hr_lag_30', 'inr_pt_lag_30', 'k_lag_30', 'lact_lag_30', 'lymph_lag_30', 'map_lag_30', 'mch_lag_30', 'mchc_lag_30', 'mcv_lag_30', 'methb_lag_30', 'mg_lag_30', 'na_lag_30', 'neut_lag_30', 'o2sat_lag_30', 'pco2_lag_30', 'ph_lag_30', 'phos_lag_30', 'plt_lag_30', 'po2_lag_30', 'ptt_lag_30', 'resp_lag_30', 'sbp_lag_30', 'temp_lag_30', 'tnt_lag_30', 'urine_lag_30', 'wbc_lag_30', 'alb_lag_31', 'alp_lag_31', 'alt_lag_31', 'ast_lag_31', 'be_lag_31', 'bicar_lag_31', 'bili_lag_31', 'bili_dir_lag_31', 'bnd_lag_31', 'bun_lag_31', 'ca_lag_31', 'cai_lag_31', 'ck_lag_31', 'ckmb_lag_31', 'cl_lag_31', 'crea_lag_31', 'crp_lag_31', 'dbp_lag_31', 'fgn_lag_31', 'fio2_lag_31', 'glu_lag_31', 'hgb_lag_31', 'hr_lag_31', 'inr_pt_lag_31', 'k_lag_31', 'lact_lag_31', 'lymph_lag_31', 'map_lag_31', 'mch_lag_31', 'mchc_lag_31', 'mcv_lag_31', 'methb_lag_31', 'mg_lag_31', 'na_lag_31', 'neut_lag_31', 'o2sat_lag_31', 'pco2_lag_31', 'ph_lag_31', 'phos_lag_31', 'plt_lag_31', 'po2_lag_31', 'ptt_lag_31', 'resp_lag_31', 'sbp_lag_31', 'temp_lag_31', 'tnt_lag_31', 'urine_lag_31', 'wbc_lag_31', 'alb_lag_32', 'alp_lag_32', 'alt_lag_32', 'ast_lag_32', 'be_lag_32', 'bicar_lag_32', 'bili_lag_32', 'bili_dir_lag_32', 'bnd_lag_32', 'bun_lag_32', 'ca_lag_32', 'cai_lag_32', 'ck_lag_32', 'ckmb_lag_32', 'cl_lag_32', 'crea_lag_32', 'crp_lag_32', 'dbp_lag_32', 'fgn_lag_32', 'fio2_lag_32', 'glu_lag_32', 'hgb_lag_32', 'hr_lag_32', 'inr_pt_lag_32', 'k_lag_32', 'lact_lag_32', 'lymph_lag_32', 'map_lag_32', 'mch_lag_32', 'mchc_lag_32', 'mcv_lag_32', 'methb_lag_32', 'mg_lag_32', 'na_lag_32', 'neut_lag_32', 'o2sat_lag_32', 'pco2_lag_32', 'ph_lag_32', 'phos_lag_32', 'plt_lag_32', 'po2_lag_32', 'ptt_lag_32', 'resp_lag_32', 'sbp_lag_32', 'temp_lag_32', 'tnt_lag_32', 'urine_lag_32', 'wbc_lag_32', 'alb_lag_33', 'alp_lag_33', 'alt_lag_33', 'ast_lag_33', 'be_lag_33', 'bicar_lag_33', 'bili_lag_33', 'bili_dir_lag_33', 'bnd_lag_33', 'bun_lag_33', 'ca_lag_33', 'cai_lag_33', 'ck_lag_33', 'ckmb_lag_33', 'cl_lag_33', 'crea_lag_33', 'crp_lag_33', 'dbp_lag_33', 'fgn_lag_33', 'fio2_lag_33', 'glu_lag_33', 'hgb_lag_33', 'hr_lag_33', 'inr_pt_lag_33', 'k_lag_33', 'lact_lag_33', 'lymph_lag_33', 'map_lag_33', 'mch_lag_33', 'mchc_lag_33', 'mcv_lag_33', 'methb_lag_33', 'mg_lag_33', 'na_lag_33', 'neut_lag_33', 'o2sat_lag_33', 'pco2_lag_33', 'ph_lag_33', 'phos_lag_33', 'plt_lag_33', 'po2_lag_33', 'ptt_lag_33', 'resp_lag_33', 'sbp_lag_33', 'temp_lag_33', 'tnt_lag_33', 'urine_lag_33', 'wbc_lag_33', 'alb_lag_34', 'alp_lag_34', 'alt_lag_34', 'ast_lag_34', 'be_lag_34', 'bicar_lag_34', 'bili_lag_34', 'bili_dir_lag_34', 'bnd_lag_34', 'bun_lag_34', 'ca_lag_34', 'cai_lag_34', 'ck_lag_34', 'ckmb_lag_34', 'cl_lag_34', 'crea_lag_34', 'crp_lag_34', 'dbp_lag_34', 'fgn_lag_34', 'fio2_lag_34', 'glu_lag_34', 'hgb_lag_34', 'hr_lag_34', 'inr_pt_lag_34', 'k_lag_34', 'lact_lag_34', 'lymph_lag_34', 'map_lag_34', 'mch_lag_34', 'mchc_lag_34', 'mcv_lag_34', 'methb_lag_34', 'mg_lag_34', 'na_lag_34', 'neut_lag_34', 'o2sat_lag_34', 'pco2_lag_34', 'ph_lag_34', 'phos_lag_34', 'plt_lag_34', 'po2_lag_34', 'ptt_lag_34', 'resp_lag_34', 'sbp_lag_34', 'temp_lag_34', 'tnt_lag_34', 'urine_lag_34', 'wbc_lag_34', 'alb_lag_35', 'alp_lag_35', 'alt_lag_35', 'ast_lag_35', 'be_lag_35', 'bicar_lag_35', 'bili_lag_35', 'bili_dir_lag_35', 'bnd_lag_35', 'bun_lag_35', 'ca_lag_35', 'cai_lag_35', 'ck_lag_35', 'ckmb_lag_35', 'cl_lag_35', 'crea_lag_35', 'crp_lag_35', 'dbp_lag_35', 'fgn_lag_35', 'fio2_lag_35', 'glu_lag_35', 'hgb_lag_35', 'hr_lag_35', 'inr_pt_lag_35', 'k_lag_35', 'lact_lag_35', 'lymph_lag_35', 'map_lag_35', 'mch_lag_35', 'mchc_lag_35', 'mcv_lag_35', 'methb_lag_35', 'mg_lag_35', 'na_lag_35', 'neut_lag_35', 'o2sat_lag_35', 'pco2_lag_35', 'ph_lag_35', 'phos_lag_35', 'plt_lag_35', 'po2_lag_35', 'ptt_lag_35', 'resp_lag_35', 'sbp_lag_35', 'temp_lag_35', 'tnt_lag_35', 'urine_lag_35', 'wbc_lag_35', 'alb_lag_36', 'alp_lag_36', 'alt_lag_36', 'ast_lag_36', 'be_lag_36', 'bicar_lag_36', 'bili_lag_36', 'bili_dir_lag_36', 'bnd_lag_36', 'bun_lag_36', 'ca_lag_36', 'cai_lag_36', 'ck_lag_36', 'ckmb_lag_36', 'cl_lag_36', 'crea_lag_36', 'crp_lag_36', 'dbp_lag_36', 'fgn_lag_36', 'fio2_lag_36', 'glu_lag_36', 'hgb_lag_36', 'hr_lag_36', 'inr_pt_lag_36', 'k_lag_36', 'lact_lag_36', 'lymph_lag_36', 'map_lag_36', 'mch_lag_36', 'mchc_lag_36', 'mcv_lag_36', 'methb_lag_36', 'mg_lag_36', 'na_lag_36', 'neut_lag_36', 'o2sat_lag_36', 'pco2_lag_36', 'ph_lag_36', 'phos_lag_36', 'plt_lag_36', 'po2_lag_36', 'ptt_lag_36', 'resp_lag_36', 'sbp_lag_36', 'temp_lag_36', 'tnt_lag_36', 'urine_lag_36', 'wbc_lag_36', 'alb_lag_37', 'alp_lag_37', 'alt_lag_37', 'ast_lag_37', 'be_lag_37', 'bicar_lag_37', 'bili_lag_37', 'bili_dir_lag_37', 'bnd_lag_37', 'bun_lag_37', 'ca_lag_37', 'cai_lag_37', 'ck_lag_37', 'ckmb_lag_37', 'cl_lag_37', 'crea_lag_37', 'crp_lag_37', 'dbp_lag_37', 'fgn_lag_37', 'fio2_lag_37', 'glu_lag_37', 'hgb_lag_37', 'hr_lag_37', 'inr_pt_lag_37', 'k_lag_37', 'lact_lag_37', 'lymph_lag_37', 'map_lag_37', 'mch_lag_37', 'mchc_lag_37', 'mcv_lag_37', 'methb_lag_37', 'mg_lag_37', 'na_lag_37', 'neut_lag_37', 'o2sat_lag_37', 'pco2_lag_37', 'ph_lag_37', 'phos_lag_37', 'plt_lag_37', 'po2_lag_37', 'ptt_lag_37', 'resp_lag_37', 'sbp_lag_37', 'temp_lag_37', 'tnt_lag_37', 'urine_lag_37', 'wbc_lag_37', 'alb_lag_38', 'alp_lag_38', 'alt_lag_38', 'ast_lag_38', 'be_lag_38', 'bicar_lag_38', 'bili_lag_38', 'bili_dir_lag_38', 'bnd_lag_38', 'bun_lag_38', 'ca_lag_38', 'cai_lag_38', 'ck_lag_38', 'ckmb_lag_38', 'cl_lag_38', 'crea_lag_38', 'crp_lag_38', 'dbp_lag_38', 'fgn_lag_38', 'fio2_lag_38', 'glu_lag_38', 'hgb_lag_38', 'hr_lag_38', 'inr_pt_lag_38', 'k_lag_38', 'lact_lag_38', 'lymph_lag_38', 'map_lag_38', 'mch_lag_38', 'mchc_lag_38', 'mcv_lag_38', 'methb_lag_38', 'mg_lag_38', 'na_lag_38', 'neut_lag_38', 'o2sat_lag_38', 'pco2_lag_38', 'ph_lag_38', 'phos_lag_38', 'plt_lag_38', 'po2_lag_38', 'ptt_lag_38', 'resp_lag_38', 'sbp_lag_38', 'temp_lag_38', 'tnt_lag_38', 'urine_lag_38', 'wbc_lag_38', 'alb_lag_39', 'alp_lag_39', 'alt_lag_39', 'ast_lag_39', 'be_lag_39', 'bicar_lag_39', 'bili_lag_39', 'bili_dir_lag_39', 'bnd_lag_39', 'bun_lag_39', 'ca_lag_39', 'cai_lag_39', 'ck_lag_39', 'ckmb_lag_39', 'cl_lag_39', 'crea_lag_39', 'crp_lag_39', 'dbp_lag_39', 'fgn_lag_39', 'fio2_lag_39', 'glu_lag_39', 'hgb_lag_39', 'hr_lag_39', 'inr_pt_lag_39', 'k_lag_39', 'lact_lag_39', 'lymph_lag_39', 'map_lag_39', 'mch_lag_39', 'mchc_lag_39', 'mcv_lag_39', 'methb_lag_39', 'mg_lag_39', 'na_lag_39', 'neut_lag_39', 'o2sat_lag_39', 'pco2_lag_39', 'ph_lag_39', 'phos_lag_39', 'plt_lag_39', 'po2_lag_39', 'ptt_lag_39', 'resp_lag_39', 'sbp_lag_39', 'temp_lag_39', 'tnt_lag_39', 'urine_lag_39', 'wbc_lag_39', 'alb_lag_40', 'alp_lag_40', 'alt_lag_40', 'ast_lag_40', 'be_lag_40', 'bicar_lag_40', 'bili_lag_40', 'bili_dir_lag_40', 'bnd_lag_40', 'bun_lag_40', 'ca_lag_40', 'cai_lag_40', 'ck_lag_40', 'ckmb_lag_40', 'cl_lag_40', 'crea_lag_40', 'crp_lag_40', 'dbp_lag_40', 'fgn_lag_40', 'fio2_lag_40', 'glu_lag_40', 'hgb_lag_40', 'hr_lag_40', 'inr_pt_lag_40', 'k_lag_40', 'lact_lag_40', 'lymph_lag_40', 'map_lag_40', 'mch_lag_40', 'mchc_lag_40', 'mcv_lag_40', 'methb_lag_40', 'mg_lag_40', 'na_lag_40', 'neut_lag_40', 'o2sat_lag_40', 'pco2_lag_40', 'ph_lag_40', 'phos_lag_40', 'plt_lag_40', 'po2_lag_40', 'ptt_lag_40', 'resp_lag_40', 'sbp_lag_40', 'temp_lag_40', 'tnt_lag_40', 'urine_lag_40', 'wbc_lag_40', 'alb_lag_41', 'alp_lag_41', 'alt_lag_41', 'ast_lag_41', 'be_lag_41', 'bicar_lag_41', 'bili_lag_41', 'bili_dir_lag_41', 'bnd_lag_41', 'bun_lag_41', 'ca_lag_41', 'cai_lag_41', 'ck_lag_41', 'ckmb_lag_41', 'cl_lag_41', 'crea_lag_41', 'crp_lag_41', 'dbp_lag_41', 'fgn_lag_41', 'fio2_lag_41', 'glu_lag_41', 'hgb_lag_41', 'hr_lag_41', 'inr_pt_lag_41', 'k_lag_41', 'lact_lag_41', 'lymph_lag_41', 'map_lag_41', 'mch_lag_41', 'mchc_lag_41', 'mcv_lag_41', 'methb_lag_41', 'mg_lag_41', 'na_lag_41', 'neut_lag_41', 'o2sat_lag_41', 'pco2_lag_41', 'ph_lag_41', 'phos_lag_41', 'plt_lag_41', 'po2_lag_41', 'ptt_lag_41', 'resp_lag_41', 'sbp_lag_41', 'temp_lag_41', 'tnt_lag_41', 'urine_lag_41', 'wbc_lag_41', 'alb_lag_42', 'alp_lag_42', 'alt_lag_42', 'ast_lag_42', 'be_lag_42', 'bicar_lag_42', 'bili_lag_42', 'bili_dir_lag_42', 'bnd_lag_42', 'bun_lag_42', 'ca_lag_42', 'cai_lag_42', 'ck_lag_42', 'ckmb_lag_42', 'cl_lag_42', 'crea_lag_42', 'crp_lag_42', 'dbp_lag_42', 'fgn_lag_42', 'fio2_lag_42', 'glu_lag_42', 'hgb_lag_42', 'hr_lag_42', 'inr_pt_lag_42', 'k_lag_42', 'lact_lag_42', 'lymph_lag_42', 'map_lag_42', 'mch_lag_42', 'mchc_lag_42', 'mcv_lag_42', 'methb_lag_42', 'mg_lag_42', 'na_lag_42', 'neut_lag_42', 'o2sat_lag_42', 'pco2_lag_42', 'ph_lag_42', 'phos_lag_42', 'plt_lag_42', 'po2_lag_42', 'ptt_lag_42', 'resp_lag_42', 'sbp_lag_42', 'temp_lag_42', 'tnt_lag_42', 'urine_lag_42', 'wbc_lag_42', 'alb_lag_43', 'alp_lag_43', 'alt_lag_43', 'ast_lag_43', 'be_lag_43', 'bicar_lag_43', 'bili_lag_43', 'bili_dir_lag_43', 'bnd_lag_43', 'bun_lag_43', 'ca_lag_43', 'cai_lag_43', 'ck_lag_43', 'ckmb_lag_43', 'cl_lag_43', 'crea_lag_43', 'crp_lag_43', 'dbp_lag_43', 'fgn_lag_43', 'fio2_lag_43', 'glu_lag_43', 'hgb_lag_43', 'hr_lag_43', 'inr_pt_lag_43', 'k_lag_43', 'lact_lag_43', 'lymph_lag_43', 'map_lag_43', 'mch_lag_43', 'mchc_lag_43', 'mcv_lag_43', 'methb_lag_43', 'mg_lag_43', 'na_lag_43', 'neut_lag_43', 'o2sat_lag_43', 'pco2_lag_43', 'ph_lag_43', 'phos_lag_43', 'plt_lag_43', 'po2_lag_43', 'ptt_lag_43', 'resp_lag_43', 'sbp_lag_43', 'temp_lag_43', 'tnt_lag_43', 'urine_lag_43', 'wbc_lag_43', 'alb_lag_44', 'alp_lag_44', 'alt_lag_44', 'ast_lag_44', 'be_lag_44', 'bicar_lag_44', 'bili_lag_44', 'bili_dir_lag_44', 'bnd_lag_44', 'bun_lag_44', 'ca_lag_44', 'cai_lag_44', 'ck_lag_44', 'ckmb_lag_44', 'cl_lag_44', 'crea_lag_44', 'crp_lag_44', 'dbp_lag_44', 'fgn_lag_44', 'fio2_lag_44', 'glu_lag_44', 'hgb_lag_44', 'hr_lag_44', 'inr_pt_lag_44', 'k_lag_44', 'lact_lag_44', 'lymph_lag_44', 'map_lag_44', 'mch_lag_44', 'mchc_lag_44', 'mcv_lag_44', 'methb_lag_44', 'mg_lag_44', 'na_lag_44', 'neut_lag_44', 'o2sat_lag_44', 'pco2_lag_44', 'ph_lag_44', 'phos_lag_44', 'plt_lag_44', 'po2_lag_44', 'ptt_lag_44', 'resp_lag_44', 'sbp_lag_44', 'temp_lag_44', 'tnt_lag_44', 'urine_lag_44', 'wbc_lag_44', 'alb_lag_45', 'alp_lag_45', 'alt_lag_45', 'ast_lag_45', 'be_lag_45', 'bicar_lag_45', 'bili_lag_45', 'bili_dir_lag_45', 'bnd_lag_45', 'bun_lag_45', 'ca_lag_45', 'cai_lag_45', 'ck_lag_45', 'ckmb_lag_45', 'cl_lag_45', 'crea_lag_45', 'crp_lag_45', 'dbp_lag_45', 'fgn_lag_45', 'fio2_lag_45', 'glu_lag_45', 'hgb_lag_45', 'hr_lag_45', 'inr_pt_lag_45', 'k_lag_45', 'lact_lag_45', 'lymph_lag_45', 'map_lag_45', 'mch_lag_45', 'mchc_lag_45', 'mcv_lag_45', 'methb_lag_45', 'mg_lag_45', 'na_lag_45', 'neut_lag_45', 'o2sat_lag_45', 'pco2_lag_45', 'ph_lag_45', 'phos_lag_45', 'plt_lag_45', 'po2_lag_45', 'ptt_lag_45', 'resp_lag_45', 'sbp_lag_45', 'temp_lag_45', 'tnt_lag_45', 'urine_lag_45', 'wbc_lag_45', 'alb_lag_46', 'alp_lag_46', 'alt_lag_46', 'ast_lag_46', 'be_lag_46', 'bicar_lag_46', 'bili_lag_46', 'bili_dir_lag_46', 'bnd_lag_46', 'bun_lag_46', 'ca_lag_46', 'cai_lag_46', 'ck_lag_46', 'ckmb_lag_46', 'cl_lag_46', 'crea_lag_46', 'crp_lag_46', 'dbp_lag_46', 'fgn_lag_46', 'fio2_lag_46', 'glu_lag_46', 'hgb_lag_46', 'hr_lag_46', 'inr_pt_lag_46', 'k_lag_46', 'lact_lag_46', 'lymph_lag_46', 'map_lag_46', 'mch_lag_46', 'mchc_lag_46', 'mcv_lag_46', 'methb_lag_46', 'mg_lag_46', 'na_lag_46', 'neut_lag_46', 'o2sat_lag_46', 'pco2_lag_46', 'ph_lag_46', 'phos_lag_46', 'plt_lag_46', 'po2_lag_46', 'ptt_lag_46', 'resp_lag_46', 'sbp_lag_46', 'temp_lag_46', 'tnt_lag_46', 'urine_lag_46', 'wbc_lag_46', 'alb_lag_47', 'alp_lag_47', 'alt_lag_47', 'ast_lag_47', 'be_lag_47', 'bicar_lag_47', 'bili_lag_47', 'bili_dir_lag_47', 'bnd_lag_47', 'bun_lag_47', 'ca_lag_47', 'cai_lag_47', 'ck_lag_47', 'ckmb_lag_47', 'cl_lag_47', 'crea_lag_47', 'crp_lag_47', 'dbp_lag_47', 'fgn_lag_47', 'fio2_lag_47', 'glu_lag_47', 'hgb_lag_47', 'hr_lag_47', 'inr_pt_lag_47', 'k_lag_47', 'lact_lag_47', 'lymph_lag_47', 'map_lag_47', 'mch_lag_47', 'mchc_lag_47', 'mcv_lag_47', 'methb_lag_47', 'mg_lag_47', 'na_lag_47', 'neut_lag_47', 'o2sat_lag_47', 'pco2_lag_47', 'ph_lag_47', 'phos_lag_47', 'plt_lag_47', 'po2_lag_47', 'ptt_lag_47', 'resp_lag_47', 'sbp_lag_47', 'temp_lag_47', 'tnt_lag_47', 'urine_lag_47', 'wbc_lag_47', 'alb_lag_48', 'alp_lag_48', 'alt_lag_48', 'ast_lag_48', 'be_lag_48', 'bicar_lag_48', 'bili_lag_48', 'bili_dir_lag_48', 'bnd_lag_48', 'bun_lag_48', 'ca_lag_48', 'cai_lag_48', 'ck_lag_48', 'ckmb_lag_48', 'cl_lag_48', 'crea_lag_48', 'crp_lag_48', 'dbp_lag_48', 'fgn_lag_48', 'fio2_lag_48', 'glu_lag_48', 'hgb_lag_48', 'hr_lag_48', 'inr_pt_lag_48', 'k_lag_48', 'lact_lag_48', 'lymph_lag_48', 'map_lag_48', 'mch_lag_48', 'mchc_lag_48', 'mcv_lag_48', 'methb_lag_48', 'mg_lag_48', 'na_lag_48', 'neut_lag_48', 'o2sat_lag_48', 'pco2_lag_48', 'ph_lag_48', 'phos_lag_48', 'plt_lag_48', 'po2_lag_48', 'ptt_lag_48', 'resp_lag_48', 'sbp_lag_48', 'temp_lag_48', 'tnt_lag_48', 'urine_lag_48', 'wbc_lag_48'] PredictionDatasetTFTpytorch.add_relative_time_idx=False -PredictionDatasetTFTpytorch.target=[ - "label", - ] +PredictionDatasetTFTpytorch.target="label" + PredictionDatasetTFTpytorch.time_varying_unknown_reals=[ - "label", + "label" ] diff --git a/configs/prediction_models/RNNpytorch.gin b/configs/prediction_models/RNNpytorch.gin index 334def2d..0e623606 100644 --- a/configs/prediction_models/RNNpytorch.gin +++ b/configs/prediction_models/RNNpytorch.gin @@ -13,8 +13,8 @@ optimizer/hyperparameter.weight_decay = 1e-6 # Model params model/hyperparameter.class_to_tune = @RNNpytorch -model/hyperparameter.hidden = (32, 256, "log-uniform", 2) -model/hyperparameter.rnn_layers=(1, 3) +model/hyperparameter.hidden = (4, 64, "log-uniform", 2) +model/hyperparameter.rnn_layers=(1,3) model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.cell_type='LSTM' model/hyperparameter.dropout = (0.0, 0.4) diff --git a/configs/prediction_models/TFTpytorch.gin b/configs/prediction_models/TFTpytorch.gin index abb81f6f..5ff809a5 100644 --- a/configs/prediction_models/TFTpytorch.gin +++ b/configs/prediction_models/TFTpytorch.gin @@ -12,7 +12,7 @@ optimizer/hyperparameter.weight_decay = 1e-6 # Model params model/hyperparameter.class_to_tune = @TFTpytorch -model/hyperparameter.hidden = (16,32,64,128) +model/hyperparameter.hidden = (16,32,64) model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.dropout = (0.0, 0.4) model/hyperparameter.dropout_att = (0.0, 0.4) @@ -25,7 +25,7 @@ PredictionDatasetTFTpytorch.max_encoder_length = 24 PredictionDatasetTFTpytorch.max_prediction_length = 24 PredictionDatasetTFTpytorch.target="label" PredictionDatasetTFTpytorch.time_varying_known_reals=["time_idx"] -PredictionDatasetTFTpytorch.add_relative_time_idx=True +PredictionDatasetTFTpytorch.add_relative_time_idx=False PredictionDatasetTFTpytorch.time_varying_unknown_categoricals=[] PredictionDatasetTFTpytorch.time_varying_unknown_reals=["alb", "alp", @@ -75,4 +75,4 @@ PredictionDatasetTFTpytorch.time_varying_unknown_reals=["alb", "tnt", "urine", "wbc",] -PredictionDatasetTFTpytorch.lagged_variables=[] \ No newline at end of file +PredictionDatasetTFTpytorch.lagged_variables=[] diff --git a/configs/prediction_models/common/DLCommon.gin b/configs/prediction_models/common/DLCommon.gin index 8f5d648e..8e4149fd 100644 --- a/configs/prediction_models/common/DLCommon.gin +++ b/configs/prediction_models/common/DLCommon.gin @@ -15,7 +15,7 @@ base_regression_preprocessor.generate_features = False # Train params train_common.optimizer = @Adam train_common.epochs = 1000 -train_common.batch_size = 128 +train_common.batch_size = 1 train_common.patience = 20 train_common.min_delta = 1e-4 diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 6287810b..0f16300b 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -3,7 +3,7 @@ import pandas as pd import gin import numpy as np -from torch import Tensor, cat, from_numpy, float32 +from torch import Tensor, cat, from_numpy, float32, min, max from torch.utils.data import Dataset import logging from typing import Dict, Tuple, Union @@ -498,7 +498,7 @@ def __init__( time_varying_unknown_categoricals=time_varying_unknown_categoricals, time_varying_unknown_reals=time_varying_unknown_reals, add_relative_time_idx=add_relative_time_idx, - add_target_scales=True, + # add_target_scales=True, add_encoder_length=True, predict_mode=True, target_normalizer=None, @@ -521,8 +521,10 @@ def get_feature_names(self): return self.column_names def randomize_labels(self, num_classes=None, min=None, max=None): - if num_classes is None: - random_target = np.random.uniform(min, max, size=len(self.data["target"][0])) + if num_classes == 1: + + random_target = np.random.uniform(self.data["target"][0].min(), + self.data["target"][0].max(), size=len(self.data["target"][0])) else: random_target = np.random.randint(num_classes, size=len(self.data["target"][0])) self.data["target"][0] = Tensor(random_target) diff --git a/icu_benchmarks/models/constants.py b/icu_benchmarks/models/constants.py index 49555d42..093d15bd 100644 --- a/icu_benchmarks/models/constants.py +++ b/icu_benchmarks/models/constants.py @@ -64,7 +64,7 @@ class DLMetrics: BINARY_CLASSIFICATION_TORCHMETRICS = { "AUC": AUROC(task="binary"), - "PR": TorchMetricsAveragePrecision(task="binary"), + # "PR": TorchMetricsAveragePrecision(task="binary"), # "PrecisionRecallCurve": TorchMetricsPrecisionRecallCurve(task="binary"), "Calibration_Error": CalibrationError(task="binary", n_bins=10), "F1": F1Score(task="binary", num_classes=2), diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 5f1d287e..1cd782a5 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -636,14 +636,14 @@ def forward( "groups": tuple_x[9], "target_scale": tuple_x[10], } - out = self.model(x_dict) + if self.num_classes == 1: + x_dict['encoder_cont'][:, :, -1] = 0.0 - # out = out["prediction"].reshape(1, -1) + out = self.model(x_dict) - # pred = self.logit(out["prediction"]) - # print(pred + pred = self.logit(out["prediction"]) - return out["prediction"] + return pred @gin.configurable @@ -697,6 +697,8 @@ def forward( "groups": tuple_x[9], "target_scale": tuple_x[10], } + if self.num_classes == 1: + x_dict['encoder_cont'][:, :, -1] = 0.0 out = self.model(x_dict) out = out["prediction"][0] diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 018c8bc3..be8e638d 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -283,23 +283,24 @@ def train_common( # print("shap", attributions_SHAP) attributions_IG = model.explantation_captum( test_loader=test_loader, - method=IntegratedGradients, log_dir=log_dir, plot=True, n_steps=35 + method=IntegratedGradients, log_dir=log_dir, plot=True, n_steps=20 ) + print(test_dataset.reals) Attention_weights = model.interpertations(test_loader, log_dir, plot=True) - # print("attention", Attention_weights) + print("attention", Attention_weights) if XAI_metric: ra2 = np.random.randint(low=0, high=100, size=24) random_attributions = np.random.normal(size=24) - F_baseline = model.Faithfulness_Correlation(test_loader, random_attributions) - F_baseline2 = model.Faithfulness_Correlation(test_loader, random_attributions) + F_baseline = model.Faithfulness_Correlation(test_loader, random_attributions, pertrub='Noise') + F_baseline2 = model.Faithfulness_Correlation(test_loader, random_attributions, pertrub='Noise') print('Random noraml faithfulness correlation', F_baseline) print('random unifrom ', F_baseline2) - F_attribution = model.Faithfulness_Correlation(test_loader, attributions_IG) + F_attribution = model.Faithfulness_Correlation(test_loader, attributions_IG, pertrub='Noise') print('Attributions faithfulness correlation', F_attribution) - F_attention = model.Faithfulness_Correlation(test_loader, Attention_weights["attention"]) + F_attention = model.Faithfulness_Correlation(test_loader, Attention_weights["attention"], pertrub='Noise') print('Attention faithfulness correlation', F_attention) # path = Path(random_model_dir) diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 41eee23b..ca3093bd 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -316,21 +316,7 @@ def step_fn(self, element, step_prefix=""): element (object): step_prefix (str): Step type, by default: test, train, val. """ - """ - if isinstance(element[0], OrderedDict): # check if the data loader is the one for the TFT nvidia implementation - data, mask = element[0], element[1].to(self.device) - - for key, value in data.items(): - value = value.float().to(self.device) - - if value.shape[-1] == 1: - value = value.squeeze(-1) - if value.dim() == 3: - value = value.permute(0, 2, 1) - if key == "target": - labels = value.squeeze() - data[key] = value - """ + if len(element) == 2: data, labels = element[0], (element[1]).to(self.device) if isinstance(data, list): @@ -474,7 +460,6 @@ def step_fn(self, element, step_prefix=""): prediction = torch.masked_select(out, mask.unsqueeze(-1)).reshape(-1, out.shape[-1]).to(self.device) target = torch.masked_select(labels, mask).to(self.device) - if prediction.shape[-1] > 1 and self.run_mode == RunMode.classification: # Classification task loss = self.loss(prediction, target.long(), weight=self.loss_weights.to(self.device)) + aux_loss @@ -544,7 +529,7 @@ def explantation_captum(self, test_loader, method, log_dir=".", plot=False, **kw explantation = method(self.forward_captum) # Reformat attributions. attr_all_timesteps = [] - for time_step in range(23, 24): + for time_step in range(0, 24): attr = explantation.attribute( data, target=(time_step), baselines=baselines, **kwargs ) @@ -564,7 +549,7 @@ def explantation_captum(self, test_loader, method, log_dir=".", plot=False, **kw # normalized_means = (means - means.min()) / (means.max() - means.min()) if plot: # Create x values (assuming you want a simple sequential x-axis) - # Assuming you have 24 values + # Assuming you have 57 variables x_values = np.arange(1, 58) # Plotting the featrue means plt.figure(figsize=(8, 6)) @@ -603,7 +588,7 @@ def explantation_captum(self, test_loader, method, log_dir=".", plot=False, **kw return means def Faithfulness_Correlation( - self, test_loader, attribution, similarity_func=None, nr_runs=100, pertrub=None, subset_size=3 + self, test_loader, attribution, similarity_func=None, nr_runs=100, pertrub=None, subset_size=6, features=False ): """ Implementation of faithfulness correlation by Bhatt et al., 2020. From f435717f49ad1124dd7ba5f14fa9c3ece5c1cd44 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Wed, 15 Nov 2023 15:49:30 +0100 Subject: [PATCH 107/142] added shap, sailency explantations and logged values --- icu_benchmarks/data/loader.py | 2 +- icu_benchmarks/models/train.py | 66 ++++++++++++++++++++++++------- icu_benchmarks/models/wrappers.py | 31 +++++++++------ icu_benchmarks/run_utils.py | 5 +++ 4 files changed, 75 insertions(+), 29 deletions(-) diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 0f16300b..57b2df55 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -498,7 +498,7 @@ def __init__( time_varying_unknown_categoricals=time_varying_unknown_categoricals, time_varying_unknown_reals=time_varying_unknown_reals, add_relative_time_idx=add_relative_time_idx, - # add_target_scales=True, + add_target_scales=True, add_encoder_length=True, predict_mode=True, target_normalizer=None, diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index be8e638d..6778d642 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -3,6 +3,7 @@ import torch import pickle import logging +import json import pandas as pd from joblib import load from torch.optim import Adam @@ -27,7 +28,7 @@ from icu_benchmarks.contants import RunMode from icu_benchmarks.data.constants import DataSplit as Split from collections import OrderedDict -from captum.attr import IntegratedGradients, ShapleyValueSampling +from captum.attr import IntegratedGradients, ShapleyValueSampling, Saliency, GuidedBackprop, LRP cpu_core_count = ( @@ -275,33 +276,68 @@ def train_common( return 0 if explain: - # - # attributions_SHAP = model.explantation_captum( - # test_loader=test_loader, - # method=ShapleyValueSampling, log_dir=log_dir, plot=True - # ) - # print("shap", attributions_SHAP) + + attributions_Saliency = model.explantation_captum( + test_loader=test_loader, + method=Saliency, log_dir=log_dir, plot=True + ) + print("saliency", attributions_Saliency) + attributions_shap = model.explantation_captum( + test_loader=test_loader, + method=ShapleyValueSampling, log_dir=log_dir, plot=True, n_samples=10 + ) + print("shap", attributions_shap) attributions_IG = model.explantation_captum( test_loader=test_loader, method=IntegratedGradients, log_dir=log_dir, plot=True, n_steps=20 ) - print(test_dataset.reals) + print("IG", attributions_IG) + Interpertations = model.interpertations(test_loader, log_dir, plot=True) + print("attention", Interpertations) + attributions_dict = { + "attributions_Saliency": attributions_Saliency.tolist(), + "attributions_IG": attributions_IG.tolist(), + "attributions_shap": attributions_shap.tolist(), + "attention_weights": Interpertations["attention"].tolist(), + "static_variables": Interpertations["static_variables"].tolist(), + "encoder_variables": Interpertations["encoder_variables"].tolist() + } + + # Path to the JSON file in log_dir + json_file_path = f"{log_dir}/Attributions.json" - Attention_weights = model.interpertations(test_loader, log_dir, plot=True) - print("attention", Attention_weights) + # Write the dictionary to a JSON file + with open(json_file_path, 'w') as json_file: + json.dump(attributions_dict, json_file) if XAI_metric: - ra2 = np.random.randint(low=0, high=100, size=24) random_attributions = np.random.normal(size=24) F_baseline = model.Faithfulness_Correlation(test_loader, random_attributions, pertrub='Noise') - F_baseline2 = model.Faithfulness_Correlation(test_loader, random_attributions, pertrub='Noise') - print('Random noraml faithfulness correlation', F_baseline) - print('random unifrom ', F_baseline2) + print('Random normal faithfulness correlation', F_baseline) + F_attribution = model.Faithfulness_Correlation(test_loader, attributions_IG, pertrub='Noise') print('Attributions faithfulness correlation', F_attribution) - F_attention = model.Faithfulness_Correlation(test_loader, Attention_weights["attention"], pertrub='Noise') + F_attention = model.Faithfulness_Correlation(test_loader, Interpertations["attention"], pertrub='Noise') print('Attention faithfulness correlation', F_attention) + F_saliency = model.Faithfulness_Correlation(test_loader, attributions_Saliency, pertrub='Noise') + print('Saliency faithfulness correlation', F_saliency) + F_shap = model.Faithfulness_Correlation(test_loader, attributions_shap, pertrub='Noise') + print('shap faithfulness correlation', F_shap) + XAI_dict = { + "Faithfulness_correlation_normal_random": F_baseline, + "Faithfulness_correlation_IG": F_attribution, + "Faithfulness_correlation_attention": F_attention, + "Faithfulness_correlation_saliency": F_saliency, + "Faithfulness_correlation_shap": F_shap + } + + # Path to the JSON file in log_dir + json_file_path = f"{log_dir}/XAI_metrics.json" + + # Write the dictionary to a JSON file + with open(json_file_path, 'w') as json_file: + json.dump(XAI_dict, json_file) # path = Path(random_model_dir) diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index ca3093bd..32e05423 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -20,7 +20,7 @@ from icu_benchmarks.contants import RunMode import matplotlib.pyplot as plt from icu_benchmarks.models.similarity_func import correlation_spearman, distance_euclidean - +import captum gin.config.external_configurable(nn.functional.nll_loss, module="torch.nn.functional") gin.config.external_configurable(nn.functional.cross_entropy, module="torch.nn.functional") gin.config.external_configurable(nn.functional.mse_loss, module="torch.nn.functional") @@ -513,15 +513,15 @@ def explantation_captum(self, test_loader, method, log_dir=".", plot=False, **kw x["target_scale"].requires_grad_(), ) baselines = ( - torch.zeros_like(data[0]).to(self.device), # encoder_cat, set to zero - torch.zeros_like(data[1]).to(self.device), # encoder_cont, set to zero - torch.zeros_like(data[2]).to(self.device), # encoder_target, set to zero + data[0].to(self.device), # encoder_cat, set to random + torch.randn_like(data[1]).to(self.device), # encoder_cont, set to random + torch.randn_like(data[2]).to(self.device), # encoder_target, set to random data[3].to(self.device), # encoder_lengths, leave unchanged - torch.zeros_like(data[4]).to(self.device), # decoder_cat, set to zero - torch.zeros_like(data[5]).to(self.device), # decoder_cont, set to zero - torch.zeros_like(data[6]).to(self.device), # decoder_target, set to zero + torch.randn_like(data[4]).to(self.device), # decoder_cat, set to random + torch.randn_like(data[5]).to(self.device), # decoder_cont, set to random + torch.randn_like(data[6]).to(self.device), # decoder_target, set to random data[7].to(self.device), # decoder_lengths, leave unchanged - torch.zeros_like(data[8]).to(self.device), # decoder_time_idx, set to zero + data[8].to(self.device), # decoder_time_idx, unchanged data[9].to(self.device), # groups, leave unchanged data[10].to(self.device), # target_scale, leave unchanged ) @@ -529,10 +529,15 @@ def explantation_captum(self, test_loader, method, log_dir=".", plot=False, **kw explantation = method(self.forward_captum) # Reformat attributions. attr_all_timesteps = [] - for time_step in range(0, 24): - attr = explantation.attribute( - data, target=(time_step), baselines=baselines, **kwargs - ) + for time_step in range(23, 24): + if method is not captum.attr.Saliency: + attr = explantation.attribute( + data, target=(time_step), baselines=baselines, **kwargs + ) + else: + attr = explantation.attribute( + data, target=(time_step), **kwargs + ) # Convert attributions to numpy array and append to the list attr_all_timesteps.append(attr[0].cpu().detach().numpy()) @@ -588,7 +593,7 @@ def explantation_captum(self, test_loader, method, log_dir=".", plot=False, **kw return means def Faithfulness_Correlation( - self, test_loader, attribution, similarity_func=None, nr_runs=100, pertrub=None, subset_size=6, features=False + self, test_loader, attribution, similarity_func=None, nr_runs=2, pertrub=None, subset_size=6, features=False ): """ Implementation of faithfulness correlation by Bhatt et al., 2020. diff --git a/icu_benchmarks/run_utils.py b/icu_benchmarks/run_utils.py index 4bc3985c..0f559e4c 100644 --- a/icu_benchmarks/run_utils.py +++ b/icu_benchmarks/run_utils.py @@ -213,11 +213,16 @@ def aggregate_results(log_dir: Path, execution_time: timedelta = None): with open(fold_iter / "val_metrics.csv", "r") as f: result = json.load(f) aggregated[repetition.name][fold_iter.name].update(result) + # Add durations to metrics if (fold_iter / "durations.json").is_file(): with open(fold_iter / "durations.json", "r") as f: result = json.load(f) aggregated[repetition.name][fold_iter.name].update(result) + if (fold_iter / "XAI_metrics.json").is_file(): + with open(fold_iter / "XAI_metrics.json", "r") as f: + result = json.load(f) + aggregated[repetition.name][fold_iter.name].update(result) # Aggregate results per metric list_scores = {} From ce078a1f305e39ab43ee3b73ca1a66777877b398 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Mon, 20 Nov 2023 13:19:09 +0100 Subject: [PATCH 108/142] renamed dataloader , rescaled target, removed target scale, target center --- configs/prediction_models/DeepARpytorch.gin | 21 ++++----- configs/prediction_models/TFTpytorch.gin | 2 +- configs/prediction_models/common/DLCommon.gin | 2 +- configs/prediction_models/common/DLTuning.gin | 2 +- configs/tasks/Regression.gin | 2 +- icu_benchmarks/data/loader.py | 8 ++-- icu_benchmarks/models/train.py | 46 ++++++++++--------- icu_benchmarks/models/wrappers.py | 18 ++++++-- 8 files changed, 55 insertions(+), 46 deletions(-) diff --git a/configs/prediction_models/DeepARpytorch.gin b/configs/prediction_models/DeepARpytorch.gin index d4962081..27652b96 100644 --- a/configs/prediction_models/DeepARpytorch.gin +++ b/configs/prediction_models/DeepARpytorch.gin @@ -1,4 +1,4 @@ -# Settings for TFT model. +# Settings for DeepAR model. # Common settings for DL models include "configs/prediction_models/common/DLCommon.gin" @@ -22,19 +22,16 @@ model/hyperparameter.lr_scheduler = "exponential" # Dataset params -PredictionDatasetTFTpytorch.max_encoder_length = 24 -PredictionDatasetTFTpytorch.max_prediction_length = 24 -PredictionDatasetTFTpytorch.time_varying_known_reals=['alb_lag_24', 'alp_lag_24', 'alt_lag_24', 'ast_lag_24', 'be_lag_24', 'bicar_lag_24', 'bili_lag_24', 'bili_dir_lag_24', 'bnd_lag_24', 'bun_lag_24', 'ca_lag_24', 'cai_lag_24', 'ck_lag_24', 'ckmb_lag_24', 'cl_lag_24', 'crea_lag_24', 'crp_lag_24', 'dbp_lag_24', 'fgn_lag_24', 'fio2_lag_24', 'glu_lag_24', 'hgb_lag_24', 'hr_lag_24', 'inr_pt_lag_24', 'k_lag_24', 'lact_lag_24', 'lymph_lag_24', 'map_lag_24', 'mch_lag_24', 'mchc_lag_24', 'mcv_lag_24', 'methb_lag_24', 'mg_lag_24', 'na_lag_24', 'neut_lag_24', 'o2sat_lag_24', 'pco2_lag_24', 'ph_lag_24', 'phos_lag_24', 'plt_lag_24', 'po2_lag_24', 'ptt_lag_24', 'resp_lag_24', 'sbp_lag_24', 'temp_lag_24', 'tnt_lag_24', 'urine_lag_24', 'wbc_lag_24', 'alb_lag_25', 'alp_lag_25', 'alt_lag_25', 'ast_lag_25', 'be_lag_25', 'bicar_lag_25', 'bili_lag_25', 'bili_dir_lag_25', 'bnd_lag_25', 'bun_lag_25', 'ca_lag_25', 'cai_lag_25', 'ck_lag_25', 'ckmb_lag_25', 'cl_lag_25', 'crea_lag_25', 'crp_lag_25', 'dbp_lag_25', 'fgn_lag_25', 'fio2_lag_25', 'glu_lag_25', 'hgb_lag_25', 'hr_lag_25', 'inr_pt_lag_25', 'k_lag_25', 'lact_lag_25', 'lymph_lag_25', 'map_lag_25', 'mch_lag_25', 'mchc_lag_25', 'mcv_lag_25', 'methb_lag_25', 'mg_lag_25', 'na_lag_25', 'neut_lag_25', 'o2sat_lag_25', 'pco2_lag_25', 'ph_lag_25', 'phos_lag_25', 'plt_lag_25', 'po2_lag_25', 'ptt_lag_25', 'resp_lag_25', 'sbp_lag_25', 'temp_lag_25', 'tnt_lag_25', 'urine_lag_25', 'wbc_lag_25', 'alb_lag_26', 'alp_lag_26', 'alt_lag_26', 'ast_lag_26', 'be_lag_26', 'bicar_lag_26', 'bili_lag_26', 'bili_dir_lag_26', 'bnd_lag_26', 'bun_lag_26', 'ca_lag_26', 'cai_lag_26', 'ck_lag_26', 'ckmb_lag_26', 'cl_lag_26', 'crea_lag_26', 'crp_lag_26', 'dbp_lag_26', 'fgn_lag_26', 'fio2_lag_26', 'glu_lag_26', 'hgb_lag_26', 'hr_lag_26', 'inr_pt_lag_26', 'k_lag_26', 'lact_lag_26', 'lymph_lag_26', 'map_lag_26', 'mch_lag_26', 'mchc_lag_26', 'mcv_lag_26', 'methb_lag_26', 'mg_lag_26', 'na_lag_26', 'neut_lag_26', 'o2sat_lag_26', 'pco2_lag_26', 'ph_lag_26', 'phos_lag_26', 'plt_lag_26', 'po2_lag_26', 'ptt_lag_26', 'resp_lag_26', 'sbp_lag_26', 'temp_lag_26', 'tnt_lag_26', 'urine_lag_26', 'wbc_lag_26', 'alb_lag_27', 'alp_lag_27', 'alt_lag_27', 'ast_lag_27', 'be_lag_27', 'bicar_lag_27', 'bili_lag_27', 'bili_dir_lag_27', 'bnd_lag_27', 'bun_lag_27', 'ca_lag_27', 'cai_lag_27', 'ck_lag_27', 'ckmb_lag_27', 'cl_lag_27', 'crea_lag_27', 'crp_lag_27', 'dbp_lag_27', 'fgn_lag_27', 'fio2_lag_27', 'glu_lag_27', 'hgb_lag_27', 'hr_lag_27', 'inr_pt_lag_27', 'k_lag_27', 'lact_lag_27', 'lymph_lag_27', 'map_lag_27', 'mch_lag_27', 'mchc_lag_27', 'mcv_lag_27', 'methb_lag_27', 'mg_lag_27', 'na_lag_27', 'neut_lag_27', 'o2sat_lag_27', 'pco2_lag_27', 'ph_lag_27', 'phos_lag_27', 'plt_lag_27', 'po2_lag_27', 'ptt_lag_27', 'resp_lag_27', 'sbp_lag_27', 'temp_lag_27', 'tnt_lag_27', 'urine_lag_27', 'wbc_lag_27', 'alb_lag_28', 'alp_lag_28', 'alt_lag_28', 'ast_lag_28', 'be_lag_28', 'bicar_lag_28', 'bili_lag_28', 'bili_dir_lag_28', 'bnd_lag_28', 'bun_lag_28', 'ca_lag_28', 'cai_lag_28', 'ck_lag_28', 'ckmb_lag_28', 'cl_lag_28', 'crea_lag_28', 'crp_lag_28', 'dbp_lag_28', 'fgn_lag_28', 'fio2_lag_28', 'glu_lag_28', 'hgb_lag_28', 'hr_lag_28', 'inr_pt_lag_28', 'k_lag_28', 'lact_lag_28', 'lymph_lag_28', 'map_lag_28', 'mch_lag_28', 'mchc_lag_28', 'mcv_lag_28', 'methb_lag_28', 'mg_lag_28', 'na_lag_28', 'neut_lag_28', 'o2sat_lag_28', 'pco2_lag_28', 'ph_lag_28', 'phos_lag_28', 'plt_lag_28', 'po2_lag_28', 'ptt_lag_28', 'resp_lag_28', 'sbp_lag_28', 'temp_lag_28', 'tnt_lag_28', 'urine_lag_28', 'wbc_lag_28', 'alb_lag_29', 'alp_lag_29', 'alt_lag_29', 'ast_lag_29', 'be_lag_29', 'bicar_lag_29', 'bili_lag_29', 'bili_dir_lag_29', 'bnd_lag_29', 'bun_lag_29', 'ca_lag_29', 'cai_lag_29', 'ck_lag_29', 'ckmb_lag_29', 'cl_lag_29', 'crea_lag_29', 'crp_lag_29', 'dbp_lag_29', 'fgn_lag_29', 'fio2_lag_29', 'glu_lag_29', 'hgb_lag_29', 'hr_lag_29', 'inr_pt_lag_29', 'k_lag_29', 'lact_lag_29', 'lymph_lag_29', 'map_lag_29', 'mch_lag_29', 'mchc_lag_29', 'mcv_lag_29', 'methb_lag_29', 'mg_lag_29', 'na_lag_29', 'neut_lag_29', 'o2sat_lag_29', 'pco2_lag_29', 'ph_lag_29', 'phos_lag_29', 'plt_lag_29', 'po2_lag_29', 'ptt_lag_29', 'resp_lag_29', 'sbp_lag_29', 'temp_lag_29', 'tnt_lag_29', 'urine_lag_29', 'wbc_lag_29', 'alb_lag_30', 'alp_lag_30', 'alt_lag_30', 'ast_lag_30', 'be_lag_30', 'bicar_lag_30', 'bili_lag_30', 'bili_dir_lag_30', 'bnd_lag_30', 'bun_lag_30', 'ca_lag_30', 'cai_lag_30', 'ck_lag_30', 'ckmb_lag_30', 'cl_lag_30', 'crea_lag_30', 'crp_lag_30', 'dbp_lag_30', 'fgn_lag_30', 'fio2_lag_30', 'glu_lag_30', 'hgb_lag_30', 'hr_lag_30', 'inr_pt_lag_30', 'k_lag_30', 'lact_lag_30', 'lymph_lag_30', 'map_lag_30', 'mch_lag_30', 'mchc_lag_30', 'mcv_lag_30', 'methb_lag_30', 'mg_lag_30', 'na_lag_30', 'neut_lag_30', 'o2sat_lag_30', 'pco2_lag_30', 'ph_lag_30', 'phos_lag_30', 'plt_lag_30', 'po2_lag_30', 'ptt_lag_30', 'resp_lag_30', 'sbp_lag_30', 'temp_lag_30', 'tnt_lag_30', 'urine_lag_30', 'wbc_lag_30', 'alb_lag_31', 'alp_lag_31', 'alt_lag_31', 'ast_lag_31', 'be_lag_31', 'bicar_lag_31', 'bili_lag_31', 'bili_dir_lag_31', 'bnd_lag_31', 'bun_lag_31', 'ca_lag_31', 'cai_lag_31', 'ck_lag_31', 'ckmb_lag_31', 'cl_lag_31', 'crea_lag_31', 'crp_lag_31', 'dbp_lag_31', 'fgn_lag_31', 'fio2_lag_31', 'glu_lag_31', 'hgb_lag_31', 'hr_lag_31', 'inr_pt_lag_31', 'k_lag_31', 'lact_lag_31', 'lymph_lag_31', 'map_lag_31', 'mch_lag_31', 'mchc_lag_31', 'mcv_lag_31', 'methb_lag_31', 'mg_lag_31', 'na_lag_31', 'neut_lag_31', 'o2sat_lag_31', 'pco2_lag_31', 'ph_lag_31', 'phos_lag_31', 'plt_lag_31', 'po2_lag_31', 'ptt_lag_31', 'resp_lag_31', 'sbp_lag_31', 'temp_lag_31', 'tnt_lag_31', 'urine_lag_31', 'wbc_lag_31', 'alb_lag_32', 'alp_lag_32', 'alt_lag_32', 'ast_lag_32', 'be_lag_32', 'bicar_lag_32', 'bili_lag_32', 'bili_dir_lag_32', 'bnd_lag_32', 'bun_lag_32', 'ca_lag_32', 'cai_lag_32', 'ck_lag_32', 'ckmb_lag_32', 'cl_lag_32', 'crea_lag_32', 'crp_lag_32', 'dbp_lag_32', 'fgn_lag_32', 'fio2_lag_32', 'glu_lag_32', 'hgb_lag_32', 'hr_lag_32', 'inr_pt_lag_32', 'k_lag_32', 'lact_lag_32', 'lymph_lag_32', 'map_lag_32', 'mch_lag_32', 'mchc_lag_32', 'mcv_lag_32', 'methb_lag_32', 'mg_lag_32', 'na_lag_32', 'neut_lag_32', 'o2sat_lag_32', 'pco2_lag_32', 'ph_lag_32', 'phos_lag_32', 'plt_lag_32', 'po2_lag_32', 'ptt_lag_32', 'resp_lag_32', 'sbp_lag_32', 'temp_lag_32', 'tnt_lag_32', 'urine_lag_32', 'wbc_lag_32', 'alb_lag_33', 'alp_lag_33', 'alt_lag_33', 'ast_lag_33', 'be_lag_33', 'bicar_lag_33', 'bili_lag_33', 'bili_dir_lag_33', 'bnd_lag_33', 'bun_lag_33', 'ca_lag_33', 'cai_lag_33', 'ck_lag_33', 'ckmb_lag_33', 'cl_lag_33', 'crea_lag_33', 'crp_lag_33', 'dbp_lag_33', 'fgn_lag_33', 'fio2_lag_33', 'glu_lag_33', 'hgb_lag_33', 'hr_lag_33', 'inr_pt_lag_33', 'k_lag_33', 'lact_lag_33', 'lymph_lag_33', 'map_lag_33', 'mch_lag_33', 'mchc_lag_33', 'mcv_lag_33', 'methb_lag_33', 'mg_lag_33', 'na_lag_33', 'neut_lag_33', 'o2sat_lag_33', 'pco2_lag_33', 'ph_lag_33', 'phos_lag_33', 'plt_lag_33', 'po2_lag_33', 'ptt_lag_33', 'resp_lag_33', 'sbp_lag_33', 'temp_lag_33', 'tnt_lag_33', 'urine_lag_33', 'wbc_lag_33', 'alb_lag_34', 'alp_lag_34', 'alt_lag_34', 'ast_lag_34', 'be_lag_34', 'bicar_lag_34', 'bili_lag_34', 'bili_dir_lag_34', 'bnd_lag_34', 'bun_lag_34', 'ca_lag_34', 'cai_lag_34', 'ck_lag_34', 'ckmb_lag_34', 'cl_lag_34', 'crea_lag_34', 'crp_lag_34', 'dbp_lag_34', 'fgn_lag_34', 'fio2_lag_34', 'glu_lag_34', 'hgb_lag_34', 'hr_lag_34', 'inr_pt_lag_34', 'k_lag_34', 'lact_lag_34', 'lymph_lag_34', 'map_lag_34', 'mch_lag_34', 'mchc_lag_34', 'mcv_lag_34', 'methb_lag_34', 'mg_lag_34', 'na_lag_34', 'neut_lag_34', 'o2sat_lag_34', 'pco2_lag_34', 'ph_lag_34', 'phos_lag_34', 'plt_lag_34', 'po2_lag_34', 'ptt_lag_34', 'resp_lag_34', 'sbp_lag_34', 'temp_lag_34', 'tnt_lag_34', 'urine_lag_34', 'wbc_lag_34', 'alb_lag_35', 'alp_lag_35', 'alt_lag_35', 'ast_lag_35', 'be_lag_35', 'bicar_lag_35', 'bili_lag_35', 'bili_dir_lag_35', 'bnd_lag_35', 'bun_lag_35', 'ca_lag_35', 'cai_lag_35', 'ck_lag_35', 'ckmb_lag_35', 'cl_lag_35', 'crea_lag_35', 'crp_lag_35', 'dbp_lag_35', 'fgn_lag_35', 'fio2_lag_35', 'glu_lag_35', 'hgb_lag_35', 'hr_lag_35', 'inr_pt_lag_35', 'k_lag_35', 'lact_lag_35', 'lymph_lag_35', 'map_lag_35', 'mch_lag_35', 'mchc_lag_35', 'mcv_lag_35', 'methb_lag_35', 'mg_lag_35', 'na_lag_35', 'neut_lag_35', 'o2sat_lag_35', 'pco2_lag_35', 'ph_lag_35', 'phos_lag_35', 'plt_lag_35', 'po2_lag_35', 'ptt_lag_35', 'resp_lag_35', 'sbp_lag_35', 'temp_lag_35', 'tnt_lag_35', 'urine_lag_35', 'wbc_lag_35', 'alb_lag_36', 'alp_lag_36', 'alt_lag_36', 'ast_lag_36', 'be_lag_36', 'bicar_lag_36', 'bili_lag_36', 'bili_dir_lag_36', 'bnd_lag_36', 'bun_lag_36', 'ca_lag_36', 'cai_lag_36', 'ck_lag_36', 'ckmb_lag_36', 'cl_lag_36', 'crea_lag_36', 'crp_lag_36', 'dbp_lag_36', 'fgn_lag_36', 'fio2_lag_36', 'glu_lag_36', 'hgb_lag_36', 'hr_lag_36', 'inr_pt_lag_36', 'k_lag_36', 'lact_lag_36', 'lymph_lag_36', 'map_lag_36', 'mch_lag_36', 'mchc_lag_36', 'mcv_lag_36', 'methb_lag_36', 'mg_lag_36', 'na_lag_36', 'neut_lag_36', 'o2sat_lag_36', 'pco2_lag_36', 'ph_lag_36', 'phos_lag_36', 'plt_lag_36', 'po2_lag_36', 'ptt_lag_36', 'resp_lag_36', 'sbp_lag_36', 'temp_lag_36', 'tnt_lag_36', 'urine_lag_36', 'wbc_lag_36', 'alb_lag_37', 'alp_lag_37', 'alt_lag_37', 'ast_lag_37', 'be_lag_37', 'bicar_lag_37', 'bili_lag_37', 'bili_dir_lag_37', 'bnd_lag_37', 'bun_lag_37', 'ca_lag_37', 'cai_lag_37', 'ck_lag_37', 'ckmb_lag_37', 'cl_lag_37', 'crea_lag_37', 'crp_lag_37', 'dbp_lag_37', 'fgn_lag_37', 'fio2_lag_37', 'glu_lag_37', 'hgb_lag_37', 'hr_lag_37', 'inr_pt_lag_37', 'k_lag_37', 'lact_lag_37', 'lymph_lag_37', 'map_lag_37', 'mch_lag_37', 'mchc_lag_37', 'mcv_lag_37', 'methb_lag_37', 'mg_lag_37', 'na_lag_37', 'neut_lag_37', 'o2sat_lag_37', 'pco2_lag_37', 'ph_lag_37', 'phos_lag_37', 'plt_lag_37', 'po2_lag_37', 'ptt_lag_37', 'resp_lag_37', 'sbp_lag_37', 'temp_lag_37', 'tnt_lag_37', 'urine_lag_37', 'wbc_lag_37', 'alb_lag_38', 'alp_lag_38', 'alt_lag_38', 'ast_lag_38', 'be_lag_38', 'bicar_lag_38', 'bili_lag_38', 'bili_dir_lag_38', 'bnd_lag_38', 'bun_lag_38', 'ca_lag_38', 'cai_lag_38', 'ck_lag_38', 'ckmb_lag_38', 'cl_lag_38', 'crea_lag_38', 'crp_lag_38', 'dbp_lag_38', 'fgn_lag_38', 'fio2_lag_38', 'glu_lag_38', 'hgb_lag_38', 'hr_lag_38', 'inr_pt_lag_38', 'k_lag_38', 'lact_lag_38', 'lymph_lag_38', 'map_lag_38', 'mch_lag_38', 'mchc_lag_38', 'mcv_lag_38', 'methb_lag_38', 'mg_lag_38', 'na_lag_38', 'neut_lag_38', 'o2sat_lag_38', 'pco2_lag_38', 'ph_lag_38', 'phos_lag_38', 'plt_lag_38', 'po2_lag_38', 'ptt_lag_38', 'resp_lag_38', 'sbp_lag_38', 'temp_lag_38', 'tnt_lag_38', 'urine_lag_38', 'wbc_lag_38', 'alb_lag_39', 'alp_lag_39', 'alt_lag_39', 'ast_lag_39', 'be_lag_39', 'bicar_lag_39', 'bili_lag_39', 'bili_dir_lag_39', 'bnd_lag_39', 'bun_lag_39', 'ca_lag_39', 'cai_lag_39', 'ck_lag_39', 'ckmb_lag_39', 'cl_lag_39', 'crea_lag_39', 'crp_lag_39', 'dbp_lag_39', 'fgn_lag_39', 'fio2_lag_39', 'glu_lag_39', 'hgb_lag_39', 'hr_lag_39', 'inr_pt_lag_39', 'k_lag_39', 'lact_lag_39', 'lymph_lag_39', 'map_lag_39', 'mch_lag_39', 'mchc_lag_39', 'mcv_lag_39', 'methb_lag_39', 'mg_lag_39', 'na_lag_39', 'neut_lag_39', 'o2sat_lag_39', 'pco2_lag_39', 'ph_lag_39', 'phos_lag_39', 'plt_lag_39', 'po2_lag_39', 'ptt_lag_39', 'resp_lag_39', 'sbp_lag_39', 'temp_lag_39', 'tnt_lag_39', 'urine_lag_39', 'wbc_lag_39', 'alb_lag_40', 'alp_lag_40', 'alt_lag_40', 'ast_lag_40', 'be_lag_40', 'bicar_lag_40', 'bili_lag_40', 'bili_dir_lag_40', 'bnd_lag_40', 'bun_lag_40', 'ca_lag_40', 'cai_lag_40', 'ck_lag_40', 'ckmb_lag_40', 'cl_lag_40', 'crea_lag_40', 'crp_lag_40', 'dbp_lag_40', 'fgn_lag_40', 'fio2_lag_40', 'glu_lag_40', 'hgb_lag_40', 'hr_lag_40', 'inr_pt_lag_40', 'k_lag_40', 'lact_lag_40', 'lymph_lag_40', 'map_lag_40', 'mch_lag_40', 'mchc_lag_40', 'mcv_lag_40', 'methb_lag_40', 'mg_lag_40', 'na_lag_40', 'neut_lag_40', 'o2sat_lag_40', 'pco2_lag_40', 'ph_lag_40', 'phos_lag_40', 'plt_lag_40', 'po2_lag_40', 'ptt_lag_40', 'resp_lag_40', 'sbp_lag_40', 'temp_lag_40', 'tnt_lag_40', 'urine_lag_40', 'wbc_lag_40', 'alb_lag_41', 'alp_lag_41', 'alt_lag_41', 'ast_lag_41', 'be_lag_41', 'bicar_lag_41', 'bili_lag_41', 'bili_dir_lag_41', 'bnd_lag_41', 'bun_lag_41', 'ca_lag_41', 'cai_lag_41', 'ck_lag_41', 'ckmb_lag_41', 'cl_lag_41', 'crea_lag_41', 'crp_lag_41', 'dbp_lag_41', 'fgn_lag_41', 'fio2_lag_41', 'glu_lag_41', 'hgb_lag_41', 'hr_lag_41', 'inr_pt_lag_41', 'k_lag_41', 'lact_lag_41', 'lymph_lag_41', 'map_lag_41', 'mch_lag_41', 'mchc_lag_41', 'mcv_lag_41', 'methb_lag_41', 'mg_lag_41', 'na_lag_41', 'neut_lag_41', 'o2sat_lag_41', 'pco2_lag_41', 'ph_lag_41', 'phos_lag_41', 'plt_lag_41', 'po2_lag_41', 'ptt_lag_41', 'resp_lag_41', 'sbp_lag_41', 'temp_lag_41', 'tnt_lag_41', 'urine_lag_41', 'wbc_lag_41', 'alb_lag_42', 'alp_lag_42', 'alt_lag_42', 'ast_lag_42', 'be_lag_42', 'bicar_lag_42', 'bili_lag_42', 'bili_dir_lag_42', 'bnd_lag_42', 'bun_lag_42', 'ca_lag_42', 'cai_lag_42', 'ck_lag_42', 'ckmb_lag_42', 'cl_lag_42', 'crea_lag_42', 'crp_lag_42', 'dbp_lag_42', 'fgn_lag_42', 'fio2_lag_42', 'glu_lag_42', 'hgb_lag_42', 'hr_lag_42', 'inr_pt_lag_42', 'k_lag_42', 'lact_lag_42', 'lymph_lag_42', 'map_lag_42', 'mch_lag_42', 'mchc_lag_42', 'mcv_lag_42', 'methb_lag_42', 'mg_lag_42', 'na_lag_42', 'neut_lag_42', 'o2sat_lag_42', 'pco2_lag_42', 'ph_lag_42', 'phos_lag_42', 'plt_lag_42', 'po2_lag_42', 'ptt_lag_42', 'resp_lag_42', 'sbp_lag_42', 'temp_lag_42', 'tnt_lag_42', 'urine_lag_42', 'wbc_lag_42', 'alb_lag_43', 'alp_lag_43', 'alt_lag_43', 'ast_lag_43', 'be_lag_43', 'bicar_lag_43', 'bili_lag_43', 'bili_dir_lag_43', 'bnd_lag_43', 'bun_lag_43', 'ca_lag_43', 'cai_lag_43', 'ck_lag_43', 'ckmb_lag_43', 'cl_lag_43', 'crea_lag_43', 'crp_lag_43', 'dbp_lag_43', 'fgn_lag_43', 'fio2_lag_43', 'glu_lag_43', 'hgb_lag_43', 'hr_lag_43', 'inr_pt_lag_43', 'k_lag_43', 'lact_lag_43', 'lymph_lag_43', 'map_lag_43', 'mch_lag_43', 'mchc_lag_43', 'mcv_lag_43', 'methb_lag_43', 'mg_lag_43', 'na_lag_43', 'neut_lag_43', 'o2sat_lag_43', 'pco2_lag_43', 'ph_lag_43', 'phos_lag_43', 'plt_lag_43', 'po2_lag_43', 'ptt_lag_43', 'resp_lag_43', 'sbp_lag_43', 'temp_lag_43', 'tnt_lag_43', 'urine_lag_43', 'wbc_lag_43', 'alb_lag_44', 'alp_lag_44', 'alt_lag_44', 'ast_lag_44', 'be_lag_44', 'bicar_lag_44', 'bili_lag_44', 'bili_dir_lag_44', 'bnd_lag_44', 'bun_lag_44', 'ca_lag_44', 'cai_lag_44', 'ck_lag_44', 'ckmb_lag_44', 'cl_lag_44', 'crea_lag_44', 'crp_lag_44', 'dbp_lag_44', 'fgn_lag_44', 'fio2_lag_44', 'glu_lag_44', 'hgb_lag_44', 'hr_lag_44', 'inr_pt_lag_44', 'k_lag_44', 'lact_lag_44', 'lymph_lag_44', 'map_lag_44', 'mch_lag_44', 'mchc_lag_44', 'mcv_lag_44', 'methb_lag_44', 'mg_lag_44', 'na_lag_44', 'neut_lag_44', 'o2sat_lag_44', 'pco2_lag_44', 'ph_lag_44', 'phos_lag_44', 'plt_lag_44', 'po2_lag_44', 'ptt_lag_44', 'resp_lag_44', 'sbp_lag_44', 'temp_lag_44', 'tnt_lag_44', 'urine_lag_44', 'wbc_lag_44', 'alb_lag_45', 'alp_lag_45', 'alt_lag_45', 'ast_lag_45', 'be_lag_45', 'bicar_lag_45', 'bili_lag_45', 'bili_dir_lag_45', 'bnd_lag_45', 'bun_lag_45', 'ca_lag_45', 'cai_lag_45', 'ck_lag_45', 'ckmb_lag_45', 'cl_lag_45', 'crea_lag_45', 'crp_lag_45', 'dbp_lag_45', 'fgn_lag_45', 'fio2_lag_45', 'glu_lag_45', 'hgb_lag_45', 'hr_lag_45', 'inr_pt_lag_45', 'k_lag_45', 'lact_lag_45', 'lymph_lag_45', 'map_lag_45', 'mch_lag_45', 'mchc_lag_45', 'mcv_lag_45', 'methb_lag_45', 'mg_lag_45', 'na_lag_45', 'neut_lag_45', 'o2sat_lag_45', 'pco2_lag_45', 'ph_lag_45', 'phos_lag_45', 'plt_lag_45', 'po2_lag_45', 'ptt_lag_45', 'resp_lag_45', 'sbp_lag_45', 'temp_lag_45', 'tnt_lag_45', 'urine_lag_45', 'wbc_lag_45', 'alb_lag_46', 'alp_lag_46', 'alt_lag_46', 'ast_lag_46', 'be_lag_46', 'bicar_lag_46', 'bili_lag_46', 'bili_dir_lag_46', 'bnd_lag_46', 'bun_lag_46', 'ca_lag_46', 'cai_lag_46', 'ck_lag_46', 'ckmb_lag_46', 'cl_lag_46', 'crea_lag_46', 'crp_lag_46', 'dbp_lag_46', 'fgn_lag_46', 'fio2_lag_46', 'glu_lag_46', 'hgb_lag_46', 'hr_lag_46', 'inr_pt_lag_46', 'k_lag_46', 'lact_lag_46', 'lymph_lag_46', 'map_lag_46', 'mch_lag_46', 'mchc_lag_46', 'mcv_lag_46', 'methb_lag_46', 'mg_lag_46', 'na_lag_46', 'neut_lag_46', 'o2sat_lag_46', 'pco2_lag_46', 'ph_lag_46', 'phos_lag_46', 'plt_lag_46', 'po2_lag_46', 'ptt_lag_46', 'resp_lag_46', 'sbp_lag_46', 'temp_lag_46', 'tnt_lag_46', 'urine_lag_46', 'wbc_lag_46', 'alb_lag_47', 'alp_lag_47', 'alt_lag_47', 'ast_lag_47', 'be_lag_47', 'bicar_lag_47', 'bili_lag_47', 'bili_dir_lag_47', 'bnd_lag_47', 'bun_lag_47', 'ca_lag_47', 'cai_lag_47', 'ck_lag_47', 'ckmb_lag_47', 'cl_lag_47', 'crea_lag_47', 'crp_lag_47', 'dbp_lag_47', 'fgn_lag_47', 'fio2_lag_47', 'glu_lag_47', 'hgb_lag_47', 'hr_lag_47', 'inr_pt_lag_47', 'k_lag_47', 'lact_lag_47', 'lymph_lag_47', 'map_lag_47', 'mch_lag_47', 'mchc_lag_47', 'mcv_lag_47', 'methb_lag_47', 'mg_lag_47', 'na_lag_47', 'neut_lag_47', 'o2sat_lag_47', 'pco2_lag_47', 'ph_lag_47', 'phos_lag_47', 'plt_lag_47', 'po2_lag_47', 'ptt_lag_47', 'resp_lag_47', 'sbp_lag_47', 'temp_lag_47', 'tnt_lag_47', 'urine_lag_47', 'wbc_lag_47', 'alb_lag_48', 'alp_lag_48', 'alt_lag_48', 'ast_lag_48', 'be_lag_48', 'bicar_lag_48', 'bili_lag_48', 'bili_dir_lag_48', 'bnd_lag_48', 'bun_lag_48', 'ca_lag_48', 'cai_lag_48', 'ck_lag_48', 'ckmb_lag_48', 'cl_lag_48', 'crea_lag_48', 'crp_lag_48', 'dbp_lag_48', 'fgn_lag_48', 'fio2_lag_48', 'glu_lag_48', 'hgb_lag_48', 'hr_lag_48', 'inr_pt_lag_48', 'k_lag_48', 'lact_lag_48', 'lymph_lag_48', 'map_lag_48', 'mch_lag_48', 'mchc_lag_48', 'mcv_lag_48', 'methb_lag_48', 'mg_lag_48', 'na_lag_48', 'neut_lag_48', 'o2sat_lag_48', 'pco2_lag_48', 'ph_lag_48', 'phos_lag_48', 'plt_lag_48', 'po2_lag_48', 'ptt_lag_48', 'resp_lag_48', 'sbp_lag_48', 'temp_lag_48', 'tnt_lag_48', 'urine_lag_48', 'wbc_lag_48'] -PredictionDatasetTFTpytorch.add_relative_time_idx=False -PredictionDatasetTFTpytorch.target="label" - -PredictionDatasetTFTpytorch.time_varying_unknown_reals=[ +PredictionDatasetpytorch.max_encoder_length = 24 +PredictionDatasetpytorch.max_prediction_length = 24 +PredictionDatasetpytorch.time_varying_known_reals=['alb_lag_24', 'alp_lag_24', 'alt_lag_24', 'ast_lag_24', 'be_lag_24', 'bicar_lag_24', 'bili_lag_24', 'bili_dir_lag_24', 'bnd_lag_24', 'bun_lag_24', 'ca_lag_24', 'cai_lag_24', 'ck_lag_24', 'ckmb_lag_24', 'cl_lag_24', 'crea_lag_24', 'crp_lag_24', 'dbp_lag_24', 'fgn_lag_24', 'fio2_lag_24', 'glu_lag_24', 'hgb_lag_24', 'hr_lag_24', 'inr_pt_lag_24', 'k_lag_24', 'lact_lag_24', 'lymph_lag_24', 'map_lag_24', 'mch_lag_24', 'mchc_lag_24', 'mcv_lag_24', 'methb_lag_24', 'mg_lag_24', 'na_lag_24', 'neut_lag_24', 'o2sat_lag_24', 'pco2_lag_24', 'ph_lag_24', 'phos_lag_24', 'plt_lag_24', 'po2_lag_24', 'ptt_lag_24', 'resp_lag_24', 'sbp_lag_24', 'temp_lag_24', 'tnt_lag_24', 'urine_lag_24', 'wbc_lag_24', 'alb_lag_25', 'alp_lag_25', 'alt_lag_25', 'ast_lag_25', 'be_lag_25', 'bicar_lag_25', 'bili_lag_25', 'bili_dir_lag_25', 'bnd_lag_25', 'bun_lag_25', 'ca_lag_25', 'cai_lag_25', 'ck_lag_25', 'ckmb_lag_25', 'cl_lag_25', 'crea_lag_25', 'crp_lag_25', 'dbp_lag_25', 'fgn_lag_25', 'fio2_lag_25', 'glu_lag_25', 'hgb_lag_25', 'hr_lag_25', 'inr_pt_lag_25', 'k_lag_25', 'lact_lag_25', 'lymph_lag_25', 'map_lag_25', 'mch_lag_25', 'mchc_lag_25', 'mcv_lag_25', 'methb_lag_25', 'mg_lag_25', 'na_lag_25', 'neut_lag_25', 'o2sat_lag_25', 'pco2_lag_25', 'ph_lag_25', 'phos_lag_25', 'plt_lag_25', 'po2_lag_25', 'ptt_lag_25', 'resp_lag_25', 'sbp_lag_25', 'temp_lag_25', 'tnt_lag_25', 'urine_lag_25', 'wbc_lag_25', 'alb_lag_26', 'alp_lag_26', 'alt_lag_26', 'ast_lag_26', 'be_lag_26', 'bicar_lag_26', 'bili_lag_26', 'bili_dir_lag_26', 'bnd_lag_26', 'bun_lag_26', 'ca_lag_26', 'cai_lag_26', 'ck_lag_26', 'ckmb_lag_26', 'cl_lag_26', 'crea_lag_26', 'crp_lag_26', 'dbp_lag_26', 'fgn_lag_26', 'fio2_lag_26', 'glu_lag_26', 'hgb_lag_26', 'hr_lag_26', 'inr_pt_lag_26', 'k_lag_26', 'lact_lag_26', 'lymph_lag_26', 'map_lag_26', 'mch_lag_26', 'mchc_lag_26', 'mcv_lag_26', 'methb_lag_26', 'mg_lag_26', 'na_lag_26', 'neut_lag_26', 'o2sat_lag_26', 'pco2_lag_26', 'ph_lag_26', 'phos_lag_26', 'plt_lag_26', 'po2_lag_26', 'ptt_lag_26', 'resp_lag_26', 'sbp_lag_26', 'temp_lag_26', 'tnt_lag_26', 'urine_lag_26', 'wbc_lag_26', 'alb_lag_27', 'alp_lag_27', 'alt_lag_27', 'ast_lag_27', 'be_lag_27', 'bicar_lag_27', 'bili_lag_27', 'bili_dir_lag_27', 'bnd_lag_27', 'bun_lag_27', 'ca_lag_27', 'cai_lag_27', 'ck_lag_27', 'ckmb_lag_27', 'cl_lag_27', 'crea_lag_27', 'crp_lag_27', 'dbp_lag_27', 'fgn_lag_27', 'fio2_lag_27', 'glu_lag_27', 'hgb_lag_27', 'hr_lag_27', 'inr_pt_lag_27', 'k_lag_27', 'lact_lag_27', 'lymph_lag_27', 'map_lag_27', 'mch_lag_27', 'mchc_lag_27', 'mcv_lag_27', 'methb_lag_27', 'mg_lag_27', 'na_lag_27', 'neut_lag_27', 'o2sat_lag_27', 'pco2_lag_27', 'ph_lag_27', 'phos_lag_27', 'plt_lag_27', 'po2_lag_27', 'ptt_lag_27', 'resp_lag_27', 'sbp_lag_27', 'temp_lag_27', 'tnt_lag_27', 'urine_lag_27', 'wbc_lag_27', 'alb_lag_28', 'alp_lag_28', 'alt_lag_28', 'ast_lag_28', 'be_lag_28', 'bicar_lag_28', 'bili_lag_28', 'bili_dir_lag_28', 'bnd_lag_28', 'bun_lag_28', 'ca_lag_28', 'cai_lag_28', 'ck_lag_28', 'ckmb_lag_28', 'cl_lag_28', 'crea_lag_28', 'crp_lag_28', 'dbp_lag_28', 'fgn_lag_28', 'fio2_lag_28', 'glu_lag_28', 'hgb_lag_28', 'hr_lag_28', 'inr_pt_lag_28', 'k_lag_28', 'lact_lag_28', 'lymph_lag_28', 'map_lag_28', 'mch_lag_28', 'mchc_lag_28', 'mcv_lag_28', 'methb_lag_28', 'mg_lag_28', 'na_lag_28', 'neut_lag_28', 'o2sat_lag_28', 'pco2_lag_28', 'ph_lag_28', 'phos_lag_28', 'plt_lag_28', 'po2_lag_28', 'ptt_lag_28', 'resp_lag_28', 'sbp_lag_28', 'temp_lag_28', 'tnt_lag_28', 'urine_lag_28', 'wbc_lag_28', 'alb_lag_29', 'alp_lag_29', 'alt_lag_29', 'ast_lag_29', 'be_lag_29', 'bicar_lag_29', 'bili_lag_29', 'bili_dir_lag_29', 'bnd_lag_29', 'bun_lag_29', 'ca_lag_29', 'cai_lag_29', 'ck_lag_29', 'ckmb_lag_29', 'cl_lag_29', 'crea_lag_29', 'crp_lag_29', 'dbp_lag_29', 'fgn_lag_29', 'fio2_lag_29', 'glu_lag_29', 'hgb_lag_29', 'hr_lag_29', 'inr_pt_lag_29', 'k_lag_29', 'lact_lag_29', 'lymph_lag_29', 'map_lag_29', 'mch_lag_29', 'mchc_lag_29', 'mcv_lag_29', 'methb_lag_29', 'mg_lag_29', 'na_lag_29', 'neut_lag_29', 'o2sat_lag_29', 'pco2_lag_29', 'ph_lag_29', 'phos_lag_29', 'plt_lag_29', 'po2_lag_29', 'ptt_lag_29', 'resp_lag_29', 'sbp_lag_29', 'temp_lag_29', 'tnt_lag_29', 'urine_lag_29', 'wbc_lag_29', 'alb_lag_30', 'alp_lag_30', 'alt_lag_30', 'ast_lag_30', 'be_lag_30', 'bicar_lag_30', 'bili_lag_30', 'bili_dir_lag_30', 'bnd_lag_30', 'bun_lag_30', 'ca_lag_30', 'cai_lag_30', 'ck_lag_30', 'ckmb_lag_30', 'cl_lag_30', 'crea_lag_30', 'crp_lag_30', 'dbp_lag_30', 'fgn_lag_30', 'fio2_lag_30', 'glu_lag_30', 'hgb_lag_30', 'hr_lag_30', 'inr_pt_lag_30', 'k_lag_30', 'lact_lag_30', 'lymph_lag_30', 'map_lag_30', 'mch_lag_30', 'mchc_lag_30', 'mcv_lag_30', 'methb_lag_30', 'mg_lag_30', 'na_lag_30', 'neut_lag_30', 'o2sat_lag_30', 'pco2_lag_30', 'ph_lag_30', 'phos_lag_30', 'plt_lag_30', 'po2_lag_30', 'ptt_lag_30', 'resp_lag_30', 'sbp_lag_30', 'temp_lag_30', 'tnt_lag_30', 'urine_lag_30', 'wbc_lag_30', 'alb_lag_31', 'alp_lag_31', 'alt_lag_31', 'ast_lag_31', 'be_lag_31', 'bicar_lag_31', 'bili_lag_31', 'bili_dir_lag_31', 'bnd_lag_31', 'bun_lag_31', 'ca_lag_31', 'cai_lag_31', 'ck_lag_31', 'ckmb_lag_31', 'cl_lag_31', 'crea_lag_31', 'crp_lag_31', 'dbp_lag_31', 'fgn_lag_31', 'fio2_lag_31', 'glu_lag_31', 'hgb_lag_31', 'hr_lag_31', 'inr_pt_lag_31', 'k_lag_31', 'lact_lag_31', 'lymph_lag_31', 'map_lag_31', 'mch_lag_31', 'mchc_lag_31', 'mcv_lag_31', 'methb_lag_31', 'mg_lag_31', 'na_lag_31', 'neut_lag_31', 'o2sat_lag_31', 'pco2_lag_31', 'ph_lag_31', 'phos_lag_31', 'plt_lag_31', 'po2_lag_31', 'ptt_lag_31', 'resp_lag_31', 'sbp_lag_31', 'temp_lag_31', 'tnt_lag_31', 'urine_lag_31', 'wbc_lag_31', 'alb_lag_32', 'alp_lag_32', 'alt_lag_32', 'ast_lag_32', 'be_lag_32', 'bicar_lag_32', 'bili_lag_32', 'bili_dir_lag_32', 'bnd_lag_32', 'bun_lag_32', 'ca_lag_32', 'cai_lag_32', 'ck_lag_32', 'ckmb_lag_32', 'cl_lag_32', 'crea_lag_32', 'crp_lag_32', 'dbp_lag_32', 'fgn_lag_32', 'fio2_lag_32', 'glu_lag_32', 'hgb_lag_32', 'hr_lag_32', 'inr_pt_lag_32', 'k_lag_32', 'lact_lag_32', 'lymph_lag_32', 'map_lag_32', 'mch_lag_32', 'mchc_lag_32', 'mcv_lag_32', 'methb_lag_32', 'mg_lag_32', 'na_lag_32', 'neut_lag_32', 'o2sat_lag_32', 'pco2_lag_32', 'ph_lag_32', 'phos_lag_32', 'plt_lag_32', 'po2_lag_32', 'ptt_lag_32', 'resp_lag_32', 'sbp_lag_32', 'temp_lag_32', 'tnt_lag_32', 'urine_lag_32', 'wbc_lag_32', 'alb_lag_33', 'alp_lag_33', 'alt_lag_33', 'ast_lag_33', 'be_lag_33', 'bicar_lag_33', 'bili_lag_33', 'bili_dir_lag_33', 'bnd_lag_33', 'bun_lag_33', 'ca_lag_33', 'cai_lag_33', 'ck_lag_33', 'ckmb_lag_33', 'cl_lag_33', 'crea_lag_33', 'crp_lag_33', 'dbp_lag_33', 'fgn_lag_33', 'fio2_lag_33', 'glu_lag_33', 'hgb_lag_33', 'hr_lag_33', 'inr_pt_lag_33', 'k_lag_33', 'lact_lag_33', 'lymph_lag_33', 'map_lag_33', 'mch_lag_33', 'mchc_lag_33', 'mcv_lag_33', 'methb_lag_33', 'mg_lag_33', 'na_lag_33', 'neut_lag_33', 'o2sat_lag_33', 'pco2_lag_33', 'ph_lag_33', 'phos_lag_33', 'plt_lag_33', 'po2_lag_33', 'ptt_lag_33', 'resp_lag_33', 'sbp_lag_33', 'temp_lag_33', 'tnt_lag_33', 'urine_lag_33', 'wbc_lag_33', 'alb_lag_34', 'alp_lag_34', 'alt_lag_34', 'ast_lag_34', 'be_lag_34', 'bicar_lag_34', 'bili_lag_34', 'bili_dir_lag_34', 'bnd_lag_34', 'bun_lag_34', 'ca_lag_34', 'cai_lag_34', 'ck_lag_34', 'ckmb_lag_34', 'cl_lag_34', 'crea_lag_34', 'crp_lag_34', 'dbp_lag_34', 'fgn_lag_34', 'fio2_lag_34', 'glu_lag_34', 'hgb_lag_34', 'hr_lag_34', 'inr_pt_lag_34', 'k_lag_34', 'lact_lag_34', 'lymph_lag_34', 'map_lag_34', 'mch_lag_34', 'mchc_lag_34', 'mcv_lag_34', 'methb_lag_34', 'mg_lag_34', 'na_lag_34', 'neut_lag_34', 'o2sat_lag_34', 'pco2_lag_34', 'ph_lag_34', 'phos_lag_34', 'plt_lag_34', 'po2_lag_34', 'ptt_lag_34', 'resp_lag_34', 'sbp_lag_34', 'temp_lag_34', 'tnt_lag_34', 'urine_lag_34', 'wbc_lag_34', 'alb_lag_35', 'alp_lag_35', 'alt_lag_35', 'ast_lag_35', 'be_lag_35', 'bicar_lag_35', 'bili_lag_35', 'bili_dir_lag_35', 'bnd_lag_35', 'bun_lag_35', 'ca_lag_35', 'cai_lag_35', 'ck_lag_35', 'ckmb_lag_35', 'cl_lag_35', 'crea_lag_35', 'crp_lag_35', 'dbp_lag_35', 'fgn_lag_35', 'fio2_lag_35', 'glu_lag_35', 'hgb_lag_35', 'hr_lag_35', 'inr_pt_lag_35', 'k_lag_35', 'lact_lag_35', 'lymph_lag_35', 'map_lag_35', 'mch_lag_35', 'mchc_lag_35', 'mcv_lag_35', 'methb_lag_35', 'mg_lag_35', 'na_lag_35', 'neut_lag_35', 'o2sat_lag_35', 'pco2_lag_35', 'ph_lag_35', 'phos_lag_35', 'plt_lag_35', 'po2_lag_35', 'ptt_lag_35', 'resp_lag_35', 'sbp_lag_35', 'temp_lag_35', 'tnt_lag_35', 'urine_lag_35', 'wbc_lag_35', 'alb_lag_36', 'alp_lag_36', 'alt_lag_36', 'ast_lag_36', 'be_lag_36', 'bicar_lag_36', 'bili_lag_36', 'bili_dir_lag_36', 'bnd_lag_36', 'bun_lag_36', 'ca_lag_36', 'cai_lag_36', 'ck_lag_36', 'ckmb_lag_36', 'cl_lag_36', 'crea_lag_36', 'crp_lag_36', 'dbp_lag_36', 'fgn_lag_36', 'fio2_lag_36', 'glu_lag_36', 'hgb_lag_36', 'hr_lag_36', 'inr_pt_lag_36', 'k_lag_36', 'lact_lag_36', 'lymph_lag_36', 'map_lag_36', 'mch_lag_36', 'mchc_lag_36', 'mcv_lag_36', 'methb_lag_36', 'mg_lag_36', 'na_lag_36', 'neut_lag_36', 'o2sat_lag_36', 'pco2_lag_36', 'ph_lag_36', 'phos_lag_36', 'plt_lag_36', 'po2_lag_36', 'ptt_lag_36', 'resp_lag_36', 'sbp_lag_36', 'temp_lag_36', 'tnt_lag_36', 'urine_lag_36', 'wbc_lag_36', 'alb_lag_37', 'alp_lag_37', 'alt_lag_37', 'ast_lag_37', 'be_lag_37', 'bicar_lag_37', 'bili_lag_37', 'bili_dir_lag_37', 'bnd_lag_37', 'bun_lag_37', 'ca_lag_37', 'cai_lag_37', 'ck_lag_37', 'ckmb_lag_37', 'cl_lag_37', 'crea_lag_37', 'crp_lag_37', 'dbp_lag_37', 'fgn_lag_37', 'fio2_lag_37', 'glu_lag_37', 'hgb_lag_37', 'hr_lag_37', 'inr_pt_lag_37', 'k_lag_37', 'lact_lag_37', 'lymph_lag_37', 'map_lag_37', 'mch_lag_37', 'mchc_lag_37', 'mcv_lag_37', 'methb_lag_37', 'mg_lag_37', 'na_lag_37', 'neut_lag_37', 'o2sat_lag_37', 'pco2_lag_37', 'ph_lag_37', 'phos_lag_37', 'plt_lag_37', 'po2_lag_37', 'ptt_lag_37', 'resp_lag_37', 'sbp_lag_37', 'temp_lag_37', 'tnt_lag_37', 'urine_lag_37', 'wbc_lag_37', 'alb_lag_38', 'alp_lag_38', 'alt_lag_38', 'ast_lag_38', 'be_lag_38', 'bicar_lag_38', 'bili_lag_38', 'bili_dir_lag_38', 'bnd_lag_38', 'bun_lag_38', 'ca_lag_38', 'cai_lag_38', 'ck_lag_38', 'ckmb_lag_38', 'cl_lag_38', 'crea_lag_38', 'crp_lag_38', 'dbp_lag_38', 'fgn_lag_38', 'fio2_lag_38', 'glu_lag_38', 'hgb_lag_38', 'hr_lag_38', 'inr_pt_lag_38', 'k_lag_38', 'lact_lag_38', 'lymph_lag_38', 'map_lag_38', 'mch_lag_38', 'mchc_lag_38', 'mcv_lag_38', 'methb_lag_38', 'mg_lag_38', 'na_lag_38', 'neut_lag_38', 'o2sat_lag_38', 'pco2_lag_38', 'ph_lag_38', 'phos_lag_38', 'plt_lag_38', 'po2_lag_38', 'ptt_lag_38', 'resp_lag_38', 'sbp_lag_38', 'temp_lag_38', 'tnt_lag_38', 'urine_lag_38', 'wbc_lag_38', 'alb_lag_39', 'alp_lag_39', 'alt_lag_39', 'ast_lag_39', 'be_lag_39', 'bicar_lag_39', 'bili_lag_39', 'bili_dir_lag_39', 'bnd_lag_39', 'bun_lag_39', 'ca_lag_39', 'cai_lag_39', 'ck_lag_39', 'ckmb_lag_39', 'cl_lag_39', 'crea_lag_39', 'crp_lag_39', 'dbp_lag_39', 'fgn_lag_39', 'fio2_lag_39', 'glu_lag_39', 'hgb_lag_39', 'hr_lag_39', 'inr_pt_lag_39', 'k_lag_39', 'lact_lag_39', 'lymph_lag_39', 'map_lag_39', 'mch_lag_39', 'mchc_lag_39', 'mcv_lag_39', 'methb_lag_39', 'mg_lag_39', 'na_lag_39', 'neut_lag_39', 'o2sat_lag_39', 'pco2_lag_39', 'ph_lag_39', 'phos_lag_39', 'plt_lag_39', 'po2_lag_39', 'ptt_lag_39', 'resp_lag_39', 'sbp_lag_39', 'temp_lag_39', 'tnt_lag_39', 'urine_lag_39', 'wbc_lag_39', 'alb_lag_40', 'alp_lag_40', 'alt_lag_40', 'ast_lag_40', 'be_lag_40', 'bicar_lag_40', 'bili_lag_40', 'bili_dir_lag_40', 'bnd_lag_40', 'bun_lag_40', 'ca_lag_40', 'cai_lag_40', 'ck_lag_40', 'ckmb_lag_40', 'cl_lag_40', 'crea_lag_40', 'crp_lag_40', 'dbp_lag_40', 'fgn_lag_40', 'fio2_lag_40', 'glu_lag_40', 'hgb_lag_40', 'hr_lag_40', 'inr_pt_lag_40', 'k_lag_40', 'lact_lag_40', 'lymph_lag_40', 'map_lag_40', 'mch_lag_40', 'mchc_lag_40', 'mcv_lag_40', 'methb_lag_40', 'mg_lag_40', 'na_lag_40', 'neut_lag_40', 'o2sat_lag_40', 'pco2_lag_40', 'ph_lag_40', 'phos_lag_40', 'plt_lag_40', 'po2_lag_40', 'ptt_lag_40', 'resp_lag_40', 'sbp_lag_40', 'temp_lag_40', 'tnt_lag_40', 'urine_lag_40', 'wbc_lag_40', 'alb_lag_41', 'alp_lag_41', 'alt_lag_41', 'ast_lag_41', 'be_lag_41', 'bicar_lag_41', 'bili_lag_41', 'bili_dir_lag_41', 'bnd_lag_41', 'bun_lag_41', 'ca_lag_41', 'cai_lag_41', 'ck_lag_41', 'ckmb_lag_41', 'cl_lag_41', 'crea_lag_41', 'crp_lag_41', 'dbp_lag_41', 'fgn_lag_41', 'fio2_lag_41', 'glu_lag_41', 'hgb_lag_41', 'hr_lag_41', 'inr_pt_lag_41', 'k_lag_41', 'lact_lag_41', 'lymph_lag_41', 'map_lag_41', 'mch_lag_41', 'mchc_lag_41', 'mcv_lag_41', 'methb_lag_41', 'mg_lag_41', 'na_lag_41', 'neut_lag_41', 'o2sat_lag_41', 'pco2_lag_41', 'ph_lag_41', 'phos_lag_41', 'plt_lag_41', 'po2_lag_41', 'ptt_lag_41', 'resp_lag_41', 'sbp_lag_41', 'temp_lag_41', 'tnt_lag_41', 'urine_lag_41', 'wbc_lag_41', 'alb_lag_42', 'alp_lag_42', 'alt_lag_42', 'ast_lag_42', 'be_lag_42', 'bicar_lag_42', 'bili_lag_42', 'bili_dir_lag_42', 'bnd_lag_42', 'bun_lag_42', 'ca_lag_42', 'cai_lag_42', 'ck_lag_42', 'ckmb_lag_42', 'cl_lag_42', 'crea_lag_42', 'crp_lag_42', 'dbp_lag_42', 'fgn_lag_42', 'fio2_lag_42', 'glu_lag_42', 'hgb_lag_42', 'hr_lag_42', 'inr_pt_lag_42', 'k_lag_42', 'lact_lag_42', 'lymph_lag_42', 'map_lag_42', 'mch_lag_42', 'mchc_lag_42', 'mcv_lag_42', 'methb_lag_42', 'mg_lag_42', 'na_lag_42', 'neut_lag_42', 'o2sat_lag_42', 'pco2_lag_42', 'ph_lag_42', 'phos_lag_42', 'plt_lag_42', 'po2_lag_42', 'ptt_lag_42', 'resp_lag_42', 'sbp_lag_42', 'temp_lag_42', 'tnt_lag_42', 'urine_lag_42', 'wbc_lag_42', 'alb_lag_43', 'alp_lag_43', 'alt_lag_43', 'ast_lag_43', 'be_lag_43', 'bicar_lag_43', 'bili_lag_43', 'bili_dir_lag_43', 'bnd_lag_43', 'bun_lag_43', 'ca_lag_43', 'cai_lag_43', 'ck_lag_43', 'ckmb_lag_43', 'cl_lag_43', 'crea_lag_43', 'crp_lag_43', 'dbp_lag_43', 'fgn_lag_43', 'fio2_lag_43', 'glu_lag_43', 'hgb_lag_43', 'hr_lag_43', 'inr_pt_lag_43', 'k_lag_43', 'lact_lag_43', 'lymph_lag_43', 'map_lag_43', 'mch_lag_43', 'mchc_lag_43', 'mcv_lag_43', 'methb_lag_43', 'mg_lag_43', 'na_lag_43', 'neut_lag_43', 'o2sat_lag_43', 'pco2_lag_43', 'ph_lag_43', 'phos_lag_43', 'plt_lag_43', 'po2_lag_43', 'ptt_lag_43', 'resp_lag_43', 'sbp_lag_43', 'temp_lag_43', 'tnt_lag_43', 'urine_lag_43', 'wbc_lag_43', 'alb_lag_44', 'alp_lag_44', 'alt_lag_44', 'ast_lag_44', 'be_lag_44', 'bicar_lag_44', 'bili_lag_44', 'bili_dir_lag_44', 'bnd_lag_44', 'bun_lag_44', 'ca_lag_44', 'cai_lag_44', 'ck_lag_44', 'ckmb_lag_44', 'cl_lag_44', 'crea_lag_44', 'crp_lag_44', 'dbp_lag_44', 'fgn_lag_44', 'fio2_lag_44', 'glu_lag_44', 'hgb_lag_44', 'hr_lag_44', 'inr_pt_lag_44', 'k_lag_44', 'lact_lag_44', 'lymph_lag_44', 'map_lag_44', 'mch_lag_44', 'mchc_lag_44', 'mcv_lag_44', 'methb_lag_44', 'mg_lag_44', 'na_lag_44', 'neut_lag_44', 'o2sat_lag_44', 'pco2_lag_44', 'ph_lag_44', 'phos_lag_44', 'plt_lag_44', 'po2_lag_44', 'ptt_lag_44', 'resp_lag_44', 'sbp_lag_44', 'temp_lag_44', 'tnt_lag_44', 'urine_lag_44', 'wbc_lag_44', 'alb_lag_45', 'alp_lag_45', 'alt_lag_45', 'ast_lag_45', 'be_lag_45', 'bicar_lag_45', 'bili_lag_45', 'bili_dir_lag_45', 'bnd_lag_45', 'bun_lag_45', 'ca_lag_45', 'cai_lag_45', 'ck_lag_45', 'ckmb_lag_45', 'cl_lag_45', 'crea_lag_45', 'crp_lag_45', 'dbp_lag_45', 'fgn_lag_45', 'fio2_lag_45', 'glu_lag_45', 'hgb_lag_45', 'hr_lag_45', 'inr_pt_lag_45', 'k_lag_45', 'lact_lag_45', 'lymph_lag_45', 'map_lag_45', 'mch_lag_45', 'mchc_lag_45', 'mcv_lag_45', 'methb_lag_45', 'mg_lag_45', 'na_lag_45', 'neut_lag_45', 'o2sat_lag_45', 'pco2_lag_45', 'ph_lag_45', 'phos_lag_45', 'plt_lag_45', 'po2_lag_45', 'ptt_lag_45', 'resp_lag_45', 'sbp_lag_45', 'temp_lag_45', 'tnt_lag_45', 'urine_lag_45', 'wbc_lag_45', 'alb_lag_46', 'alp_lag_46', 'alt_lag_46', 'ast_lag_46', 'be_lag_46', 'bicar_lag_46', 'bili_lag_46', 'bili_dir_lag_46', 'bnd_lag_46', 'bun_lag_46', 'ca_lag_46', 'cai_lag_46', 'ck_lag_46', 'ckmb_lag_46', 'cl_lag_46', 'crea_lag_46', 'crp_lag_46', 'dbp_lag_46', 'fgn_lag_46', 'fio2_lag_46', 'glu_lag_46', 'hgb_lag_46', 'hr_lag_46', 'inr_pt_lag_46', 'k_lag_46', 'lact_lag_46', 'lymph_lag_46', 'map_lag_46', 'mch_lag_46', 'mchc_lag_46', 'mcv_lag_46', 'methb_lag_46', 'mg_lag_46', 'na_lag_46', 'neut_lag_46', 'o2sat_lag_46', 'pco2_lag_46', 'ph_lag_46', 'phos_lag_46', 'plt_lag_46', 'po2_lag_46', 'ptt_lag_46', 'resp_lag_46', 'sbp_lag_46', 'temp_lag_46', 'tnt_lag_46', 'urine_lag_46', 'wbc_lag_46', 'alb_lag_47', 'alp_lag_47', 'alt_lag_47', 'ast_lag_47', 'be_lag_47', 'bicar_lag_47', 'bili_lag_47', 'bili_dir_lag_47', 'bnd_lag_47', 'bun_lag_47', 'ca_lag_47', 'cai_lag_47', 'ck_lag_47', 'ckmb_lag_47', 'cl_lag_47', 'crea_lag_47', 'crp_lag_47', 'dbp_lag_47', 'fgn_lag_47', 'fio2_lag_47', 'glu_lag_47', 'hgb_lag_47', 'hr_lag_47', 'inr_pt_lag_47', 'k_lag_47', 'lact_lag_47', 'lymph_lag_47', 'map_lag_47', 'mch_lag_47', 'mchc_lag_47', 'mcv_lag_47', 'methb_lag_47', 'mg_lag_47', 'na_lag_47', 'neut_lag_47', 'o2sat_lag_47', 'pco2_lag_47', 'ph_lag_47', 'phos_lag_47', 'plt_lag_47', 'po2_lag_47', 'ptt_lag_47', 'resp_lag_47', 'sbp_lag_47', 'temp_lag_47', 'tnt_lag_47', 'urine_lag_47', 'wbc_lag_47', 'alb_lag_48', 'alp_lag_48', 'alt_lag_48', 'ast_lag_48', 'be_lag_48', 'bicar_lag_48', 'bili_lag_48', 'bili_dir_lag_48', 'bnd_lag_48', 'bun_lag_48', 'ca_lag_48', 'cai_lag_48', 'ck_lag_48', 'ckmb_lag_48', 'cl_lag_48', 'crea_lag_48', 'crp_lag_48', 'dbp_lag_48', 'fgn_lag_48', 'fio2_lag_48', 'glu_lag_48', 'hgb_lag_48', 'hr_lag_48', 'inr_pt_lag_48', 'k_lag_48', 'lact_lag_48', 'lymph_lag_48', 'map_lag_48', 'mch_lag_48', 'mchc_lag_48', 'mcv_lag_48', 'methb_lag_48', 'mg_lag_48', 'na_lag_48', 'neut_lag_48', 'o2sat_lag_48', 'pco2_lag_48', 'ph_lag_48', 'phos_lag_48', 'plt_lag_48', 'po2_lag_48', 'ptt_lag_48', 'resp_lag_48', 'sbp_lag_48', 'temp_lag_48', 'tnt_lag_48', 'urine_lag_48', 'wbc_lag_48'] +PredictionDatasetpytorch.add_relative_time_idx=False +PredictionDatasetpytorch.target="label" + PredictionDatasetTFTpytorch.time_varying_unknown_reals=[ "label" ] - - -PredictionDatasetTFTpytorch.time_varying_unknown_categoricals=[] -PredictionDatasetTFTpytorch.lagged_variables=["alb", +PredictionDatasetpytorch.time_varying_unknown_categoricals=[] +PredictionDatasetpytorch.lagged_variables=["alb", "alp", "alt", "ast", diff --git a/configs/prediction_models/TFTpytorch.gin b/configs/prediction_models/TFTpytorch.gin index 5ff809a5..ebd26e4e 100644 --- a/configs/prediction_models/TFTpytorch.gin +++ b/configs/prediction_models/TFTpytorch.gin @@ -22,7 +22,7 @@ model/hyperparameter.lr_scheduler = "exponential" # Dataset params PredictionDatasetTFTpytorch.max_encoder_length = 24 -PredictionDatasetTFTpytorch.max_prediction_length = 24 +PredictionDatasetTFTpytorch.max_prediction_length = 1 PredictionDatasetTFTpytorch.target="label" PredictionDatasetTFTpytorch.time_varying_known_reals=["time_idx"] PredictionDatasetTFTpytorch.add_relative_time_idx=False diff --git a/configs/prediction_models/common/DLCommon.gin b/configs/prediction_models/common/DLCommon.gin index 8e4149fd..fa4b440f 100644 --- a/configs/prediction_models/common/DLCommon.gin +++ b/configs/prediction_models/common/DLCommon.gin @@ -15,7 +15,7 @@ base_regression_preprocessor.generate_features = False # Train params train_common.optimizer = @Adam train_common.epochs = 1000 -train_common.batch_size = 1 +train_common.batch_size = 64 train_common.patience = 20 train_common.min_delta = 1e-4 diff --git a/configs/prediction_models/common/DLTuning.gin b/configs/prediction_models/common/DLTuning.gin index b4d13e12..ec0240e2 100644 --- a/configs/prediction_models/common/DLTuning.gin +++ b/configs/prediction_models/common/DLTuning.gin @@ -1,5 +1,5 @@ # Hyperparameter tuner settings for Deep Learning. tune_hyperparameters.scopes = ["model", "optimizer"] tune_hyperparameters.n_initial_points = 5 -tune_hyperparameters.n_calls = 30 +tune_hyperparameters.n_calls = 15 tune_hyperparameters.folds_to_tune_on = 2 \ No newline at end of file diff --git a/configs/tasks/Regression.gin b/configs/tasks/Regression.gin index 6b6ac607..1234f707 100644 --- a/configs/tasks/Regression.gin +++ b/configs/tasks/Regression.gin @@ -26,7 +26,7 @@ preprocess.use_static = True # SPECIFYING REGRESSION OUTCOME SCALING base_regression_preprocessor.outcome_min = 0 -base_regression_preprocessor.outcome_max = 15 +base_regression_preprocessor.outcome_max = 168 # SELECTING DATASET PredictionDataset.vars = %vars diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 57b2df55..68109f4b 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -429,8 +429,8 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: return from_numpy(window.values).to(float32) -@gin.configurable("PredictionDatasetTFTpytorch") -class PredictionDatasetTFTpytorch(TimeSeriesDataSet): +@gin.configurable("PredictionDatasetpytorch") +class PredictionDatasetpytorch(TimeSeriesDataSet): """Subclass of timeseries dataset works with pyotrch forecasting library . Args: @@ -498,8 +498,8 @@ def __init__( time_varying_unknown_categoricals=time_varying_unknown_categoricals, time_varying_unknown_reals=time_varying_unknown_reals, add_relative_time_idx=add_relative_time_idx, - add_target_scales=True, - add_encoder_length=True, + # add_target_scales=True, + # add_encoder_length=True, predict_mode=True, target_normalizer=None, ) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 6778d642..c1306d14 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -22,7 +22,7 @@ from icu_benchmarks.data.loader import ( PredictionDataset, ImputationDataset, - PredictionDatasetTFTpytorch, + PredictionDatasetpytorch, ) from icu_benchmarks.models.utils import save_config_file, JSONMetricsLogger from icu_benchmarks.contants import RunMode @@ -111,7 +111,7 @@ def train_common( ImputationDataset if mode == RunMode.imputation else ( - PredictionDatasetTFTpytorch if (pytorch_forecasting) else PredictionDataset + PredictionDatasetpytorch if (pytorch_forecasting) else PredictionDataset ) ) @@ -277,31 +277,37 @@ def train_common( if explain: - attributions_Saliency = model.explantation_captum( - test_loader=test_loader, - method=Saliency, log_dir=log_dir, plot=True - ) - print("saliency", attributions_Saliency) + # attributions_Saliency = model.explantation_captum( + # test_loader=test_loader, + # method=Saliency, log_dir=log_dir, plot=True + # ) + + # print("saliency", attributions_Saliency) + """ attributions_shap = model.explantation_captum( test_loader=test_loader, method=ShapleyValueSampling, log_dir=log_dir, plot=True, n_samples=10 ) print("shap", attributions_shap) - attributions_IG = model.explantation_captum( - test_loader=test_loader, - method=IntegratedGradients, log_dir=log_dir, plot=True, n_steps=20 - ) - print("IG", attributions_IG) + """ + # attributions_IG = model.explantation_captum( + # test_loader=test_loader, + # method=IntegratedGradients, log_dir=log_dir, plot=True, n_steps=20 + # ) + # print("IG", attributions_IG) Interpertations = model.interpertations(test_loader, log_dir, plot=True) print("attention", Interpertations) attributions_dict = { "attributions_Saliency": attributions_Saliency.tolist(), "attributions_IG": attributions_IG.tolist(), - "attributions_shap": attributions_shap.tolist(), + # "attributions_shap": attributions_shap.tolist(), "attention_weights": Interpertations["attention"].tolist(), "static_variables": Interpertations["static_variables"].tolist(), "encoder_variables": Interpertations["encoder_variables"].tolist() } + variable_importance = np.concatenate((Interpertations["static_variables"].cpu().detach().numpy(), + Interpertations["encoder_variables"].cpu().detach().numpy())) + ind = np.argpartition(variable_importance, -5)[-5:] # Path to the JSON file in log_dir json_file_path = f"{log_dir}/Attributions.json" @@ -310,26 +316,24 @@ def train_common( with open(json_file_path, 'w') as json_file: json.dump(attributions_dict, json_file) if XAI_metric: - random_attributions = np.random.normal(size=24) - - F_baseline = model.Faithfulness_Correlation(test_loader, random_attributions, pertrub='Noise') + F_baseline = model.Faithfulness_Correlation(test_loader, random_attributions, pertrub='Noise', ind=ind) print('Random normal faithfulness correlation', F_baseline) - F_attribution = model.Faithfulness_Correlation(test_loader, attributions_IG, pertrub='Noise') print('Attributions faithfulness correlation', F_attribution) - F_attention = model.Faithfulness_Correlation(test_loader, Interpertations["attention"], pertrub='Noise') + F_attention = model.Faithfulness_Correlation( + test_loader, Interpertations["attention"], pertrub='Noise', ind=ind) print('Attention faithfulness correlation', F_attention) F_saliency = model.Faithfulness_Correlation(test_loader, attributions_Saliency, pertrub='Noise') print('Saliency faithfulness correlation', F_saliency) - F_shap = model.Faithfulness_Correlation(test_loader, attributions_shap, pertrub='Noise') - print('shap faithfulness correlation', F_shap) + # F_shap = model.Faithfulness_Correlation(test_loader, attributions_shap, pertrub='Noise') + # print('shap faithfulness correlation', F_shap) XAI_dict = { "Faithfulness_correlation_normal_random": F_baseline, "Faithfulness_correlation_IG": F_attribution, "Faithfulness_correlation_attention": F_attention, "Faithfulness_correlation_saliency": F_saliency, - "Faithfulness_correlation_shap": F_shap + # "Faithfulness_correlation_shap": F_shap } # Path to the JSON file in log_dir diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 32e05423..3b956900 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -529,7 +529,7 @@ def explantation_captum(self, test_loader, method, log_dir=".", plot=False, **kw explantation = method(self.forward_captum) # Reformat attributions. attr_all_timesteps = [] - for time_step in range(23, 24): + for time_step in range(0, 24): if method is not captum.attr.Saliency: attr = explantation.attribute( data, target=(time_step), baselines=baselines, **kwargs @@ -593,7 +593,7 @@ def explantation_captum(self, test_loader, method, log_dir=".", plot=False, **kw return means def Faithfulness_Correlation( - self, test_loader, attribution, similarity_func=None, nr_runs=2, pertrub=None, subset_size=6, features=False + self, test_loader, attribution, similarity_func=None, nr_runs=2, pertrub=None, subset_size=3, ind=[] ): """ Implementation of faithfulness correlation by Bhatt et al., 2020. @@ -654,16 +654,24 @@ def Faithfulness_Correlation( if pertrub == "Noise": # add normal noise to input + noise = torch.randn_like(x["encoder_cont"]) + if (len(ind) == 0): + x["encoder_cont"][:, a_ix, :] += noise[:, a_ix, :] + else: + + x["encoder_cont"][:, a_ix[:, None], ind] += noise[:, a_ix[:, None], ind] - x["encoder_cont"][:, a_ix, :] += noise[:, a_ix, :] elif pertrub == "baseline": # Create a mask tensor with zeros at specified time steps and ones everywhere else # pytorch bug need to change to cpu for next step and then revert mask = torch.ones_like(x["encoder_cont"]).cpu() - - mask[:, a_ix, :] = 0 + if (len(ind) == 0): + mask[:, a_ix, :] = 0 + else: + mask[:, a_ix, ind] = 0 mask = mask.to(x["encoder_cont"].device) + x["encoder_cont"] = x["encoder_cont"] * mask data = ( x["encoder_cat"], From dd3bdac833db983ee9117d0be3e4131970d94261 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Mon, 20 Nov 2023 13:19:48 +0100 Subject: [PATCH 109/142] changed gin to match DL --- configs/prediction_models/RNNpytorch.gin | 18 +++++++++--------- configs/prediction_models/TFTpytorch.gin | 16 ++++++++-------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/configs/prediction_models/RNNpytorch.gin b/configs/prediction_models/RNNpytorch.gin index 0e623606..75233dca 100644 --- a/configs/prediction_models/RNNpytorch.gin +++ b/configs/prediction_models/RNNpytorch.gin @@ -1,4 +1,4 @@ -# Settings for TFT model. +# Settings for RNN model. # Common settings for DL models include "configs/prediction_models/common/DLCommon.gin" @@ -22,18 +22,18 @@ model/hyperparameter.lr_scheduler = "exponential" # Dataset params -PredictionDatasetTFTpytorch.max_encoder_length = 24 -PredictionDatasetTFTpytorch.max_prediction_length = 24 -PredictionDatasetTFTpytorch.time_varying_known_reals=['alb_lag_24', 'alp_lag_24', 'alt_lag_24', 'ast_lag_24', 'be_lag_24', 'bicar_lag_24', 'bili_lag_24', 'bili_dir_lag_24', 'bnd_lag_24', 'bun_lag_24', 'ca_lag_24', 'cai_lag_24', 'ck_lag_24', 'ckmb_lag_24', 'cl_lag_24', 'crea_lag_24', 'crp_lag_24', 'dbp_lag_24', 'fgn_lag_24', 'fio2_lag_24', 'glu_lag_24', 'hgb_lag_24', 'hr_lag_24', 'inr_pt_lag_24', 'k_lag_24', 'lact_lag_24', 'lymph_lag_24', 'map_lag_24', 'mch_lag_24', 'mchc_lag_24', 'mcv_lag_24', 'methb_lag_24', 'mg_lag_24', 'na_lag_24', 'neut_lag_24', 'o2sat_lag_24', 'pco2_lag_24', 'ph_lag_24', 'phos_lag_24', 'plt_lag_24', 'po2_lag_24', 'ptt_lag_24', 'resp_lag_24', 'sbp_lag_24', 'temp_lag_24', 'tnt_lag_24', 'urine_lag_24', 'wbc_lag_24', 'alb_lag_25', 'alp_lag_25', 'alt_lag_25', 'ast_lag_25', 'be_lag_25', 'bicar_lag_25', 'bili_lag_25', 'bili_dir_lag_25', 'bnd_lag_25', 'bun_lag_25', 'ca_lag_25', 'cai_lag_25', 'ck_lag_25', 'ckmb_lag_25', 'cl_lag_25', 'crea_lag_25', 'crp_lag_25', 'dbp_lag_25', 'fgn_lag_25', 'fio2_lag_25', 'glu_lag_25', 'hgb_lag_25', 'hr_lag_25', 'inr_pt_lag_25', 'k_lag_25', 'lact_lag_25', 'lymph_lag_25', 'map_lag_25', 'mch_lag_25', 'mchc_lag_25', 'mcv_lag_25', 'methb_lag_25', 'mg_lag_25', 'na_lag_25', 'neut_lag_25', 'o2sat_lag_25', 'pco2_lag_25', 'ph_lag_25', 'phos_lag_25', 'plt_lag_25', 'po2_lag_25', 'ptt_lag_25', 'resp_lag_25', 'sbp_lag_25', 'temp_lag_25', 'tnt_lag_25', 'urine_lag_25', 'wbc_lag_25', 'alb_lag_26', 'alp_lag_26', 'alt_lag_26', 'ast_lag_26', 'be_lag_26', 'bicar_lag_26', 'bili_lag_26', 'bili_dir_lag_26', 'bnd_lag_26', 'bun_lag_26', 'ca_lag_26', 'cai_lag_26', 'ck_lag_26', 'ckmb_lag_26', 'cl_lag_26', 'crea_lag_26', 'crp_lag_26', 'dbp_lag_26', 'fgn_lag_26', 'fio2_lag_26', 'glu_lag_26', 'hgb_lag_26', 'hr_lag_26', 'inr_pt_lag_26', 'k_lag_26', 'lact_lag_26', 'lymph_lag_26', 'map_lag_26', 'mch_lag_26', 'mchc_lag_26', 'mcv_lag_26', 'methb_lag_26', 'mg_lag_26', 'na_lag_26', 'neut_lag_26', 'o2sat_lag_26', 'pco2_lag_26', 'ph_lag_26', 'phos_lag_26', 'plt_lag_26', 'po2_lag_26', 'ptt_lag_26', 'resp_lag_26', 'sbp_lag_26', 'temp_lag_26', 'tnt_lag_26', 'urine_lag_26', 'wbc_lag_26', 'alb_lag_27', 'alp_lag_27', 'alt_lag_27', 'ast_lag_27', 'be_lag_27', 'bicar_lag_27', 'bili_lag_27', 'bili_dir_lag_27', 'bnd_lag_27', 'bun_lag_27', 'ca_lag_27', 'cai_lag_27', 'ck_lag_27', 'ckmb_lag_27', 'cl_lag_27', 'crea_lag_27', 'crp_lag_27', 'dbp_lag_27', 'fgn_lag_27', 'fio2_lag_27', 'glu_lag_27', 'hgb_lag_27', 'hr_lag_27', 'inr_pt_lag_27', 'k_lag_27', 'lact_lag_27', 'lymph_lag_27', 'map_lag_27', 'mch_lag_27', 'mchc_lag_27', 'mcv_lag_27', 'methb_lag_27', 'mg_lag_27', 'na_lag_27', 'neut_lag_27', 'o2sat_lag_27', 'pco2_lag_27', 'ph_lag_27', 'phos_lag_27', 'plt_lag_27', 'po2_lag_27', 'ptt_lag_27', 'resp_lag_27', 'sbp_lag_27', 'temp_lag_27', 'tnt_lag_27', 'urine_lag_27', 'wbc_lag_27', 'alb_lag_28', 'alp_lag_28', 'alt_lag_28', 'ast_lag_28', 'be_lag_28', 'bicar_lag_28', 'bili_lag_28', 'bili_dir_lag_28', 'bnd_lag_28', 'bun_lag_28', 'ca_lag_28', 'cai_lag_28', 'ck_lag_28', 'ckmb_lag_28', 'cl_lag_28', 'crea_lag_28', 'crp_lag_28', 'dbp_lag_28', 'fgn_lag_28', 'fio2_lag_28', 'glu_lag_28', 'hgb_lag_28', 'hr_lag_28', 'inr_pt_lag_28', 'k_lag_28', 'lact_lag_28', 'lymph_lag_28', 'map_lag_28', 'mch_lag_28', 'mchc_lag_28', 'mcv_lag_28', 'methb_lag_28', 'mg_lag_28', 'na_lag_28', 'neut_lag_28', 'o2sat_lag_28', 'pco2_lag_28', 'ph_lag_28', 'phos_lag_28', 'plt_lag_28', 'po2_lag_28', 'ptt_lag_28', 'resp_lag_28', 'sbp_lag_28', 'temp_lag_28', 'tnt_lag_28', 'urine_lag_28', 'wbc_lag_28', 'alb_lag_29', 'alp_lag_29', 'alt_lag_29', 'ast_lag_29', 'be_lag_29', 'bicar_lag_29', 'bili_lag_29', 'bili_dir_lag_29', 'bnd_lag_29', 'bun_lag_29', 'ca_lag_29', 'cai_lag_29', 'ck_lag_29', 'ckmb_lag_29', 'cl_lag_29', 'crea_lag_29', 'crp_lag_29', 'dbp_lag_29', 'fgn_lag_29', 'fio2_lag_29', 'glu_lag_29', 'hgb_lag_29', 'hr_lag_29', 'inr_pt_lag_29', 'k_lag_29', 'lact_lag_29', 'lymph_lag_29', 'map_lag_29', 'mch_lag_29', 'mchc_lag_29', 'mcv_lag_29', 'methb_lag_29', 'mg_lag_29', 'na_lag_29', 'neut_lag_29', 'o2sat_lag_29', 'pco2_lag_29', 'ph_lag_29', 'phos_lag_29', 'plt_lag_29', 'po2_lag_29', 'ptt_lag_29', 'resp_lag_29', 'sbp_lag_29', 'temp_lag_29', 'tnt_lag_29', 'urine_lag_29', 'wbc_lag_29', 'alb_lag_30', 'alp_lag_30', 'alt_lag_30', 'ast_lag_30', 'be_lag_30', 'bicar_lag_30', 'bili_lag_30', 'bili_dir_lag_30', 'bnd_lag_30', 'bun_lag_30', 'ca_lag_30', 'cai_lag_30', 'ck_lag_30', 'ckmb_lag_30', 'cl_lag_30', 'crea_lag_30', 'crp_lag_30', 'dbp_lag_30', 'fgn_lag_30', 'fio2_lag_30', 'glu_lag_30', 'hgb_lag_30', 'hr_lag_30', 'inr_pt_lag_30', 'k_lag_30', 'lact_lag_30', 'lymph_lag_30', 'map_lag_30', 'mch_lag_30', 'mchc_lag_30', 'mcv_lag_30', 'methb_lag_30', 'mg_lag_30', 'na_lag_30', 'neut_lag_30', 'o2sat_lag_30', 'pco2_lag_30', 'ph_lag_30', 'phos_lag_30', 'plt_lag_30', 'po2_lag_30', 'ptt_lag_30', 'resp_lag_30', 'sbp_lag_30', 'temp_lag_30', 'tnt_lag_30', 'urine_lag_30', 'wbc_lag_30', 'alb_lag_31', 'alp_lag_31', 'alt_lag_31', 'ast_lag_31', 'be_lag_31', 'bicar_lag_31', 'bili_lag_31', 'bili_dir_lag_31', 'bnd_lag_31', 'bun_lag_31', 'ca_lag_31', 'cai_lag_31', 'ck_lag_31', 'ckmb_lag_31', 'cl_lag_31', 'crea_lag_31', 'crp_lag_31', 'dbp_lag_31', 'fgn_lag_31', 'fio2_lag_31', 'glu_lag_31', 'hgb_lag_31', 'hr_lag_31', 'inr_pt_lag_31', 'k_lag_31', 'lact_lag_31', 'lymph_lag_31', 'map_lag_31', 'mch_lag_31', 'mchc_lag_31', 'mcv_lag_31', 'methb_lag_31', 'mg_lag_31', 'na_lag_31', 'neut_lag_31', 'o2sat_lag_31', 'pco2_lag_31', 'ph_lag_31', 'phos_lag_31', 'plt_lag_31', 'po2_lag_31', 'ptt_lag_31', 'resp_lag_31', 'sbp_lag_31', 'temp_lag_31', 'tnt_lag_31', 'urine_lag_31', 'wbc_lag_31', 'alb_lag_32', 'alp_lag_32', 'alt_lag_32', 'ast_lag_32', 'be_lag_32', 'bicar_lag_32', 'bili_lag_32', 'bili_dir_lag_32', 'bnd_lag_32', 'bun_lag_32', 'ca_lag_32', 'cai_lag_32', 'ck_lag_32', 'ckmb_lag_32', 'cl_lag_32', 'crea_lag_32', 'crp_lag_32', 'dbp_lag_32', 'fgn_lag_32', 'fio2_lag_32', 'glu_lag_32', 'hgb_lag_32', 'hr_lag_32', 'inr_pt_lag_32', 'k_lag_32', 'lact_lag_32', 'lymph_lag_32', 'map_lag_32', 'mch_lag_32', 'mchc_lag_32', 'mcv_lag_32', 'methb_lag_32', 'mg_lag_32', 'na_lag_32', 'neut_lag_32', 'o2sat_lag_32', 'pco2_lag_32', 'ph_lag_32', 'phos_lag_32', 'plt_lag_32', 'po2_lag_32', 'ptt_lag_32', 'resp_lag_32', 'sbp_lag_32', 'temp_lag_32', 'tnt_lag_32', 'urine_lag_32', 'wbc_lag_32', 'alb_lag_33', 'alp_lag_33', 'alt_lag_33', 'ast_lag_33', 'be_lag_33', 'bicar_lag_33', 'bili_lag_33', 'bili_dir_lag_33', 'bnd_lag_33', 'bun_lag_33', 'ca_lag_33', 'cai_lag_33', 'ck_lag_33', 'ckmb_lag_33', 'cl_lag_33', 'crea_lag_33', 'crp_lag_33', 'dbp_lag_33', 'fgn_lag_33', 'fio2_lag_33', 'glu_lag_33', 'hgb_lag_33', 'hr_lag_33', 'inr_pt_lag_33', 'k_lag_33', 'lact_lag_33', 'lymph_lag_33', 'map_lag_33', 'mch_lag_33', 'mchc_lag_33', 'mcv_lag_33', 'methb_lag_33', 'mg_lag_33', 'na_lag_33', 'neut_lag_33', 'o2sat_lag_33', 'pco2_lag_33', 'ph_lag_33', 'phos_lag_33', 'plt_lag_33', 'po2_lag_33', 'ptt_lag_33', 'resp_lag_33', 'sbp_lag_33', 'temp_lag_33', 'tnt_lag_33', 'urine_lag_33', 'wbc_lag_33', 'alb_lag_34', 'alp_lag_34', 'alt_lag_34', 'ast_lag_34', 'be_lag_34', 'bicar_lag_34', 'bili_lag_34', 'bili_dir_lag_34', 'bnd_lag_34', 'bun_lag_34', 'ca_lag_34', 'cai_lag_34', 'ck_lag_34', 'ckmb_lag_34', 'cl_lag_34', 'crea_lag_34', 'crp_lag_34', 'dbp_lag_34', 'fgn_lag_34', 'fio2_lag_34', 'glu_lag_34', 'hgb_lag_34', 'hr_lag_34', 'inr_pt_lag_34', 'k_lag_34', 'lact_lag_34', 'lymph_lag_34', 'map_lag_34', 'mch_lag_34', 'mchc_lag_34', 'mcv_lag_34', 'methb_lag_34', 'mg_lag_34', 'na_lag_34', 'neut_lag_34', 'o2sat_lag_34', 'pco2_lag_34', 'ph_lag_34', 'phos_lag_34', 'plt_lag_34', 'po2_lag_34', 'ptt_lag_34', 'resp_lag_34', 'sbp_lag_34', 'temp_lag_34', 'tnt_lag_34', 'urine_lag_34', 'wbc_lag_34', 'alb_lag_35', 'alp_lag_35', 'alt_lag_35', 'ast_lag_35', 'be_lag_35', 'bicar_lag_35', 'bili_lag_35', 'bili_dir_lag_35', 'bnd_lag_35', 'bun_lag_35', 'ca_lag_35', 'cai_lag_35', 'ck_lag_35', 'ckmb_lag_35', 'cl_lag_35', 'crea_lag_35', 'crp_lag_35', 'dbp_lag_35', 'fgn_lag_35', 'fio2_lag_35', 'glu_lag_35', 'hgb_lag_35', 'hr_lag_35', 'inr_pt_lag_35', 'k_lag_35', 'lact_lag_35', 'lymph_lag_35', 'map_lag_35', 'mch_lag_35', 'mchc_lag_35', 'mcv_lag_35', 'methb_lag_35', 'mg_lag_35', 'na_lag_35', 'neut_lag_35', 'o2sat_lag_35', 'pco2_lag_35', 'ph_lag_35', 'phos_lag_35', 'plt_lag_35', 'po2_lag_35', 'ptt_lag_35', 'resp_lag_35', 'sbp_lag_35', 'temp_lag_35', 'tnt_lag_35', 'urine_lag_35', 'wbc_lag_35', 'alb_lag_36', 'alp_lag_36', 'alt_lag_36', 'ast_lag_36', 'be_lag_36', 'bicar_lag_36', 'bili_lag_36', 'bili_dir_lag_36', 'bnd_lag_36', 'bun_lag_36', 'ca_lag_36', 'cai_lag_36', 'ck_lag_36', 'ckmb_lag_36', 'cl_lag_36', 'crea_lag_36', 'crp_lag_36', 'dbp_lag_36', 'fgn_lag_36', 'fio2_lag_36', 'glu_lag_36', 'hgb_lag_36', 'hr_lag_36', 'inr_pt_lag_36', 'k_lag_36', 'lact_lag_36', 'lymph_lag_36', 'map_lag_36', 'mch_lag_36', 'mchc_lag_36', 'mcv_lag_36', 'methb_lag_36', 'mg_lag_36', 'na_lag_36', 'neut_lag_36', 'o2sat_lag_36', 'pco2_lag_36', 'ph_lag_36', 'phos_lag_36', 'plt_lag_36', 'po2_lag_36', 'ptt_lag_36', 'resp_lag_36', 'sbp_lag_36', 'temp_lag_36', 'tnt_lag_36', 'urine_lag_36', 'wbc_lag_36', 'alb_lag_37', 'alp_lag_37', 'alt_lag_37', 'ast_lag_37', 'be_lag_37', 'bicar_lag_37', 'bili_lag_37', 'bili_dir_lag_37', 'bnd_lag_37', 'bun_lag_37', 'ca_lag_37', 'cai_lag_37', 'ck_lag_37', 'ckmb_lag_37', 'cl_lag_37', 'crea_lag_37', 'crp_lag_37', 'dbp_lag_37', 'fgn_lag_37', 'fio2_lag_37', 'glu_lag_37', 'hgb_lag_37', 'hr_lag_37', 'inr_pt_lag_37', 'k_lag_37', 'lact_lag_37', 'lymph_lag_37', 'map_lag_37', 'mch_lag_37', 'mchc_lag_37', 'mcv_lag_37', 'methb_lag_37', 'mg_lag_37', 'na_lag_37', 'neut_lag_37', 'o2sat_lag_37', 'pco2_lag_37', 'ph_lag_37', 'phos_lag_37', 'plt_lag_37', 'po2_lag_37', 'ptt_lag_37', 'resp_lag_37', 'sbp_lag_37', 'temp_lag_37', 'tnt_lag_37', 'urine_lag_37', 'wbc_lag_37', 'alb_lag_38', 'alp_lag_38', 'alt_lag_38', 'ast_lag_38', 'be_lag_38', 'bicar_lag_38', 'bili_lag_38', 'bili_dir_lag_38', 'bnd_lag_38', 'bun_lag_38', 'ca_lag_38', 'cai_lag_38', 'ck_lag_38', 'ckmb_lag_38', 'cl_lag_38', 'crea_lag_38', 'crp_lag_38', 'dbp_lag_38', 'fgn_lag_38', 'fio2_lag_38', 'glu_lag_38', 'hgb_lag_38', 'hr_lag_38', 'inr_pt_lag_38', 'k_lag_38', 'lact_lag_38', 'lymph_lag_38', 'map_lag_38', 'mch_lag_38', 'mchc_lag_38', 'mcv_lag_38', 'methb_lag_38', 'mg_lag_38', 'na_lag_38', 'neut_lag_38', 'o2sat_lag_38', 'pco2_lag_38', 'ph_lag_38', 'phos_lag_38', 'plt_lag_38', 'po2_lag_38', 'ptt_lag_38', 'resp_lag_38', 'sbp_lag_38', 'temp_lag_38', 'tnt_lag_38', 'urine_lag_38', 'wbc_lag_38', 'alb_lag_39', 'alp_lag_39', 'alt_lag_39', 'ast_lag_39', 'be_lag_39', 'bicar_lag_39', 'bili_lag_39', 'bili_dir_lag_39', 'bnd_lag_39', 'bun_lag_39', 'ca_lag_39', 'cai_lag_39', 'ck_lag_39', 'ckmb_lag_39', 'cl_lag_39', 'crea_lag_39', 'crp_lag_39', 'dbp_lag_39', 'fgn_lag_39', 'fio2_lag_39', 'glu_lag_39', 'hgb_lag_39', 'hr_lag_39', 'inr_pt_lag_39', 'k_lag_39', 'lact_lag_39', 'lymph_lag_39', 'map_lag_39', 'mch_lag_39', 'mchc_lag_39', 'mcv_lag_39', 'methb_lag_39', 'mg_lag_39', 'na_lag_39', 'neut_lag_39', 'o2sat_lag_39', 'pco2_lag_39', 'ph_lag_39', 'phos_lag_39', 'plt_lag_39', 'po2_lag_39', 'ptt_lag_39', 'resp_lag_39', 'sbp_lag_39', 'temp_lag_39', 'tnt_lag_39', 'urine_lag_39', 'wbc_lag_39', 'alb_lag_40', 'alp_lag_40', 'alt_lag_40', 'ast_lag_40', 'be_lag_40', 'bicar_lag_40', 'bili_lag_40', 'bili_dir_lag_40', 'bnd_lag_40', 'bun_lag_40', 'ca_lag_40', 'cai_lag_40', 'ck_lag_40', 'ckmb_lag_40', 'cl_lag_40', 'crea_lag_40', 'crp_lag_40', 'dbp_lag_40', 'fgn_lag_40', 'fio2_lag_40', 'glu_lag_40', 'hgb_lag_40', 'hr_lag_40', 'inr_pt_lag_40', 'k_lag_40', 'lact_lag_40', 'lymph_lag_40', 'map_lag_40', 'mch_lag_40', 'mchc_lag_40', 'mcv_lag_40', 'methb_lag_40', 'mg_lag_40', 'na_lag_40', 'neut_lag_40', 'o2sat_lag_40', 'pco2_lag_40', 'ph_lag_40', 'phos_lag_40', 'plt_lag_40', 'po2_lag_40', 'ptt_lag_40', 'resp_lag_40', 'sbp_lag_40', 'temp_lag_40', 'tnt_lag_40', 'urine_lag_40', 'wbc_lag_40', 'alb_lag_41', 'alp_lag_41', 'alt_lag_41', 'ast_lag_41', 'be_lag_41', 'bicar_lag_41', 'bili_lag_41', 'bili_dir_lag_41', 'bnd_lag_41', 'bun_lag_41', 'ca_lag_41', 'cai_lag_41', 'ck_lag_41', 'ckmb_lag_41', 'cl_lag_41', 'crea_lag_41', 'crp_lag_41', 'dbp_lag_41', 'fgn_lag_41', 'fio2_lag_41', 'glu_lag_41', 'hgb_lag_41', 'hr_lag_41', 'inr_pt_lag_41', 'k_lag_41', 'lact_lag_41', 'lymph_lag_41', 'map_lag_41', 'mch_lag_41', 'mchc_lag_41', 'mcv_lag_41', 'methb_lag_41', 'mg_lag_41', 'na_lag_41', 'neut_lag_41', 'o2sat_lag_41', 'pco2_lag_41', 'ph_lag_41', 'phos_lag_41', 'plt_lag_41', 'po2_lag_41', 'ptt_lag_41', 'resp_lag_41', 'sbp_lag_41', 'temp_lag_41', 'tnt_lag_41', 'urine_lag_41', 'wbc_lag_41', 'alb_lag_42', 'alp_lag_42', 'alt_lag_42', 'ast_lag_42', 'be_lag_42', 'bicar_lag_42', 'bili_lag_42', 'bili_dir_lag_42', 'bnd_lag_42', 'bun_lag_42', 'ca_lag_42', 'cai_lag_42', 'ck_lag_42', 'ckmb_lag_42', 'cl_lag_42', 'crea_lag_42', 'crp_lag_42', 'dbp_lag_42', 'fgn_lag_42', 'fio2_lag_42', 'glu_lag_42', 'hgb_lag_42', 'hr_lag_42', 'inr_pt_lag_42', 'k_lag_42', 'lact_lag_42', 'lymph_lag_42', 'map_lag_42', 'mch_lag_42', 'mchc_lag_42', 'mcv_lag_42', 'methb_lag_42', 'mg_lag_42', 'na_lag_42', 'neut_lag_42', 'o2sat_lag_42', 'pco2_lag_42', 'ph_lag_42', 'phos_lag_42', 'plt_lag_42', 'po2_lag_42', 'ptt_lag_42', 'resp_lag_42', 'sbp_lag_42', 'temp_lag_42', 'tnt_lag_42', 'urine_lag_42', 'wbc_lag_42', 'alb_lag_43', 'alp_lag_43', 'alt_lag_43', 'ast_lag_43', 'be_lag_43', 'bicar_lag_43', 'bili_lag_43', 'bili_dir_lag_43', 'bnd_lag_43', 'bun_lag_43', 'ca_lag_43', 'cai_lag_43', 'ck_lag_43', 'ckmb_lag_43', 'cl_lag_43', 'crea_lag_43', 'crp_lag_43', 'dbp_lag_43', 'fgn_lag_43', 'fio2_lag_43', 'glu_lag_43', 'hgb_lag_43', 'hr_lag_43', 'inr_pt_lag_43', 'k_lag_43', 'lact_lag_43', 'lymph_lag_43', 'map_lag_43', 'mch_lag_43', 'mchc_lag_43', 'mcv_lag_43', 'methb_lag_43', 'mg_lag_43', 'na_lag_43', 'neut_lag_43', 'o2sat_lag_43', 'pco2_lag_43', 'ph_lag_43', 'phos_lag_43', 'plt_lag_43', 'po2_lag_43', 'ptt_lag_43', 'resp_lag_43', 'sbp_lag_43', 'temp_lag_43', 'tnt_lag_43', 'urine_lag_43', 'wbc_lag_43', 'alb_lag_44', 'alp_lag_44', 'alt_lag_44', 'ast_lag_44', 'be_lag_44', 'bicar_lag_44', 'bili_lag_44', 'bili_dir_lag_44', 'bnd_lag_44', 'bun_lag_44', 'ca_lag_44', 'cai_lag_44', 'ck_lag_44', 'ckmb_lag_44', 'cl_lag_44', 'crea_lag_44', 'crp_lag_44', 'dbp_lag_44', 'fgn_lag_44', 'fio2_lag_44', 'glu_lag_44', 'hgb_lag_44', 'hr_lag_44', 'inr_pt_lag_44', 'k_lag_44', 'lact_lag_44', 'lymph_lag_44', 'map_lag_44', 'mch_lag_44', 'mchc_lag_44', 'mcv_lag_44', 'methb_lag_44', 'mg_lag_44', 'na_lag_44', 'neut_lag_44', 'o2sat_lag_44', 'pco2_lag_44', 'ph_lag_44', 'phos_lag_44', 'plt_lag_44', 'po2_lag_44', 'ptt_lag_44', 'resp_lag_44', 'sbp_lag_44', 'temp_lag_44', 'tnt_lag_44', 'urine_lag_44', 'wbc_lag_44', 'alb_lag_45', 'alp_lag_45', 'alt_lag_45', 'ast_lag_45', 'be_lag_45', 'bicar_lag_45', 'bili_lag_45', 'bili_dir_lag_45', 'bnd_lag_45', 'bun_lag_45', 'ca_lag_45', 'cai_lag_45', 'ck_lag_45', 'ckmb_lag_45', 'cl_lag_45', 'crea_lag_45', 'crp_lag_45', 'dbp_lag_45', 'fgn_lag_45', 'fio2_lag_45', 'glu_lag_45', 'hgb_lag_45', 'hr_lag_45', 'inr_pt_lag_45', 'k_lag_45', 'lact_lag_45', 'lymph_lag_45', 'map_lag_45', 'mch_lag_45', 'mchc_lag_45', 'mcv_lag_45', 'methb_lag_45', 'mg_lag_45', 'na_lag_45', 'neut_lag_45', 'o2sat_lag_45', 'pco2_lag_45', 'ph_lag_45', 'phos_lag_45', 'plt_lag_45', 'po2_lag_45', 'ptt_lag_45', 'resp_lag_45', 'sbp_lag_45', 'temp_lag_45', 'tnt_lag_45', 'urine_lag_45', 'wbc_lag_45', 'alb_lag_46', 'alp_lag_46', 'alt_lag_46', 'ast_lag_46', 'be_lag_46', 'bicar_lag_46', 'bili_lag_46', 'bili_dir_lag_46', 'bnd_lag_46', 'bun_lag_46', 'ca_lag_46', 'cai_lag_46', 'ck_lag_46', 'ckmb_lag_46', 'cl_lag_46', 'crea_lag_46', 'crp_lag_46', 'dbp_lag_46', 'fgn_lag_46', 'fio2_lag_46', 'glu_lag_46', 'hgb_lag_46', 'hr_lag_46', 'inr_pt_lag_46', 'k_lag_46', 'lact_lag_46', 'lymph_lag_46', 'map_lag_46', 'mch_lag_46', 'mchc_lag_46', 'mcv_lag_46', 'methb_lag_46', 'mg_lag_46', 'na_lag_46', 'neut_lag_46', 'o2sat_lag_46', 'pco2_lag_46', 'ph_lag_46', 'phos_lag_46', 'plt_lag_46', 'po2_lag_46', 'ptt_lag_46', 'resp_lag_46', 'sbp_lag_46', 'temp_lag_46', 'tnt_lag_46', 'urine_lag_46', 'wbc_lag_46', 'alb_lag_47', 'alp_lag_47', 'alt_lag_47', 'ast_lag_47', 'be_lag_47', 'bicar_lag_47', 'bili_lag_47', 'bili_dir_lag_47', 'bnd_lag_47', 'bun_lag_47', 'ca_lag_47', 'cai_lag_47', 'ck_lag_47', 'ckmb_lag_47', 'cl_lag_47', 'crea_lag_47', 'crp_lag_47', 'dbp_lag_47', 'fgn_lag_47', 'fio2_lag_47', 'glu_lag_47', 'hgb_lag_47', 'hr_lag_47', 'inr_pt_lag_47', 'k_lag_47', 'lact_lag_47', 'lymph_lag_47', 'map_lag_47', 'mch_lag_47', 'mchc_lag_47', 'mcv_lag_47', 'methb_lag_47', 'mg_lag_47', 'na_lag_47', 'neut_lag_47', 'o2sat_lag_47', 'pco2_lag_47', 'ph_lag_47', 'phos_lag_47', 'plt_lag_47', 'po2_lag_47', 'ptt_lag_47', 'resp_lag_47', 'sbp_lag_47', 'temp_lag_47', 'tnt_lag_47', 'urine_lag_47', 'wbc_lag_47', 'alb_lag_48', 'alp_lag_48', 'alt_lag_48', 'ast_lag_48', 'be_lag_48', 'bicar_lag_48', 'bili_lag_48', 'bili_dir_lag_48', 'bnd_lag_48', 'bun_lag_48', 'ca_lag_48', 'cai_lag_48', 'ck_lag_48', 'ckmb_lag_48', 'cl_lag_48', 'crea_lag_48', 'crp_lag_48', 'dbp_lag_48', 'fgn_lag_48', 'fio2_lag_48', 'glu_lag_48', 'hgb_lag_48', 'hr_lag_48', 'inr_pt_lag_48', 'k_lag_48', 'lact_lag_48', 'lymph_lag_48', 'map_lag_48', 'mch_lag_48', 'mchc_lag_48', 'mcv_lag_48', 'methb_lag_48', 'mg_lag_48', 'na_lag_48', 'neut_lag_48', 'o2sat_lag_48', 'pco2_lag_48', 'ph_lag_48', 'phos_lag_48', 'plt_lag_48', 'po2_lag_48', 'ptt_lag_48', 'resp_lag_48', 'sbp_lag_48', 'temp_lag_48', 'tnt_lag_48', 'urine_lag_48', 'wbc_lag_48'] +PredictionDatasetpytorch.max_encoder_length = 24 +PredictionDatasetpytorch.max_prediction_length = 24 +PredictionDatasetpytorch.time_varying_known_reals=['alb_lag_24', 'alp_lag_24', 'alt_lag_24', 'ast_lag_24', 'be_lag_24', 'bicar_lag_24', 'bili_lag_24', 'bili_dir_lag_24', 'bnd_lag_24', 'bun_lag_24', 'ca_lag_24', 'cai_lag_24', 'ck_lag_24', 'ckmb_lag_24', 'cl_lag_24', 'crea_lag_24', 'crp_lag_24', 'dbp_lag_24', 'fgn_lag_24', 'fio2_lag_24', 'glu_lag_24', 'hgb_lag_24', 'hr_lag_24', 'inr_pt_lag_24', 'k_lag_24', 'lact_lag_24', 'lymph_lag_24', 'map_lag_24', 'mch_lag_24', 'mchc_lag_24', 'mcv_lag_24', 'methb_lag_24', 'mg_lag_24', 'na_lag_24', 'neut_lag_24', 'o2sat_lag_24', 'pco2_lag_24', 'ph_lag_24', 'phos_lag_24', 'plt_lag_24', 'po2_lag_24', 'ptt_lag_24', 'resp_lag_24', 'sbp_lag_24', 'temp_lag_24', 'tnt_lag_24', 'urine_lag_24', 'wbc_lag_24', 'alb_lag_25', 'alp_lag_25', 'alt_lag_25', 'ast_lag_25', 'be_lag_25', 'bicar_lag_25', 'bili_lag_25', 'bili_dir_lag_25', 'bnd_lag_25', 'bun_lag_25', 'ca_lag_25', 'cai_lag_25', 'ck_lag_25', 'ckmb_lag_25', 'cl_lag_25', 'crea_lag_25', 'crp_lag_25', 'dbp_lag_25', 'fgn_lag_25', 'fio2_lag_25', 'glu_lag_25', 'hgb_lag_25', 'hr_lag_25', 'inr_pt_lag_25', 'k_lag_25', 'lact_lag_25', 'lymph_lag_25', 'map_lag_25', 'mch_lag_25', 'mchc_lag_25', 'mcv_lag_25', 'methb_lag_25', 'mg_lag_25', 'na_lag_25', 'neut_lag_25', 'o2sat_lag_25', 'pco2_lag_25', 'ph_lag_25', 'phos_lag_25', 'plt_lag_25', 'po2_lag_25', 'ptt_lag_25', 'resp_lag_25', 'sbp_lag_25', 'temp_lag_25', 'tnt_lag_25', 'urine_lag_25', 'wbc_lag_25', 'alb_lag_26', 'alp_lag_26', 'alt_lag_26', 'ast_lag_26', 'be_lag_26', 'bicar_lag_26', 'bili_lag_26', 'bili_dir_lag_26', 'bnd_lag_26', 'bun_lag_26', 'ca_lag_26', 'cai_lag_26', 'ck_lag_26', 'ckmb_lag_26', 'cl_lag_26', 'crea_lag_26', 'crp_lag_26', 'dbp_lag_26', 'fgn_lag_26', 'fio2_lag_26', 'glu_lag_26', 'hgb_lag_26', 'hr_lag_26', 'inr_pt_lag_26', 'k_lag_26', 'lact_lag_26', 'lymph_lag_26', 'map_lag_26', 'mch_lag_26', 'mchc_lag_26', 'mcv_lag_26', 'methb_lag_26', 'mg_lag_26', 'na_lag_26', 'neut_lag_26', 'o2sat_lag_26', 'pco2_lag_26', 'ph_lag_26', 'phos_lag_26', 'plt_lag_26', 'po2_lag_26', 'ptt_lag_26', 'resp_lag_26', 'sbp_lag_26', 'temp_lag_26', 'tnt_lag_26', 'urine_lag_26', 'wbc_lag_26', 'alb_lag_27', 'alp_lag_27', 'alt_lag_27', 'ast_lag_27', 'be_lag_27', 'bicar_lag_27', 'bili_lag_27', 'bili_dir_lag_27', 'bnd_lag_27', 'bun_lag_27', 'ca_lag_27', 'cai_lag_27', 'ck_lag_27', 'ckmb_lag_27', 'cl_lag_27', 'crea_lag_27', 'crp_lag_27', 'dbp_lag_27', 'fgn_lag_27', 'fio2_lag_27', 'glu_lag_27', 'hgb_lag_27', 'hr_lag_27', 'inr_pt_lag_27', 'k_lag_27', 'lact_lag_27', 'lymph_lag_27', 'map_lag_27', 'mch_lag_27', 'mchc_lag_27', 'mcv_lag_27', 'methb_lag_27', 'mg_lag_27', 'na_lag_27', 'neut_lag_27', 'o2sat_lag_27', 'pco2_lag_27', 'ph_lag_27', 'phos_lag_27', 'plt_lag_27', 'po2_lag_27', 'ptt_lag_27', 'resp_lag_27', 'sbp_lag_27', 'temp_lag_27', 'tnt_lag_27', 'urine_lag_27', 'wbc_lag_27', 'alb_lag_28', 'alp_lag_28', 'alt_lag_28', 'ast_lag_28', 'be_lag_28', 'bicar_lag_28', 'bili_lag_28', 'bili_dir_lag_28', 'bnd_lag_28', 'bun_lag_28', 'ca_lag_28', 'cai_lag_28', 'ck_lag_28', 'ckmb_lag_28', 'cl_lag_28', 'crea_lag_28', 'crp_lag_28', 'dbp_lag_28', 'fgn_lag_28', 'fio2_lag_28', 'glu_lag_28', 'hgb_lag_28', 'hr_lag_28', 'inr_pt_lag_28', 'k_lag_28', 'lact_lag_28', 'lymph_lag_28', 'map_lag_28', 'mch_lag_28', 'mchc_lag_28', 'mcv_lag_28', 'methb_lag_28', 'mg_lag_28', 'na_lag_28', 'neut_lag_28', 'o2sat_lag_28', 'pco2_lag_28', 'ph_lag_28', 'phos_lag_28', 'plt_lag_28', 'po2_lag_28', 'ptt_lag_28', 'resp_lag_28', 'sbp_lag_28', 'temp_lag_28', 'tnt_lag_28', 'urine_lag_28', 'wbc_lag_28', 'alb_lag_29', 'alp_lag_29', 'alt_lag_29', 'ast_lag_29', 'be_lag_29', 'bicar_lag_29', 'bili_lag_29', 'bili_dir_lag_29', 'bnd_lag_29', 'bun_lag_29', 'ca_lag_29', 'cai_lag_29', 'ck_lag_29', 'ckmb_lag_29', 'cl_lag_29', 'crea_lag_29', 'crp_lag_29', 'dbp_lag_29', 'fgn_lag_29', 'fio2_lag_29', 'glu_lag_29', 'hgb_lag_29', 'hr_lag_29', 'inr_pt_lag_29', 'k_lag_29', 'lact_lag_29', 'lymph_lag_29', 'map_lag_29', 'mch_lag_29', 'mchc_lag_29', 'mcv_lag_29', 'methb_lag_29', 'mg_lag_29', 'na_lag_29', 'neut_lag_29', 'o2sat_lag_29', 'pco2_lag_29', 'ph_lag_29', 'phos_lag_29', 'plt_lag_29', 'po2_lag_29', 'ptt_lag_29', 'resp_lag_29', 'sbp_lag_29', 'temp_lag_29', 'tnt_lag_29', 'urine_lag_29', 'wbc_lag_29', 'alb_lag_30', 'alp_lag_30', 'alt_lag_30', 'ast_lag_30', 'be_lag_30', 'bicar_lag_30', 'bili_lag_30', 'bili_dir_lag_30', 'bnd_lag_30', 'bun_lag_30', 'ca_lag_30', 'cai_lag_30', 'ck_lag_30', 'ckmb_lag_30', 'cl_lag_30', 'crea_lag_30', 'crp_lag_30', 'dbp_lag_30', 'fgn_lag_30', 'fio2_lag_30', 'glu_lag_30', 'hgb_lag_30', 'hr_lag_30', 'inr_pt_lag_30', 'k_lag_30', 'lact_lag_30', 'lymph_lag_30', 'map_lag_30', 'mch_lag_30', 'mchc_lag_30', 'mcv_lag_30', 'methb_lag_30', 'mg_lag_30', 'na_lag_30', 'neut_lag_30', 'o2sat_lag_30', 'pco2_lag_30', 'ph_lag_30', 'phos_lag_30', 'plt_lag_30', 'po2_lag_30', 'ptt_lag_30', 'resp_lag_30', 'sbp_lag_30', 'temp_lag_30', 'tnt_lag_30', 'urine_lag_30', 'wbc_lag_30', 'alb_lag_31', 'alp_lag_31', 'alt_lag_31', 'ast_lag_31', 'be_lag_31', 'bicar_lag_31', 'bili_lag_31', 'bili_dir_lag_31', 'bnd_lag_31', 'bun_lag_31', 'ca_lag_31', 'cai_lag_31', 'ck_lag_31', 'ckmb_lag_31', 'cl_lag_31', 'crea_lag_31', 'crp_lag_31', 'dbp_lag_31', 'fgn_lag_31', 'fio2_lag_31', 'glu_lag_31', 'hgb_lag_31', 'hr_lag_31', 'inr_pt_lag_31', 'k_lag_31', 'lact_lag_31', 'lymph_lag_31', 'map_lag_31', 'mch_lag_31', 'mchc_lag_31', 'mcv_lag_31', 'methb_lag_31', 'mg_lag_31', 'na_lag_31', 'neut_lag_31', 'o2sat_lag_31', 'pco2_lag_31', 'ph_lag_31', 'phos_lag_31', 'plt_lag_31', 'po2_lag_31', 'ptt_lag_31', 'resp_lag_31', 'sbp_lag_31', 'temp_lag_31', 'tnt_lag_31', 'urine_lag_31', 'wbc_lag_31', 'alb_lag_32', 'alp_lag_32', 'alt_lag_32', 'ast_lag_32', 'be_lag_32', 'bicar_lag_32', 'bili_lag_32', 'bili_dir_lag_32', 'bnd_lag_32', 'bun_lag_32', 'ca_lag_32', 'cai_lag_32', 'ck_lag_32', 'ckmb_lag_32', 'cl_lag_32', 'crea_lag_32', 'crp_lag_32', 'dbp_lag_32', 'fgn_lag_32', 'fio2_lag_32', 'glu_lag_32', 'hgb_lag_32', 'hr_lag_32', 'inr_pt_lag_32', 'k_lag_32', 'lact_lag_32', 'lymph_lag_32', 'map_lag_32', 'mch_lag_32', 'mchc_lag_32', 'mcv_lag_32', 'methb_lag_32', 'mg_lag_32', 'na_lag_32', 'neut_lag_32', 'o2sat_lag_32', 'pco2_lag_32', 'ph_lag_32', 'phos_lag_32', 'plt_lag_32', 'po2_lag_32', 'ptt_lag_32', 'resp_lag_32', 'sbp_lag_32', 'temp_lag_32', 'tnt_lag_32', 'urine_lag_32', 'wbc_lag_32', 'alb_lag_33', 'alp_lag_33', 'alt_lag_33', 'ast_lag_33', 'be_lag_33', 'bicar_lag_33', 'bili_lag_33', 'bili_dir_lag_33', 'bnd_lag_33', 'bun_lag_33', 'ca_lag_33', 'cai_lag_33', 'ck_lag_33', 'ckmb_lag_33', 'cl_lag_33', 'crea_lag_33', 'crp_lag_33', 'dbp_lag_33', 'fgn_lag_33', 'fio2_lag_33', 'glu_lag_33', 'hgb_lag_33', 'hr_lag_33', 'inr_pt_lag_33', 'k_lag_33', 'lact_lag_33', 'lymph_lag_33', 'map_lag_33', 'mch_lag_33', 'mchc_lag_33', 'mcv_lag_33', 'methb_lag_33', 'mg_lag_33', 'na_lag_33', 'neut_lag_33', 'o2sat_lag_33', 'pco2_lag_33', 'ph_lag_33', 'phos_lag_33', 'plt_lag_33', 'po2_lag_33', 'ptt_lag_33', 'resp_lag_33', 'sbp_lag_33', 'temp_lag_33', 'tnt_lag_33', 'urine_lag_33', 'wbc_lag_33', 'alb_lag_34', 'alp_lag_34', 'alt_lag_34', 'ast_lag_34', 'be_lag_34', 'bicar_lag_34', 'bili_lag_34', 'bili_dir_lag_34', 'bnd_lag_34', 'bun_lag_34', 'ca_lag_34', 'cai_lag_34', 'ck_lag_34', 'ckmb_lag_34', 'cl_lag_34', 'crea_lag_34', 'crp_lag_34', 'dbp_lag_34', 'fgn_lag_34', 'fio2_lag_34', 'glu_lag_34', 'hgb_lag_34', 'hr_lag_34', 'inr_pt_lag_34', 'k_lag_34', 'lact_lag_34', 'lymph_lag_34', 'map_lag_34', 'mch_lag_34', 'mchc_lag_34', 'mcv_lag_34', 'methb_lag_34', 'mg_lag_34', 'na_lag_34', 'neut_lag_34', 'o2sat_lag_34', 'pco2_lag_34', 'ph_lag_34', 'phos_lag_34', 'plt_lag_34', 'po2_lag_34', 'ptt_lag_34', 'resp_lag_34', 'sbp_lag_34', 'temp_lag_34', 'tnt_lag_34', 'urine_lag_34', 'wbc_lag_34', 'alb_lag_35', 'alp_lag_35', 'alt_lag_35', 'ast_lag_35', 'be_lag_35', 'bicar_lag_35', 'bili_lag_35', 'bili_dir_lag_35', 'bnd_lag_35', 'bun_lag_35', 'ca_lag_35', 'cai_lag_35', 'ck_lag_35', 'ckmb_lag_35', 'cl_lag_35', 'crea_lag_35', 'crp_lag_35', 'dbp_lag_35', 'fgn_lag_35', 'fio2_lag_35', 'glu_lag_35', 'hgb_lag_35', 'hr_lag_35', 'inr_pt_lag_35', 'k_lag_35', 'lact_lag_35', 'lymph_lag_35', 'map_lag_35', 'mch_lag_35', 'mchc_lag_35', 'mcv_lag_35', 'methb_lag_35', 'mg_lag_35', 'na_lag_35', 'neut_lag_35', 'o2sat_lag_35', 'pco2_lag_35', 'ph_lag_35', 'phos_lag_35', 'plt_lag_35', 'po2_lag_35', 'ptt_lag_35', 'resp_lag_35', 'sbp_lag_35', 'temp_lag_35', 'tnt_lag_35', 'urine_lag_35', 'wbc_lag_35', 'alb_lag_36', 'alp_lag_36', 'alt_lag_36', 'ast_lag_36', 'be_lag_36', 'bicar_lag_36', 'bili_lag_36', 'bili_dir_lag_36', 'bnd_lag_36', 'bun_lag_36', 'ca_lag_36', 'cai_lag_36', 'ck_lag_36', 'ckmb_lag_36', 'cl_lag_36', 'crea_lag_36', 'crp_lag_36', 'dbp_lag_36', 'fgn_lag_36', 'fio2_lag_36', 'glu_lag_36', 'hgb_lag_36', 'hr_lag_36', 'inr_pt_lag_36', 'k_lag_36', 'lact_lag_36', 'lymph_lag_36', 'map_lag_36', 'mch_lag_36', 'mchc_lag_36', 'mcv_lag_36', 'methb_lag_36', 'mg_lag_36', 'na_lag_36', 'neut_lag_36', 'o2sat_lag_36', 'pco2_lag_36', 'ph_lag_36', 'phos_lag_36', 'plt_lag_36', 'po2_lag_36', 'ptt_lag_36', 'resp_lag_36', 'sbp_lag_36', 'temp_lag_36', 'tnt_lag_36', 'urine_lag_36', 'wbc_lag_36', 'alb_lag_37', 'alp_lag_37', 'alt_lag_37', 'ast_lag_37', 'be_lag_37', 'bicar_lag_37', 'bili_lag_37', 'bili_dir_lag_37', 'bnd_lag_37', 'bun_lag_37', 'ca_lag_37', 'cai_lag_37', 'ck_lag_37', 'ckmb_lag_37', 'cl_lag_37', 'crea_lag_37', 'crp_lag_37', 'dbp_lag_37', 'fgn_lag_37', 'fio2_lag_37', 'glu_lag_37', 'hgb_lag_37', 'hr_lag_37', 'inr_pt_lag_37', 'k_lag_37', 'lact_lag_37', 'lymph_lag_37', 'map_lag_37', 'mch_lag_37', 'mchc_lag_37', 'mcv_lag_37', 'methb_lag_37', 'mg_lag_37', 'na_lag_37', 'neut_lag_37', 'o2sat_lag_37', 'pco2_lag_37', 'ph_lag_37', 'phos_lag_37', 'plt_lag_37', 'po2_lag_37', 'ptt_lag_37', 'resp_lag_37', 'sbp_lag_37', 'temp_lag_37', 'tnt_lag_37', 'urine_lag_37', 'wbc_lag_37', 'alb_lag_38', 'alp_lag_38', 'alt_lag_38', 'ast_lag_38', 'be_lag_38', 'bicar_lag_38', 'bili_lag_38', 'bili_dir_lag_38', 'bnd_lag_38', 'bun_lag_38', 'ca_lag_38', 'cai_lag_38', 'ck_lag_38', 'ckmb_lag_38', 'cl_lag_38', 'crea_lag_38', 'crp_lag_38', 'dbp_lag_38', 'fgn_lag_38', 'fio2_lag_38', 'glu_lag_38', 'hgb_lag_38', 'hr_lag_38', 'inr_pt_lag_38', 'k_lag_38', 'lact_lag_38', 'lymph_lag_38', 'map_lag_38', 'mch_lag_38', 'mchc_lag_38', 'mcv_lag_38', 'methb_lag_38', 'mg_lag_38', 'na_lag_38', 'neut_lag_38', 'o2sat_lag_38', 'pco2_lag_38', 'ph_lag_38', 'phos_lag_38', 'plt_lag_38', 'po2_lag_38', 'ptt_lag_38', 'resp_lag_38', 'sbp_lag_38', 'temp_lag_38', 'tnt_lag_38', 'urine_lag_38', 'wbc_lag_38', 'alb_lag_39', 'alp_lag_39', 'alt_lag_39', 'ast_lag_39', 'be_lag_39', 'bicar_lag_39', 'bili_lag_39', 'bili_dir_lag_39', 'bnd_lag_39', 'bun_lag_39', 'ca_lag_39', 'cai_lag_39', 'ck_lag_39', 'ckmb_lag_39', 'cl_lag_39', 'crea_lag_39', 'crp_lag_39', 'dbp_lag_39', 'fgn_lag_39', 'fio2_lag_39', 'glu_lag_39', 'hgb_lag_39', 'hr_lag_39', 'inr_pt_lag_39', 'k_lag_39', 'lact_lag_39', 'lymph_lag_39', 'map_lag_39', 'mch_lag_39', 'mchc_lag_39', 'mcv_lag_39', 'methb_lag_39', 'mg_lag_39', 'na_lag_39', 'neut_lag_39', 'o2sat_lag_39', 'pco2_lag_39', 'ph_lag_39', 'phos_lag_39', 'plt_lag_39', 'po2_lag_39', 'ptt_lag_39', 'resp_lag_39', 'sbp_lag_39', 'temp_lag_39', 'tnt_lag_39', 'urine_lag_39', 'wbc_lag_39', 'alb_lag_40', 'alp_lag_40', 'alt_lag_40', 'ast_lag_40', 'be_lag_40', 'bicar_lag_40', 'bili_lag_40', 'bili_dir_lag_40', 'bnd_lag_40', 'bun_lag_40', 'ca_lag_40', 'cai_lag_40', 'ck_lag_40', 'ckmb_lag_40', 'cl_lag_40', 'crea_lag_40', 'crp_lag_40', 'dbp_lag_40', 'fgn_lag_40', 'fio2_lag_40', 'glu_lag_40', 'hgb_lag_40', 'hr_lag_40', 'inr_pt_lag_40', 'k_lag_40', 'lact_lag_40', 'lymph_lag_40', 'map_lag_40', 'mch_lag_40', 'mchc_lag_40', 'mcv_lag_40', 'methb_lag_40', 'mg_lag_40', 'na_lag_40', 'neut_lag_40', 'o2sat_lag_40', 'pco2_lag_40', 'ph_lag_40', 'phos_lag_40', 'plt_lag_40', 'po2_lag_40', 'ptt_lag_40', 'resp_lag_40', 'sbp_lag_40', 'temp_lag_40', 'tnt_lag_40', 'urine_lag_40', 'wbc_lag_40', 'alb_lag_41', 'alp_lag_41', 'alt_lag_41', 'ast_lag_41', 'be_lag_41', 'bicar_lag_41', 'bili_lag_41', 'bili_dir_lag_41', 'bnd_lag_41', 'bun_lag_41', 'ca_lag_41', 'cai_lag_41', 'ck_lag_41', 'ckmb_lag_41', 'cl_lag_41', 'crea_lag_41', 'crp_lag_41', 'dbp_lag_41', 'fgn_lag_41', 'fio2_lag_41', 'glu_lag_41', 'hgb_lag_41', 'hr_lag_41', 'inr_pt_lag_41', 'k_lag_41', 'lact_lag_41', 'lymph_lag_41', 'map_lag_41', 'mch_lag_41', 'mchc_lag_41', 'mcv_lag_41', 'methb_lag_41', 'mg_lag_41', 'na_lag_41', 'neut_lag_41', 'o2sat_lag_41', 'pco2_lag_41', 'ph_lag_41', 'phos_lag_41', 'plt_lag_41', 'po2_lag_41', 'ptt_lag_41', 'resp_lag_41', 'sbp_lag_41', 'temp_lag_41', 'tnt_lag_41', 'urine_lag_41', 'wbc_lag_41', 'alb_lag_42', 'alp_lag_42', 'alt_lag_42', 'ast_lag_42', 'be_lag_42', 'bicar_lag_42', 'bili_lag_42', 'bili_dir_lag_42', 'bnd_lag_42', 'bun_lag_42', 'ca_lag_42', 'cai_lag_42', 'ck_lag_42', 'ckmb_lag_42', 'cl_lag_42', 'crea_lag_42', 'crp_lag_42', 'dbp_lag_42', 'fgn_lag_42', 'fio2_lag_42', 'glu_lag_42', 'hgb_lag_42', 'hr_lag_42', 'inr_pt_lag_42', 'k_lag_42', 'lact_lag_42', 'lymph_lag_42', 'map_lag_42', 'mch_lag_42', 'mchc_lag_42', 'mcv_lag_42', 'methb_lag_42', 'mg_lag_42', 'na_lag_42', 'neut_lag_42', 'o2sat_lag_42', 'pco2_lag_42', 'ph_lag_42', 'phos_lag_42', 'plt_lag_42', 'po2_lag_42', 'ptt_lag_42', 'resp_lag_42', 'sbp_lag_42', 'temp_lag_42', 'tnt_lag_42', 'urine_lag_42', 'wbc_lag_42', 'alb_lag_43', 'alp_lag_43', 'alt_lag_43', 'ast_lag_43', 'be_lag_43', 'bicar_lag_43', 'bili_lag_43', 'bili_dir_lag_43', 'bnd_lag_43', 'bun_lag_43', 'ca_lag_43', 'cai_lag_43', 'ck_lag_43', 'ckmb_lag_43', 'cl_lag_43', 'crea_lag_43', 'crp_lag_43', 'dbp_lag_43', 'fgn_lag_43', 'fio2_lag_43', 'glu_lag_43', 'hgb_lag_43', 'hr_lag_43', 'inr_pt_lag_43', 'k_lag_43', 'lact_lag_43', 'lymph_lag_43', 'map_lag_43', 'mch_lag_43', 'mchc_lag_43', 'mcv_lag_43', 'methb_lag_43', 'mg_lag_43', 'na_lag_43', 'neut_lag_43', 'o2sat_lag_43', 'pco2_lag_43', 'ph_lag_43', 'phos_lag_43', 'plt_lag_43', 'po2_lag_43', 'ptt_lag_43', 'resp_lag_43', 'sbp_lag_43', 'temp_lag_43', 'tnt_lag_43', 'urine_lag_43', 'wbc_lag_43', 'alb_lag_44', 'alp_lag_44', 'alt_lag_44', 'ast_lag_44', 'be_lag_44', 'bicar_lag_44', 'bili_lag_44', 'bili_dir_lag_44', 'bnd_lag_44', 'bun_lag_44', 'ca_lag_44', 'cai_lag_44', 'ck_lag_44', 'ckmb_lag_44', 'cl_lag_44', 'crea_lag_44', 'crp_lag_44', 'dbp_lag_44', 'fgn_lag_44', 'fio2_lag_44', 'glu_lag_44', 'hgb_lag_44', 'hr_lag_44', 'inr_pt_lag_44', 'k_lag_44', 'lact_lag_44', 'lymph_lag_44', 'map_lag_44', 'mch_lag_44', 'mchc_lag_44', 'mcv_lag_44', 'methb_lag_44', 'mg_lag_44', 'na_lag_44', 'neut_lag_44', 'o2sat_lag_44', 'pco2_lag_44', 'ph_lag_44', 'phos_lag_44', 'plt_lag_44', 'po2_lag_44', 'ptt_lag_44', 'resp_lag_44', 'sbp_lag_44', 'temp_lag_44', 'tnt_lag_44', 'urine_lag_44', 'wbc_lag_44', 'alb_lag_45', 'alp_lag_45', 'alt_lag_45', 'ast_lag_45', 'be_lag_45', 'bicar_lag_45', 'bili_lag_45', 'bili_dir_lag_45', 'bnd_lag_45', 'bun_lag_45', 'ca_lag_45', 'cai_lag_45', 'ck_lag_45', 'ckmb_lag_45', 'cl_lag_45', 'crea_lag_45', 'crp_lag_45', 'dbp_lag_45', 'fgn_lag_45', 'fio2_lag_45', 'glu_lag_45', 'hgb_lag_45', 'hr_lag_45', 'inr_pt_lag_45', 'k_lag_45', 'lact_lag_45', 'lymph_lag_45', 'map_lag_45', 'mch_lag_45', 'mchc_lag_45', 'mcv_lag_45', 'methb_lag_45', 'mg_lag_45', 'na_lag_45', 'neut_lag_45', 'o2sat_lag_45', 'pco2_lag_45', 'ph_lag_45', 'phos_lag_45', 'plt_lag_45', 'po2_lag_45', 'ptt_lag_45', 'resp_lag_45', 'sbp_lag_45', 'temp_lag_45', 'tnt_lag_45', 'urine_lag_45', 'wbc_lag_45', 'alb_lag_46', 'alp_lag_46', 'alt_lag_46', 'ast_lag_46', 'be_lag_46', 'bicar_lag_46', 'bili_lag_46', 'bili_dir_lag_46', 'bnd_lag_46', 'bun_lag_46', 'ca_lag_46', 'cai_lag_46', 'ck_lag_46', 'ckmb_lag_46', 'cl_lag_46', 'crea_lag_46', 'crp_lag_46', 'dbp_lag_46', 'fgn_lag_46', 'fio2_lag_46', 'glu_lag_46', 'hgb_lag_46', 'hr_lag_46', 'inr_pt_lag_46', 'k_lag_46', 'lact_lag_46', 'lymph_lag_46', 'map_lag_46', 'mch_lag_46', 'mchc_lag_46', 'mcv_lag_46', 'methb_lag_46', 'mg_lag_46', 'na_lag_46', 'neut_lag_46', 'o2sat_lag_46', 'pco2_lag_46', 'ph_lag_46', 'phos_lag_46', 'plt_lag_46', 'po2_lag_46', 'ptt_lag_46', 'resp_lag_46', 'sbp_lag_46', 'temp_lag_46', 'tnt_lag_46', 'urine_lag_46', 'wbc_lag_46', 'alb_lag_47', 'alp_lag_47', 'alt_lag_47', 'ast_lag_47', 'be_lag_47', 'bicar_lag_47', 'bili_lag_47', 'bili_dir_lag_47', 'bnd_lag_47', 'bun_lag_47', 'ca_lag_47', 'cai_lag_47', 'ck_lag_47', 'ckmb_lag_47', 'cl_lag_47', 'crea_lag_47', 'crp_lag_47', 'dbp_lag_47', 'fgn_lag_47', 'fio2_lag_47', 'glu_lag_47', 'hgb_lag_47', 'hr_lag_47', 'inr_pt_lag_47', 'k_lag_47', 'lact_lag_47', 'lymph_lag_47', 'map_lag_47', 'mch_lag_47', 'mchc_lag_47', 'mcv_lag_47', 'methb_lag_47', 'mg_lag_47', 'na_lag_47', 'neut_lag_47', 'o2sat_lag_47', 'pco2_lag_47', 'ph_lag_47', 'phos_lag_47', 'plt_lag_47', 'po2_lag_47', 'ptt_lag_47', 'resp_lag_47', 'sbp_lag_47', 'temp_lag_47', 'tnt_lag_47', 'urine_lag_47', 'wbc_lag_47', 'alb_lag_48', 'alp_lag_48', 'alt_lag_48', 'ast_lag_48', 'be_lag_48', 'bicar_lag_48', 'bili_lag_48', 'bili_dir_lag_48', 'bnd_lag_48', 'bun_lag_48', 'ca_lag_48', 'cai_lag_48', 'ck_lag_48', 'ckmb_lag_48', 'cl_lag_48', 'crea_lag_48', 'crp_lag_48', 'dbp_lag_48', 'fgn_lag_48', 'fio2_lag_48', 'glu_lag_48', 'hgb_lag_48', 'hr_lag_48', 'inr_pt_lag_48', 'k_lag_48', 'lact_lag_48', 'lymph_lag_48', 'map_lag_48', 'mch_lag_48', 'mchc_lag_48', 'mcv_lag_48', 'methb_lag_48', 'mg_lag_48', 'na_lag_48', 'neut_lag_48', 'o2sat_lag_48', 'pco2_lag_48', 'ph_lag_48', 'phos_lag_48', 'plt_lag_48', 'po2_lag_48', 'ptt_lag_48', 'resp_lag_48', 'sbp_lag_48', 'temp_lag_48', 'tnt_lag_48', 'urine_lag_48', 'wbc_lag_48'] -PredictionDatasetTFTpytorch.add_relative_time_idx=False -PredictionDatasetTFTpytorch.target="label" +PredictionDatasetpytorch.add_relative_time_idx=False +PredictionDatasetpytorch.target="label" -PredictionDatasetTFTpytorch.time_varying_unknown_reals=[ +PredictionDatasetpytorch.time_varying_unknown_reals=[ "label", ] -PredictionDatasetTFTpytorch.time_varying_unknown_categoricals=[] -PredictionDatasetTFTpytorch.lagged_variables=["alb", +PredictionDatasetpytorch.time_varying_unknown_categoricals=[] +PredictionDatasetpytorch.lagged_variables=["alb", "alp", "alt", "ast", diff --git a/configs/prediction_models/TFTpytorch.gin b/configs/prediction_models/TFTpytorch.gin index ebd26e4e..94a42740 100644 --- a/configs/prediction_models/TFTpytorch.gin +++ b/configs/prediction_models/TFTpytorch.gin @@ -21,13 +21,13 @@ model/hyperparameter.lr_scheduler = "exponential" # Dataset params -PredictionDatasetTFTpytorch.max_encoder_length = 24 -PredictionDatasetTFTpytorch.max_prediction_length = 1 -PredictionDatasetTFTpytorch.target="label" -PredictionDatasetTFTpytorch.time_varying_known_reals=["time_idx"] -PredictionDatasetTFTpytorch.add_relative_time_idx=False -PredictionDatasetTFTpytorch.time_varying_unknown_categoricals=[] -PredictionDatasetTFTpytorch.time_varying_unknown_reals=["alb", +PredictionDatasetpytorch.max_encoder_length = 24 +PredictionDatasetpytorch.max_prediction_length = 1 +PredictionDatasetpytorch.target="label" +PredictionDatasetpytorch.time_varying_known_reals=["time_idx"] +PredictionDatasetpytorch.add_relative_time_idx=False +PredictionDatasetpytorch.time_varying_unknown_categoricals=[] +PredictionDatasetpytorch.time_varying_unknown_reals=["alb", "alp", "alt", "ast", @@ -75,4 +75,4 @@ PredictionDatasetTFTpytorch.time_varying_unknown_reals=["alb", "tnt", "urine", "wbc",] -PredictionDatasetTFTpytorch.lagged_variables=[] +PredictionDatasetpytorch.lagged_variables=[] From 2defb3799bca3f2bbec504d012f947997434e3b1 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Mon, 20 Nov 2023 15:23:17 +0100 Subject: [PATCH 110/142] changes to accomadte for los attribution, code more reusable now --- configs/prediction_models/DeepARpytorch.gin | 2 +- icu_benchmarks/models/train.py | 85 ++++++++-------- icu_benchmarks/models/wrappers.py | 101 ++++++++++---------- 3 files changed, 95 insertions(+), 93 deletions(-) diff --git a/configs/prediction_models/DeepARpytorch.gin b/configs/prediction_models/DeepARpytorch.gin index 27652b96..bb18cbd0 100644 --- a/configs/prediction_models/DeepARpytorch.gin +++ b/configs/prediction_models/DeepARpytorch.gin @@ -27,7 +27,7 @@ PredictionDatasetpytorch.max_prediction_length = 24 PredictionDatasetpytorch.time_varying_known_reals=['alb_lag_24', 'alp_lag_24', 'alt_lag_24', 'ast_lag_24', 'be_lag_24', 'bicar_lag_24', 'bili_lag_24', 'bili_dir_lag_24', 'bnd_lag_24', 'bun_lag_24', 'ca_lag_24', 'cai_lag_24', 'ck_lag_24', 'ckmb_lag_24', 'cl_lag_24', 'crea_lag_24', 'crp_lag_24', 'dbp_lag_24', 'fgn_lag_24', 'fio2_lag_24', 'glu_lag_24', 'hgb_lag_24', 'hr_lag_24', 'inr_pt_lag_24', 'k_lag_24', 'lact_lag_24', 'lymph_lag_24', 'map_lag_24', 'mch_lag_24', 'mchc_lag_24', 'mcv_lag_24', 'methb_lag_24', 'mg_lag_24', 'na_lag_24', 'neut_lag_24', 'o2sat_lag_24', 'pco2_lag_24', 'ph_lag_24', 'phos_lag_24', 'plt_lag_24', 'po2_lag_24', 'ptt_lag_24', 'resp_lag_24', 'sbp_lag_24', 'temp_lag_24', 'tnt_lag_24', 'urine_lag_24', 'wbc_lag_24', 'alb_lag_25', 'alp_lag_25', 'alt_lag_25', 'ast_lag_25', 'be_lag_25', 'bicar_lag_25', 'bili_lag_25', 'bili_dir_lag_25', 'bnd_lag_25', 'bun_lag_25', 'ca_lag_25', 'cai_lag_25', 'ck_lag_25', 'ckmb_lag_25', 'cl_lag_25', 'crea_lag_25', 'crp_lag_25', 'dbp_lag_25', 'fgn_lag_25', 'fio2_lag_25', 'glu_lag_25', 'hgb_lag_25', 'hr_lag_25', 'inr_pt_lag_25', 'k_lag_25', 'lact_lag_25', 'lymph_lag_25', 'map_lag_25', 'mch_lag_25', 'mchc_lag_25', 'mcv_lag_25', 'methb_lag_25', 'mg_lag_25', 'na_lag_25', 'neut_lag_25', 'o2sat_lag_25', 'pco2_lag_25', 'ph_lag_25', 'phos_lag_25', 'plt_lag_25', 'po2_lag_25', 'ptt_lag_25', 'resp_lag_25', 'sbp_lag_25', 'temp_lag_25', 'tnt_lag_25', 'urine_lag_25', 'wbc_lag_25', 'alb_lag_26', 'alp_lag_26', 'alt_lag_26', 'ast_lag_26', 'be_lag_26', 'bicar_lag_26', 'bili_lag_26', 'bili_dir_lag_26', 'bnd_lag_26', 'bun_lag_26', 'ca_lag_26', 'cai_lag_26', 'ck_lag_26', 'ckmb_lag_26', 'cl_lag_26', 'crea_lag_26', 'crp_lag_26', 'dbp_lag_26', 'fgn_lag_26', 'fio2_lag_26', 'glu_lag_26', 'hgb_lag_26', 'hr_lag_26', 'inr_pt_lag_26', 'k_lag_26', 'lact_lag_26', 'lymph_lag_26', 'map_lag_26', 'mch_lag_26', 'mchc_lag_26', 'mcv_lag_26', 'methb_lag_26', 'mg_lag_26', 'na_lag_26', 'neut_lag_26', 'o2sat_lag_26', 'pco2_lag_26', 'ph_lag_26', 'phos_lag_26', 'plt_lag_26', 'po2_lag_26', 'ptt_lag_26', 'resp_lag_26', 'sbp_lag_26', 'temp_lag_26', 'tnt_lag_26', 'urine_lag_26', 'wbc_lag_26', 'alb_lag_27', 'alp_lag_27', 'alt_lag_27', 'ast_lag_27', 'be_lag_27', 'bicar_lag_27', 'bili_lag_27', 'bili_dir_lag_27', 'bnd_lag_27', 'bun_lag_27', 'ca_lag_27', 'cai_lag_27', 'ck_lag_27', 'ckmb_lag_27', 'cl_lag_27', 'crea_lag_27', 'crp_lag_27', 'dbp_lag_27', 'fgn_lag_27', 'fio2_lag_27', 'glu_lag_27', 'hgb_lag_27', 'hr_lag_27', 'inr_pt_lag_27', 'k_lag_27', 'lact_lag_27', 'lymph_lag_27', 'map_lag_27', 'mch_lag_27', 'mchc_lag_27', 'mcv_lag_27', 'methb_lag_27', 'mg_lag_27', 'na_lag_27', 'neut_lag_27', 'o2sat_lag_27', 'pco2_lag_27', 'ph_lag_27', 'phos_lag_27', 'plt_lag_27', 'po2_lag_27', 'ptt_lag_27', 'resp_lag_27', 'sbp_lag_27', 'temp_lag_27', 'tnt_lag_27', 'urine_lag_27', 'wbc_lag_27', 'alb_lag_28', 'alp_lag_28', 'alt_lag_28', 'ast_lag_28', 'be_lag_28', 'bicar_lag_28', 'bili_lag_28', 'bili_dir_lag_28', 'bnd_lag_28', 'bun_lag_28', 'ca_lag_28', 'cai_lag_28', 'ck_lag_28', 'ckmb_lag_28', 'cl_lag_28', 'crea_lag_28', 'crp_lag_28', 'dbp_lag_28', 'fgn_lag_28', 'fio2_lag_28', 'glu_lag_28', 'hgb_lag_28', 'hr_lag_28', 'inr_pt_lag_28', 'k_lag_28', 'lact_lag_28', 'lymph_lag_28', 'map_lag_28', 'mch_lag_28', 'mchc_lag_28', 'mcv_lag_28', 'methb_lag_28', 'mg_lag_28', 'na_lag_28', 'neut_lag_28', 'o2sat_lag_28', 'pco2_lag_28', 'ph_lag_28', 'phos_lag_28', 'plt_lag_28', 'po2_lag_28', 'ptt_lag_28', 'resp_lag_28', 'sbp_lag_28', 'temp_lag_28', 'tnt_lag_28', 'urine_lag_28', 'wbc_lag_28', 'alb_lag_29', 'alp_lag_29', 'alt_lag_29', 'ast_lag_29', 'be_lag_29', 'bicar_lag_29', 'bili_lag_29', 'bili_dir_lag_29', 'bnd_lag_29', 'bun_lag_29', 'ca_lag_29', 'cai_lag_29', 'ck_lag_29', 'ckmb_lag_29', 'cl_lag_29', 'crea_lag_29', 'crp_lag_29', 'dbp_lag_29', 'fgn_lag_29', 'fio2_lag_29', 'glu_lag_29', 'hgb_lag_29', 'hr_lag_29', 'inr_pt_lag_29', 'k_lag_29', 'lact_lag_29', 'lymph_lag_29', 'map_lag_29', 'mch_lag_29', 'mchc_lag_29', 'mcv_lag_29', 'methb_lag_29', 'mg_lag_29', 'na_lag_29', 'neut_lag_29', 'o2sat_lag_29', 'pco2_lag_29', 'ph_lag_29', 'phos_lag_29', 'plt_lag_29', 'po2_lag_29', 'ptt_lag_29', 'resp_lag_29', 'sbp_lag_29', 'temp_lag_29', 'tnt_lag_29', 'urine_lag_29', 'wbc_lag_29', 'alb_lag_30', 'alp_lag_30', 'alt_lag_30', 'ast_lag_30', 'be_lag_30', 'bicar_lag_30', 'bili_lag_30', 'bili_dir_lag_30', 'bnd_lag_30', 'bun_lag_30', 'ca_lag_30', 'cai_lag_30', 'ck_lag_30', 'ckmb_lag_30', 'cl_lag_30', 'crea_lag_30', 'crp_lag_30', 'dbp_lag_30', 'fgn_lag_30', 'fio2_lag_30', 'glu_lag_30', 'hgb_lag_30', 'hr_lag_30', 'inr_pt_lag_30', 'k_lag_30', 'lact_lag_30', 'lymph_lag_30', 'map_lag_30', 'mch_lag_30', 'mchc_lag_30', 'mcv_lag_30', 'methb_lag_30', 'mg_lag_30', 'na_lag_30', 'neut_lag_30', 'o2sat_lag_30', 'pco2_lag_30', 'ph_lag_30', 'phos_lag_30', 'plt_lag_30', 'po2_lag_30', 'ptt_lag_30', 'resp_lag_30', 'sbp_lag_30', 'temp_lag_30', 'tnt_lag_30', 'urine_lag_30', 'wbc_lag_30', 'alb_lag_31', 'alp_lag_31', 'alt_lag_31', 'ast_lag_31', 'be_lag_31', 'bicar_lag_31', 'bili_lag_31', 'bili_dir_lag_31', 'bnd_lag_31', 'bun_lag_31', 'ca_lag_31', 'cai_lag_31', 'ck_lag_31', 'ckmb_lag_31', 'cl_lag_31', 'crea_lag_31', 'crp_lag_31', 'dbp_lag_31', 'fgn_lag_31', 'fio2_lag_31', 'glu_lag_31', 'hgb_lag_31', 'hr_lag_31', 'inr_pt_lag_31', 'k_lag_31', 'lact_lag_31', 'lymph_lag_31', 'map_lag_31', 'mch_lag_31', 'mchc_lag_31', 'mcv_lag_31', 'methb_lag_31', 'mg_lag_31', 'na_lag_31', 'neut_lag_31', 'o2sat_lag_31', 'pco2_lag_31', 'ph_lag_31', 'phos_lag_31', 'plt_lag_31', 'po2_lag_31', 'ptt_lag_31', 'resp_lag_31', 'sbp_lag_31', 'temp_lag_31', 'tnt_lag_31', 'urine_lag_31', 'wbc_lag_31', 'alb_lag_32', 'alp_lag_32', 'alt_lag_32', 'ast_lag_32', 'be_lag_32', 'bicar_lag_32', 'bili_lag_32', 'bili_dir_lag_32', 'bnd_lag_32', 'bun_lag_32', 'ca_lag_32', 'cai_lag_32', 'ck_lag_32', 'ckmb_lag_32', 'cl_lag_32', 'crea_lag_32', 'crp_lag_32', 'dbp_lag_32', 'fgn_lag_32', 'fio2_lag_32', 'glu_lag_32', 'hgb_lag_32', 'hr_lag_32', 'inr_pt_lag_32', 'k_lag_32', 'lact_lag_32', 'lymph_lag_32', 'map_lag_32', 'mch_lag_32', 'mchc_lag_32', 'mcv_lag_32', 'methb_lag_32', 'mg_lag_32', 'na_lag_32', 'neut_lag_32', 'o2sat_lag_32', 'pco2_lag_32', 'ph_lag_32', 'phos_lag_32', 'plt_lag_32', 'po2_lag_32', 'ptt_lag_32', 'resp_lag_32', 'sbp_lag_32', 'temp_lag_32', 'tnt_lag_32', 'urine_lag_32', 'wbc_lag_32', 'alb_lag_33', 'alp_lag_33', 'alt_lag_33', 'ast_lag_33', 'be_lag_33', 'bicar_lag_33', 'bili_lag_33', 'bili_dir_lag_33', 'bnd_lag_33', 'bun_lag_33', 'ca_lag_33', 'cai_lag_33', 'ck_lag_33', 'ckmb_lag_33', 'cl_lag_33', 'crea_lag_33', 'crp_lag_33', 'dbp_lag_33', 'fgn_lag_33', 'fio2_lag_33', 'glu_lag_33', 'hgb_lag_33', 'hr_lag_33', 'inr_pt_lag_33', 'k_lag_33', 'lact_lag_33', 'lymph_lag_33', 'map_lag_33', 'mch_lag_33', 'mchc_lag_33', 'mcv_lag_33', 'methb_lag_33', 'mg_lag_33', 'na_lag_33', 'neut_lag_33', 'o2sat_lag_33', 'pco2_lag_33', 'ph_lag_33', 'phos_lag_33', 'plt_lag_33', 'po2_lag_33', 'ptt_lag_33', 'resp_lag_33', 'sbp_lag_33', 'temp_lag_33', 'tnt_lag_33', 'urine_lag_33', 'wbc_lag_33', 'alb_lag_34', 'alp_lag_34', 'alt_lag_34', 'ast_lag_34', 'be_lag_34', 'bicar_lag_34', 'bili_lag_34', 'bili_dir_lag_34', 'bnd_lag_34', 'bun_lag_34', 'ca_lag_34', 'cai_lag_34', 'ck_lag_34', 'ckmb_lag_34', 'cl_lag_34', 'crea_lag_34', 'crp_lag_34', 'dbp_lag_34', 'fgn_lag_34', 'fio2_lag_34', 'glu_lag_34', 'hgb_lag_34', 'hr_lag_34', 'inr_pt_lag_34', 'k_lag_34', 'lact_lag_34', 'lymph_lag_34', 'map_lag_34', 'mch_lag_34', 'mchc_lag_34', 'mcv_lag_34', 'methb_lag_34', 'mg_lag_34', 'na_lag_34', 'neut_lag_34', 'o2sat_lag_34', 'pco2_lag_34', 'ph_lag_34', 'phos_lag_34', 'plt_lag_34', 'po2_lag_34', 'ptt_lag_34', 'resp_lag_34', 'sbp_lag_34', 'temp_lag_34', 'tnt_lag_34', 'urine_lag_34', 'wbc_lag_34', 'alb_lag_35', 'alp_lag_35', 'alt_lag_35', 'ast_lag_35', 'be_lag_35', 'bicar_lag_35', 'bili_lag_35', 'bili_dir_lag_35', 'bnd_lag_35', 'bun_lag_35', 'ca_lag_35', 'cai_lag_35', 'ck_lag_35', 'ckmb_lag_35', 'cl_lag_35', 'crea_lag_35', 'crp_lag_35', 'dbp_lag_35', 'fgn_lag_35', 'fio2_lag_35', 'glu_lag_35', 'hgb_lag_35', 'hr_lag_35', 'inr_pt_lag_35', 'k_lag_35', 'lact_lag_35', 'lymph_lag_35', 'map_lag_35', 'mch_lag_35', 'mchc_lag_35', 'mcv_lag_35', 'methb_lag_35', 'mg_lag_35', 'na_lag_35', 'neut_lag_35', 'o2sat_lag_35', 'pco2_lag_35', 'ph_lag_35', 'phos_lag_35', 'plt_lag_35', 'po2_lag_35', 'ptt_lag_35', 'resp_lag_35', 'sbp_lag_35', 'temp_lag_35', 'tnt_lag_35', 'urine_lag_35', 'wbc_lag_35', 'alb_lag_36', 'alp_lag_36', 'alt_lag_36', 'ast_lag_36', 'be_lag_36', 'bicar_lag_36', 'bili_lag_36', 'bili_dir_lag_36', 'bnd_lag_36', 'bun_lag_36', 'ca_lag_36', 'cai_lag_36', 'ck_lag_36', 'ckmb_lag_36', 'cl_lag_36', 'crea_lag_36', 'crp_lag_36', 'dbp_lag_36', 'fgn_lag_36', 'fio2_lag_36', 'glu_lag_36', 'hgb_lag_36', 'hr_lag_36', 'inr_pt_lag_36', 'k_lag_36', 'lact_lag_36', 'lymph_lag_36', 'map_lag_36', 'mch_lag_36', 'mchc_lag_36', 'mcv_lag_36', 'methb_lag_36', 'mg_lag_36', 'na_lag_36', 'neut_lag_36', 'o2sat_lag_36', 'pco2_lag_36', 'ph_lag_36', 'phos_lag_36', 'plt_lag_36', 'po2_lag_36', 'ptt_lag_36', 'resp_lag_36', 'sbp_lag_36', 'temp_lag_36', 'tnt_lag_36', 'urine_lag_36', 'wbc_lag_36', 'alb_lag_37', 'alp_lag_37', 'alt_lag_37', 'ast_lag_37', 'be_lag_37', 'bicar_lag_37', 'bili_lag_37', 'bili_dir_lag_37', 'bnd_lag_37', 'bun_lag_37', 'ca_lag_37', 'cai_lag_37', 'ck_lag_37', 'ckmb_lag_37', 'cl_lag_37', 'crea_lag_37', 'crp_lag_37', 'dbp_lag_37', 'fgn_lag_37', 'fio2_lag_37', 'glu_lag_37', 'hgb_lag_37', 'hr_lag_37', 'inr_pt_lag_37', 'k_lag_37', 'lact_lag_37', 'lymph_lag_37', 'map_lag_37', 'mch_lag_37', 'mchc_lag_37', 'mcv_lag_37', 'methb_lag_37', 'mg_lag_37', 'na_lag_37', 'neut_lag_37', 'o2sat_lag_37', 'pco2_lag_37', 'ph_lag_37', 'phos_lag_37', 'plt_lag_37', 'po2_lag_37', 'ptt_lag_37', 'resp_lag_37', 'sbp_lag_37', 'temp_lag_37', 'tnt_lag_37', 'urine_lag_37', 'wbc_lag_37', 'alb_lag_38', 'alp_lag_38', 'alt_lag_38', 'ast_lag_38', 'be_lag_38', 'bicar_lag_38', 'bili_lag_38', 'bili_dir_lag_38', 'bnd_lag_38', 'bun_lag_38', 'ca_lag_38', 'cai_lag_38', 'ck_lag_38', 'ckmb_lag_38', 'cl_lag_38', 'crea_lag_38', 'crp_lag_38', 'dbp_lag_38', 'fgn_lag_38', 'fio2_lag_38', 'glu_lag_38', 'hgb_lag_38', 'hr_lag_38', 'inr_pt_lag_38', 'k_lag_38', 'lact_lag_38', 'lymph_lag_38', 'map_lag_38', 'mch_lag_38', 'mchc_lag_38', 'mcv_lag_38', 'methb_lag_38', 'mg_lag_38', 'na_lag_38', 'neut_lag_38', 'o2sat_lag_38', 'pco2_lag_38', 'ph_lag_38', 'phos_lag_38', 'plt_lag_38', 'po2_lag_38', 'ptt_lag_38', 'resp_lag_38', 'sbp_lag_38', 'temp_lag_38', 'tnt_lag_38', 'urine_lag_38', 'wbc_lag_38', 'alb_lag_39', 'alp_lag_39', 'alt_lag_39', 'ast_lag_39', 'be_lag_39', 'bicar_lag_39', 'bili_lag_39', 'bili_dir_lag_39', 'bnd_lag_39', 'bun_lag_39', 'ca_lag_39', 'cai_lag_39', 'ck_lag_39', 'ckmb_lag_39', 'cl_lag_39', 'crea_lag_39', 'crp_lag_39', 'dbp_lag_39', 'fgn_lag_39', 'fio2_lag_39', 'glu_lag_39', 'hgb_lag_39', 'hr_lag_39', 'inr_pt_lag_39', 'k_lag_39', 'lact_lag_39', 'lymph_lag_39', 'map_lag_39', 'mch_lag_39', 'mchc_lag_39', 'mcv_lag_39', 'methb_lag_39', 'mg_lag_39', 'na_lag_39', 'neut_lag_39', 'o2sat_lag_39', 'pco2_lag_39', 'ph_lag_39', 'phos_lag_39', 'plt_lag_39', 'po2_lag_39', 'ptt_lag_39', 'resp_lag_39', 'sbp_lag_39', 'temp_lag_39', 'tnt_lag_39', 'urine_lag_39', 'wbc_lag_39', 'alb_lag_40', 'alp_lag_40', 'alt_lag_40', 'ast_lag_40', 'be_lag_40', 'bicar_lag_40', 'bili_lag_40', 'bili_dir_lag_40', 'bnd_lag_40', 'bun_lag_40', 'ca_lag_40', 'cai_lag_40', 'ck_lag_40', 'ckmb_lag_40', 'cl_lag_40', 'crea_lag_40', 'crp_lag_40', 'dbp_lag_40', 'fgn_lag_40', 'fio2_lag_40', 'glu_lag_40', 'hgb_lag_40', 'hr_lag_40', 'inr_pt_lag_40', 'k_lag_40', 'lact_lag_40', 'lymph_lag_40', 'map_lag_40', 'mch_lag_40', 'mchc_lag_40', 'mcv_lag_40', 'methb_lag_40', 'mg_lag_40', 'na_lag_40', 'neut_lag_40', 'o2sat_lag_40', 'pco2_lag_40', 'ph_lag_40', 'phos_lag_40', 'plt_lag_40', 'po2_lag_40', 'ptt_lag_40', 'resp_lag_40', 'sbp_lag_40', 'temp_lag_40', 'tnt_lag_40', 'urine_lag_40', 'wbc_lag_40', 'alb_lag_41', 'alp_lag_41', 'alt_lag_41', 'ast_lag_41', 'be_lag_41', 'bicar_lag_41', 'bili_lag_41', 'bili_dir_lag_41', 'bnd_lag_41', 'bun_lag_41', 'ca_lag_41', 'cai_lag_41', 'ck_lag_41', 'ckmb_lag_41', 'cl_lag_41', 'crea_lag_41', 'crp_lag_41', 'dbp_lag_41', 'fgn_lag_41', 'fio2_lag_41', 'glu_lag_41', 'hgb_lag_41', 'hr_lag_41', 'inr_pt_lag_41', 'k_lag_41', 'lact_lag_41', 'lymph_lag_41', 'map_lag_41', 'mch_lag_41', 'mchc_lag_41', 'mcv_lag_41', 'methb_lag_41', 'mg_lag_41', 'na_lag_41', 'neut_lag_41', 'o2sat_lag_41', 'pco2_lag_41', 'ph_lag_41', 'phos_lag_41', 'plt_lag_41', 'po2_lag_41', 'ptt_lag_41', 'resp_lag_41', 'sbp_lag_41', 'temp_lag_41', 'tnt_lag_41', 'urine_lag_41', 'wbc_lag_41', 'alb_lag_42', 'alp_lag_42', 'alt_lag_42', 'ast_lag_42', 'be_lag_42', 'bicar_lag_42', 'bili_lag_42', 'bili_dir_lag_42', 'bnd_lag_42', 'bun_lag_42', 'ca_lag_42', 'cai_lag_42', 'ck_lag_42', 'ckmb_lag_42', 'cl_lag_42', 'crea_lag_42', 'crp_lag_42', 'dbp_lag_42', 'fgn_lag_42', 'fio2_lag_42', 'glu_lag_42', 'hgb_lag_42', 'hr_lag_42', 'inr_pt_lag_42', 'k_lag_42', 'lact_lag_42', 'lymph_lag_42', 'map_lag_42', 'mch_lag_42', 'mchc_lag_42', 'mcv_lag_42', 'methb_lag_42', 'mg_lag_42', 'na_lag_42', 'neut_lag_42', 'o2sat_lag_42', 'pco2_lag_42', 'ph_lag_42', 'phos_lag_42', 'plt_lag_42', 'po2_lag_42', 'ptt_lag_42', 'resp_lag_42', 'sbp_lag_42', 'temp_lag_42', 'tnt_lag_42', 'urine_lag_42', 'wbc_lag_42', 'alb_lag_43', 'alp_lag_43', 'alt_lag_43', 'ast_lag_43', 'be_lag_43', 'bicar_lag_43', 'bili_lag_43', 'bili_dir_lag_43', 'bnd_lag_43', 'bun_lag_43', 'ca_lag_43', 'cai_lag_43', 'ck_lag_43', 'ckmb_lag_43', 'cl_lag_43', 'crea_lag_43', 'crp_lag_43', 'dbp_lag_43', 'fgn_lag_43', 'fio2_lag_43', 'glu_lag_43', 'hgb_lag_43', 'hr_lag_43', 'inr_pt_lag_43', 'k_lag_43', 'lact_lag_43', 'lymph_lag_43', 'map_lag_43', 'mch_lag_43', 'mchc_lag_43', 'mcv_lag_43', 'methb_lag_43', 'mg_lag_43', 'na_lag_43', 'neut_lag_43', 'o2sat_lag_43', 'pco2_lag_43', 'ph_lag_43', 'phos_lag_43', 'plt_lag_43', 'po2_lag_43', 'ptt_lag_43', 'resp_lag_43', 'sbp_lag_43', 'temp_lag_43', 'tnt_lag_43', 'urine_lag_43', 'wbc_lag_43', 'alb_lag_44', 'alp_lag_44', 'alt_lag_44', 'ast_lag_44', 'be_lag_44', 'bicar_lag_44', 'bili_lag_44', 'bili_dir_lag_44', 'bnd_lag_44', 'bun_lag_44', 'ca_lag_44', 'cai_lag_44', 'ck_lag_44', 'ckmb_lag_44', 'cl_lag_44', 'crea_lag_44', 'crp_lag_44', 'dbp_lag_44', 'fgn_lag_44', 'fio2_lag_44', 'glu_lag_44', 'hgb_lag_44', 'hr_lag_44', 'inr_pt_lag_44', 'k_lag_44', 'lact_lag_44', 'lymph_lag_44', 'map_lag_44', 'mch_lag_44', 'mchc_lag_44', 'mcv_lag_44', 'methb_lag_44', 'mg_lag_44', 'na_lag_44', 'neut_lag_44', 'o2sat_lag_44', 'pco2_lag_44', 'ph_lag_44', 'phos_lag_44', 'plt_lag_44', 'po2_lag_44', 'ptt_lag_44', 'resp_lag_44', 'sbp_lag_44', 'temp_lag_44', 'tnt_lag_44', 'urine_lag_44', 'wbc_lag_44', 'alb_lag_45', 'alp_lag_45', 'alt_lag_45', 'ast_lag_45', 'be_lag_45', 'bicar_lag_45', 'bili_lag_45', 'bili_dir_lag_45', 'bnd_lag_45', 'bun_lag_45', 'ca_lag_45', 'cai_lag_45', 'ck_lag_45', 'ckmb_lag_45', 'cl_lag_45', 'crea_lag_45', 'crp_lag_45', 'dbp_lag_45', 'fgn_lag_45', 'fio2_lag_45', 'glu_lag_45', 'hgb_lag_45', 'hr_lag_45', 'inr_pt_lag_45', 'k_lag_45', 'lact_lag_45', 'lymph_lag_45', 'map_lag_45', 'mch_lag_45', 'mchc_lag_45', 'mcv_lag_45', 'methb_lag_45', 'mg_lag_45', 'na_lag_45', 'neut_lag_45', 'o2sat_lag_45', 'pco2_lag_45', 'ph_lag_45', 'phos_lag_45', 'plt_lag_45', 'po2_lag_45', 'ptt_lag_45', 'resp_lag_45', 'sbp_lag_45', 'temp_lag_45', 'tnt_lag_45', 'urine_lag_45', 'wbc_lag_45', 'alb_lag_46', 'alp_lag_46', 'alt_lag_46', 'ast_lag_46', 'be_lag_46', 'bicar_lag_46', 'bili_lag_46', 'bili_dir_lag_46', 'bnd_lag_46', 'bun_lag_46', 'ca_lag_46', 'cai_lag_46', 'ck_lag_46', 'ckmb_lag_46', 'cl_lag_46', 'crea_lag_46', 'crp_lag_46', 'dbp_lag_46', 'fgn_lag_46', 'fio2_lag_46', 'glu_lag_46', 'hgb_lag_46', 'hr_lag_46', 'inr_pt_lag_46', 'k_lag_46', 'lact_lag_46', 'lymph_lag_46', 'map_lag_46', 'mch_lag_46', 'mchc_lag_46', 'mcv_lag_46', 'methb_lag_46', 'mg_lag_46', 'na_lag_46', 'neut_lag_46', 'o2sat_lag_46', 'pco2_lag_46', 'ph_lag_46', 'phos_lag_46', 'plt_lag_46', 'po2_lag_46', 'ptt_lag_46', 'resp_lag_46', 'sbp_lag_46', 'temp_lag_46', 'tnt_lag_46', 'urine_lag_46', 'wbc_lag_46', 'alb_lag_47', 'alp_lag_47', 'alt_lag_47', 'ast_lag_47', 'be_lag_47', 'bicar_lag_47', 'bili_lag_47', 'bili_dir_lag_47', 'bnd_lag_47', 'bun_lag_47', 'ca_lag_47', 'cai_lag_47', 'ck_lag_47', 'ckmb_lag_47', 'cl_lag_47', 'crea_lag_47', 'crp_lag_47', 'dbp_lag_47', 'fgn_lag_47', 'fio2_lag_47', 'glu_lag_47', 'hgb_lag_47', 'hr_lag_47', 'inr_pt_lag_47', 'k_lag_47', 'lact_lag_47', 'lymph_lag_47', 'map_lag_47', 'mch_lag_47', 'mchc_lag_47', 'mcv_lag_47', 'methb_lag_47', 'mg_lag_47', 'na_lag_47', 'neut_lag_47', 'o2sat_lag_47', 'pco2_lag_47', 'ph_lag_47', 'phos_lag_47', 'plt_lag_47', 'po2_lag_47', 'ptt_lag_47', 'resp_lag_47', 'sbp_lag_47', 'temp_lag_47', 'tnt_lag_47', 'urine_lag_47', 'wbc_lag_47', 'alb_lag_48', 'alp_lag_48', 'alt_lag_48', 'ast_lag_48', 'be_lag_48', 'bicar_lag_48', 'bili_lag_48', 'bili_dir_lag_48', 'bnd_lag_48', 'bun_lag_48', 'ca_lag_48', 'cai_lag_48', 'ck_lag_48', 'ckmb_lag_48', 'cl_lag_48', 'crea_lag_48', 'crp_lag_48', 'dbp_lag_48', 'fgn_lag_48', 'fio2_lag_48', 'glu_lag_48', 'hgb_lag_48', 'hr_lag_48', 'inr_pt_lag_48', 'k_lag_48', 'lact_lag_48', 'lymph_lag_48', 'map_lag_48', 'mch_lag_48', 'mchc_lag_48', 'mcv_lag_48', 'methb_lag_48', 'mg_lag_48', 'na_lag_48', 'neut_lag_48', 'o2sat_lag_48', 'pco2_lag_48', 'ph_lag_48', 'phos_lag_48', 'plt_lag_48', 'po2_lag_48', 'ptt_lag_48', 'resp_lag_48', 'sbp_lag_48', 'temp_lag_48', 'tnt_lag_48', 'urine_lag_48', 'wbc_lag_48'] PredictionDatasetpytorch.add_relative_time_idx=False PredictionDatasetpytorch.target="label" - PredictionDatasetTFTpytorch.time_varying_unknown_reals=[ +PredictionDatasetpytorch.time_varying_unknown_reals=[ "label" ] PredictionDatasetpytorch.time_varying_unknown_categoricals=[] diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index c1306d14..bf06ac0c 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -28,7 +28,7 @@ from icu_benchmarks.contants import RunMode from icu_benchmarks.data.constants import DataSplit as Split from collections import OrderedDict -from captum.attr import IntegratedGradients, ShapleyValueSampling, Saliency, GuidedBackprop, LRP +from captum.attr import IntegratedGradients, ShapleyValueSampling, Saliency, GuidedBackprop, LRP, FeatureAblation, Lime cpu_core_count = ( @@ -276,38 +276,49 @@ def train_common( return 0 if explain: - - # attributions_Saliency = model.explantation_captum( - # test_loader=test_loader, - # method=Saliency, log_dir=log_dir, plot=True - # ) - - # print("saliency", attributions_Saliency) - """ - attributions_shap = model.explantation_captum( - test_loader=test_loader, - method=ShapleyValueSampling, log_dir=log_dir, plot=True, n_samples=10 - ) - print("shap", attributions_shap) - """ - # attributions_IG = model.explantation_captum( - # test_loader=test_loader, - # method=IntegratedGradients, log_dir=log_dir, plot=True, n_steps=20 - # ) - # print("IG", attributions_IG) + attributions_dict = {} Interpertations = model.interpertations(test_loader, log_dir, plot=True) + attributions_dict["attention_weights"]= Interpertations["attention"].tolist() + attributions_dict["static_variables"]= Interpertations["static_variables"].tolist() + attributions_dict["encoder_variables"]= Interpertations["encoder_variables"].tolist() print("attention", Interpertations) - attributions_dict = { - "attributions_Saliency": attributions_Saliency.tolist(), - "attributions_IG": attributions_IG.tolist(), - # "attributions_shap": attributions_shap.tolist(), - "attention_weights": Interpertations["attention"].tolist(), - "static_variables": Interpertations["static_variables"].tolist(), - "encoder_variables": Interpertations["encoder_variables"].tolist() + if XAI_metric: + random_attributions = np.random.normal(size=24) + F_baseline = model.Faithfulness_Correlation(test_loader, random_attributions, pertrub='Noise') + print('Random normal faithfulness correlation', F_baseline) + F_attention = model.Faithfulness_Correlation( + test_loader, Interpertations["attention"], pertrub='Noise') + print('Attention faithfulness correlation', F_attention) + methods = { + "Saliency": Saliency, + "Lime": Lime, + "IG": IntegratedGradients, } - variable_importance = np.concatenate((Interpertations["static_variables"].cpu().detach().numpy(), - Interpertations["encoder_variables"].cpu().detach().numpy())) - ind = np.argpartition(variable_importance, -5)[-5:] + for key,item in methods.items(): + attribution= + attributions_Saliency = model.explantation_captum( + test_loader=test_loader, + method=Saliency, log_dir=log_dir, plot=True + ) + + print("saliency", attributions_Saliency) + + attributions_lime = model.explantation_captum( + test_loader=test_loader, + method=Lime, log_dir=log_dir, plot=True, n_samples=10 + ) + print("FA", attributions_lime) + + attributions_IG = model.explantation_captum( + test_loader=test_loader, + method=IntegratedGradients, log_dir=log_dir, plot=True, n_steps=20 + ) + print("IG", attributions_IG) + + + # variable_importance = np.concatenate((Interpertations["static_variables"].cpu().detach().numpy(), + # Interpertations["encoder_variables"].cpu().detach().numpy())) + # ind = np.argpartition(variable_importance, -5)[-5:] # Path to the JSON file in log_dir json_file_path = f"{log_dir}/Attributions.json" @@ -316,24 +327,20 @@ def train_common( with open(json_file_path, 'w') as json_file: json.dump(attributions_dict, json_file) if XAI_metric: - random_attributions = np.random.normal(size=24) - F_baseline = model.Faithfulness_Correlation(test_loader, random_attributions, pertrub='Noise', ind=ind) - print('Random normal faithfulness correlation', F_baseline) + F_attribution = model.Faithfulness_Correlation(test_loader, attributions_IG, pertrub='Noise') print('Attributions faithfulness correlation', F_attribution) - F_attention = model.Faithfulness_Correlation( - test_loader, Interpertations["attention"], pertrub='Noise', ind=ind) - print('Attention faithfulness correlation', F_attention) + F_saliency = model.Faithfulness_Correlation(test_loader, attributions_Saliency, pertrub='Noise') print('Saliency faithfulness correlation', F_saliency) - # F_shap = model.Faithfulness_Correlation(test_loader, attributions_shap, pertrub='Noise') - # print('shap faithfulness correlation', F_shap) + F_lime = model.Faithfulness_Correlation(test_loader, attributions_lime, pertrub='Noise') + print('shap faithfulness correlation', F_lime) XAI_dict = { "Faithfulness_correlation_normal_random": F_baseline, "Faithfulness_correlation_IG": F_attribution, "Faithfulness_correlation_attention": F_attention, "Faithfulness_correlation_saliency": F_saliency, - # "Faithfulness_correlation_shap": F_shap + "Faithfulness_correlation_shap": F_lime } # Path to the JSON file in log_dir diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 3b956900..5898c8fa 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -528,39 +528,36 @@ def explantation_captum(self, test_loader, method, log_dir=".", plot=False, **kw explantation = method(self.forward_captum) # Reformat attributions. - attr_all_timesteps = [] - for time_step in range(0, 24): - if method is not captum.attr.Saliency: - attr = explantation.attribute( - data, target=(time_step), baselines=baselines, **kwargs - ) - else: - attr = explantation.attribute( - data, target=(time_step), **kwargs - ) + # attr_all_timesteps = [] + # for time_step in range(0, 24): + if method is not captum.attr.Saliency: + attr = explantation.attribute( + data, baselines=baselines, **kwargs + ) + else: + attr = explantation.attribute( + data, **kwargs + ) # Convert attributions to numpy array and append to the list - - attr_all_timesteps.append(attr[0].cpu().detach().numpy()) - all_attrs.append(np.mean(attr_all_timesteps, axis=0)) + all_attrs.append(attr[0].cpu().detach().numpy()) # Concatenate a‚ttribution values for all instances along the batch dimension - all_attrs = np.mean(all_attrs, axis=0) - - means_feature = all_attrs.mean(axis=(0)) - # self.log('Attribution Featues', means_feature) - # Compute mean along the batch dimension - means = all_attrs.mean(axis=(1)) - # self.log('Attribution Time steps', means) - # Normalize the means values to range [0, 1] + + all_attrs = all_attrs.mean(axis=(0)) + + features_attrs = all_attrs.mean(axis=(0)) + + timestep_attrs = all_attrs.mean(axis=(1)) + # normalized_means = (means - means.min()) / (means.max() - means.min()) if plot: # Create x values (assuming you want a simple sequential x-axis) # Assuming you have 57 variables - x_values = np.arange(1, 58) + x_values = np.arange(1, 54) # Plotting the featrue means plt.figure(figsize=(8, 6)) plt.plot( x_values, - means_feature, + features_attrs, marker="o", color="skyblue", linestyle="-", @@ -577,7 +574,7 @@ def explantation_captum(self, test_loader, method, log_dir=".", plot=False, **kw x_values = np.arange(1, 25) plt.plot( x_values, - means, + timestep_attrs, marker="o", color="skyblue", linestyle="-", @@ -590,10 +587,10 @@ def explantation_captum(self, test_loader, method, log_dir=".", plot=False, **kw plt.xticks(x_values) # Set x-ticks to match the number of features plt.tight_layout() plt.savefig(log_dir / "attribution_plot.png", bbox_inches="tight") - return means + return all_attrs, features_attrs, timestep_attrs def Faithfulness_Correlation( - self, test_loader, attribution, similarity_func=None, nr_runs=2, pertrub=None, subset_size=3, ind=[] + self, test_loader, attribution, similarity_func=None, nr_runs=100, pertrub=None, subset_size=3, feature=False, time_step=True, feature_timestep=False ): """ Implementation of faithfulness correlation by Bhatt et al., 2020. @@ -656,20 +653,16 @@ def Faithfulness_Correlation( # add normal noise to input noise = torch.randn_like(x["encoder_cont"]) - if (len(ind) == 0): - x["encoder_cont"][:, a_ix, :] += noise[:, a_ix, :] - else: - x["encoder_cont"][:, a_ix[:, None], ind] += noise[:, a_ix[:, None], ind] + x["encoder_cont"][:, a_ix, :] += noise[:, a_ix, :] elif pertrub == "baseline": # Create a mask tensor with zeros at specified time steps and ones everywhere else # pytorch bug need to change to cpu for next step and then revert mask = torch.ones_like(x["encoder_cont"]).cpu() - if (len(ind) == 0): - mask[:, a_ix, :] = 0 - else: - mask[:, a_ix, ind] = 0 + + mask[:, a_ix, :] = 0 + mask = mask.to(x["encoder_cont"].device) x["encoder_cont"] = x["encoder_cont"] * mask @@ -697,7 +690,7 @@ def Faithfulness_Correlation( score = np.nanmean(similarities) return score - def Data_Randomization(self, test_loader, attribution, explain_method, random_model, similarity_func=None, test_dataset=None): + def Data_Randomization(self, test_loader, attribution, explain_method, random_model, similarity_func=None, test_dataset=None, **kwargs): """ Implementation of the Random Logit Metric by Sixt et al., 2020. @@ -740,31 +733,33 @@ def Data_Randomization(self, test_loader, attribution, explain_method, random_mo ) baselines = ( - torch.zeros_like(data[0]).to(random_model.device), # encoder_cat, set to zero - torch.zeros_like(data[1]).to(random_model.device), # encoder_cont, set to zero - torch.zeros_like(data[2]).to(random_model.device), # encoder_target, set to zero - data[3].to(random_model.device), # encoder_lengths, leave unchanged - torch.zeros_like(data[4]).to(random_model.device), # decoder_cat, set to zero - torch.zeros_like(data[5]).to(random_model.device), # decoder_cont, set to zero - torch.zeros_like(data[6]).to(random_model.device), # decoder_target, set to zero - data[7].to(random_model.device), # decoder_lengths, leave unchanged - torch.zeros_like(data[8]).to(random_model.device), # decoder_time_idx, set to zero - data[9].to(random_model.device), # groups, leave unchanged - data[10].to(random_model.device), # target_scale, leave unchanged + data[0].to(self.device), # encoder_cat, set to random + torch.randn_like(data[1]).to(self.device), # encoder_cont, set to random + torch.randn_like(data[2]).to(self.device), # encoder_target, set to random + data[3].to(self.device), # encoder_lengths, leave unchanged + torch.randn_like(data[4]).to(self.device), # decoder_cat, set to random + torch.randn_like(data[5]).to(self.device), # decoder_cont, set to random + torch.randn_like(data[6]).to(self.device), # decoder_target, set to random + data[7].to(self.device), # decoder_lengths, leave unchanged + data[8].to(self.device), # decoder_time_idx, unchanged + data[9].to(self.device), # groups, leave unchanged + data[10].to(self.device), # target_scale, leave unchanged ) explantation = explain_method(random_model.forward_captum) # Reformat attributions. - attr_all_timesteps = [] - for time_step in range(0, 24): - attr, delta = explantation.attribute( - data, target=(time_step, 1), return_convergence_delta=True, baselines=baselines, n_steps=35 + if explain_method is not captum.attr.Saliency: + attr = explantation.attribute( + data, baselines=baselines, **kwargs + ) + else: + attr = explantation.attribute( + data, **kwargs ) # Convert attributions to numpy array and append to the list - attr_all_timesteps.append(attr[0].cpu().detach().numpy()) - a_perturbed.append(np.mean(attr_all_timesteps, axis=0)) + a_perturbed.append(attr[0].cpu().detach().numpy()) - a_perturbed = np.mean(a_perturbed, axis=(0, 1, 3)) + a_perturbed = np.mean(a_perturbed, axis=(0)) # Normalize the means values to range [0, 1] # normalized_a_perturbed = (a_perturbed - a_perturbed.min()) / (a_perturbed.max() - a_perturbed.min()) From 4c8ee4a8a736c00f1d1c1bec84a4c3795db9aa2a Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Tue, 21 Nov 2023 16:23:49 +0100 Subject: [PATCH 111/142] changed to allow for timesteps and variable per timestep attribution --- icu_benchmarks/models/train.py | 99 ++++++++++++++++--------------- icu_benchmarks/models/wrappers.py | 67 +++++++++++++-------- 2 files changed, 93 insertions(+), 73 deletions(-) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index bf06ac0c..c0a055f5 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -278,47 +278,66 @@ def train_common( if explain: attributions_dict = {} Interpertations = model.interpertations(test_loader, log_dir, plot=True) - attributions_dict["attention_weights"]= Interpertations["attention"].tolist() - attributions_dict["static_variables"]= Interpertations["static_variables"].tolist() - attributions_dict["encoder_variables"]= Interpertations["encoder_variables"].tolist() + attributions_dict["attention_weights"] = Interpertations["attention"].tolist() + attributions_dict["static_variables"] = Interpertations["static_variables"].tolist() + attributions_dict["encoder_variables"] = Interpertations["encoder_variables"].tolist() print("attention", Interpertations) + model.train() + XAI_dict = {} if XAI_metric: - random_attributions = np.random.normal(size=24) - F_baseline = model.Faithfulness_Correlation(test_loader, random_attributions, pertrub='Noise') - print('Random normal faithfulness correlation', F_baseline) + XAI_dict = {} + # random attribution per timestep + random_attributions_ts = np.random.normal(size=24) + F_baseline_ts = model.Faithfulness_Correlation( + test_loader, random_attributions_ts, pertrub='Noise', subset_size=4, time_step=True, nr_runs=10) + print('Random normal faithfulness correlation for timesteps', F_baseline_ts) + F_attention = model.Faithfulness_Correlation( - test_loader, Interpertations["attention"], pertrub='Noise') + test_loader, Interpertations["attention"], pertrub='Noise', subset_size=4, time_step=True, nr_runs=10) print('Attention faithfulness correlation', F_attention) + # random attribution per variable per timestep + random_attributions_v_ts = np.random.normal(size=[24, 53]) + F_baseline_v_ts = model.Faithfulness_Correlation( + test_loader, random_attributions_v_ts, pertrub='Noise', feature_timestep=True, nr_runs=10, subset_size=[4, 9]) + print('Random normal faithfulness correlation for variables per timesteps', F_baseline_v_ts) + + XAI_dict["attention_faith"] = F_attention + XAI_dict["random_faith_timestep"] = random_attributions_ts + XAI_dict["random_faith_var_timestep"] = random_attributions_v_ts.tolist() methods = { "Saliency": Saliency, - "Lime": Lime, + # "Lime": Lime, "IG": IntegratedGradients, - } - for key,item in methods.items(): - attribution= - attributions_Saliency = model.explantation_captum( - test_loader=test_loader, - method=Saliency, log_dir=log_dir, plot=True - ) - print("saliency", attributions_Saliency) - - attributions_lime = model.explantation_captum( - test_loader=test_loader, - method=Lime, log_dir=log_dir, plot=True, n_samples=10 - ) - print("FA", attributions_lime) - attributions_IG = model.explantation_captum( - test_loader=test_loader, - method=IntegratedGradients, log_dir=log_dir, plot=True, n_steps=20 - ) - print("IG", attributions_IG) - - - # variable_importance = np.concatenate((Interpertations["static_variables"].cpu().detach().numpy(), - # Interpertations["encoder_variables"].cpu().detach().numpy())) - # ind = np.argpartition(variable_importance, -5)[-5:] + } + for key, item in methods.items(): + if key == "IG": + all_attrs, features_attrs, timestep_attrs = model.explantation_captum( + test_loader=test_loader, + method=item, log_dir=log_dir, plot=True, n_steps=20 + ) + else: + all_attrs, features_attrs, timestep_attrs = model.explantation_captum( + test_loader=test_loader, + method=item, log_dir=log_dir, plot=True + ) + attributions_dict["{}_all".format(key)] = all_attrs.tolist() + attributions_dict["{}_timesteps".format(key)] = timestep_attrs.tolist() + attributions_dict["{}_features".format(key)] = features_attrs.tolist() + + print("{}".format(key), all_attrs, features_attrs, timestep_attrs) + if XAI_metric: + faithfulness_timesteps = model.Faithfulness_Correlation( + test_loader, timestep_attrs, pertrub='Noise', time_step=True, subset_size=4, nr_runs=10) + print('Attributions faithfulness timesteps correlation', faithfulness_timesteps) + XAI_dict["{}_faith_timesteps".format(key)] = faithfulness_timesteps.tolist() + random_attributions = np.random.normal(np.shape(all_attrs)) + + faithfulness_timesteps_variable = model.Faithfulness_Correlation( + test_loader, all_attrs, pertrub='Noise', feature_timestep=True, subset_size=[4, 9], nr_runs=10) + print('Attributions faithfulness variable per timestep correlation', faithfulness_timesteps_variable) + XAI_dict["{}_faith_variable_per_timestep".format(key)] = faithfulness_timesteps_variable.tolist() # Path to the JSON file in log_dir json_file_path = f"{log_dir}/Attributions.json" @@ -326,22 +345,6 @@ def train_common( # Write the dictionary to a JSON file with open(json_file_path, 'w') as json_file: json.dump(attributions_dict, json_file) - if XAI_metric: - - F_attribution = model.Faithfulness_Correlation(test_loader, attributions_IG, pertrub='Noise') - print('Attributions faithfulness correlation', F_attribution) - - F_saliency = model.Faithfulness_Correlation(test_loader, attributions_Saliency, pertrub='Noise') - print('Saliency faithfulness correlation', F_saliency) - F_lime = model.Faithfulness_Correlation(test_loader, attributions_lime, pertrub='Noise') - print('shap faithfulness correlation', F_lime) - XAI_dict = { - "Faithfulness_correlation_normal_random": F_baseline, - "Faithfulness_correlation_IG": F_attribution, - "Faithfulness_correlation_attention": F_attention, - "Faithfulness_correlation_saliency": F_saliency, - "Faithfulness_correlation_shap": F_lime - } # Path to the JSON file in log_dir json_file_path = f"{log_dir}/XAI_metrics.json" diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 5898c8fa..576913a3 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -541,13 +541,15 @@ def explantation_captum(self, test_loader, method, log_dir=".", plot=False, **kw # Convert attributions to numpy array and append to the list all_attrs.append(attr[0].cpu().detach().numpy()) # Concatenate a‚ttribution values for all instances along the batch dimension - - all_attrs = all_attrs.mean(axis=(0)) - + # print('b4', all_attrs) + # print('b4 shape', np.shape(all_attrs)) + all_attrs = np.array(all_attrs).mean(axis=(0)) + # print('after', all_attrs) + # print('after shape', np.shape(all_attrs)) features_attrs = all_attrs.mean(axis=(0)) - + # print('feat shape ', np.shape(features_attrs)) timestep_attrs = all_attrs.mean(axis=(1)) - + # print('timestep shape', np.shape(timestep_attrs)) # normalized_means = (means - means.min()) / (means.max() - means.min()) if plot: # Create x values (assuming you want a simple sequential x-axis) @@ -590,7 +592,7 @@ def explantation_captum(self, test_loader, method, log_dir=".", plot=False, **kw return all_attrs, features_attrs, timestep_attrs def Faithfulness_Correlation( - self, test_loader, attribution, similarity_func=None, nr_runs=100, pertrub=None, subset_size=3, feature=False, time_step=True, feature_timestep=False + self, test_loader, attribution, similarity_func=None, nr_runs=100, pertrub=None, subset_size=3, feature=False, time_step=False, feature_timestep=False ): """ Implementation of faithfulness correlation by Bhatt et al., 2020. @@ -643,29 +645,38 @@ def Faithfulness_Correlation( y_pred = self(data).detach().cpu().numpy() pred_deltas = [] att_sums = [] + + def add_noise(x, indices, time_step, feature_timestep): + noise = torch.randn_like(x["encoder_cont"]) + if time_step: + x["encoder_cont"][:, indices, :] += noise[:, indices, :] + elif feature_timestep: + + x["encoder_cont"][:, indices[0], :][:, :, indices[1]] += noise[:, indices[0], :][:, :, indices[1]] + + def apply_baseline(x, indices, time_step, feature_timestep): + mask = torch.ones_like(x["encoder_cont"]).cpu() + if time_step: + mask[:, indices, :] = 0 + elif feature_timestep: + mask[:, indices[0], :][:, :, indices[1]] = 0 + mask = mask.to(x["encoder_cont"].device) + x["encoder_cont"] *= mask for i_ix in range(nr_runs): # Randomly mask by subset size. - a_ix = np.random.choice(x["encoder_cont"].shape[1], subset_size, replace=False) - # Move a_ix_tensor to the same device as mask + if time_step: + a_ix = np.random.choice(24, subset_size, replace=False) + elif feature_timestep: + timesteps_idx = np.random.choice(24, subset_size[0], replace=False) + variables_idx = np.random.choice(53, subset_size[1], replace=False) + a_ix = [timesteps_idx, variables_idx] if pertrub == "Noise": - # add normal noise to input - - noise = torch.randn_like(x["encoder_cont"]) - - x["encoder_cont"][:, a_ix, :] += noise[:, a_ix, :] - + add_noise(x, a_ix, time_step, feature_timestep) elif pertrub == "baseline": - # Create a mask tensor with zeros at specified time steps and ones everywhere else - # pytorch bug need to change to cpu for next step and then revert - mask = torch.ones_like(x["encoder_cont"]).cpu() - - mask[:, a_ix, :] = 0 + apply_baseline(x, a_ix, time_step, feature_timestep) - mask = mask.to(x["encoder_cont"].device) - - x["encoder_cont"] = x["encoder_cont"] * mask data = ( x["encoder_cat"], x["encoder_cont"], @@ -681,13 +692,19 @@ def Faithfulness_Correlation( ) # Predict on perturbed input x. y_pred_perturb = self(data).detach().cpu().numpy() - pred_deltas.append((y_pred - y_pred_perturb).mean(axis=(0, 2))) + pred_deltas.append((y_pred - y_pred_perturb).mean()) # Sum attributions of the random subset. + if time_step: + att_sums.append(np.sum(attribution[a_ix])) + elif feature_timestep: + row_indices = a_ix[0] + col_indices = a_ix[1] + att_sums.append(np.sum(attribution[np.ix_(row_indices, col_indices)])) - att_sums.append(np.sum(attribution[a_ix])) similarities.append(similarity_func(pred_deltas, att_sums)) - score = np.nanmean(similarities) + + score = np.nanmean(similarities) return score def Data_Randomization(self, test_loader, attribution, explain_method, random_model, similarity_func=None, test_dataset=None, **kwargs): From d3ea3d32485bf5060cec4da018df3c857861be49 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Tue, 21 Nov 2023 17:28:16 +0100 Subject: [PATCH 112/142] latest changes --- icu_benchmarks/cross_validation.py | 5 +- icu_benchmarks/data/loader.py | 6 +- icu_benchmarks/models/dl_models.py | 4 +- icu_benchmarks/models/train.py | 125 ++++++++++------------- icu_benchmarks/models/wrappers.py | 76 +++++++------- icu_benchmarks/run.py | 8 +- icu_benchmarks/run_utils.py | 3 +- icu_benchmarks/tuning/hyperparameters.py | 2 +- 8 files changed, 102 insertions(+), 127 deletions(-) diff --git a/icu_benchmarks/cross_validation.py b/icu_benchmarks/cross_validation.py index 7d3f217e..535d9619 100644 --- a/icu_benchmarks/cross_validation.py +++ b/icu_benchmarks/cross_validation.py @@ -42,7 +42,7 @@ def execute_repeated_cv( pytorch_forecasting: bool = False, XAI_metric: bool = False, random_labels: bool = False, - random_model_dir: str = None + random_model_dir: str = None, ) -> float: """Preprocesses data and trains a model for each fold. @@ -107,7 +107,6 @@ def execute_repeated_cv( pretrained_imputation_model=pretrained_imputation_model, runmode=mode, complete_train=complete_train, - ) preprocess_time = datetime.now() - start_time @@ -129,7 +128,7 @@ def execute_repeated_cv( pytorch_forecasting=pytorch_forecasting, XAI_metric=XAI_metric, random_labels=random_labels, - random_model_dir=random_model_dir + random_model_dir=random_model_dir, ) train_time = datetime.now() - start_time diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 68109f4b..75722831 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -522,9 +522,9 @@ def get_feature_names(self): def randomize_labels(self, num_classes=None, min=None, max=None): if num_classes == 1: - - random_target = np.random.uniform(self.data["target"][0].min(), - self.data["target"][0].max(), size=len(self.data["target"][0])) + random_target = np.random.uniform( + self.data["target"][0].min(), self.data["target"][0].max(), size=len(self.data["target"][0]) + ) else: random_target = np.random.randint(num_classes, size=len(self.data["target"][0])) self.data["target"][0] = Tensor(random_target) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 1cd782a5..ad73a479 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -637,7 +637,7 @@ def forward( "target_scale": tuple_x[10], } if self.num_classes == 1: - x_dict['encoder_cont'][:, :, -1] = 0.0 + x_dict["encoder_cont"][:, :, -1] = 0.0 out = self.model(x_dict) @@ -698,7 +698,7 @@ def forward( "target_scale": tuple_x[10], } if self.num_classes == 1: - x_dict['encoder_cont'][:, :, -1] = 0.0 + x_dict["encoder_cont"][:, :, -1] = 0.0 out = self.model(x_dict) out = out["prediction"][0] diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index c0a055f5..18341a99 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -31,9 +31,7 @@ from captum.attr import IntegratedGradients, ShapleyValueSampling, Saliency, GuidedBackprop, LRP, FeatureAblation, Lime -cpu_core_count = ( - len(os.sched_getaffinity(0)) if hasattr(os, "sched_getaffinity") else os.cpu_count() -) +cpu_core_count = len(os.sched_getaffinity(0)) if hasattr(os, "sched_getaffinity") else os.cpu_count() def assure_minimum_length(dataset): @@ -76,7 +74,7 @@ def train_common( pytorch_forecasting: bool = False, XAI_metric: bool = False, random_labels: bool = False, - random_model_dir: str = None + random_model_dir: str = None, ): """Common wrapper to train all benchmarked models. @@ -110,25 +108,15 @@ def train_common( dataset_class = ( ImputationDataset if mode == RunMode.imputation - else ( - PredictionDatasetpytorch if (pytorch_forecasting) else PredictionDataset - ) + else (PredictionDatasetpytorch if (pytorch_forecasting) else PredictionDataset) ) logging.info(f"Logging to directory: {log_dir}.") - save_config_file( - log_dir - ) # We save the operative config before and also after training + save_config_file(log_dir) # We save the operative config before and also after training - train_dataset = dataset_class( - data, split=Split.train, ram_cache=ram_cache, name=dataset_names["train"] - ) - val_dataset = dataset_class( - data, split=Split.val, ram_cache=ram_cache, name=dataset_names["val"] - ) - train_dataset, val_dataset = assure_minimum_length( - train_dataset - ), assure_minimum_length(val_dataset) + train_dataset = dataset_class(data, split=Split.train, ram_cache=ram_cache, name=dataset_names["train"]) + val_dataset = dataset_class(data, split=Split.val, ram_cache=ram_cache, name=dataset_names["val"]) + train_dataset, val_dataset = assure_minimum_length(train_dataset), assure_minimum_length(val_dataset) batch_size = min(batch_size, len(train_dataset), len(val_dataset)) test_dataset = dataset_class(data, split=test_on, name=dataset_names["test"]) test_dataset = assure_minimum_length(test_dataset) @@ -219,9 +207,7 @@ def train_common( if load_weights: model = load_model(model, source_dir, pl_model=pl_model) else: - model = model( - optimizer=optimizer, input_size=data_shape, epochs=epochs, run_mode=mode - ) + model = model(optimizer=optimizer, input_size=data_shape, epochs=epochs, run_mode=mode) model.set_weight(weight, train_dataset) model.set_trained_columns(train_dataset.get_feature_names()) @@ -261,9 +247,7 @@ def train_common( if model.requires_backprop: logging.info("Training DL model.") - trainer.fit( - model, train_dataloaders=train_loader, val_dataloaders=val_loader - ) + trainer.fit(model, train_dataloaders=train_loader, val_dataloaders=val_loader) logging.info("Training complete.") else: logging.info("Training ML model.") @@ -277,50 +261,51 @@ def train_common( if explain: attributions_dict = {} - Interpertations = model.interpertations(test_loader, log_dir, plot=True) - attributions_dict["attention_weights"] = Interpertations["attention"].tolist() - attributions_dict["static_variables"] = Interpertations["static_variables"].tolist() - attributions_dict["encoder_variables"] = Interpertations["encoder_variables"].tolist() - print("attention", Interpertations) - model.train() + # Interpertations = model.interpertations(test_loader, log_dir, plot=True) + # attributions_dict["attention_weights"] = Interpertations["attention"].tolist() + # attributions_dict["static_variables"] = Interpertations["static_variables"].tolist() + # attributions_dict["encoder_variables"] = Interpertations["encoder_variables"].tolist() + # print("attention", Interpertations) + # model.train() XAI_dict = {} - if XAI_metric: - XAI_dict = {} - # random attribution per timestep - random_attributions_ts = np.random.normal(size=24) - F_baseline_ts = model.Faithfulness_Correlation( - test_loader, random_attributions_ts, pertrub='Noise', subset_size=4, time_step=True, nr_runs=10) - print('Random normal faithfulness correlation for timesteps', F_baseline_ts) - - F_attention = model.Faithfulness_Correlation( - test_loader, Interpertations["attention"], pertrub='Noise', subset_size=4, time_step=True, nr_runs=10) - print('Attention faithfulness correlation', F_attention) - # random attribution per variable per timestep - random_attributions_v_ts = np.random.normal(size=[24, 53]) - F_baseline_v_ts = model.Faithfulness_Correlation( - test_loader, random_attributions_v_ts, pertrub='Noise', feature_timestep=True, nr_runs=10, subset_size=[4, 9]) - print('Random normal faithfulness correlation for variables per timesteps', F_baseline_v_ts) - - XAI_dict["attention_faith"] = F_attention - XAI_dict["random_faith_timestep"] = random_attributions_ts - XAI_dict["random_faith_var_timestep"] = random_attributions_v_ts.tolist() + # if XAI_metric: + + # random attribution per timestep + # random_attributions_ts = np.random.normal(size=24) + # F_baseline_ts = model.Faithfulness_Correlation( + # test_loader, random_attributions_ts, pertrub='Noise', subset_size=4, time_step=True, nr_runs=100) + # print('Random normal faithfulness correlation for timesteps', F_baseline_ts) + + # F_attention = model.Faithfulness_Correlation( + # test_loader, Interpertations["attention"], pertrub='Noise', subset_size=4, time_step=True, nr_runs=100) + # print('Attention faithfulness correlation', F_attention) + # random attribution per variable per timestep + # random_attributions_v_ts = np.random.normal(size=[24, 53]) + # F_baseline_v_ts = model.Faithfulness_Correlation( + # test_loader, random_attributions_v_ts, pertrub='Noise', feature_timestep=True, nr_runs=10, subset_size=[4, 9]) + # print('Random normal faithfulness correlation for variables per timesteps', F_baseline_v_ts) + + # XAI_dict["attention_faith"] = F_attention.tolist() + # XAI_dict["random_faith_timestep"] = random_attributions_ts.tolist() + # XAI_dict["random_faith_var_timestep"] = random_attributions_v_ts.tolist() methods = { - "Saliency": Saliency, + # "Saliency": Saliency, # "Lime": Lime, - "IG": IntegratedGradients, - - + # "IG": IntegratedGradients, + "FA": FeatureAblation } for key, item in methods.items(): if key == "IG": all_attrs, features_attrs, timestep_attrs = model.explantation_captum( - test_loader=test_loader, - method=item, log_dir=log_dir, plot=True, n_steps=20 + test_loader=test_loader, method=item, log_dir=log_dir, plot=True, n_steps=50 + ) + if key == "Lime": + all_attrs, features_attrs, timestep_attrs = model.explantation_captum( + test_loader=test_loader, method=item, log_dir=log_dir, plot=True, return_input_shape=True ) else: all_attrs, features_attrs, timestep_attrs = model.explantation_captum( - test_loader=test_loader, - method=item, log_dir=log_dir, plot=True + test_loader=test_loader, method=item, log_dir=log_dir, plot=True ) attributions_dict["{}_all".format(key)] = all_attrs.tolist() attributions_dict["{}_timesteps".format(key)] = timestep_attrs.tolist() @@ -329,28 +314,30 @@ def train_common( print("{}".format(key), all_attrs, features_attrs, timestep_attrs) if XAI_metric: faithfulness_timesteps = model.Faithfulness_Correlation( - test_loader, timestep_attrs, pertrub='Noise', time_step=True, subset_size=4, nr_runs=10) - print('Attributions faithfulness timesteps correlation', faithfulness_timesteps) + test_loader, timestep_attrs, pertrub="Noise", time_step=True, subset_size=4, nr_runs=100 + ) + print("Attributions faithfulness timesteps correlation", faithfulness_timesteps) XAI_dict["{}_faith_timesteps".format(key)] = faithfulness_timesteps.tolist() random_attributions = np.random.normal(np.shape(all_attrs)) faithfulness_timesteps_variable = model.Faithfulness_Correlation( - test_loader, all_attrs, pertrub='Noise', feature_timestep=True, subset_size=[4, 9], nr_runs=10) - print('Attributions faithfulness variable per timestep correlation', faithfulness_timesteps_variable) + test_loader, all_attrs, pertrub="Noise", feature_timestep=True, subset_size=[4, 9], nr_runs=100 + ) + print("Attributions faithfulness variable per timestep correlation", faithfulness_timesteps_variable) XAI_dict["{}_faith_variable_per_timestep".format(key)] = faithfulness_timesteps_variable.tolist() # Path to the JSON file in log_dir json_file_path = f"{log_dir}/Attributions.json" # Write the dictionary to a JSON file - with open(json_file_path, 'w') as json_file: + with open(json_file_path, "w") as json_file: json.dump(attributions_dict, json_file) # Path to the JSON file in log_dir json_file_path = f"{log_dir}/XAI_metrics.json" # Write the dictionary to a JSON file - with open(json_file_path, 'w') as json_file: + with open(json_file_path, "w") as json_file: json.dump(XAI_dict, json_file) # path = Path(random_model_dir) @@ -370,9 +357,7 @@ def train_common( # print('Distance Data randmoization score attention', R_attention) model.set_weight("balanced", train_dataset) - test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0][ - "test/loss" - ] + test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0]["test/loss"] save_config_file(log_dir) return test_loss @@ -390,9 +375,7 @@ def load_model(model, source_dir, pl_model=True, train_dataset=None, optimizer=N return Exception(f"No weights to load at path : {source_dir}") if pl_model: if train_dataset is not None: - model = model.load_from_checkpoint( - model_path, dataset=train_dataset, optimizer=optimizer - ) + model = model.load_from_checkpoint(model_path, dataset=train_dataset, optimizer=optimizer) else: model = model.load_from_checkpoint(model_path) diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 576913a3..6385b677 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -21,6 +21,7 @@ import matplotlib.pyplot as plt from icu_benchmarks.models.similarity_func import correlation_spearman, distance_euclidean import captum + gin.config.external_configurable(nn.functional.nll_loss, module="torch.nn.functional") gin.config.external_configurable(nn.functional.cross_entropy, module="torch.nn.functional") gin.config.external_configurable(nn.functional.mse_loss, module="torch.nn.functional") @@ -156,12 +157,10 @@ def on_train_start(self): return super().on_train_start() def finalize_step(self, step_prefix=""): - try: for name, metric in self.metrics[step_prefix].items(): try: - value = np.float32(metric.compute()) if isinstance( - metric.compute(), np.float64) else metric.compute() + value = np.float32(metric.compute()) if isinstance(metric.compute(), np.float64) else metric.compute() self.log_dict({f"{step_prefix}/{name}": value}, sync_dist=True) except (NotComputableError, ValueError) as e: @@ -491,10 +490,9 @@ def step_fn(self, element, step_prefix=""): def explantation_captum(self, test_loader, method, log_dir=".", plot=False, **kwargs): # Initialize lists to store attribution values for all instances all_attrs = [] - + method_name = method.__name__ # Loop through the test_loader to compute attributions for all instances for batch in test_loader: - for key, value in batch[0].items(): batch[0][key] = batch[0][key].to(self.device) x = batch[0] @@ -513,11 +511,11 @@ def explantation_captum(self, test_loader, method, log_dir=".", plot=False, **kw x["target_scale"].requires_grad_(), ) baselines = ( - data[0].to(self.device), # encoder_cat, set to random + data[0].to(self.device), # encoder_cat, no cat variables torch.randn_like(data[1]).to(self.device), # encoder_cont, set to random torch.randn_like(data[2]).to(self.device), # encoder_target, set to random data[3].to(self.device), # encoder_lengths, leave unchanged - torch.randn_like(data[4]).to(self.device), # decoder_cat, set to random + data[4].to(self.device), # decoder_cat, no cat variables torch.randn_like(data[5]).to(self.device), # decoder_cont, set to random torch.randn_like(data[6]).to(self.device), # decoder_target, set to random data[7].to(self.device), # decoder_lengths, leave unchanged @@ -531,29 +529,23 @@ def explantation_captum(self, test_loader, method, log_dir=".", plot=False, **kw # attr_all_timesteps = [] # for time_step in range(0, 24): if method is not captum.attr.Saliency: - attr = explantation.attribute( - data, baselines=baselines, **kwargs - ) + attr = explantation.attribute(data, baselines=baselines, **kwargs) else: - attr = explantation.attribute( - data, **kwargs - ) - # Convert attributions to numpy array and append to the list + attr = explantation.attribute(data, **kwargs) + print(attr) + + print(attr[0].shape) + # Convert attributions to numpy array and append to the list all_attrs.append(attr[0].cpu().detach().numpy()) - # Concatenate a‚ttribution values for all instances along the batch dimension - # print('b4', all_attrs) - # print('b4 shape', np.shape(all_attrs)) + all_attrs = np.array(all_attrs).mean(axis=(0)) - # print('after', all_attrs) - # print('after shape', np.shape(all_attrs)) + features_attrs = all_attrs.mean(axis=(0)) - # print('feat shape ', np.shape(features_attrs)) + timestep_attrs = all_attrs.mean(axis=(1)) - # print('timestep shape', np.shape(timestep_attrs)) + # normalized_means = (means - means.min()) / (means.max() - means.min()) if plot: - # Create x values (assuming you want a simple sequential x-axis) - # Assuming you have 57 variables x_values = np.arange(1, 54) # Plotting the featrue means plt.figure(figsize=(8, 6)) @@ -567,11 +559,11 @@ def explantation_captum(self, test_loader, method, log_dir=".", plot=False, **kw markersize=8, ) plt.xlabel("Time Step") - plt.ylabel(" Attribution") - plt.title("Attribution Values") + plt.ylabel("{} Attribution".format(method_name)) + plt.title("{} Attribution Values".format(method_name)) plt.xticks(x_values) # Set x-ticks to match the number of features plt.tight_layout() - plt.savefig(log_dir / "attribution_features_plot.png", bbox_inches="tight") + plt.savefig(log_dir / "{}_attribution_features_plot.png".format(method_name), bbox_inches="tight") plt.figure(figsize=(8, 6)) x_values = np.arange(1, 25) plt.plot( @@ -584,15 +576,24 @@ def explantation_captum(self, test_loader, method, log_dir=".", plot=False, **kw markersize=8, ) plt.xlabel("Time Step") - plt.ylabel("Normalized Attribution") - plt.title("Attribution Values") + plt.ylabel("{} Attribution ".format(method_name)) + plt.title("{} Attribution Values".format(method_name)) plt.xticks(x_values) # Set x-ticks to match the number of features plt.tight_layout() - plt.savefig(log_dir / "attribution_plot.png", bbox_inches="tight") + plt.savefig(log_dir / "{}_attribution_plot.png".format(method_name), bbox_inches="tight") return all_attrs, features_attrs, timestep_attrs def Faithfulness_Correlation( - self, test_loader, attribution, similarity_func=None, nr_runs=100, pertrub=None, subset_size=3, feature=False, time_step=False, feature_timestep=False + self, + test_loader, + attribution, + similarity_func=None, + nr_runs=100, + pertrub=None, + subset_size=3, + feature=False, + time_step=False, + feature_timestep=False, ): """ Implementation of faithfulness correlation by Bhatt et al., 2020. @@ -651,7 +652,6 @@ def add_noise(x, indices, time_step, feature_timestep): if time_step: x["encoder_cont"][:, indices, :] += noise[:, indices, :] elif feature_timestep: - x["encoder_cont"][:, indices[0], :][:, :, indices[1]] += noise[:, indices[0], :][:, :, indices[1]] def apply_baseline(x, indices, time_step, feature_timestep): @@ -662,6 +662,7 @@ def apply_baseline(x, indices, time_step, feature_timestep): mask[:, indices[0], :][:, :, indices[1]] = 0 mask = mask.to(x["encoder_cont"].device) x["encoder_cont"] *= mask + for i_ix in range(nr_runs): # Randomly mask by subset size. @@ -707,7 +708,9 @@ def apply_baseline(x, indices, time_step, feature_timestep): score = np.nanmean(similarities) return score - def Data_Randomization(self, test_loader, attribution, explain_method, random_model, similarity_func=None, test_dataset=None, **kwargs): + def Data_Randomization( + self, test_loader, attribution, explain_method, random_model, similarity_func=None, test_dataset=None, **kwargs + ): """ Implementation of the Random Logit Metric by Sixt et al., 2020. @@ -725,7 +728,6 @@ def Data_Randomization(self, test_loader, attribution, explain_method, random_mo if similarity_func is None: similarity_func = distance_euclidean if explain_method == "Attention": - Attention_weights = random_model.interpertations(test_loader) score = similarity_func(Attention_weights["attention"].cpu().numpy(), attribution.cpu().numpy()) @@ -766,13 +768,9 @@ def Data_Randomization(self, test_loader, attribution, explain_method, random_mo explantation = explain_method(random_model.forward_captum) # Reformat attributions. if explain_method is not captum.attr.Saliency: - attr = explantation.attribute( - data, baselines=baselines, **kwargs - ) + attr = explantation.attribute(data, baselines=baselines, **kwargs) else: - attr = explantation.attribute( - data, **kwargs - ) + attr = explantation.attribute(data, **kwargs) # Convert attributions to numpy array and append to the list a_perturbed.append(attr[0].cpu().detach().numpy()) diff --git a/icu_benchmarks/run.py b/icu_benchmarks/run.py index 182a1814..1faadab6 100644 --- a/icu_benchmarks/run.py +++ b/icu_benchmarks/run.py @@ -117,8 +117,7 @@ def main(my_args=tuple(sys.argv[1:])): name_datasets(args.name, args.name, args.name) run_dir = create_run_dir(log_dir) source_dir = args.source_dir - logging.info( - f"Will load weights from {source_dir} and bind train gin-config. Note: this might override your config.") + logging.info(f"Will load weights from {source_dir} and bind train gin-config. Note: this might override your config.") gin.parse_config_file(source_dir / "train_config.gin") elif args.samples and args.source_dir is not None: # Train model with limited samples and bind existing config logging.info("Binding train gin-config. Note: this might override your config.") @@ -131,8 +130,7 @@ def main(my_args=tuple(sys.argv[1:])): name_datasets(args.name, args.name, args.name) hp_checkpoint = log_dir / args.hp_checkpoint if args.hp_checkpoint else None model_path = ( - Path("configs") / ("imputation_models" if mode == - RunMode.imputation else "prediction_models") / f"{model}.gin" + Path("configs") / ("imputation_models" if mode == RunMode.imputation else "prediction_models") / f"{model}.gin" ) gin_config_files = ( [Path(f"configs/experiments/{args.experiment}.gin")] @@ -155,8 +153,6 @@ def main(my_args=tuple(sys.argv[1:])): verbose=verbose, pytorch_forecasting=pytorch_forecasting, random_labels=random_labels, - - ) log_full_line(f"Logging to {run_dir.resolve()}", level=logging.INFO) diff --git a/icu_benchmarks/run_utils.py b/icu_benchmarks/run_utils.py index 0f559e4c..944295ea 100644 --- a/icu_benchmarks/run_utils.py +++ b/icu_benchmarks/run_utils.py @@ -305,8 +305,7 @@ def load_pretrained_imputation_model(use_pretrained_imputation): pretrained_imputation_model_checkpoint = torch.load(use_pretrained_imputation, map_location=torch.device("cpu")) if isinstance(pretrained_imputation_model_checkpoint, dict): imputation_model_class = pretrained_imputation_model_checkpoint["class"] - pretrained_imputation_model = imputation_model_class( - **pretrained_imputation_model_checkpoint["hyper_parameters"]) + pretrained_imputation_model = imputation_model_class(**pretrained_imputation_model_checkpoint["hyper_parameters"]) pretrained_imputation_model.set_trained_columns(pretrained_imputation_model_checkpoint["trained_columns"]) pretrained_imputation_model.load_state_dict(pretrained_imputation_model_checkpoint["state_dict"]) else: diff --git a/icu_benchmarks/tuning/hyperparameters.py b/icu_benchmarks/tuning/hyperparameters.py index 7c02735e..1458a7c2 100644 --- a/icu_benchmarks/tuning/hyperparameters.py +++ b/icu_benchmarks/tuning/hyperparameters.py @@ -118,7 +118,7 @@ def bind_params_and_train(hyperparams): verbose=verbose, wandb=wandb, pytorch_forecasting=pytorch_forecasting, - random_labels=random_labels + random_labels=random_labels, ) header = ["ITERATION"] + hyperparams_names + ["LOSS AT ITERATION"] From 54a6f18e97a54a4078554faa67614607dffcd66d Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Fri, 24 Nov 2023 14:41:20 +0100 Subject: [PATCH 113/142] latest --- configs/prediction_models/DeepARpytorch.gin | 12 +- configs/prediction_models/RNNpytorch.gin | 9 +- configs/tasks/Regression.gin | 4 +- icu_benchmarks/data/loader.py | 9 +- icu_benchmarks/data/preprocessor.py | 13 +- icu_benchmarks/models/dl_models.py | 15 +- icu_benchmarks/models/train.py | 70 ++-- icu_benchmarks/models/wrappers.py | 391 +++++++++++++------- 8 files changed, 329 insertions(+), 194 deletions(-) diff --git a/configs/prediction_models/DeepARpytorch.gin b/configs/prediction_models/DeepARpytorch.gin index bb18cbd0..1b7a9ca0 100644 --- a/configs/prediction_models/DeepARpytorch.gin +++ b/configs/prediction_models/DeepARpytorch.gin @@ -23,15 +23,17 @@ model/hyperparameter.lr_scheduler = "exponential" # Dataset params PredictionDatasetpytorch.max_encoder_length = 24 -PredictionDatasetpytorch.max_prediction_length = 24 -PredictionDatasetpytorch.time_varying_known_reals=['alb_lag_24', 'alp_lag_24', 'alt_lag_24', 'ast_lag_24', 'be_lag_24', 'bicar_lag_24', 'bili_lag_24', 'bili_dir_lag_24', 'bnd_lag_24', 'bun_lag_24', 'ca_lag_24', 'cai_lag_24', 'ck_lag_24', 'ckmb_lag_24', 'cl_lag_24', 'crea_lag_24', 'crp_lag_24', 'dbp_lag_24', 'fgn_lag_24', 'fio2_lag_24', 'glu_lag_24', 'hgb_lag_24', 'hr_lag_24', 'inr_pt_lag_24', 'k_lag_24', 'lact_lag_24', 'lymph_lag_24', 'map_lag_24', 'mch_lag_24', 'mchc_lag_24', 'mcv_lag_24', 'methb_lag_24', 'mg_lag_24', 'na_lag_24', 'neut_lag_24', 'o2sat_lag_24', 'pco2_lag_24', 'ph_lag_24', 'phos_lag_24', 'plt_lag_24', 'po2_lag_24', 'ptt_lag_24', 'resp_lag_24', 'sbp_lag_24', 'temp_lag_24', 'tnt_lag_24', 'urine_lag_24', 'wbc_lag_24', 'alb_lag_25', 'alp_lag_25', 'alt_lag_25', 'ast_lag_25', 'be_lag_25', 'bicar_lag_25', 'bili_lag_25', 'bili_dir_lag_25', 'bnd_lag_25', 'bun_lag_25', 'ca_lag_25', 'cai_lag_25', 'ck_lag_25', 'ckmb_lag_25', 'cl_lag_25', 'crea_lag_25', 'crp_lag_25', 'dbp_lag_25', 'fgn_lag_25', 'fio2_lag_25', 'glu_lag_25', 'hgb_lag_25', 'hr_lag_25', 'inr_pt_lag_25', 'k_lag_25', 'lact_lag_25', 'lymph_lag_25', 'map_lag_25', 'mch_lag_25', 'mchc_lag_25', 'mcv_lag_25', 'methb_lag_25', 'mg_lag_25', 'na_lag_25', 'neut_lag_25', 'o2sat_lag_25', 'pco2_lag_25', 'ph_lag_25', 'phos_lag_25', 'plt_lag_25', 'po2_lag_25', 'ptt_lag_25', 'resp_lag_25', 'sbp_lag_25', 'temp_lag_25', 'tnt_lag_25', 'urine_lag_25', 'wbc_lag_25', 'alb_lag_26', 'alp_lag_26', 'alt_lag_26', 'ast_lag_26', 'be_lag_26', 'bicar_lag_26', 'bili_lag_26', 'bili_dir_lag_26', 'bnd_lag_26', 'bun_lag_26', 'ca_lag_26', 'cai_lag_26', 'ck_lag_26', 'ckmb_lag_26', 'cl_lag_26', 'crea_lag_26', 'crp_lag_26', 'dbp_lag_26', 'fgn_lag_26', 'fio2_lag_26', 'glu_lag_26', 'hgb_lag_26', 'hr_lag_26', 'inr_pt_lag_26', 'k_lag_26', 'lact_lag_26', 'lymph_lag_26', 'map_lag_26', 'mch_lag_26', 'mchc_lag_26', 'mcv_lag_26', 'methb_lag_26', 'mg_lag_26', 'na_lag_26', 'neut_lag_26', 'o2sat_lag_26', 'pco2_lag_26', 'ph_lag_26', 'phos_lag_26', 'plt_lag_26', 'po2_lag_26', 'ptt_lag_26', 'resp_lag_26', 'sbp_lag_26', 'temp_lag_26', 'tnt_lag_26', 'urine_lag_26', 'wbc_lag_26', 'alb_lag_27', 'alp_lag_27', 'alt_lag_27', 'ast_lag_27', 'be_lag_27', 'bicar_lag_27', 'bili_lag_27', 'bili_dir_lag_27', 'bnd_lag_27', 'bun_lag_27', 'ca_lag_27', 'cai_lag_27', 'ck_lag_27', 'ckmb_lag_27', 'cl_lag_27', 'crea_lag_27', 'crp_lag_27', 'dbp_lag_27', 'fgn_lag_27', 'fio2_lag_27', 'glu_lag_27', 'hgb_lag_27', 'hr_lag_27', 'inr_pt_lag_27', 'k_lag_27', 'lact_lag_27', 'lymph_lag_27', 'map_lag_27', 'mch_lag_27', 'mchc_lag_27', 'mcv_lag_27', 'methb_lag_27', 'mg_lag_27', 'na_lag_27', 'neut_lag_27', 'o2sat_lag_27', 'pco2_lag_27', 'ph_lag_27', 'phos_lag_27', 'plt_lag_27', 'po2_lag_27', 'ptt_lag_27', 'resp_lag_27', 'sbp_lag_27', 'temp_lag_27', 'tnt_lag_27', 'urine_lag_27', 'wbc_lag_27', 'alb_lag_28', 'alp_lag_28', 'alt_lag_28', 'ast_lag_28', 'be_lag_28', 'bicar_lag_28', 'bili_lag_28', 'bili_dir_lag_28', 'bnd_lag_28', 'bun_lag_28', 'ca_lag_28', 'cai_lag_28', 'ck_lag_28', 'ckmb_lag_28', 'cl_lag_28', 'crea_lag_28', 'crp_lag_28', 'dbp_lag_28', 'fgn_lag_28', 'fio2_lag_28', 'glu_lag_28', 'hgb_lag_28', 'hr_lag_28', 'inr_pt_lag_28', 'k_lag_28', 'lact_lag_28', 'lymph_lag_28', 'map_lag_28', 'mch_lag_28', 'mchc_lag_28', 'mcv_lag_28', 'methb_lag_28', 'mg_lag_28', 'na_lag_28', 'neut_lag_28', 'o2sat_lag_28', 'pco2_lag_28', 'ph_lag_28', 'phos_lag_28', 'plt_lag_28', 'po2_lag_28', 'ptt_lag_28', 'resp_lag_28', 'sbp_lag_28', 'temp_lag_28', 'tnt_lag_28', 'urine_lag_28', 'wbc_lag_28', 'alb_lag_29', 'alp_lag_29', 'alt_lag_29', 'ast_lag_29', 'be_lag_29', 'bicar_lag_29', 'bili_lag_29', 'bili_dir_lag_29', 'bnd_lag_29', 'bun_lag_29', 'ca_lag_29', 'cai_lag_29', 'ck_lag_29', 'ckmb_lag_29', 'cl_lag_29', 'crea_lag_29', 'crp_lag_29', 'dbp_lag_29', 'fgn_lag_29', 'fio2_lag_29', 'glu_lag_29', 'hgb_lag_29', 'hr_lag_29', 'inr_pt_lag_29', 'k_lag_29', 'lact_lag_29', 'lymph_lag_29', 'map_lag_29', 'mch_lag_29', 'mchc_lag_29', 'mcv_lag_29', 'methb_lag_29', 'mg_lag_29', 'na_lag_29', 'neut_lag_29', 'o2sat_lag_29', 'pco2_lag_29', 'ph_lag_29', 'phos_lag_29', 'plt_lag_29', 'po2_lag_29', 'ptt_lag_29', 'resp_lag_29', 'sbp_lag_29', 'temp_lag_29', 'tnt_lag_29', 'urine_lag_29', 'wbc_lag_29', 'alb_lag_30', 'alp_lag_30', 'alt_lag_30', 'ast_lag_30', 'be_lag_30', 'bicar_lag_30', 'bili_lag_30', 'bili_dir_lag_30', 'bnd_lag_30', 'bun_lag_30', 'ca_lag_30', 'cai_lag_30', 'ck_lag_30', 'ckmb_lag_30', 'cl_lag_30', 'crea_lag_30', 'crp_lag_30', 'dbp_lag_30', 'fgn_lag_30', 'fio2_lag_30', 'glu_lag_30', 'hgb_lag_30', 'hr_lag_30', 'inr_pt_lag_30', 'k_lag_30', 'lact_lag_30', 'lymph_lag_30', 'map_lag_30', 'mch_lag_30', 'mchc_lag_30', 'mcv_lag_30', 'methb_lag_30', 'mg_lag_30', 'na_lag_30', 'neut_lag_30', 'o2sat_lag_30', 'pco2_lag_30', 'ph_lag_30', 'phos_lag_30', 'plt_lag_30', 'po2_lag_30', 'ptt_lag_30', 'resp_lag_30', 'sbp_lag_30', 'temp_lag_30', 'tnt_lag_30', 'urine_lag_30', 'wbc_lag_30', 'alb_lag_31', 'alp_lag_31', 'alt_lag_31', 'ast_lag_31', 'be_lag_31', 'bicar_lag_31', 'bili_lag_31', 'bili_dir_lag_31', 'bnd_lag_31', 'bun_lag_31', 'ca_lag_31', 'cai_lag_31', 'ck_lag_31', 'ckmb_lag_31', 'cl_lag_31', 'crea_lag_31', 'crp_lag_31', 'dbp_lag_31', 'fgn_lag_31', 'fio2_lag_31', 'glu_lag_31', 'hgb_lag_31', 'hr_lag_31', 'inr_pt_lag_31', 'k_lag_31', 'lact_lag_31', 'lymph_lag_31', 'map_lag_31', 'mch_lag_31', 'mchc_lag_31', 'mcv_lag_31', 'methb_lag_31', 'mg_lag_31', 'na_lag_31', 'neut_lag_31', 'o2sat_lag_31', 'pco2_lag_31', 'ph_lag_31', 'phos_lag_31', 'plt_lag_31', 'po2_lag_31', 'ptt_lag_31', 'resp_lag_31', 'sbp_lag_31', 'temp_lag_31', 'tnt_lag_31', 'urine_lag_31', 'wbc_lag_31', 'alb_lag_32', 'alp_lag_32', 'alt_lag_32', 'ast_lag_32', 'be_lag_32', 'bicar_lag_32', 'bili_lag_32', 'bili_dir_lag_32', 'bnd_lag_32', 'bun_lag_32', 'ca_lag_32', 'cai_lag_32', 'ck_lag_32', 'ckmb_lag_32', 'cl_lag_32', 'crea_lag_32', 'crp_lag_32', 'dbp_lag_32', 'fgn_lag_32', 'fio2_lag_32', 'glu_lag_32', 'hgb_lag_32', 'hr_lag_32', 'inr_pt_lag_32', 'k_lag_32', 'lact_lag_32', 'lymph_lag_32', 'map_lag_32', 'mch_lag_32', 'mchc_lag_32', 'mcv_lag_32', 'methb_lag_32', 'mg_lag_32', 'na_lag_32', 'neut_lag_32', 'o2sat_lag_32', 'pco2_lag_32', 'ph_lag_32', 'phos_lag_32', 'plt_lag_32', 'po2_lag_32', 'ptt_lag_32', 'resp_lag_32', 'sbp_lag_32', 'temp_lag_32', 'tnt_lag_32', 'urine_lag_32', 'wbc_lag_32', 'alb_lag_33', 'alp_lag_33', 'alt_lag_33', 'ast_lag_33', 'be_lag_33', 'bicar_lag_33', 'bili_lag_33', 'bili_dir_lag_33', 'bnd_lag_33', 'bun_lag_33', 'ca_lag_33', 'cai_lag_33', 'ck_lag_33', 'ckmb_lag_33', 'cl_lag_33', 'crea_lag_33', 'crp_lag_33', 'dbp_lag_33', 'fgn_lag_33', 'fio2_lag_33', 'glu_lag_33', 'hgb_lag_33', 'hr_lag_33', 'inr_pt_lag_33', 'k_lag_33', 'lact_lag_33', 'lymph_lag_33', 'map_lag_33', 'mch_lag_33', 'mchc_lag_33', 'mcv_lag_33', 'methb_lag_33', 'mg_lag_33', 'na_lag_33', 'neut_lag_33', 'o2sat_lag_33', 'pco2_lag_33', 'ph_lag_33', 'phos_lag_33', 'plt_lag_33', 'po2_lag_33', 'ptt_lag_33', 'resp_lag_33', 'sbp_lag_33', 'temp_lag_33', 'tnt_lag_33', 'urine_lag_33', 'wbc_lag_33', 'alb_lag_34', 'alp_lag_34', 'alt_lag_34', 'ast_lag_34', 'be_lag_34', 'bicar_lag_34', 'bili_lag_34', 'bili_dir_lag_34', 'bnd_lag_34', 'bun_lag_34', 'ca_lag_34', 'cai_lag_34', 'ck_lag_34', 'ckmb_lag_34', 'cl_lag_34', 'crea_lag_34', 'crp_lag_34', 'dbp_lag_34', 'fgn_lag_34', 'fio2_lag_34', 'glu_lag_34', 'hgb_lag_34', 'hr_lag_34', 'inr_pt_lag_34', 'k_lag_34', 'lact_lag_34', 'lymph_lag_34', 'map_lag_34', 'mch_lag_34', 'mchc_lag_34', 'mcv_lag_34', 'methb_lag_34', 'mg_lag_34', 'na_lag_34', 'neut_lag_34', 'o2sat_lag_34', 'pco2_lag_34', 'ph_lag_34', 'phos_lag_34', 'plt_lag_34', 'po2_lag_34', 'ptt_lag_34', 'resp_lag_34', 'sbp_lag_34', 'temp_lag_34', 'tnt_lag_34', 'urine_lag_34', 'wbc_lag_34', 'alb_lag_35', 'alp_lag_35', 'alt_lag_35', 'ast_lag_35', 'be_lag_35', 'bicar_lag_35', 'bili_lag_35', 'bili_dir_lag_35', 'bnd_lag_35', 'bun_lag_35', 'ca_lag_35', 'cai_lag_35', 'ck_lag_35', 'ckmb_lag_35', 'cl_lag_35', 'crea_lag_35', 'crp_lag_35', 'dbp_lag_35', 'fgn_lag_35', 'fio2_lag_35', 'glu_lag_35', 'hgb_lag_35', 'hr_lag_35', 'inr_pt_lag_35', 'k_lag_35', 'lact_lag_35', 'lymph_lag_35', 'map_lag_35', 'mch_lag_35', 'mchc_lag_35', 'mcv_lag_35', 'methb_lag_35', 'mg_lag_35', 'na_lag_35', 'neut_lag_35', 'o2sat_lag_35', 'pco2_lag_35', 'ph_lag_35', 'phos_lag_35', 'plt_lag_35', 'po2_lag_35', 'ptt_lag_35', 'resp_lag_35', 'sbp_lag_35', 'temp_lag_35', 'tnt_lag_35', 'urine_lag_35', 'wbc_lag_35', 'alb_lag_36', 'alp_lag_36', 'alt_lag_36', 'ast_lag_36', 'be_lag_36', 'bicar_lag_36', 'bili_lag_36', 'bili_dir_lag_36', 'bnd_lag_36', 'bun_lag_36', 'ca_lag_36', 'cai_lag_36', 'ck_lag_36', 'ckmb_lag_36', 'cl_lag_36', 'crea_lag_36', 'crp_lag_36', 'dbp_lag_36', 'fgn_lag_36', 'fio2_lag_36', 'glu_lag_36', 'hgb_lag_36', 'hr_lag_36', 'inr_pt_lag_36', 'k_lag_36', 'lact_lag_36', 'lymph_lag_36', 'map_lag_36', 'mch_lag_36', 'mchc_lag_36', 'mcv_lag_36', 'methb_lag_36', 'mg_lag_36', 'na_lag_36', 'neut_lag_36', 'o2sat_lag_36', 'pco2_lag_36', 'ph_lag_36', 'phos_lag_36', 'plt_lag_36', 'po2_lag_36', 'ptt_lag_36', 'resp_lag_36', 'sbp_lag_36', 'temp_lag_36', 'tnt_lag_36', 'urine_lag_36', 'wbc_lag_36', 'alb_lag_37', 'alp_lag_37', 'alt_lag_37', 'ast_lag_37', 'be_lag_37', 'bicar_lag_37', 'bili_lag_37', 'bili_dir_lag_37', 'bnd_lag_37', 'bun_lag_37', 'ca_lag_37', 'cai_lag_37', 'ck_lag_37', 'ckmb_lag_37', 'cl_lag_37', 'crea_lag_37', 'crp_lag_37', 'dbp_lag_37', 'fgn_lag_37', 'fio2_lag_37', 'glu_lag_37', 'hgb_lag_37', 'hr_lag_37', 'inr_pt_lag_37', 'k_lag_37', 'lact_lag_37', 'lymph_lag_37', 'map_lag_37', 'mch_lag_37', 'mchc_lag_37', 'mcv_lag_37', 'methb_lag_37', 'mg_lag_37', 'na_lag_37', 'neut_lag_37', 'o2sat_lag_37', 'pco2_lag_37', 'ph_lag_37', 'phos_lag_37', 'plt_lag_37', 'po2_lag_37', 'ptt_lag_37', 'resp_lag_37', 'sbp_lag_37', 'temp_lag_37', 'tnt_lag_37', 'urine_lag_37', 'wbc_lag_37', 'alb_lag_38', 'alp_lag_38', 'alt_lag_38', 'ast_lag_38', 'be_lag_38', 'bicar_lag_38', 'bili_lag_38', 'bili_dir_lag_38', 'bnd_lag_38', 'bun_lag_38', 'ca_lag_38', 'cai_lag_38', 'ck_lag_38', 'ckmb_lag_38', 'cl_lag_38', 'crea_lag_38', 'crp_lag_38', 'dbp_lag_38', 'fgn_lag_38', 'fio2_lag_38', 'glu_lag_38', 'hgb_lag_38', 'hr_lag_38', 'inr_pt_lag_38', 'k_lag_38', 'lact_lag_38', 'lymph_lag_38', 'map_lag_38', 'mch_lag_38', 'mchc_lag_38', 'mcv_lag_38', 'methb_lag_38', 'mg_lag_38', 'na_lag_38', 'neut_lag_38', 'o2sat_lag_38', 'pco2_lag_38', 'ph_lag_38', 'phos_lag_38', 'plt_lag_38', 'po2_lag_38', 'ptt_lag_38', 'resp_lag_38', 'sbp_lag_38', 'temp_lag_38', 'tnt_lag_38', 'urine_lag_38', 'wbc_lag_38', 'alb_lag_39', 'alp_lag_39', 'alt_lag_39', 'ast_lag_39', 'be_lag_39', 'bicar_lag_39', 'bili_lag_39', 'bili_dir_lag_39', 'bnd_lag_39', 'bun_lag_39', 'ca_lag_39', 'cai_lag_39', 'ck_lag_39', 'ckmb_lag_39', 'cl_lag_39', 'crea_lag_39', 'crp_lag_39', 'dbp_lag_39', 'fgn_lag_39', 'fio2_lag_39', 'glu_lag_39', 'hgb_lag_39', 'hr_lag_39', 'inr_pt_lag_39', 'k_lag_39', 'lact_lag_39', 'lymph_lag_39', 'map_lag_39', 'mch_lag_39', 'mchc_lag_39', 'mcv_lag_39', 'methb_lag_39', 'mg_lag_39', 'na_lag_39', 'neut_lag_39', 'o2sat_lag_39', 'pco2_lag_39', 'ph_lag_39', 'phos_lag_39', 'plt_lag_39', 'po2_lag_39', 'ptt_lag_39', 'resp_lag_39', 'sbp_lag_39', 'temp_lag_39', 'tnt_lag_39', 'urine_lag_39', 'wbc_lag_39', 'alb_lag_40', 'alp_lag_40', 'alt_lag_40', 'ast_lag_40', 'be_lag_40', 'bicar_lag_40', 'bili_lag_40', 'bili_dir_lag_40', 'bnd_lag_40', 'bun_lag_40', 'ca_lag_40', 'cai_lag_40', 'ck_lag_40', 'ckmb_lag_40', 'cl_lag_40', 'crea_lag_40', 'crp_lag_40', 'dbp_lag_40', 'fgn_lag_40', 'fio2_lag_40', 'glu_lag_40', 'hgb_lag_40', 'hr_lag_40', 'inr_pt_lag_40', 'k_lag_40', 'lact_lag_40', 'lymph_lag_40', 'map_lag_40', 'mch_lag_40', 'mchc_lag_40', 'mcv_lag_40', 'methb_lag_40', 'mg_lag_40', 'na_lag_40', 'neut_lag_40', 'o2sat_lag_40', 'pco2_lag_40', 'ph_lag_40', 'phos_lag_40', 'plt_lag_40', 'po2_lag_40', 'ptt_lag_40', 'resp_lag_40', 'sbp_lag_40', 'temp_lag_40', 'tnt_lag_40', 'urine_lag_40', 'wbc_lag_40', 'alb_lag_41', 'alp_lag_41', 'alt_lag_41', 'ast_lag_41', 'be_lag_41', 'bicar_lag_41', 'bili_lag_41', 'bili_dir_lag_41', 'bnd_lag_41', 'bun_lag_41', 'ca_lag_41', 'cai_lag_41', 'ck_lag_41', 'ckmb_lag_41', 'cl_lag_41', 'crea_lag_41', 'crp_lag_41', 'dbp_lag_41', 'fgn_lag_41', 'fio2_lag_41', 'glu_lag_41', 'hgb_lag_41', 'hr_lag_41', 'inr_pt_lag_41', 'k_lag_41', 'lact_lag_41', 'lymph_lag_41', 'map_lag_41', 'mch_lag_41', 'mchc_lag_41', 'mcv_lag_41', 'methb_lag_41', 'mg_lag_41', 'na_lag_41', 'neut_lag_41', 'o2sat_lag_41', 'pco2_lag_41', 'ph_lag_41', 'phos_lag_41', 'plt_lag_41', 'po2_lag_41', 'ptt_lag_41', 'resp_lag_41', 'sbp_lag_41', 'temp_lag_41', 'tnt_lag_41', 'urine_lag_41', 'wbc_lag_41', 'alb_lag_42', 'alp_lag_42', 'alt_lag_42', 'ast_lag_42', 'be_lag_42', 'bicar_lag_42', 'bili_lag_42', 'bili_dir_lag_42', 'bnd_lag_42', 'bun_lag_42', 'ca_lag_42', 'cai_lag_42', 'ck_lag_42', 'ckmb_lag_42', 'cl_lag_42', 'crea_lag_42', 'crp_lag_42', 'dbp_lag_42', 'fgn_lag_42', 'fio2_lag_42', 'glu_lag_42', 'hgb_lag_42', 'hr_lag_42', 'inr_pt_lag_42', 'k_lag_42', 'lact_lag_42', 'lymph_lag_42', 'map_lag_42', 'mch_lag_42', 'mchc_lag_42', 'mcv_lag_42', 'methb_lag_42', 'mg_lag_42', 'na_lag_42', 'neut_lag_42', 'o2sat_lag_42', 'pco2_lag_42', 'ph_lag_42', 'phos_lag_42', 'plt_lag_42', 'po2_lag_42', 'ptt_lag_42', 'resp_lag_42', 'sbp_lag_42', 'temp_lag_42', 'tnt_lag_42', 'urine_lag_42', 'wbc_lag_42', 'alb_lag_43', 'alp_lag_43', 'alt_lag_43', 'ast_lag_43', 'be_lag_43', 'bicar_lag_43', 'bili_lag_43', 'bili_dir_lag_43', 'bnd_lag_43', 'bun_lag_43', 'ca_lag_43', 'cai_lag_43', 'ck_lag_43', 'ckmb_lag_43', 'cl_lag_43', 'crea_lag_43', 'crp_lag_43', 'dbp_lag_43', 'fgn_lag_43', 'fio2_lag_43', 'glu_lag_43', 'hgb_lag_43', 'hr_lag_43', 'inr_pt_lag_43', 'k_lag_43', 'lact_lag_43', 'lymph_lag_43', 'map_lag_43', 'mch_lag_43', 'mchc_lag_43', 'mcv_lag_43', 'methb_lag_43', 'mg_lag_43', 'na_lag_43', 'neut_lag_43', 'o2sat_lag_43', 'pco2_lag_43', 'ph_lag_43', 'phos_lag_43', 'plt_lag_43', 'po2_lag_43', 'ptt_lag_43', 'resp_lag_43', 'sbp_lag_43', 'temp_lag_43', 'tnt_lag_43', 'urine_lag_43', 'wbc_lag_43', 'alb_lag_44', 'alp_lag_44', 'alt_lag_44', 'ast_lag_44', 'be_lag_44', 'bicar_lag_44', 'bili_lag_44', 'bili_dir_lag_44', 'bnd_lag_44', 'bun_lag_44', 'ca_lag_44', 'cai_lag_44', 'ck_lag_44', 'ckmb_lag_44', 'cl_lag_44', 'crea_lag_44', 'crp_lag_44', 'dbp_lag_44', 'fgn_lag_44', 'fio2_lag_44', 'glu_lag_44', 'hgb_lag_44', 'hr_lag_44', 'inr_pt_lag_44', 'k_lag_44', 'lact_lag_44', 'lymph_lag_44', 'map_lag_44', 'mch_lag_44', 'mchc_lag_44', 'mcv_lag_44', 'methb_lag_44', 'mg_lag_44', 'na_lag_44', 'neut_lag_44', 'o2sat_lag_44', 'pco2_lag_44', 'ph_lag_44', 'phos_lag_44', 'plt_lag_44', 'po2_lag_44', 'ptt_lag_44', 'resp_lag_44', 'sbp_lag_44', 'temp_lag_44', 'tnt_lag_44', 'urine_lag_44', 'wbc_lag_44', 'alb_lag_45', 'alp_lag_45', 'alt_lag_45', 'ast_lag_45', 'be_lag_45', 'bicar_lag_45', 'bili_lag_45', 'bili_dir_lag_45', 'bnd_lag_45', 'bun_lag_45', 'ca_lag_45', 'cai_lag_45', 'ck_lag_45', 'ckmb_lag_45', 'cl_lag_45', 'crea_lag_45', 'crp_lag_45', 'dbp_lag_45', 'fgn_lag_45', 'fio2_lag_45', 'glu_lag_45', 'hgb_lag_45', 'hr_lag_45', 'inr_pt_lag_45', 'k_lag_45', 'lact_lag_45', 'lymph_lag_45', 'map_lag_45', 'mch_lag_45', 'mchc_lag_45', 'mcv_lag_45', 'methb_lag_45', 'mg_lag_45', 'na_lag_45', 'neut_lag_45', 'o2sat_lag_45', 'pco2_lag_45', 'ph_lag_45', 'phos_lag_45', 'plt_lag_45', 'po2_lag_45', 'ptt_lag_45', 'resp_lag_45', 'sbp_lag_45', 'temp_lag_45', 'tnt_lag_45', 'urine_lag_45', 'wbc_lag_45', 'alb_lag_46', 'alp_lag_46', 'alt_lag_46', 'ast_lag_46', 'be_lag_46', 'bicar_lag_46', 'bili_lag_46', 'bili_dir_lag_46', 'bnd_lag_46', 'bun_lag_46', 'ca_lag_46', 'cai_lag_46', 'ck_lag_46', 'ckmb_lag_46', 'cl_lag_46', 'crea_lag_46', 'crp_lag_46', 'dbp_lag_46', 'fgn_lag_46', 'fio2_lag_46', 'glu_lag_46', 'hgb_lag_46', 'hr_lag_46', 'inr_pt_lag_46', 'k_lag_46', 'lact_lag_46', 'lymph_lag_46', 'map_lag_46', 'mch_lag_46', 'mchc_lag_46', 'mcv_lag_46', 'methb_lag_46', 'mg_lag_46', 'na_lag_46', 'neut_lag_46', 'o2sat_lag_46', 'pco2_lag_46', 'ph_lag_46', 'phos_lag_46', 'plt_lag_46', 'po2_lag_46', 'ptt_lag_46', 'resp_lag_46', 'sbp_lag_46', 'temp_lag_46', 'tnt_lag_46', 'urine_lag_46', 'wbc_lag_46', 'alb_lag_47', 'alp_lag_47', 'alt_lag_47', 'ast_lag_47', 'be_lag_47', 'bicar_lag_47', 'bili_lag_47', 'bili_dir_lag_47', 'bnd_lag_47', 'bun_lag_47', 'ca_lag_47', 'cai_lag_47', 'ck_lag_47', 'ckmb_lag_47', 'cl_lag_47', 'crea_lag_47', 'crp_lag_47', 'dbp_lag_47', 'fgn_lag_47', 'fio2_lag_47', 'glu_lag_47', 'hgb_lag_47', 'hr_lag_47', 'inr_pt_lag_47', 'k_lag_47', 'lact_lag_47', 'lymph_lag_47', 'map_lag_47', 'mch_lag_47', 'mchc_lag_47', 'mcv_lag_47', 'methb_lag_47', 'mg_lag_47', 'na_lag_47', 'neut_lag_47', 'o2sat_lag_47', 'pco2_lag_47', 'ph_lag_47', 'phos_lag_47', 'plt_lag_47', 'po2_lag_47', 'ptt_lag_47', 'resp_lag_47', 'sbp_lag_47', 'temp_lag_47', 'tnt_lag_47', 'urine_lag_47', 'wbc_lag_47', 'alb_lag_48', 'alp_lag_48', 'alt_lag_48', 'ast_lag_48', 'be_lag_48', 'bicar_lag_48', 'bili_lag_48', 'bili_dir_lag_48', 'bnd_lag_48', 'bun_lag_48', 'ca_lag_48', 'cai_lag_48', 'ck_lag_48', 'ckmb_lag_48', 'cl_lag_48', 'crea_lag_48', 'crp_lag_48', 'dbp_lag_48', 'fgn_lag_48', 'fio2_lag_48', 'glu_lag_48', 'hgb_lag_48', 'hr_lag_48', 'inr_pt_lag_48', 'k_lag_48', 'lact_lag_48', 'lymph_lag_48', 'map_lag_48', 'mch_lag_48', 'mchc_lag_48', 'mcv_lag_48', 'methb_lag_48', 'mg_lag_48', 'na_lag_48', 'neut_lag_48', 'o2sat_lag_48', 'pco2_lag_48', 'ph_lag_48', 'phos_lag_48', 'plt_lag_48', 'po2_lag_48', 'ptt_lag_48', 'resp_lag_48', 'sbp_lag_48', 'temp_lag_48', 'tnt_lag_48', 'urine_lag_48', 'wbc_lag_48'] +PredictionDatasetpytorch.max_prediction_length = 1 +PredictionDatasetpytorch.time_varying_known_reals=['alb_lag_1', 'alp_lag_1', 'alt_lag_1', 'ast_lag_1', 'be_lag_1', 'bicar_lag_1', 'bili_lag_1', 'bili_dir_lag_1', 'bnd_lag_1', 'bun_lag_1', 'ca_lag_1', 'cai_lag_1', 'ck_lag_1', 'ckmb_lag_1', 'cl_lag_1', 'crea_lag_1', 'crp_lag_1', 'dbp_lag_1', 'fgn_lag_1', 'fio2_lag_1', 'glu_lag_1', 'hgb_lag_1', 'hr_lag_1', 'inr_pt_lag_1', 'k_lag_1', 'lact_lag_1', 'lymph_lag_1', 'map_lag_1', 'mch_lag_1', 'mchc_lag_1', 'mcv_lag_1', 'methb_lag_1', 'mg_lag_1', 'na_lag_1', 'neut_lag_1', 'o2sat_lag_1', 'pco2_lag_1', 'ph_lag_1', 'phos_lag_1', 'plt_lag_1', 'po2_lag_1', 'ptt_lag_1', 'resp_lag_1', 'sbp_lag_1', 'temp_lag_1', 'tnt_lag_1', 'urine_lag_1', 'wbc_lag_1', 'alb_lag_2', 'alp_lag_2', 'alt_lag_2', 'ast_lag_2', 'be_lag_2', 'bicar_lag_2', 'bili_lag_2', 'bili_dir_lag_2', 'bnd_lag_2', 'bun_lag_2', 'ca_lag_2', 'cai_lag_2', 'ck_lag_2', 'ckmb_lag_2', 'cl_lag_2', 'crea_lag_2', 'crp_lag_2', 'dbp_lag_2', 'fgn_lag_2', 'fio2_lag_2', 'glu_lag_2', 'hgb_lag_2', 'hr_lag_2', 'inr_pt_lag_2', 'k_lag_2', 'lact_lag_2', 'lymph_lag_2', 'map_lag_2', 'mch_lag_2', 'mchc_lag_2', 'mcv_lag_2', 'methb_lag_2', 'mg_lag_2', 'na_lag_2', 'neut_lag_2', 'o2sat_lag_2', 'pco2_lag_2', 'ph_lag_2', 'phos_lag_2', 'plt_lag_2', 'po2_lag_2', 'ptt_lag_2', 'resp_lag_2', 'sbp_lag_2', 'temp_lag_2', 'tnt_lag_2', 'urine_lag_2', 'wbc_lag_2', 'alb_lag_3', 'alp_lag_3', 'alt_lag_3', 'ast_lag_3', 'be_lag_3', 'bicar_lag_3', 'bili_lag_3', 'bili_dir_lag_3', 'bnd_lag_3', 'bun_lag_3', 'ca_lag_3', 'cai_lag_3', 'ck_lag_3', 'ckmb_lag_3', 'cl_lag_3', 'crea_lag_3', 'crp_lag_3', 'dbp_lag_3', 'fgn_lag_3', 'fio2_lag_3', 'glu_lag_3', 'hgb_lag_3', 'hr_lag_3', 'inr_pt_lag_3', 'k_lag_3', 'lact_lag_3', 'lymph_lag_3', 'map_lag_3', 'mch_lag_3', 'mchc_lag_3', 'mcv_lag_3', 'methb_lag_3', 'mg_lag_3', 'na_lag_3', 'neut_lag_3', 'o2sat_lag_3', 'pco2_lag_3', 'ph_lag_3', 'phos_lag_3', 'plt_lag_3', 'po2_lag_3', 'ptt_lag_3', 'resp_lag_3', 'sbp_lag_3', 'temp_lag_3', 'tnt_lag_3', 'urine_lag_3', 'wbc_lag_3', 'alb_lag_4', 'alp_lag_4', 'alt_lag_4', 'ast_lag_4', 'be_lag_4', 'bicar_lag_4', 'bili_lag_4', 'bili_dir_lag_4', 'bnd_lag_4', 'bun_lag_4', 'ca_lag_4', 'cai_lag_4', 'ck_lag_4', 'ckmb_lag_4', 'cl_lag_4', 'crea_lag_4', 'crp_lag_4', 'dbp_lag_4', 'fgn_lag_4', 'fio2_lag_4', 'glu_lag_4', 'hgb_lag_4', 'hr_lag_4', 'inr_pt_lag_4', 'k_lag_4', 'lact_lag_4', 'lymph_lag_4', 'map_lag_4', 'mch_lag_4', 'mchc_lag_4', 'mcv_lag_4', 'methb_lag_4', 'mg_lag_4', 'na_lag_4', 'neut_lag_4', 'o2sat_lag_4', 'pco2_lag_4', 'ph_lag_4', 'phos_lag_4', 'plt_lag_4', 'po2_lag_4', 'ptt_lag_4', 'resp_lag_4', 'sbp_lag_4', 'temp_lag_4', 'tnt_lag_4', 'urine_lag_4', 'wbc_lag_4', 'alb_lag_5', 'alp_lag_5', 'alt_lag_5', 'ast_lag_5', 'be_lag_5', 'bicar_lag_5', 'bili_lag_5', 'bili_dir_lag_5', 'bnd_lag_5', 'bun_lag_5', 'ca_lag_5', 'cai_lag_5', 'ck_lag_5', 'ckmb_lag_5', 'cl_lag_5', 'crea_lag_5', 'crp_lag_5', 'dbp_lag_5', 'fgn_lag_5', 'fio2_lag_5', 'glu_lag_5', 'hgb_lag_5', 'hr_lag_5', 'inr_pt_lag_5', 'k_lag_5', 'lact_lag_5', 'lymph_lag_5', 'map_lag_5', 'mch_lag_5', 'mchc_lag_5', 'mcv_lag_5', 'methb_lag_5', 'mg_lag_5', 'na_lag_5', 'neut_lag_5', 'o2sat_lag_5', 'pco2_lag_5', 'ph_lag_5', 'phos_lag_5', 'plt_lag_5', 'po2_lag_5', 'ptt_lag_5', 'resp_lag_5', 'sbp_lag_5', 'temp_lag_5', 'tnt_lag_5', 'urine_lag_5', 'wbc_lag_5', 'alb_lag_6', 'alp_lag_6', 'alt_lag_6', 'ast_lag_6', 'be_lag_6', 'bicar_lag_6', 'bili_lag_6', 'bili_dir_lag_6', 'bnd_lag_6', 'bun_lag_6', 'ca_lag_6', 'cai_lag_6', 'ck_lag_6', 'ckmb_lag_6', 'cl_lag_6', 'crea_lag_6', 'crp_lag_6', 'dbp_lag_6', 'fgn_lag_6', 'fio2_lag_6', 'glu_lag_6', 'hgb_lag_6', 'hr_lag_6', 'inr_pt_lag_6', 'k_lag_6', 'lact_lag_6', 'lymph_lag_6', 'map_lag_6', 'mch_lag_6', 'mchc_lag_6', 'mcv_lag_6', 'methb_lag_6', 'mg_lag_6', 'na_lag_6', 'neut_lag_6', 'o2sat_lag_6', 'pco2_lag_6', 'ph_lag_6', 'phos_lag_6', 'plt_lag_6', 'po2_lag_6', 'ptt_lag_6', 'resp_lag_6', 'sbp_lag_6', 'temp_lag_6', 'tnt_lag_6', 'urine_lag_6', 'wbc_lag_6', 'alb_lag_7', 'alp_lag_7', 'alt_lag_7', 'ast_lag_7', 'be_lag_7', 'bicar_lag_7', 'bili_lag_7', 'bili_dir_lag_7', 'bnd_lag_7', 'bun_lag_7', 'ca_lag_7', 'cai_lag_7', 'ck_lag_7', 'ckmb_lag_7', 'cl_lag_7', 'crea_lag_7', 'crp_lag_7', 'dbp_lag_7', 'fgn_lag_7', 'fio2_lag_7', 'glu_lag_7', 'hgb_lag_7', 'hr_lag_7', 'inr_pt_lag_7', 'k_lag_7', 'lact_lag_7', 'lymph_lag_7', 'map_lag_7', 'mch_lag_7', 'mchc_lag_7', 'mcv_lag_7', 'methb_lag_7', 'mg_lag_7', 'na_lag_7', 'neut_lag_7', 'o2sat_lag_7', 'pco2_lag_7', 'ph_lag_7', 'phos_lag_7', 'plt_lag_7', 'po2_lag_7', 'ptt_lag_7', 'resp_lag_7', 'sbp_lag_7', 'temp_lag_7', 'tnt_lag_7', 'urine_lag_7', 'wbc_lag_7', 'alb_lag_8', 'alp_lag_8', 'alt_lag_8', 'ast_lag_8', 'be_lag_8', 'bicar_lag_8', 'bili_lag_8', 'bili_dir_lag_8', 'bnd_lag_8', 'bun_lag_8', 'ca_lag_8', 'cai_lag_8', 'ck_lag_8', 'ckmb_lag_8', 'cl_lag_8', 'crea_lag_8', 'crp_lag_8', 'dbp_lag_8', 'fgn_lag_8', 'fio2_lag_8', 'glu_lag_8', 'hgb_lag_8', 'hr_lag_8', 'inr_pt_lag_8', 'k_lag_8', 'lact_lag_8', 'lymph_lag_8', 'map_lag_8', 'mch_lag_8', 'mchc_lag_8', 'mcv_lag_8', 'methb_lag_8', 'mg_lag_8', 'na_lag_8', 'neut_lag_8', 'o2sat_lag_8', 'pco2_lag_8', 'ph_lag_8', 'phos_lag_8', 'plt_lag_8', 'po2_lag_8', 'ptt_lag_8', 'resp_lag_8', 'sbp_lag_8', 'temp_lag_8', 'tnt_lag_8', 'urine_lag_8', 'wbc_lag_8', 'alb_lag_9', 'alp_lag_9', 'alt_lag_9', 'ast_lag_9', 'be_lag_9', 'bicar_lag_9', 'bili_lag_9', 'bili_dir_lag_9', 'bnd_lag_9', 'bun_lag_9', 'ca_lag_9', 'cai_lag_9', 'ck_lag_9', 'ckmb_lag_9', 'cl_lag_9', 'crea_lag_9', 'crp_lag_9', 'dbp_lag_9', 'fgn_lag_9', 'fio2_lag_9', 'glu_lag_9', 'hgb_lag_9', 'hr_lag_9', 'inr_pt_lag_9', 'k_lag_9', 'lact_lag_9', 'lymph_lag_9', 'map_lag_9', 'mch_lag_9', 'mchc_lag_9', 'mcv_lag_9', 'methb_lag_9', 'mg_lag_9', 'na_lag_9', 'neut_lag_9', 'o2sat_lag_9', 'pco2_lag_9', 'ph_lag_9', 'phos_lag_9', 'plt_lag_9', 'po2_lag_9', 'ptt_lag_9', 'resp_lag_9', 'sbp_lag_9', 'temp_lag_9', 'tnt_lag_9', 'urine_lag_9', 'wbc_lag_9', 'alb_lag_10', 'alp_lag_10', 'alt_lag_10', 'ast_lag_10', 'be_lag_10', 'bicar_lag_10', 'bili_lag_10', 'bili_dir_lag_10', 'bnd_lag_10', 'bun_lag_10', 'ca_lag_10', 'cai_lag_10', 'ck_lag_10', 'ckmb_lag_10', 'cl_lag_10', 'crea_lag_10', 'crp_lag_10', 'dbp_lag_10', 'fgn_lag_10', 'fio2_lag_10', 'glu_lag_10', 'hgb_lag_10', 'hr_lag_10', 'inr_pt_lag_10', 'k_lag_10', 'lact_lag_10', 'lymph_lag_10', 'map_lag_10', 'mch_lag_10', 'mchc_lag_10', 'mcv_lag_10', 'methb_lag_10', 'mg_lag_10', 'na_lag_10', 'neut_lag_10', 'o2sat_lag_10', 'pco2_lag_10', 'ph_lag_10', 'phos_lag_10', 'plt_lag_10', 'po2_lag_10', 'ptt_lag_10', 'resp_lag_10', 'sbp_lag_10', 'temp_lag_10', 'tnt_lag_10', 'urine_lag_10', 'wbc_lag_10', 'alb_lag_11', 'alp_lag_11', 'alt_lag_11', 'ast_lag_11', 'be_lag_11', 'bicar_lag_11', 'bili_lag_11', 'bili_dir_lag_11', 'bnd_lag_11', 'bun_lag_11', 'ca_lag_11', 'cai_lag_11', 'ck_lag_11', 'ckmb_lag_11', 'cl_lag_11', 'crea_lag_11', 'crp_lag_11', 'dbp_lag_11', 'fgn_lag_11', 'fio2_lag_11', 'glu_lag_11', 'hgb_lag_11', 'hr_lag_11', 'inr_pt_lag_11', 'k_lag_11', 'lact_lag_11', 'lymph_lag_11', 'map_lag_11', 'mch_lag_11', 'mchc_lag_11', 'mcv_lag_11', 'methb_lag_11', 'mg_lag_11', 'na_lag_11', 'neut_lag_11', 'o2sat_lag_11', 'pco2_lag_11', 'ph_lag_11', 'phos_lag_11', 'plt_lag_11', 'po2_lag_11', 'ptt_lag_11', 'resp_lag_11', 'sbp_lag_11', 'temp_lag_11', 'tnt_lag_11', 'urine_lag_11', 'wbc_lag_11', 'alb_lag_12', 'alp_lag_12', 'alt_lag_12', 'ast_lag_12', 'be_lag_12', 'bicar_lag_12', 'bili_lag_12', 'bili_dir_lag_12', 'bnd_lag_12', 'bun_lag_12', 'ca_lag_12', 'cai_lag_12', 'ck_lag_12', 'ckmb_lag_12', 'cl_lag_12', 'crea_lag_12', 'crp_lag_12', 'dbp_lag_12', 'fgn_lag_12', 'fio2_lag_12', 'glu_lag_12', 'hgb_lag_12', 'hr_lag_12', 'inr_pt_lag_12', 'k_lag_12', 'lact_lag_12', 'lymph_lag_12', 'map_lag_12', 'mch_lag_12', 'mchc_lag_12', 'mcv_lag_12', 'methb_lag_12', 'mg_lag_12', 'na_lag_12', 'neut_lag_12', 'o2sat_lag_12', 'pco2_lag_12', 'ph_lag_12', 'phos_lag_12', 'plt_lag_12', 'po2_lag_12', 'ptt_lag_12', 'resp_lag_12', 'sbp_lag_12', 'temp_lag_12', 'tnt_lag_12', 'urine_lag_12', 'wbc_lag_12', 'alb_lag_13', 'alp_lag_13', 'alt_lag_13', 'ast_lag_13', 'be_lag_13', 'bicar_lag_13', 'bili_lag_13', 'bili_dir_lag_13', 'bnd_lag_13', 'bun_lag_13', 'ca_lag_13', 'cai_lag_13', 'ck_lag_13', 'ckmb_lag_13', 'cl_lag_13', 'crea_lag_13', 'crp_lag_13', 'dbp_lag_13', 'fgn_lag_13', 'fio2_lag_13', 'glu_lag_13', 'hgb_lag_13', 'hr_lag_13', 'inr_pt_lag_13', 'k_lag_13', 'lact_lag_13', 'lymph_lag_13', 'map_lag_13', 'mch_lag_13', 'mchc_lag_13', 'mcv_lag_13', 'methb_lag_13', 'mg_lag_13', 'na_lag_13', 'neut_lag_13', 'o2sat_lag_13', 'pco2_lag_13', 'ph_lag_13', 'phos_lag_13', 'plt_lag_13', 'po2_lag_13', 'ptt_lag_13', 'resp_lag_13', 'sbp_lag_13', 'temp_lag_13', 'tnt_lag_13', 'urine_lag_13', 'wbc_lag_13', 'alb_lag_14', 'alp_lag_14', 'alt_lag_14', 'ast_lag_14', 'be_lag_14', 'bicar_lag_14', 'bili_lag_14', 'bili_dir_lag_14', 'bnd_lag_14', 'bun_lag_14', 'ca_lag_14', 'cai_lag_14', 'ck_lag_14', 'ckmb_lag_14', 'cl_lag_14', 'crea_lag_14', 'crp_lag_14', 'dbp_lag_14', 'fgn_lag_14', 'fio2_lag_14', 'glu_lag_14', 'hgb_lag_14', 'hr_lag_14', 'inr_pt_lag_14', 'k_lag_14', 'lact_lag_14', 'lymph_lag_14', 'map_lag_14', 'mch_lag_14', 'mchc_lag_14', 'mcv_lag_14', 'methb_lag_14', 'mg_lag_14', 'na_lag_14', 'neut_lag_14', 'o2sat_lag_14', 'pco2_lag_14', 'ph_lag_14', 'phos_lag_14', 'plt_lag_14', 'po2_lag_14', 'ptt_lag_14', 'resp_lag_14', 'sbp_lag_14', 'temp_lag_14', 'tnt_lag_14', 'urine_lag_14', 'wbc_lag_14', 'alb_lag_15', 'alp_lag_15', 'alt_lag_15', 'ast_lag_15', 'be_lag_15', 'bicar_lag_15', 'bili_lag_15', 'bili_dir_lag_15', 'bnd_lag_15', 'bun_lag_15', 'ca_lag_15', 'cai_lag_15', 'ck_lag_15', 'ckmb_lag_15', 'cl_lag_15', 'crea_lag_15', 'crp_lag_15', 'dbp_lag_15', 'fgn_lag_15', 'fio2_lag_15', 'glu_lag_15', 'hgb_lag_15', 'hr_lag_15', 'inr_pt_lag_15', 'k_lag_15', 'lact_lag_15', 'lymph_lag_15', 'map_lag_15', 'mch_lag_15', 'mchc_lag_15', 'mcv_lag_15', 'methb_lag_15', 'mg_lag_15', 'na_lag_15', 'neut_lag_15', 'o2sat_lag_15', 'pco2_lag_15', 'ph_lag_15', 'phos_lag_15', 'plt_lag_15', 'po2_lag_15', 'ptt_lag_15', 'resp_lag_15', 'sbp_lag_15', 'temp_lag_15', 'tnt_lag_15', 'urine_lag_15', 'wbc_lag_15', 'alb_lag_16', 'alp_lag_16', 'alt_lag_16', 'ast_lag_16', 'be_lag_16', 'bicar_lag_16', 'bili_lag_16', 'bili_dir_lag_16', 'bnd_lag_16', 'bun_lag_16', 'ca_lag_16', 'cai_lag_16', 'ck_lag_16', 'ckmb_lag_16', 'cl_lag_16', 'crea_lag_16', 'crp_lag_16', 'dbp_lag_16', 'fgn_lag_16', 'fio2_lag_16', 'glu_lag_16', 'hgb_lag_16', 'hr_lag_16', 'inr_pt_lag_16', 'k_lag_16', 'lact_lag_16', 'lymph_lag_16', 'map_lag_16', 'mch_lag_16', 'mchc_lag_16', 'mcv_lag_16', 'methb_lag_16', 'mg_lag_16', 'na_lag_16', 'neut_lag_16', 'o2sat_lag_16', 'pco2_lag_16', 'ph_lag_16', 'phos_lag_16', 'plt_lag_16', 'po2_lag_16', 'ptt_lag_16', 'resp_lag_16', 'sbp_lag_16', 'temp_lag_16', 'tnt_lag_16', 'urine_lag_16', 'wbc_lag_16', 'alb_lag_17', 'alp_lag_17', 'alt_lag_17', 'ast_lag_17', 'be_lag_17', 'bicar_lag_17', 'bili_lag_17', 'bili_dir_lag_17', 'bnd_lag_17', 'bun_lag_17', 'ca_lag_17', 'cai_lag_17', 'ck_lag_17', 'ckmb_lag_17', 'cl_lag_17', 'crea_lag_17', 'crp_lag_17', 'dbp_lag_17', 'fgn_lag_17', 'fio2_lag_17', 'glu_lag_17', 'hgb_lag_17', 'hr_lag_17', 'inr_pt_lag_17', 'k_lag_17', 'lact_lag_17', 'lymph_lag_17', 'map_lag_17', 'mch_lag_17', 'mchc_lag_17', 'mcv_lag_17', 'methb_lag_17', 'mg_lag_17', 'na_lag_17', 'neut_lag_17', 'o2sat_lag_17', 'pco2_lag_17', 'ph_lag_17', 'phos_lag_17', 'plt_lag_17', 'po2_lag_17', 'ptt_lag_17', 'resp_lag_17', 'sbp_lag_17', 'temp_lag_17', 'tnt_lag_17', 'urine_lag_17', 'wbc_lag_17', 'alb_lag_18', 'alp_lag_18', 'alt_lag_18', 'ast_lag_18', 'be_lag_18', 'bicar_lag_18', 'bili_lag_18', 'bili_dir_lag_18', 'bnd_lag_18', 'bun_lag_18', 'ca_lag_18', 'cai_lag_18', 'ck_lag_18', 'ckmb_lag_18', 'cl_lag_18', 'crea_lag_18', 'crp_lag_18', 'dbp_lag_18', 'fgn_lag_18', 'fio2_lag_18', 'glu_lag_18', 'hgb_lag_18', 'hr_lag_18', 'inr_pt_lag_18', 'k_lag_18', 'lact_lag_18', 'lymph_lag_18', 'map_lag_18', 'mch_lag_18', 'mchc_lag_18', 'mcv_lag_18', 'methb_lag_18', 'mg_lag_18', 'na_lag_18', 'neut_lag_18', 'o2sat_lag_18', 'pco2_lag_18', 'ph_lag_18', 'phos_lag_18', 'plt_lag_18', 'po2_lag_18', 'ptt_lag_18', 'resp_lag_18', 'sbp_lag_18', 'temp_lag_18', 'tnt_lag_18', 'urine_lag_18', 'wbc_lag_18', 'alb_lag_19', 'alp_lag_19', 'alt_lag_19', 'ast_lag_19', 'be_lag_19', 'bicar_lag_19', 'bili_lag_19', 'bili_dir_lag_19', 'bnd_lag_19', 'bun_lag_19', 'ca_lag_19', 'cai_lag_19', 'ck_lag_19', 'ckmb_lag_19', 'cl_lag_19', 'crea_lag_19', 'crp_lag_19', 'dbp_lag_19', 'fgn_lag_19', 'fio2_lag_19', 'glu_lag_19', 'hgb_lag_19', 'hr_lag_19', 'inr_pt_lag_19', 'k_lag_19', 'lact_lag_19', 'lymph_lag_19', 'map_lag_19', 'mch_lag_19', 'mchc_lag_19', 'mcv_lag_19', 'methb_lag_19', 'mg_lag_19', 'na_lag_19', 'neut_lag_19', 'o2sat_lag_19', 'pco2_lag_19', 'ph_lag_19', 'phos_lag_19', 'plt_lag_19', 'po2_lag_19', 'ptt_lag_19', 'resp_lag_19', 'sbp_lag_19', 'temp_lag_19', 'tnt_lag_19', 'urine_lag_19', 'wbc_lag_19', 'alb_lag_20', 'alp_lag_20', 'alt_lag_20', 'ast_lag_20', 'be_lag_20', 'bicar_lag_20', 'bili_lag_20', 'bili_dir_lag_20', 'bnd_lag_20', 'bun_lag_20', 'ca_lag_20', 'cai_lag_20', 'ck_lag_20', 'ckmb_lag_20', 'cl_lag_20', 'crea_lag_20', 'crp_lag_20', 'dbp_lag_20', 'fgn_lag_20', 'fio2_lag_20', 'glu_lag_20', 'hgb_lag_20', 'hr_lag_20', 'inr_pt_lag_20', 'k_lag_20', 'lact_lag_20', 'lymph_lag_20', 'map_lag_20', 'mch_lag_20', 'mchc_lag_20', 'mcv_lag_20', 'methb_lag_20', 'mg_lag_20', 'na_lag_20', 'neut_lag_20', 'o2sat_lag_20', 'pco2_lag_20', 'ph_lag_20', 'phos_lag_20', 'plt_lag_20', 'po2_lag_20', 'ptt_lag_20', 'resp_lag_20', 'sbp_lag_20', 'temp_lag_20', 'tnt_lag_20', 'urine_lag_20', 'wbc_lag_20', 'alb_lag_21', 'alp_lag_21', 'alt_lag_21', 'ast_lag_21', 'be_lag_21', 'bicar_lag_21', 'bili_lag_21', 'bili_dir_lag_21', 'bnd_lag_21', 'bun_lag_21', 'ca_lag_21', 'cai_lag_21', 'ck_lag_21', 'ckmb_lag_21', 'cl_lag_21', 'crea_lag_21', 'crp_lag_21', 'dbp_lag_21', 'fgn_lag_21', 'fio2_lag_21', 'glu_lag_21', 'hgb_lag_21', 'hr_lag_21', 'inr_pt_lag_21', 'k_lag_21', 'lact_lag_21', 'lymph_lag_21', 'map_lag_21', 'mch_lag_21', 'mchc_lag_21', 'mcv_lag_21', 'methb_lag_21', 'mg_lag_21', 'na_lag_21', 'neut_lag_21', 'o2sat_lag_21', 'pco2_lag_21', 'ph_lag_21', 'phos_lag_21', 'plt_lag_21', 'po2_lag_21', 'ptt_lag_21', 'resp_lag_21', 'sbp_lag_21', 'temp_lag_21', 'tnt_lag_21', 'urine_lag_21', 'wbc_lag_21', 'alb_lag_22', 'alp_lag_22', 'alt_lag_22', 'ast_lag_22', 'be_lag_22', 'bicar_lag_22', 'bili_lag_22', 'bili_dir_lag_22', 'bnd_lag_22', 'bun_lag_22', 'ca_lag_22', 'cai_lag_22', 'ck_lag_22', 'ckmb_lag_22', 'cl_lag_22', 'crea_lag_22', 'crp_lag_22', 'dbp_lag_22', 'fgn_lag_22', 'fio2_lag_22', 'glu_lag_22', 'hgb_lag_22', 'hr_lag_22', 'inr_pt_lag_22', 'k_lag_22', 'lact_lag_22', 'lymph_lag_22', 'map_lag_22', 'mch_lag_22', 'mchc_lag_22', 'mcv_lag_22', 'methb_lag_22', 'mg_lag_22', 'na_lag_22', 'neut_lag_22', 'o2sat_lag_22', 'pco2_lag_22', 'ph_lag_22', 'phos_lag_22', 'plt_lag_22', 'po2_lag_22', 'ptt_lag_22', 'resp_lag_22', 'sbp_lag_22', 'temp_lag_22', 'tnt_lag_22', 'urine_lag_22', 'wbc_lag_22', 'alb_lag_23', 'alp_lag_23', 'alt_lag_23', 'ast_lag_23', 'be_lag_23', 'bicar_lag_23', 'bili_lag_23', 'bili_dir_lag_23', 'bnd_lag_23', 'bun_lag_23', 'ca_lag_23', 'cai_lag_23', 'ck_lag_23', 'ckmb_lag_23', 'cl_lag_23', 'crea_lag_23', 'crp_lag_23', 'dbp_lag_23', 'fgn_lag_23', 'fio2_lag_23', 'glu_lag_23', 'hgb_lag_23', 'hr_lag_23', 'inr_pt_lag_23', 'k_lag_23', 'lact_lag_23', 'lymph_lag_23', 'map_lag_23', 'mch_lag_23', 'mchc_lag_23', 'mcv_lag_23', 'methb_lag_23', 'mg_lag_23', 'na_lag_23', 'neut_lag_23', 'o2sat_lag_23', 'pco2_lag_23', 'ph_lag_23', 'phos_lag_23', 'plt_lag_23', 'po2_lag_23', 'ptt_lag_23', 'resp_lag_23', 'sbp_lag_23', 'temp_lag_23', 'tnt_lag_23', 'urine_lag_23', 'wbc_lag_23', 'alb_lag_24', 'alp_lag_24', 'alt_lag_24', 'ast_lag_24', 'be_lag_24', 'bicar_lag_24', 'bili_lag_24', 'bili_dir_lag_24', 'bnd_lag_24', 'bun_lag_24', 'ca_lag_24', 'cai_lag_24', 'ck_lag_24', 'ckmb_lag_24', 'cl_lag_24', 'crea_lag_24', 'crp_lag_24', 'dbp_lag_24', 'fgn_lag_24', 'fio2_lag_24', 'glu_lag_24', 'hgb_lag_24', 'hr_lag_24', 'inr_pt_lag_24', 'k_lag_24', 'lact_lag_24', 'lymph_lag_24', 'map_lag_24', 'mch_lag_24', 'mchc_lag_24', 'mcv_lag_24', 'methb_lag_24', 'mg_lag_24', 'na_lag_24', 'neut_lag_24', 'o2sat_lag_24', 'pco2_lag_24', 'ph_lag_24', 'phos_lag_24', 'plt_lag_24', 'po2_lag_24', 'ptt_lag_24', 'resp_lag_24', 'sbp_lag_24', 'temp_lag_24', 'tnt_lag_24', 'urine_lag_24', 'wbc_lag_24' ] PredictionDatasetpytorch.add_relative_time_idx=False PredictionDatasetpytorch.target="label" + PredictionDatasetpytorch.time_varying_unknown_reals=[ - "label" + "label", + ] PredictionDatasetpytorch.time_varying_unknown_categoricals=[] -PredictionDatasetpytorch.lagged_variables=["alb", +PredictionDatasetpytorch.lagged_variables=[ "alb", "alp", "alt", "ast", @@ -78,6 +80,6 @@ PredictionDatasetpytorch.lagged_variables=["alb", "temp", "tnt", "urine", - "wbc",] + "wbc"] \ No newline at end of file diff --git a/configs/prediction_models/RNNpytorch.gin b/configs/prediction_models/RNNpytorch.gin index 75233dca..3969df0f 100644 --- a/configs/prediction_models/RNNpytorch.gin +++ b/configs/prediction_models/RNNpytorch.gin @@ -23,17 +23,18 @@ model/hyperparameter.lr_scheduler = "exponential" # Dataset params PredictionDatasetpytorch.max_encoder_length = 24 -PredictionDatasetpytorch.max_prediction_length = 24 -PredictionDatasetpytorch.time_varying_known_reals=['alb_lag_24', 'alp_lag_24', 'alt_lag_24', 'ast_lag_24', 'be_lag_24', 'bicar_lag_24', 'bili_lag_24', 'bili_dir_lag_24', 'bnd_lag_24', 'bun_lag_24', 'ca_lag_24', 'cai_lag_24', 'ck_lag_24', 'ckmb_lag_24', 'cl_lag_24', 'crea_lag_24', 'crp_lag_24', 'dbp_lag_24', 'fgn_lag_24', 'fio2_lag_24', 'glu_lag_24', 'hgb_lag_24', 'hr_lag_24', 'inr_pt_lag_24', 'k_lag_24', 'lact_lag_24', 'lymph_lag_24', 'map_lag_24', 'mch_lag_24', 'mchc_lag_24', 'mcv_lag_24', 'methb_lag_24', 'mg_lag_24', 'na_lag_24', 'neut_lag_24', 'o2sat_lag_24', 'pco2_lag_24', 'ph_lag_24', 'phos_lag_24', 'plt_lag_24', 'po2_lag_24', 'ptt_lag_24', 'resp_lag_24', 'sbp_lag_24', 'temp_lag_24', 'tnt_lag_24', 'urine_lag_24', 'wbc_lag_24', 'alb_lag_25', 'alp_lag_25', 'alt_lag_25', 'ast_lag_25', 'be_lag_25', 'bicar_lag_25', 'bili_lag_25', 'bili_dir_lag_25', 'bnd_lag_25', 'bun_lag_25', 'ca_lag_25', 'cai_lag_25', 'ck_lag_25', 'ckmb_lag_25', 'cl_lag_25', 'crea_lag_25', 'crp_lag_25', 'dbp_lag_25', 'fgn_lag_25', 'fio2_lag_25', 'glu_lag_25', 'hgb_lag_25', 'hr_lag_25', 'inr_pt_lag_25', 'k_lag_25', 'lact_lag_25', 'lymph_lag_25', 'map_lag_25', 'mch_lag_25', 'mchc_lag_25', 'mcv_lag_25', 'methb_lag_25', 'mg_lag_25', 'na_lag_25', 'neut_lag_25', 'o2sat_lag_25', 'pco2_lag_25', 'ph_lag_25', 'phos_lag_25', 'plt_lag_25', 'po2_lag_25', 'ptt_lag_25', 'resp_lag_25', 'sbp_lag_25', 'temp_lag_25', 'tnt_lag_25', 'urine_lag_25', 'wbc_lag_25', 'alb_lag_26', 'alp_lag_26', 'alt_lag_26', 'ast_lag_26', 'be_lag_26', 'bicar_lag_26', 'bili_lag_26', 'bili_dir_lag_26', 'bnd_lag_26', 'bun_lag_26', 'ca_lag_26', 'cai_lag_26', 'ck_lag_26', 'ckmb_lag_26', 'cl_lag_26', 'crea_lag_26', 'crp_lag_26', 'dbp_lag_26', 'fgn_lag_26', 'fio2_lag_26', 'glu_lag_26', 'hgb_lag_26', 'hr_lag_26', 'inr_pt_lag_26', 'k_lag_26', 'lact_lag_26', 'lymph_lag_26', 'map_lag_26', 'mch_lag_26', 'mchc_lag_26', 'mcv_lag_26', 'methb_lag_26', 'mg_lag_26', 'na_lag_26', 'neut_lag_26', 'o2sat_lag_26', 'pco2_lag_26', 'ph_lag_26', 'phos_lag_26', 'plt_lag_26', 'po2_lag_26', 'ptt_lag_26', 'resp_lag_26', 'sbp_lag_26', 'temp_lag_26', 'tnt_lag_26', 'urine_lag_26', 'wbc_lag_26', 'alb_lag_27', 'alp_lag_27', 'alt_lag_27', 'ast_lag_27', 'be_lag_27', 'bicar_lag_27', 'bili_lag_27', 'bili_dir_lag_27', 'bnd_lag_27', 'bun_lag_27', 'ca_lag_27', 'cai_lag_27', 'ck_lag_27', 'ckmb_lag_27', 'cl_lag_27', 'crea_lag_27', 'crp_lag_27', 'dbp_lag_27', 'fgn_lag_27', 'fio2_lag_27', 'glu_lag_27', 'hgb_lag_27', 'hr_lag_27', 'inr_pt_lag_27', 'k_lag_27', 'lact_lag_27', 'lymph_lag_27', 'map_lag_27', 'mch_lag_27', 'mchc_lag_27', 'mcv_lag_27', 'methb_lag_27', 'mg_lag_27', 'na_lag_27', 'neut_lag_27', 'o2sat_lag_27', 'pco2_lag_27', 'ph_lag_27', 'phos_lag_27', 'plt_lag_27', 'po2_lag_27', 'ptt_lag_27', 'resp_lag_27', 'sbp_lag_27', 'temp_lag_27', 'tnt_lag_27', 'urine_lag_27', 'wbc_lag_27', 'alb_lag_28', 'alp_lag_28', 'alt_lag_28', 'ast_lag_28', 'be_lag_28', 'bicar_lag_28', 'bili_lag_28', 'bili_dir_lag_28', 'bnd_lag_28', 'bun_lag_28', 'ca_lag_28', 'cai_lag_28', 'ck_lag_28', 'ckmb_lag_28', 'cl_lag_28', 'crea_lag_28', 'crp_lag_28', 'dbp_lag_28', 'fgn_lag_28', 'fio2_lag_28', 'glu_lag_28', 'hgb_lag_28', 'hr_lag_28', 'inr_pt_lag_28', 'k_lag_28', 'lact_lag_28', 'lymph_lag_28', 'map_lag_28', 'mch_lag_28', 'mchc_lag_28', 'mcv_lag_28', 'methb_lag_28', 'mg_lag_28', 'na_lag_28', 'neut_lag_28', 'o2sat_lag_28', 'pco2_lag_28', 'ph_lag_28', 'phos_lag_28', 'plt_lag_28', 'po2_lag_28', 'ptt_lag_28', 'resp_lag_28', 'sbp_lag_28', 'temp_lag_28', 'tnt_lag_28', 'urine_lag_28', 'wbc_lag_28', 'alb_lag_29', 'alp_lag_29', 'alt_lag_29', 'ast_lag_29', 'be_lag_29', 'bicar_lag_29', 'bili_lag_29', 'bili_dir_lag_29', 'bnd_lag_29', 'bun_lag_29', 'ca_lag_29', 'cai_lag_29', 'ck_lag_29', 'ckmb_lag_29', 'cl_lag_29', 'crea_lag_29', 'crp_lag_29', 'dbp_lag_29', 'fgn_lag_29', 'fio2_lag_29', 'glu_lag_29', 'hgb_lag_29', 'hr_lag_29', 'inr_pt_lag_29', 'k_lag_29', 'lact_lag_29', 'lymph_lag_29', 'map_lag_29', 'mch_lag_29', 'mchc_lag_29', 'mcv_lag_29', 'methb_lag_29', 'mg_lag_29', 'na_lag_29', 'neut_lag_29', 'o2sat_lag_29', 'pco2_lag_29', 'ph_lag_29', 'phos_lag_29', 'plt_lag_29', 'po2_lag_29', 'ptt_lag_29', 'resp_lag_29', 'sbp_lag_29', 'temp_lag_29', 'tnt_lag_29', 'urine_lag_29', 'wbc_lag_29', 'alb_lag_30', 'alp_lag_30', 'alt_lag_30', 'ast_lag_30', 'be_lag_30', 'bicar_lag_30', 'bili_lag_30', 'bili_dir_lag_30', 'bnd_lag_30', 'bun_lag_30', 'ca_lag_30', 'cai_lag_30', 'ck_lag_30', 'ckmb_lag_30', 'cl_lag_30', 'crea_lag_30', 'crp_lag_30', 'dbp_lag_30', 'fgn_lag_30', 'fio2_lag_30', 'glu_lag_30', 'hgb_lag_30', 'hr_lag_30', 'inr_pt_lag_30', 'k_lag_30', 'lact_lag_30', 'lymph_lag_30', 'map_lag_30', 'mch_lag_30', 'mchc_lag_30', 'mcv_lag_30', 'methb_lag_30', 'mg_lag_30', 'na_lag_30', 'neut_lag_30', 'o2sat_lag_30', 'pco2_lag_30', 'ph_lag_30', 'phos_lag_30', 'plt_lag_30', 'po2_lag_30', 'ptt_lag_30', 'resp_lag_30', 'sbp_lag_30', 'temp_lag_30', 'tnt_lag_30', 'urine_lag_30', 'wbc_lag_30', 'alb_lag_31', 'alp_lag_31', 'alt_lag_31', 'ast_lag_31', 'be_lag_31', 'bicar_lag_31', 'bili_lag_31', 'bili_dir_lag_31', 'bnd_lag_31', 'bun_lag_31', 'ca_lag_31', 'cai_lag_31', 'ck_lag_31', 'ckmb_lag_31', 'cl_lag_31', 'crea_lag_31', 'crp_lag_31', 'dbp_lag_31', 'fgn_lag_31', 'fio2_lag_31', 'glu_lag_31', 'hgb_lag_31', 'hr_lag_31', 'inr_pt_lag_31', 'k_lag_31', 'lact_lag_31', 'lymph_lag_31', 'map_lag_31', 'mch_lag_31', 'mchc_lag_31', 'mcv_lag_31', 'methb_lag_31', 'mg_lag_31', 'na_lag_31', 'neut_lag_31', 'o2sat_lag_31', 'pco2_lag_31', 'ph_lag_31', 'phos_lag_31', 'plt_lag_31', 'po2_lag_31', 'ptt_lag_31', 'resp_lag_31', 'sbp_lag_31', 'temp_lag_31', 'tnt_lag_31', 'urine_lag_31', 'wbc_lag_31', 'alb_lag_32', 'alp_lag_32', 'alt_lag_32', 'ast_lag_32', 'be_lag_32', 'bicar_lag_32', 'bili_lag_32', 'bili_dir_lag_32', 'bnd_lag_32', 'bun_lag_32', 'ca_lag_32', 'cai_lag_32', 'ck_lag_32', 'ckmb_lag_32', 'cl_lag_32', 'crea_lag_32', 'crp_lag_32', 'dbp_lag_32', 'fgn_lag_32', 'fio2_lag_32', 'glu_lag_32', 'hgb_lag_32', 'hr_lag_32', 'inr_pt_lag_32', 'k_lag_32', 'lact_lag_32', 'lymph_lag_32', 'map_lag_32', 'mch_lag_32', 'mchc_lag_32', 'mcv_lag_32', 'methb_lag_32', 'mg_lag_32', 'na_lag_32', 'neut_lag_32', 'o2sat_lag_32', 'pco2_lag_32', 'ph_lag_32', 'phos_lag_32', 'plt_lag_32', 'po2_lag_32', 'ptt_lag_32', 'resp_lag_32', 'sbp_lag_32', 'temp_lag_32', 'tnt_lag_32', 'urine_lag_32', 'wbc_lag_32', 'alb_lag_33', 'alp_lag_33', 'alt_lag_33', 'ast_lag_33', 'be_lag_33', 'bicar_lag_33', 'bili_lag_33', 'bili_dir_lag_33', 'bnd_lag_33', 'bun_lag_33', 'ca_lag_33', 'cai_lag_33', 'ck_lag_33', 'ckmb_lag_33', 'cl_lag_33', 'crea_lag_33', 'crp_lag_33', 'dbp_lag_33', 'fgn_lag_33', 'fio2_lag_33', 'glu_lag_33', 'hgb_lag_33', 'hr_lag_33', 'inr_pt_lag_33', 'k_lag_33', 'lact_lag_33', 'lymph_lag_33', 'map_lag_33', 'mch_lag_33', 'mchc_lag_33', 'mcv_lag_33', 'methb_lag_33', 'mg_lag_33', 'na_lag_33', 'neut_lag_33', 'o2sat_lag_33', 'pco2_lag_33', 'ph_lag_33', 'phos_lag_33', 'plt_lag_33', 'po2_lag_33', 'ptt_lag_33', 'resp_lag_33', 'sbp_lag_33', 'temp_lag_33', 'tnt_lag_33', 'urine_lag_33', 'wbc_lag_33', 'alb_lag_34', 'alp_lag_34', 'alt_lag_34', 'ast_lag_34', 'be_lag_34', 'bicar_lag_34', 'bili_lag_34', 'bili_dir_lag_34', 'bnd_lag_34', 'bun_lag_34', 'ca_lag_34', 'cai_lag_34', 'ck_lag_34', 'ckmb_lag_34', 'cl_lag_34', 'crea_lag_34', 'crp_lag_34', 'dbp_lag_34', 'fgn_lag_34', 'fio2_lag_34', 'glu_lag_34', 'hgb_lag_34', 'hr_lag_34', 'inr_pt_lag_34', 'k_lag_34', 'lact_lag_34', 'lymph_lag_34', 'map_lag_34', 'mch_lag_34', 'mchc_lag_34', 'mcv_lag_34', 'methb_lag_34', 'mg_lag_34', 'na_lag_34', 'neut_lag_34', 'o2sat_lag_34', 'pco2_lag_34', 'ph_lag_34', 'phos_lag_34', 'plt_lag_34', 'po2_lag_34', 'ptt_lag_34', 'resp_lag_34', 'sbp_lag_34', 'temp_lag_34', 'tnt_lag_34', 'urine_lag_34', 'wbc_lag_34', 'alb_lag_35', 'alp_lag_35', 'alt_lag_35', 'ast_lag_35', 'be_lag_35', 'bicar_lag_35', 'bili_lag_35', 'bili_dir_lag_35', 'bnd_lag_35', 'bun_lag_35', 'ca_lag_35', 'cai_lag_35', 'ck_lag_35', 'ckmb_lag_35', 'cl_lag_35', 'crea_lag_35', 'crp_lag_35', 'dbp_lag_35', 'fgn_lag_35', 'fio2_lag_35', 'glu_lag_35', 'hgb_lag_35', 'hr_lag_35', 'inr_pt_lag_35', 'k_lag_35', 'lact_lag_35', 'lymph_lag_35', 'map_lag_35', 'mch_lag_35', 'mchc_lag_35', 'mcv_lag_35', 'methb_lag_35', 'mg_lag_35', 'na_lag_35', 'neut_lag_35', 'o2sat_lag_35', 'pco2_lag_35', 'ph_lag_35', 'phos_lag_35', 'plt_lag_35', 'po2_lag_35', 'ptt_lag_35', 'resp_lag_35', 'sbp_lag_35', 'temp_lag_35', 'tnt_lag_35', 'urine_lag_35', 'wbc_lag_35', 'alb_lag_36', 'alp_lag_36', 'alt_lag_36', 'ast_lag_36', 'be_lag_36', 'bicar_lag_36', 'bili_lag_36', 'bili_dir_lag_36', 'bnd_lag_36', 'bun_lag_36', 'ca_lag_36', 'cai_lag_36', 'ck_lag_36', 'ckmb_lag_36', 'cl_lag_36', 'crea_lag_36', 'crp_lag_36', 'dbp_lag_36', 'fgn_lag_36', 'fio2_lag_36', 'glu_lag_36', 'hgb_lag_36', 'hr_lag_36', 'inr_pt_lag_36', 'k_lag_36', 'lact_lag_36', 'lymph_lag_36', 'map_lag_36', 'mch_lag_36', 'mchc_lag_36', 'mcv_lag_36', 'methb_lag_36', 'mg_lag_36', 'na_lag_36', 'neut_lag_36', 'o2sat_lag_36', 'pco2_lag_36', 'ph_lag_36', 'phos_lag_36', 'plt_lag_36', 'po2_lag_36', 'ptt_lag_36', 'resp_lag_36', 'sbp_lag_36', 'temp_lag_36', 'tnt_lag_36', 'urine_lag_36', 'wbc_lag_36', 'alb_lag_37', 'alp_lag_37', 'alt_lag_37', 'ast_lag_37', 'be_lag_37', 'bicar_lag_37', 'bili_lag_37', 'bili_dir_lag_37', 'bnd_lag_37', 'bun_lag_37', 'ca_lag_37', 'cai_lag_37', 'ck_lag_37', 'ckmb_lag_37', 'cl_lag_37', 'crea_lag_37', 'crp_lag_37', 'dbp_lag_37', 'fgn_lag_37', 'fio2_lag_37', 'glu_lag_37', 'hgb_lag_37', 'hr_lag_37', 'inr_pt_lag_37', 'k_lag_37', 'lact_lag_37', 'lymph_lag_37', 'map_lag_37', 'mch_lag_37', 'mchc_lag_37', 'mcv_lag_37', 'methb_lag_37', 'mg_lag_37', 'na_lag_37', 'neut_lag_37', 'o2sat_lag_37', 'pco2_lag_37', 'ph_lag_37', 'phos_lag_37', 'plt_lag_37', 'po2_lag_37', 'ptt_lag_37', 'resp_lag_37', 'sbp_lag_37', 'temp_lag_37', 'tnt_lag_37', 'urine_lag_37', 'wbc_lag_37', 'alb_lag_38', 'alp_lag_38', 'alt_lag_38', 'ast_lag_38', 'be_lag_38', 'bicar_lag_38', 'bili_lag_38', 'bili_dir_lag_38', 'bnd_lag_38', 'bun_lag_38', 'ca_lag_38', 'cai_lag_38', 'ck_lag_38', 'ckmb_lag_38', 'cl_lag_38', 'crea_lag_38', 'crp_lag_38', 'dbp_lag_38', 'fgn_lag_38', 'fio2_lag_38', 'glu_lag_38', 'hgb_lag_38', 'hr_lag_38', 'inr_pt_lag_38', 'k_lag_38', 'lact_lag_38', 'lymph_lag_38', 'map_lag_38', 'mch_lag_38', 'mchc_lag_38', 'mcv_lag_38', 'methb_lag_38', 'mg_lag_38', 'na_lag_38', 'neut_lag_38', 'o2sat_lag_38', 'pco2_lag_38', 'ph_lag_38', 'phos_lag_38', 'plt_lag_38', 'po2_lag_38', 'ptt_lag_38', 'resp_lag_38', 'sbp_lag_38', 'temp_lag_38', 'tnt_lag_38', 'urine_lag_38', 'wbc_lag_38', 'alb_lag_39', 'alp_lag_39', 'alt_lag_39', 'ast_lag_39', 'be_lag_39', 'bicar_lag_39', 'bili_lag_39', 'bili_dir_lag_39', 'bnd_lag_39', 'bun_lag_39', 'ca_lag_39', 'cai_lag_39', 'ck_lag_39', 'ckmb_lag_39', 'cl_lag_39', 'crea_lag_39', 'crp_lag_39', 'dbp_lag_39', 'fgn_lag_39', 'fio2_lag_39', 'glu_lag_39', 'hgb_lag_39', 'hr_lag_39', 'inr_pt_lag_39', 'k_lag_39', 'lact_lag_39', 'lymph_lag_39', 'map_lag_39', 'mch_lag_39', 'mchc_lag_39', 'mcv_lag_39', 'methb_lag_39', 'mg_lag_39', 'na_lag_39', 'neut_lag_39', 'o2sat_lag_39', 'pco2_lag_39', 'ph_lag_39', 'phos_lag_39', 'plt_lag_39', 'po2_lag_39', 'ptt_lag_39', 'resp_lag_39', 'sbp_lag_39', 'temp_lag_39', 'tnt_lag_39', 'urine_lag_39', 'wbc_lag_39', 'alb_lag_40', 'alp_lag_40', 'alt_lag_40', 'ast_lag_40', 'be_lag_40', 'bicar_lag_40', 'bili_lag_40', 'bili_dir_lag_40', 'bnd_lag_40', 'bun_lag_40', 'ca_lag_40', 'cai_lag_40', 'ck_lag_40', 'ckmb_lag_40', 'cl_lag_40', 'crea_lag_40', 'crp_lag_40', 'dbp_lag_40', 'fgn_lag_40', 'fio2_lag_40', 'glu_lag_40', 'hgb_lag_40', 'hr_lag_40', 'inr_pt_lag_40', 'k_lag_40', 'lact_lag_40', 'lymph_lag_40', 'map_lag_40', 'mch_lag_40', 'mchc_lag_40', 'mcv_lag_40', 'methb_lag_40', 'mg_lag_40', 'na_lag_40', 'neut_lag_40', 'o2sat_lag_40', 'pco2_lag_40', 'ph_lag_40', 'phos_lag_40', 'plt_lag_40', 'po2_lag_40', 'ptt_lag_40', 'resp_lag_40', 'sbp_lag_40', 'temp_lag_40', 'tnt_lag_40', 'urine_lag_40', 'wbc_lag_40', 'alb_lag_41', 'alp_lag_41', 'alt_lag_41', 'ast_lag_41', 'be_lag_41', 'bicar_lag_41', 'bili_lag_41', 'bili_dir_lag_41', 'bnd_lag_41', 'bun_lag_41', 'ca_lag_41', 'cai_lag_41', 'ck_lag_41', 'ckmb_lag_41', 'cl_lag_41', 'crea_lag_41', 'crp_lag_41', 'dbp_lag_41', 'fgn_lag_41', 'fio2_lag_41', 'glu_lag_41', 'hgb_lag_41', 'hr_lag_41', 'inr_pt_lag_41', 'k_lag_41', 'lact_lag_41', 'lymph_lag_41', 'map_lag_41', 'mch_lag_41', 'mchc_lag_41', 'mcv_lag_41', 'methb_lag_41', 'mg_lag_41', 'na_lag_41', 'neut_lag_41', 'o2sat_lag_41', 'pco2_lag_41', 'ph_lag_41', 'phos_lag_41', 'plt_lag_41', 'po2_lag_41', 'ptt_lag_41', 'resp_lag_41', 'sbp_lag_41', 'temp_lag_41', 'tnt_lag_41', 'urine_lag_41', 'wbc_lag_41', 'alb_lag_42', 'alp_lag_42', 'alt_lag_42', 'ast_lag_42', 'be_lag_42', 'bicar_lag_42', 'bili_lag_42', 'bili_dir_lag_42', 'bnd_lag_42', 'bun_lag_42', 'ca_lag_42', 'cai_lag_42', 'ck_lag_42', 'ckmb_lag_42', 'cl_lag_42', 'crea_lag_42', 'crp_lag_42', 'dbp_lag_42', 'fgn_lag_42', 'fio2_lag_42', 'glu_lag_42', 'hgb_lag_42', 'hr_lag_42', 'inr_pt_lag_42', 'k_lag_42', 'lact_lag_42', 'lymph_lag_42', 'map_lag_42', 'mch_lag_42', 'mchc_lag_42', 'mcv_lag_42', 'methb_lag_42', 'mg_lag_42', 'na_lag_42', 'neut_lag_42', 'o2sat_lag_42', 'pco2_lag_42', 'ph_lag_42', 'phos_lag_42', 'plt_lag_42', 'po2_lag_42', 'ptt_lag_42', 'resp_lag_42', 'sbp_lag_42', 'temp_lag_42', 'tnt_lag_42', 'urine_lag_42', 'wbc_lag_42', 'alb_lag_43', 'alp_lag_43', 'alt_lag_43', 'ast_lag_43', 'be_lag_43', 'bicar_lag_43', 'bili_lag_43', 'bili_dir_lag_43', 'bnd_lag_43', 'bun_lag_43', 'ca_lag_43', 'cai_lag_43', 'ck_lag_43', 'ckmb_lag_43', 'cl_lag_43', 'crea_lag_43', 'crp_lag_43', 'dbp_lag_43', 'fgn_lag_43', 'fio2_lag_43', 'glu_lag_43', 'hgb_lag_43', 'hr_lag_43', 'inr_pt_lag_43', 'k_lag_43', 'lact_lag_43', 'lymph_lag_43', 'map_lag_43', 'mch_lag_43', 'mchc_lag_43', 'mcv_lag_43', 'methb_lag_43', 'mg_lag_43', 'na_lag_43', 'neut_lag_43', 'o2sat_lag_43', 'pco2_lag_43', 'ph_lag_43', 'phos_lag_43', 'plt_lag_43', 'po2_lag_43', 'ptt_lag_43', 'resp_lag_43', 'sbp_lag_43', 'temp_lag_43', 'tnt_lag_43', 'urine_lag_43', 'wbc_lag_43', 'alb_lag_44', 'alp_lag_44', 'alt_lag_44', 'ast_lag_44', 'be_lag_44', 'bicar_lag_44', 'bili_lag_44', 'bili_dir_lag_44', 'bnd_lag_44', 'bun_lag_44', 'ca_lag_44', 'cai_lag_44', 'ck_lag_44', 'ckmb_lag_44', 'cl_lag_44', 'crea_lag_44', 'crp_lag_44', 'dbp_lag_44', 'fgn_lag_44', 'fio2_lag_44', 'glu_lag_44', 'hgb_lag_44', 'hr_lag_44', 'inr_pt_lag_44', 'k_lag_44', 'lact_lag_44', 'lymph_lag_44', 'map_lag_44', 'mch_lag_44', 'mchc_lag_44', 'mcv_lag_44', 'methb_lag_44', 'mg_lag_44', 'na_lag_44', 'neut_lag_44', 'o2sat_lag_44', 'pco2_lag_44', 'ph_lag_44', 'phos_lag_44', 'plt_lag_44', 'po2_lag_44', 'ptt_lag_44', 'resp_lag_44', 'sbp_lag_44', 'temp_lag_44', 'tnt_lag_44', 'urine_lag_44', 'wbc_lag_44', 'alb_lag_45', 'alp_lag_45', 'alt_lag_45', 'ast_lag_45', 'be_lag_45', 'bicar_lag_45', 'bili_lag_45', 'bili_dir_lag_45', 'bnd_lag_45', 'bun_lag_45', 'ca_lag_45', 'cai_lag_45', 'ck_lag_45', 'ckmb_lag_45', 'cl_lag_45', 'crea_lag_45', 'crp_lag_45', 'dbp_lag_45', 'fgn_lag_45', 'fio2_lag_45', 'glu_lag_45', 'hgb_lag_45', 'hr_lag_45', 'inr_pt_lag_45', 'k_lag_45', 'lact_lag_45', 'lymph_lag_45', 'map_lag_45', 'mch_lag_45', 'mchc_lag_45', 'mcv_lag_45', 'methb_lag_45', 'mg_lag_45', 'na_lag_45', 'neut_lag_45', 'o2sat_lag_45', 'pco2_lag_45', 'ph_lag_45', 'phos_lag_45', 'plt_lag_45', 'po2_lag_45', 'ptt_lag_45', 'resp_lag_45', 'sbp_lag_45', 'temp_lag_45', 'tnt_lag_45', 'urine_lag_45', 'wbc_lag_45', 'alb_lag_46', 'alp_lag_46', 'alt_lag_46', 'ast_lag_46', 'be_lag_46', 'bicar_lag_46', 'bili_lag_46', 'bili_dir_lag_46', 'bnd_lag_46', 'bun_lag_46', 'ca_lag_46', 'cai_lag_46', 'ck_lag_46', 'ckmb_lag_46', 'cl_lag_46', 'crea_lag_46', 'crp_lag_46', 'dbp_lag_46', 'fgn_lag_46', 'fio2_lag_46', 'glu_lag_46', 'hgb_lag_46', 'hr_lag_46', 'inr_pt_lag_46', 'k_lag_46', 'lact_lag_46', 'lymph_lag_46', 'map_lag_46', 'mch_lag_46', 'mchc_lag_46', 'mcv_lag_46', 'methb_lag_46', 'mg_lag_46', 'na_lag_46', 'neut_lag_46', 'o2sat_lag_46', 'pco2_lag_46', 'ph_lag_46', 'phos_lag_46', 'plt_lag_46', 'po2_lag_46', 'ptt_lag_46', 'resp_lag_46', 'sbp_lag_46', 'temp_lag_46', 'tnt_lag_46', 'urine_lag_46', 'wbc_lag_46', 'alb_lag_47', 'alp_lag_47', 'alt_lag_47', 'ast_lag_47', 'be_lag_47', 'bicar_lag_47', 'bili_lag_47', 'bili_dir_lag_47', 'bnd_lag_47', 'bun_lag_47', 'ca_lag_47', 'cai_lag_47', 'ck_lag_47', 'ckmb_lag_47', 'cl_lag_47', 'crea_lag_47', 'crp_lag_47', 'dbp_lag_47', 'fgn_lag_47', 'fio2_lag_47', 'glu_lag_47', 'hgb_lag_47', 'hr_lag_47', 'inr_pt_lag_47', 'k_lag_47', 'lact_lag_47', 'lymph_lag_47', 'map_lag_47', 'mch_lag_47', 'mchc_lag_47', 'mcv_lag_47', 'methb_lag_47', 'mg_lag_47', 'na_lag_47', 'neut_lag_47', 'o2sat_lag_47', 'pco2_lag_47', 'ph_lag_47', 'phos_lag_47', 'plt_lag_47', 'po2_lag_47', 'ptt_lag_47', 'resp_lag_47', 'sbp_lag_47', 'temp_lag_47', 'tnt_lag_47', 'urine_lag_47', 'wbc_lag_47', 'alb_lag_48', 'alp_lag_48', 'alt_lag_48', 'ast_lag_48', 'be_lag_48', 'bicar_lag_48', 'bili_lag_48', 'bili_dir_lag_48', 'bnd_lag_48', 'bun_lag_48', 'ca_lag_48', 'cai_lag_48', 'ck_lag_48', 'ckmb_lag_48', 'cl_lag_48', 'crea_lag_48', 'crp_lag_48', 'dbp_lag_48', 'fgn_lag_48', 'fio2_lag_48', 'glu_lag_48', 'hgb_lag_48', 'hr_lag_48', 'inr_pt_lag_48', 'k_lag_48', 'lact_lag_48', 'lymph_lag_48', 'map_lag_48', 'mch_lag_48', 'mchc_lag_48', 'mcv_lag_48', 'methb_lag_48', 'mg_lag_48', 'na_lag_48', 'neut_lag_48', 'o2sat_lag_48', 'pco2_lag_48', 'ph_lag_48', 'phos_lag_48', 'plt_lag_48', 'po2_lag_48', 'ptt_lag_48', 'resp_lag_48', 'sbp_lag_48', 'temp_lag_48', 'tnt_lag_48', 'urine_lag_48', 'wbc_lag_48'] +PredictionDatasetpytorch.max_prediction_length = 1 +PredictionDatasetpytorch.time_varying_known_reals=['alb_lag_1', 'alp_lag_1', 'alt_lag_1', 'ast_lag_1', 'be_lag_1', 'bicar_lag_1', 'bili_lag_1', 'bili_dir_lag_1', 'bnd_lag_1', 'bun_lag_1', 'ca_lag_1', 'cai_lag_1', 'ck_lag_1', 'ckmb_lag_1', 'cl_lag_1', 'crea_lag_1', 'crp_lag_1', 'dbp_lag_1', 'fgn_lag_1', 'fio2_lag_1', 'glu_lag_1', 'hgb_lag_1', 'hr_lag_1', 'inr_pt_lag_1', 'k_lag_1', 'lact_lag_1', 'lymph_lag_1', 'map_lag_1', 'mch_lag_1', 'mchc_lag_1', 'mcv_lag_1', 'methb_lag_1', 'mg_lag_1', 'na_lag_1', 'neut_lag_1', 'o2sat_lag_1', 'pco2_lag_1', 'ph_lag_1', 'phos_lag_1', 'plt_lag_1', 'po2_lag_1', 'ptt_lag_1', 'resp_lag_1', 'sbp_lag_1', 'temp_lag_1', 'tnt_lag_1', 'urine_lag_1', 'wbc_lag_1', 'alb_lag_2', 'alp_lag_2', 'alt_lag_2', 'ast_lag_2', 'be_lag_2', 'bicar_lag_2', 'bili_lag_2', 'bili_dir_lag_2', 'bnd_lag_2', 'bun_lag_2', 'ca_lag_2', 'cai_lag_2', 'ck_lag_2', 'ckmb_lag_2', 'cl_lag_2', 'crea_lag_2', 'crp_lag_2', 'dbp_lag_2', 'fgn_lag_2', 'fio2_lag_2', 'glu_lag_2', 'hgb_lag_2', 'hr_lag_2', 'inr_pt_lag_2', 'k_lag_2', 'lact_lag_2', 'lymph_lag_2', 'map_lag_2', 'mch_lag_2', 'mchc_lag_2', 'mcv_lag_2', 'methb_lag_2', 'mg_lag_2', 'na_lag_2', 'neut_lag_2', 'o2sat_lag_2', 'pco2_lag_2', 'ph_lag_2', 'phos_lag_2', 'plt_lag_2', 'po2_lag_2', 'ptt_lag_2', 'resp_lag_2', 'sbp_lag_2', 'temp_lag_2', 'tnt_lag_2', 'urine_lag_2', 'wbc_lag_2', 'alb_lag_3', 'alp_lag_3', 'alt_lag_3', 'ast_lag_3', 'be_lag_3', 'bicar_lag_3', 'bili_lag_3', 'bili_dir_lag_3', 'bnd_lag_3', 'bun_lag_3', 'ca_lag_3', 'cai_lag_3', 'ck_lag_3', 'ckmb_lag_3', 'cl_lag_3', 'crea_lag_3', 'crp_lag_3', 'dbp_lag_3', 'fgn_lag_3', 'fio2_lag_3', 'glu_lag_3', 'hgb_lag_3', 'hr_lag_3', 'inr_pt_lag_3', 'k_lag_3', 'lact_lag_3', 'lymph_lag_3', 'map_lag_3', 'mch_lag_3', 'mchc_lag_3', 'mcv_lag_3', 'methb_lag_3', 'mg_lag_3', 'na_lag_3', 'neut_lag_3', 'o2sat_lag_3', 'pco2_lag_3', 'ph_lag_3', 'phos_lag_3', 'plt_lag_3', 'po2_lag_3', 'ptt_lag_3', 'resp_lag_3', 'sbp_lag_3', 'temp_lag_3', 'tnt_lag_3', 'urine_lag_3', 'wbc_lag_3', 'alb_lag_4', 'alp_lag_4', 'alt_lag_4', 'ast_lag_4', 'be_lag_4', 'bicar_lag_4', 'bili_lag_4', 'bili_dir_lag_4', 'bnd_lag_4', 'bun_lag_4', 'ca_lag_4', 'cai_lag_4', 'ck_lag_4', 'ckmb_lag_4', 'cl_lag_4', 'crea_lag_4', 'crp_lag_4', 'dbp_lag_4', 'fgn_lag_4', 'fio2_lag_4', 'glu_lag_4', 'hgb_lag_4', 'hr_lag_4', 'inr_pt_lag_4', 'k_lag_4', 'lact_lag_4', 'lymph_lag_4', 'map_lag_4', 'mch_lag_4', 'mchc_lag_4', 'mcv_lag_4', 'methb_lag_4', 'mg_lag_4', 'na_lag_4', 'neut_lag_4', 'o2sat_lag_4', 'pco2_lag_4', 'ph_lag_4', 'phos_lag_4', 'plt_lag_4', 'po2_lag_4', 'ptt_lag_4', 'resp_lag_4', 'sbp_lag_4', 'temp_lag_4', 'tnt_lag_4', 'urine_lag_4', 'wbc_lag_4', 'alb_lag_5', 'alp_lag_5', 'alt_lag_5', 'ast_lag_5', 'be_lag_5', 'bicar_lag_5', 'bili_lag_5', 'bili_dir_lag_5', 'bnd_lag_5', 'bun_lag_5', 'ca_lag_5', 'cai_lag_5', 'ck_lag_5', 'ckmb_lag_5', 'cl_lag_5', 'crea_lag_5', 'crp_lag_5', 'dbp_lag_5', 'fgn_lag_5', 'fio2_lag_5', 'glu_lag_5', 'hgb_lag_5', 'hr_lag_5', 'inr_pt_lag_5', 'k_lag_5', 'lact_lag_5', 'lymph_lag_5', 'map_lag_5', 'mch_lag_5', 'mchc_lag_5', 'mcv_lag_5', 'methb_lag_5', 'mg_lag_5', 'na_lag_5', 'neut_lag_5', 'o2sat_lag_5', 'pco2_lag_5', 'ph_lag_5', 'phos_lag_5', 'plt_lag_5', 'po2_lag_5', 'ptt_lag_5', 'resp_lag_5', 'sbp_lag_5', 'temp_lag_5', 'tnt_lag_5', 'urine_lag_5', 'wbc_lag_5', 'alb_lag_6', 'alp_lag_6', 'alt_lag_6', 'ast_lag_6', 'be_lag_6', 'bicar_lag_6', 'bili_lag_6', 'bili_dir_lag_6', 'bnd_lag_6', 'bun_lag_6', 'ca_lag_6', 'cai_lag_6', 'ck_lag_6', 'ckmb_lag_6', 'cl_lag_6', 'crea_lag_6', 'crp_lag_6', 'dbp_lag_6', 'fgn_lag_6', 'fio2_lag_6', 'glu_lag_6', 'hgb_lag_6', 'hr_lag_6', 'inr_pt_lag_6', 'k_lag_6', 'lact_lag_6', 'lymph_lag_6', 'map_lag_6', 'mch_lag_6', 'mchc_lag_6', 'mcv_lag_6', 'methb_lag_6', 'mg_lag_6', 'na_lag_6', 'neut_lag_6', 'o2sat_lag_6', 'pco2_lag_6', 'ph_lag_6', 'phos_lag_6', 'plt_lag_6', 'po2_lag_6', 'ptt_lag_6', 'resp_lag_6', 'sbp_lag_6', 'temp_lag_6', 'tnt_lag_6', 'urine_lag_6', 'wbc_lag_6', 'alb_lag_7', 'alp_lag_7', 'alt_lag_7', 'ast_lag_7', 'be_lag_7', 'bicar_lag_7', 'bili_lag_7', 'bili_dir_lag_7', 'bnd_lag_7', 'bun_lag_7', 'ca_lag_7', 'cai_lag_7', 'ck_lag_7', 'ckmb_lag_7', 'cl_lag_7', 'crea_lag_7', 'crp_lag_7', 'dbp_lag_7', 'fgn_lag_7', 'fio2_lag_7', 'glu_lag_7', 'hgb_lag_7', 'hr_lag_7', 'inr_pt_lag_7', 'k_lag_7', 'lact_lag_7', 'lymph_lag_7', 'map_lag_7', 'mch_lag_7', 'mchc_lag_7', 'mcv_lag_7', 'methb_lag_7', 'mg_lag_7', 'na_lag_7', 'neut_lag_7', 'o2sat_lag_7', 'pco2_lag_7', 'ph_lag_7', 'phos_lag_7', 'plt_lag_7', 'po2_lag_7', 'ptt_lag_7', 'resp_lag_7', 'sbp_lag_7', 'temp_lag_7', 'tnt_lag_7', 'urine_lag_7', 'wbc_lag_7', 'alb_lag_8', 'alp_lag_8', 'alt_lag_8', 'ast_lag_8', 'be_lag_8', 'bicar_lag_8', 'bili_lag_8', 'bili_dir_lag_8', 'bnd_lag_8', 'bun_lag_8', 'ca_lag_8', 'cai_lag_8', 'ck_lag_8', 'ckmb_lag_8', 'cl_lag_8', 'crea_lag_8', 'crp_lag_8', 'dbp_lag_8', 'fgn_lag_8', 'fio2_lag_8', 'glu_lag_8', 'hgb_lag_8', 'hr_lag_8', 'inr_pt_lag_8', 'k_lag_8', 'lact_lag_8', 'lymph_lag_8', 'map_lag_8', 'mch_lag_8', 'mchc_lag_8', 'mcv_lag_8', 'methb_lag_8', 'mg_lag_8', 'na_lag_8', 'neut_lag_8', 'o2sat_lag_8', 'pco2_lag_8', 'ph_lag_8', 'phos_lag_8', 'plt_lag_8', 'po2_lag_8', 'ptt_lag_8', 'resp_lag_8', 'sbp_lag_8', 'temp_lag_8', 'tnt_lag_8', 'urine_lag_8', 'wbc_lag_8', 'alb_lag_9', 'alp_lag_9', 'alt_lag_9', 'ast_lag_9', 'be_lag_9', 'bicar_lag_9', 'bili_lag_9', 'bili_dir_lag_9', 'bnd_lag_9', 'bun_lag_9', 'ca_lag_9', 'cai_lag_9', 'ck_lag_9', 'ckmb_lag_9', 'cl_lag_9', 'crea_lag_9', 'crp_lag_9', 'dbp_lag_9', 'fgn_lag_9', 'fio2_lag_9', 'glu_lag_9', 'hgb_lag_9', 'hr_lag_9', 'inr_pt_lag_9', 'k_lag_9', 'lact_lag_9', 'lymph_lag_9', 'map_lag_9', 'mch_lag_9', 'mchc_lag_9', 'mcv_lag_9', 'methb_lag_9', 'mg_lag_9', 'na_lag_9', 'neut_lag_9', 'o2sat_lag_9', 'pco2_lag_9', 'ph_lag_9', 'phos_lag_9', 'plt_lag_9', 'po2_lag_9', 'ptt_lag_9', 'resp_lag_9', 'sbp_lag_9', 'temp_lag_9', 'tnt_lag_9', 'urine_lag_9', 'wbc_lag_9', 'alb_lag_10', 'alp_lag_10', 'alt_lag_10', 'ast_lag_10', 'be_lag_10', 'bicar_lag_10', 'bili_lag_10', 'bili_dir_lag_10', 'bnd_lag_10', 'bun_lag_10', 'ca_lag_10', 'cai_lag_10', 'ck_lag_10', 'ckmb_lag_10', 'cl_lag_10', 'crea_lag_10', 'crp_lag_10', 'dbp_lag_10', 'fgn_lag_10', 'fio2_lag_10', 'glu_lag_10', 'hgb_lag_10', 'hr_lag_10', 'inr_pt_lag_10', 'k_lag_10', 'lact_lag_10', 'lymph_lag_10', 'map_lag_10', 'mch_lag_10', 'mchc_lag_10', 'mcv_lag_10', 'methb_lag_10', 'mg_lag_10', 'na_lag_10', 'neut_lag_10', 'o2sat_lag_10', 'pco2_lag_10', 'ph_lag_10', 'phos_lag_10', 'plt_lag_10', 'po2_lag_10', 'ptt_lag_10', 'resp_lag_10', 'sbp_lag_10', 'temp_lag_10', 'tnt_lag_10', 'urine_lag_10', 'wbc_lag_10', 'alb_lag_11', 'alp_lag_11', 'alt_lag_11', 'ast_lag_11', 'be_lag_11', 'bicar_lag_11', 'bili_lag_11', 'bili_dir_lag_11', 'bnd_lag_11', 'bun_lag_11', 'ca_lag_11', 'cai_lag_11', 'ck_lag_11', 'ckmb_lag_11', 'cl_lag_11', 'crea_lag_11', 'crp_lag_11', 'dbp_lag_11', 'fgn_lag_11', 'fio2_lag_11', 'glu_lag_11', 'hgb_lag_11', 'hr_lag_11', 'inr_pt_lag_11', 'k_lag_11', 'lact_lag_11', 'lymph_lag_11', 'map_lag_11', 'mch_lag_11', 'mchc_lag_11', 'mcv_lag_11', 'methb_lag_11', 'mg_lag_11', 'na_lag_11', 'neut_lag_11', 'o2sat_lag_11', 'pco2_lag_11', 'ph_lag_11', 'phos_lag_11', 'plt_lag_11', 'po2_lag_11', 'ptt_lag_11', 'resp_lag_11', 'sbp_lag_11', 'temp_lag_11', 'tnt_lag_11', 'urine_lag_11', 'wbc_lag_11', 'alb_lag_12', 'alp_lag_12', 'alt_lag_12', 'ast_lag_12', 'be_lag_12', 'bicar_lag_12', 'bili_lag_12', 'bili_dir_lag_12', 'bnd_lag_12', 'bun_lag_12', 'ca_lag_12', 'cai_lag_12', 'ck_lag_12', 'ckmb_lag_12', 'cl_lag_12', 'crea_lag_12', 'crp_lag_12', 'dbp_lag_12', 'fgn_lag_12', 'fio2_lag_12', 'glu_lag_12', 'hgb_lag_12', 'hr_lag_12', 'inr_pt_lag_12', 'k_lag_12', 'lact_lag_12', 'lymph_lag_12', 'map_lag_12', 'mch_lag_12', 'mchc_lag_12', 'mcv_lag_12', 'methb_lag_12', 'mg_lag_12', 'na_lag_12', 'neut_lag_12', 'o2sat_lag_12', 'pco2_lag_12', 'ph_lag_12', 'phos_lag_12', 'plt_lag_12', 'po2_lag_12', 'ptt_lag_12', 'resp_lag_12', 'sbp_lag_12', 'temp_lag_12', 'tnt_lag_12', 'urine_lag_12', 'wbc_lag_12', 'alb_lag_13', 'alp_lag_13', 'alt_lag_13', 'ast_lag_13', 'be_lag_13', 'bicar_lag_13', 'bili_lag_13', 'bili_dir_lag_13', 'bnd_lag_13', 'bun_lag_13', 'ca_lag_13', 'cai_lag_13', 'ck_lag_13', 'ckmb_lag_13', 'cl_lag_13', 'crea_lag_13', 'crp_lag_13', 'dbp_lag_13', 'fgn_lag_13', 'fio2_lag_13', 'glu_lag_13', 'hgb_lag_13', 'hr_lag_13', 'inr_pt_lag_13', 'k_lag_13', 'lact_lag_13', 'lymph_lag_13', 'map_lag_13', 'mch_lag_13', 'mchc_lag_13', 'mcv_lag_13', 'methb_lag_13', 'mg_lag_13', 'na_lag_13', 'neut_lag_13', 'o2sat_lag_13', 'pco2_lag_13', 'ph_lag_13', 'phos_lag_13', 'plt_lag_13', 'po2_lag_13', 'ptt_lag_13', 'resp_lag_13', 'sbp_lag_13', 'temp_lag_13', 'tnt_lag_13', 'urine_lag_13', 'wbc_lag_13', 'alb_lag_14', 'alp_lag_14', 'alt_lag_14', 'ast_lag_14', 'be_lag_14', 'bicar_lag_14', 'bili_lag_14', 'bili_dir_lag_14', 'bnd_lag_14', 'bun_lag_14', 'ca_lag_14', 'cai_lag_14', 'ck_lag_14', 'ckmb_lag_14', 'cl_lag_14', 'crea_lag_14', 'crp_lag_14', 'dbp_lag_14', 'fgn_lag_14', 'fio2_lag_14', 'glu_lag_14', 'hgb_lag_14', 'hr_lag_14', 'inr_pt_lag_14', 'k_lag_14', 'lact_lag_14', 'lymph_lag_14', 'map_lag_14', 'mch_lag_14', 'mchc_lag_14', 'mcv_lag_14', 'methb_lag_14', 'mg_lag_14', 'na_lag_14', 'neut_lag_14', 'o2sat_lag_14', 'pco2_lag_14', 'ph_lag_14', 'phos_lag_14', 'plt_lag_14', 'po2_lag_14', 'ptt_lag_14', 'resp_lag_14', 'sbp_lag_14', 'temp_lag_14', 'tnt_lag_14', 'urine_lag_14', 'wbc_lag_14', 'alb_lag_15', 'alp_lag_15', 'alt_lag_15', 'ast_lag_15', 'be_lag_15', 'bicar_lag_15', 'bili_lag_15', 'bili_dir_lag_15', 'bnd_lag_15', 'bun_lag_15', 'ca_lag_15', 'cai_lag_15', 'ck_lag_15', 'ckmb_lag_15', 'cl_lag_15', 'crea_lag_15', 'crp_lag_15', 'dbp_lag_15', 'fgn_lag_15', 'fio2_lag_15', 'glu_lag_15', 'hgb_lag_15', 'hr_lag_15', 'inr_pt_lag_15', 'k_lag_15', 'lact_lag_15', 'lymph_lag_15', 'map_lag_15', 'mch_lag_15', 'mchc_lag_15', 'mcv_lag_15', 'methb_lag_15', 'mg_lag_15', 'na_lag_15', 'neut_lag_15', 'o2sat_lag_15', 'pco2_lag_15', 'ph_lag_15', 'phos_lag_15', 'plt_lag_15', 'po2_lag_15', 'ptt_lag_15', 'resp_lag_15', 'sbp_lag_15', 'temp_lag_15', 'tnt_lag_15', 'urine_lag_15', 'wbc_lag_15', 'alb_lag_16', 'alp_lag_16', 'alt_lag_16', 'ast_lag_16', 'be_lag_16', 'bicar_lag_16', 'bili_lag_16', 'bili_dir_lag_16', 'bnd_lag_16', 'bun_lag_16', 'ca_lag_16', 'cai_lag_16', 'ck_lag_16', 'ckmb_lag_16', 'cl_lag_16', 'crea_lag_16', 'crp_lag_16', 'dbp_lag_16', 'fgn_lag_16', 'fio2_lag_16', 'glu_lag_16', 'hgb_lag_16', 'hr_lag_16', 'inr_pt_lag_16', 'k_lag_16', 'lact_lag_16', 'lymph_lag_16', 'map_lag_16', 'mch_lag_16', 'mchc_lag_16', 'mcv_lag_16', 'methb_lag_16', 'mg_lag_16', 'na_lag_16', 'neut_lag_16', 'o2sat_lag_16', 'pco2_lag_16', 'ph_lag_16', 'phos_lag_16', 'plt_lag_16', 'po2_lag_16', 'ptt_lag_16', 'resp_lag_16', 'sbp_lag_16', 'temp_lag_16', 'tnt_lag_16', 'urine_lag_16', 'wbc_lag_16', 'alb_lag_17', 'alp_lag_17', 'alt_lag_17', 'ast_lag_17', 'be_lag_17', 'bicar_lag_17', 'bili_lag_17', 'bili_dir_lag_17', 'bnd_lag_17', 'bun_lag_17', 'ca_lag_17', 'cai_lag_17', 'ck_lag_17', 'ckmb_lag_17', 'cl_lag_17', 'crea_lag_17', 'crp_lag_17', 'dbp_lag_17', 'fgn_lag_17', 'fio2_lag_17', 'glu_lag_17', 'hgb_lag_17', 'hr_lag_17', 'inr_pt_lag_17', 'k_lag_17', 'lact_lag_17', 'lymph_lag_17', 'map_lag_17', 'mch_lag_17', 'mchc_lag_17', 'mcv_lag_17', 'methb_lag_17', 'mg_lag_17', 'na_lag_17', 'neut_lag_17', 'o2sat_lag_17', 'pco2_lag_17', 'ph_lag_17', 'phos_lag_17', 'plt_lag_17', 'po2_lag_17', 'ptt_lag_17', 'resp_lag_17', 'sbp_lag_17', 'temp_lag_17', 'tnt_lag_17', 'urine_lag_17', 'wbc_lag_17', 'alb_lag_18', 'alp_lag_18', 'alt_lag_18', 'ast_lag_18', 'be_lag_18', 'bicar_lag_18', 'bili_lag_18', 'bili_dir_lag_18', 'bnd_lag_18', 'bun_lag_18', 'ca_lag_18', 'cai_lag_18', 'ck_lag_18', 'ckmb_lag_18', 'cl_lag_18', 'crea_lag_18', 'crp_lag_18', 'dbp_lag_18', 'fgn_lag_18', 'fio2_lag_18', 'glu_lag_18', 'hgb_lag_18', 'hr_lag_18', 'inr_pt_lag_18', 'k_lag_18', 'lact_lag_18', 'lymph_lag_18', 'map_lag_18', 'mch_lag_18', 'mchc_lag_18', 'mcv_lag_18', 'methb_lag_18', 'mg_lag_18', 'na_lag_18', 'neut_lag_18', 'o2sat_lag_18', 'pco2_lag_18', 'ph_lag_18', 'phos_lag_18', 'plt_lag_18', 'po2_lag_18', 'ptt_lag_18', 'resp_lag_18', 'sbp_lag_18', 'temp_lag_18', 'tnt_lag_18', 'urine_lag_18', 'wbc_lag_18', 'alb_lag_19', 'alp_lag_19', 'alt_lag_19', 'ast_lag_19', 'be_lag_19', 'bicar_lag_19', 'bili_lag_19', 'bili_dir_lag_19', 'bnd_lag_19', 'bun_lag_19', 'ca_lag_19', 'cai_lag_19', 'ck_lag_19', 'ckmb_lag_19', 'cl_lag_19', 'crea_lag_19', 'crp_lag_19', 'dbp_lag_19', 'fgn_lag_19', 'fio2_lag_19', 'glu_lag_19', 'hgb_lag_19', 'hr_lag_19', 'inr_pt_lag_19', 'k_lag_19', 'lact_lag_19', 'lymph_lag_19', 'map_lag_19', 'mch_lag_19', 'mchc_lag_19', 'mcv_lag_19', 'methb_lag_19', 'mg_lag_19', 'na_lag_19', 'neut_lag_19', 'o2sat_lag_19', 'pco2_lag_19', 'ph_lag_19', 'phos_lag_19', 'plt_lag_19', 'po2_lag_19', 'ptt_lag_19', 'resp_lag_19', 'sbp_lag_19', 'temp_lag_19', 'tnt_lag_19', 'urine_lag_19', 'wbc_lag_19', 'alb_lag_20', 'alp_lag_20', 'alt_lag_20', 'ast_lag_20', 'be_lag_20', 'bicar_lag_20', 'bili_lag_20', 'bili_dir_lag_20', 'bnd_lag_20', 'bun_lag_20', 'ca_lag_20', 'cai_lag_20', 'ck_lag_20', 'ckmb_lag_20', 'cl_lag_20', 'crea_lag_20', 'crp_lag_20', 'dbp_lag_20', 'fgn_lag_20', 'fio2_lag_20', 'glu_lag_20', 'hgb_lag_20', 'hr_lag_20', 'inr_pt_lag_20', 'k_lag_20', 'lact_lag_20', 'lymph_lag_20', 'map_lag_20', 'mch_lag_20', 'mchc_lag_20', 'mcv_lag_20', 'methb_lag_20', 'mg_lag_20', 'na_lag_20', 'neut_lag_20', 'o2sat_lag_20', 'pco2_lag_20', 'ph_lag_20', 'phos_lag_20', 'plt_lag_20', 'po2_lag_20', 'ptt_lag_20', 'resp_lag_20', 'sbp_lag_20', 'temp_lag_20', 'tnt_lag_20', 'urine_lag_20', 'wbc_lag_20', 'alb_lag_21', 'alp_lag_21', 'alt_lag_21', 'ast_lag_21', 'be_lag_21', 'bicar_lag_21', 'bili_lag_21', 'bili_dir_lag_21', 'bnd_lag_21', 'bun_lag_21', 'ca_lag_21', 'cai_lag_21', 'ck_lag_21', 'ckmb_lag_21', 'cl_lag_21', 'crea_lag_21', 'crp_lag_21', 'dbp_lag_21', 'fgn_lag_21', 'fio2_lag_21', 'glu_lag_21', 'hgb_lag_21', 'hr_lag_21', 'inr_pt_lag_21', 'k_lag_21', 'lact_lag_21', 'lymph_lag_21', 'map_lag_21', 'mch_lag_21', 'mchc_lag_21', 'mcv_lag_21', 'methb_lag_21', 'mg_lag_21', 'na_lag_21', 'neut_lag_21', 'o2sat_lag_21', 'pco2_lag_21', 'ph_lag_21', 'phos_lag_21', 'plt_lag_21', 'po2_lag_21', 'ptt_lag_21', 'resp_lag_21', 'sbp_lag_21', 'temp_lag_21', 'tnt_lag_21', 'urine_lag_21', 'wbc_lag_21', 'alb_lag_22', 'alp_lag_22', 'alt_lag_22', 'ast_lag_22', 'be_lag_22', 'bicar_lag_22', 'bili_lag_22', 'bili_dir_lag_22', 'bnd_lag_22', 'bun_lag_22', 'ca_lag_22', 'cai_lag_22', 'ck_lag_22', 'ckmb_lag_22', 'cl_lag_22', 'crea_lag_22', 'crp_lag_22', 'dbp_lag_22', 'fgn_lag_22', 'fio2_lag_22', 'glu_lag_22', 'hgb_lag_22', 'hr_lag_22', 'inr_pt_lag_22', 'k_lag_22', 'lact_lag_22', 'lymph_lag_22', 'map_lag_22', 'mch_lag_22', 'mchc_lag_22', 'mcv_lag_22', 'methb_lag_22', 'mg_lag_22', 'na_lag_22', 'neut_lag_22', 'o2sat_lag_22', 'pco2_lag_22', 'ph_lag_22', 'phos_lag_22', 'plt_lag_22', 'po2_lag_22', 'ptt_lag_22', 'resp_lag_22', 'sbp_lag_22', 'temp_lag_22', 'tnt_lag_22', 'urine_lag_22', 'wbc_lag_22', 'alb_lag_23', 'alp_lag_23', 'alt_lag_23', 'ast_lag_23', 'be_lag_23', 'bicar_lag_23', 'bili_lag_23', 'bili_dir_lag_23', 'bnd_lag_23', 'bun_lag_23', 'ca_lag_23', 'cai_lag_23', 'ck_lag_23', 'ckmb_lag_23', 'cl_lag_23', 'crea_lag_23', 'crp_lag_23', 'dbp_lag_23', 'fgn_lag_23', 'fio2_lag_23', 'glu_lag_23', 'hgb_lag_23', 'hr_lag_23', 'inr_pt_lag_23', 'k_lag_23', 'lact_lag_23', 'lymph_lag_23', 'map_lag_23', 'mch_lag_23', 'mchc_lag_23', 'mcv_lag_23', 'methb_lag_23', 'mg_lag_23', 'na_lag_23', 'neut_lag_23', 'o2sat_lag_23', 'pco2_lag_23', 'ph_lag_23', 'phos_lag_23', 'plt_lag_23', 'po2_lag_23', 'ptt_lag_23', 'resp_lag_23', 'sbp_lag_23', 'temp_lag_23', 'tnt_lag_23', 'urine_lag_23', 'wbc_lag_23', 'alb_lag_24', 'alp_lag_24', 'alt_lag_24', 'ast_lag_24', 'be_lag_24', 'bicar_lag_24', 'bili_lag_24', 'bili_dir_lag_24', 'bnd_lag_24', 'bun_lag_24', 'ca_lag_24', 'cai_lag_24', 'ck_lag_24', 'ckmb_lag_24', 'cl_lag_24', 'crea_lag_24', 'crp_lag_24', 'dbp_lag_24', 'fgn_lag_24', 'fio2_lag_24', 'glu_lag_24', 'hgb_lag_24', 'hr_lag_24', 'inr_pt_lag_24', 'k_lag_24', 'lact_lag_24', 'lymph_lag_24', 'map_lag_24', 'mch_lag_24', 'mchc_lag_24', 'mcv_lag_24', 'methb_lag_24', 'mg_lag_24', 'na_lag_24', 'neut_lag_24', 'o2sat_lag_24', 'pco2_lag_24', 'ph_lag_24', 'phos_lag_24', 'plt_lag_24', 'po2_lag_24', 'ptt_lag_24', 'resp_lag_24', 'sbp_lag_24', 'temp_lag_24', 'tnt_lag_24', 'urine_lag_24', 'wbc_lag_24' ] PredictionDatasetpytorch.add_relative_time_idx=False PredictionDatasetpytorch.target="label" PredictionDatasetpytorch.time_varying_unknown_reals=[ "label", + ] PredictionDatasetpytorch.time_varying_unknown_categoricals=[] -PredictionDatasetpytorch.lagged_variables=["alb", +PredictionDatasetpytorch.lagged_variables=[ "alb", "alp", "alt", "ast", @@ -80,4 +81,4 @@ PredictionDatasetpytorch.lagged_variables=["alb", "temp", "tnt", "urine", - "wbc",] \ No newline at end of file + "wbc"] \ No newline at end of file diff --git a/configs/tasks/Regression.gin b/configs/tasks/Regression.gin index 1234f707..75041763 100644 --- a/configs/tasks/Regression.gin +++ b/configs/tasks/Regression.gin @@ -15,8 +15,8 @@ train_common.weight = "balanced" train_common.ram_cache = True # LOSS FUNCTION -DLPredictionWrapper.loss = @mse_loss -DLPredictionPytorchForecastingWrapper.loss = @mse_loss +DLPredictionWrapper.loss = @l1_loss +DLPredictionPytorchForecastingWrapper.loss = @l1_loss MLWrapper.loss = @mean_squared_error # SELECTING PREPROCESSOR diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 75722831..96ba9695 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -10,7 +10,7 @@ from icu_benchmarks.imputation.amputations import ampute_data from .constants import DataSegment as Segment from .constants import DataSplit as Split -from pytorch_forecasting import TimeSeriesDataSet +from pytorch_forecasting import TimeSeriesDataSet, GroupNormalizer class CommonDataset(Dataset): @@ -463,6 +463,7 @@ def __init__( ) # create an incremental column indicating the time step(required by constructor) data = data.get(split) # get split labels = data["OUTCOME"] + features = data["FEATURES"] self.name = name self.data = pd.merge(labels, features, on=["stay_id", "time"]) @@ -471,7 +472,7 @@ def __init__( self.data["label"] = self.data["label"].astype(float) columns_to_lag = lagged_variables grouped = self.data.sort_values("time_idx").groupby("stay_id") - for lag in range(max_encoder_length, max_encoder_length + max_prediction_length + 1): + for lag in range(1, max_encoder_length + 1): for column in columns_to_lag: # Create a new column with lagged values self.data[f"{column}_lag_{lag}"] = grouped[column].shift(lag, fill_value=0) @@ -501,7 +502,9 @@ def __init__( # add_target_scales=True, # add_encoder_length=True, predict_mode=True, - target_normalizer=None, + target_normalizer=GroupNormalizer( + groups=["stay_id"], transformation="relu" + ), ) def get_balance(self) -> list: diff --git a/icu_benchmarks/data/preprocessor.py b/icu_benchmarks/data/preprocessor.py index 69b90f5b..9da25308 100644 --- a/icu_benchmarks/data/preprocessor.py +++ b/icu_benchmarks/data/preprocessor.py @@ -117,7 +117,8 @@ def _process_static(self, data, vars): sta_rec.add_step(StepScale()) sta_rec.add_step(StepImputeFastZeroFill(sel=all_numeric_predictors())) - sta_rec.add_step(StepSklearn(SimpleImputer(missing_values=None, strategy="most_frequent"), sel=has_type("object"))) + sta_rec.add_step(StepSklearn(SimpleImputer(missing_values=None, + strategy="most_frequent"), sel=has_type("object"))) sta_rec.add_step(StepSklearn(LabelEncoder(), sel=has_type("object"), columnwise=True)) data = apply_recipe_to_splits(sta_rec, data, Segment.static, self.save_cache, self.load_cache) @@ -205,8 +206,11 @@ def apply(self, data, vars): Returns: Preprocessed data. """ - for split in [Split.train, Split.val, Split.test]: - data = self._process_outcome(data, vars, split) + + self.outcome_max = data["train"]["OUTCOME"]["label"].max() + self.outcome_min = data["train"]["OUTCOME"]["label"].min() + # for split in [Split.train, Split.val, Split.test]: + # data = self._process_outcome(data, vars, split) data = super().apply(data, vars) return data @@ -284,7 +288,8 @@ def _process_dynamic_data(self, data, vars): if self.filter_missing_values: rows_to_remove = data[Segment.dynamic][vars[Segment.dynamic]].isna().sum(axis=1) != 0 ids_to_remove = data[Segment.dynamic].loc[rows_to_remove][vars["GROUP"]].unique() - data = {table_name: table.loc[~table[vars["GROUP"]].isin(ids_to_remove)] for table_name, table in data.items()} + data = {table_name: table.loc[~table[vars["GROUP"]].isin( + ids_to_remove)] for table_name, table in data.items()} logging.info(f"Removed {len(ids_to_remove)} stays with missing values.") return data diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index ad73a479..57107b60 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -14,7 +14,7 @@ from icu_benchmarks.models.wrappers import DLPredictionWrapper, DLPredictionPytorchForecastingWrapper from torch import Tensor, FloatTensor, zeros_like, ones_like, randn_like, is_tensor from pytorch_forecasting import TemporalFusionTransformer, RecurrentNetwork, DeepAR -from pytorch_forecasting.metrics import QuantileLoss +from pytorch_forecasting.metrics import QuantileLoss, MAE import matplotlib.pyplot as plt @@ -510,13 +510,6 @@ def forward( pred = self.logit(out["prediction"]) return pred - """ - def forward(self, x): - out = self.model(x) - pred = self.logit(out["prediction"]) - return pred - """ - def actual_vs_predictions_plot(self, dataloader): predictions = self.model.predict(dataloader, return_x=True) predictions_vs_actuals = self.model.calculate_prediction_actual_by_variable(predictions.x, predictions.output) @@ -525,7 +518,7 @@ def actual_vs_predictions_plot(self, dataloader): def interpertations(self, dataloader, log_dir=".", plot=False): raw_predictions = self.model.predict(dataloader, return_x=True, mode="raw") - interpretation = self.model.interpret_output(raw_predictions.output, reduction="sum") + interpretation = self.model.interpret_output(raw_predictions.output, reduction="mean") if plot: figs = self.model.plot_interpretation(interpretation) for key, fig in figs.items(): @@ -700,7 +693,7 @@ def forward( if self.num_classes == 1: x_dict["encoder_cont"][:, :, -1] = 0.0 out = self.model(x_dict) - out = out["prediction"][0] - pred = self.logit(out) + pred = self.logit(out["prediction"]) + return pred diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 18341a99..4a18deff 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -260,44 +260,37 @@ def train_common( return 0 if explain: + # local_index = np.random.randint(low=0, high=batch_size, size=1) attributions_dict = {} - # Interpertations = model.interpertations(test_loader, log_dir, plot=True) - # attributions_dict["attention_weights"] = Interpertations["attention"].tolist() - # attributions_dict["static_variables"] = Interpertations["static_variables"].tolist() - # attributions_dict["encoder_variables"] = Interpertations["encoder_variables"].tolist() - # print("attention", Interpertations) - # model.train() + + model.train() XAI_dict = {} # if XAI_metric: # random attribution per timestep - # random_attributions_ts = np.random.normal(size=24) - # F_baseline_ts = model.Faithfulness_Correlation( - # test_loader, random_attributions_ts, pertrub='Noise', subset_size=4, time_step=True, nr_runs=100) - # print('Random normal faithfulness correlation for timesteps', F_baseline_ts) - - # F_attention = model.Faithfulness_Correlation( - # test_loader, Interpertations["attention"], pertrub='Noise', subset_size=4, time_step=True, nr_runs=100) - # print('Attention faithfulness correlation', F_attention) + random_attributions_ts = np.random.normal(size=24) + F_baseline_ts = model.Faithfulness_Correlation( + test_loader, random_attributions_ts, pertrub='baseline', subset_size=4, time_step=True, nr_runs=10) + print('Random normal faithfulness correlation for timesteps', F_baseline_ts) + # random attribution per variable per timestep - # random_attributions_v_ts = np.random.normal(size=[24, 53]) - # F_baseline_v_ts = model.Faithfulness_Correlation( - # test_loader, random_attributions_v_ts, pertrub='Noise', feature_timestep=True, nr_runs=10, subset_size=[4, 9]) - # print('Random normal faithfulness correlation for variables per timesteps', F_baseline_v_ts) - - # XAI_dict["attention_faith"] = F_attention.tolist() - # XAI_dict["random_faith_timestep"] = random_attributions_ts.tolist() - # XAI_dict["random_faith_var_timestep"] = random_attributions_v_ts.tolist() + random_attributions_v_ts = np.random.normal(size=[24, 53]) + F_baseline_v_ts = model.Faithfulness_Correlation( + test_loader, random_attributions_v_ts, pertrub='baseline', feature_timestep=True, nr_runs=10, subset_size=[4, 9]) + print('Random normal faithfulness correlation for variables per timesteps', F_baseline_v_ts) + + XAI_dict["random_faith_timestep"] = random_attributions_ts.tolist() + XAI_dict["random_faith_var_timestep"] = random_attributions_v_ts.tolist() methods = { - # "Saliency": Saliency, - # "Lime": Lime, + "Saliency": Saliency, + # "Lime": Lime, # "IG": IntegratedGradients, - "FA": FeatureAblation + # "FA": FeatureAblation } for key, item in methods.items(): if key == "IG": all_attrs, features_attrs, timestep_attrs = model.explantation_captum( - test_loader=test_loader, method=item, log_dir=log_dir, plot=True, n_steps=50 + test_loader=test_loader, method=item, log_dir=log_dir, plot=True, n_steps=10 ) if key == "Lime": all_attrs, features_attrs, timestep_attrs = model.explantation_captum( @@ -310,22 +303,33 @@ def train_common( attributions_dict["{}_all".format(key)] = all_attrs.tolist() attributions_dict["{}_timesteps".format(key)] = timestep_attrs.tolist() attributions_dict["{}_features".format(key)] = features_attrs.tolist() - - print("{}".format(key), all_attrs, features_attrs, timestep_attrs) + print(np.shape(all_attrs), 'shape of attr all shoulld be 24x53') + print(np.shape(timestep_attrs), 'shape of attr ts shoulld be 24') + print(np.shape(features_attrs), 'shape of attr var shoulld be 53') + # print("{}".format(key), all_attrs, features_attrs, timestep_attrs) if XAI_metric: faithfulness_timesteps = model.Faithfulness_Correlation( - test_loader, timestep_attrs, pertrub="Noise", time_step=True, subset_size=4, nr_runs=100 + test_loader, timestep_attrs, pertrub="baseline", time_step=True, subset_size=4, nr_runs=10 ) - print("Attributions faithfulness timesteps correlation", faithfulness_timesteps) + print("{} Attributions faithfulness timesteps correlation".format(key), faithfulness_timesteps) XAI_dict["{}_faith_timesteps".format(key)] = faithfulness_timesteps.tolist() random_attributions = np.random.normal(np.shape(all_attrs)) faithfulness_timesteps_variable = model.Faithfulness_Correlation( - test_loader, all_attrs, pertrub="Noise", feature_timestep=True, subset_size=[4, 9], nr_runs=100 + test_loader, all_attrs, pertrub="baseline", feature_timestep=True, subset_size=[4, 9], nr_runs=10 ) - print("Attributions faithfulness variable per timestep correlation", faithfulness_timesteps_variable) + print("{}_Attributions faithfulness variable per timestep correlation".format( + key), faithfulness_timesteps_variable) XAI_dict["{}_faith_variable_per_timestep".format(key)] = faithfulness_timesteps_variable.tolist() - + Interpertations = model.interpertations(test_loader, log_dir, plot=True) + attributions_dict["attention_weights"] = Interpertations["attention"].tolist() + attributions_dict["static_variables"] = Interpertations["static_variables"].tolist() + attributions_dict["encoder_variables"] = Interpertations["encoder_variables"].tolist() + print("attention", Interpertations) + F_attention = model.Faithfulness_Correlation( + test_loader, Interpertations["attention"], pertrub='baseline', subset_size=4, time_step=True, nr_runs=10) + print('Attention faithfulness correlation', F_attention) + XAI_dict["attention_faith"] = F_attention.tolist() # Path to the JSON file in log_dir json_file_path = f"{log_dir}/Attributions.json" diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 6385b677..dacfbfb3 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -4,7 +4,7 @@ import torchmetrics from sklearn.metrics import log_loss, mean_squared_error import torch -from torch.nn import MSELoss, CrossEntropyLoss +from torch.nn import MSELoss, CrossEntropyLoss, L1Loss import torch.nn as nn from torch import Tensor, FloatTensor from torch.optim import Optimizer, Adam @@ -19,13 +19,13 @@ from icu_benchmarks.models.constants import MLMetrics, DLMetrics from icu_benchmarks.contants import RunMode import matplotlib.pyplot as plt -from icu_benchmarks.models.similarity_func import correlation_spearman, distance_euclidean +from icu_benchmarks.models.similarity_func import correlation_spearman, distance_euclidean, correlation_pearson import captum gin.config.external_configurable(nn.functional.nll_loss, module="torch.nn.functional") gin.config.external_configurable(nn.functional.cross_entropy, module="torch.nn.functional") gin.config.external_configurable(nn.functional.mse_loss, module="torch.nn.functional") - +gin.config.external_configurable(nn.functional.l1_loss, module="torch.nn.functional") gin.config.external_configurable(mean_squared_error, module="sklearn.metrics") gin.config.external_configurable(log_loss, module="sklearn.metrics") @@ -160,7 +160,8 @@ def finalize_step(self, step_prefix=""): try: for name, metric in self.metrics[step_prefix].items(): try: - value = np.float32(metric.compute()) if isinstance(metric.compute(), np.float64) else metric.compute() + value = np.float32(metric.compute()) if isinstance( + metric.compute(), np.float64) else metric.compute() self.log_dict({f"{step_prefix}/{name}": value}, sync_dist=True) except (NotComputableError, ValueError) as e: @@ -431,21 +432,8 @@ def step_fn(self, element, step_prefix=""): if isinstance(labels, list): labels = labels[-1] - data = ( - dic["encoder_cat"], - dic["encoder_cont"], - dic["encoder_target"], - dic["encoder_lengths"], - dic["decoder_cat"], - dic["decoder_cont"], - dic["decoder_target"], - dic["decoder_lengths"], - dic["decoder_time_idx"], - dic["groups"], - dic["target_scale"], - ) - mask = torch.ones_like(labels).bool() + data = self.prep_data(dic) out = self(data) @@ -456,15 +444,17 @@ def step_fn(self, element, step_prefix=""): aux_loss = 0 # Get prediction and target - prediction = torch.masked_select(out, mask.unsqueeze(-1)).reshape(-1, out.shape[-1]).to(self.device) + prediction = out.to(self.device).squeeze(-1) + + target = labels.to(self.device) - target = torch.masked_select(labels, mask).to(self.device) if prediction.shape[-1] > 1 and self.run_mode == RunMode.classification: # Classification task loss = self.loss(prediction, target.long(), weight=self.loss_weights.to(self.device)) + aux_loss # Returns torch.long because negative log likelihood loss elif self.run_mode == RunMode.regression: # Regression task + loss = self.loss(prediction[:, 0], target.float()) + aux_loss else: raise ValueError(f"Run mode {self.run_mode} not yet supported. Please implement it.") @@ -487,56 +477,70 @@ def step_fn(self, element, step_prefix=""): self.log(f"{step_prefix}/loss", loss, on_step=False, on_epoch=True, sync_dist=True) return loss - def explantation_captum(self, test_loader, method, log_dir=".", plot=False, **kwargs): + def prep_data_captum(self, x): + data = ( + x["encoder_cat"].float().requires_grad_(), + x["encoder_cont"].requires_grad_(), + x["encoder_target"].float().requires_grad_(), + x["encoder_lengths"].float().requires_grad_(), + x["decoder_cat"].float().requires_grad_(), + x["decoder_cont"].requires_grad_(), + x["decoder_target"].float().requires_grad_(), + x["decoder_lengths"].float().requires_grad_(), + x["decoder_time_idx"].float().requires_grad_(), + x["groups"].float().requires_grad_(), + x["target_scale"].requires_grad_(), + ) + baselines = ( + data[0].to(self.device), # encoder_cat, no cat variables + torch.zeros_like(data[1]).to(self.device), # encoder_cont, set to random + torch.zeros_like(data[2]).to(self.device), # encoder_target, set to random + data[3].to(self.device), # encoder_lengths, leave unchanged + data[4].to(self.device), # decoder_cat, no cat variables + torch.zeros_like(data[5]).to(self.device), # decoder_cont, set to random + torch.zeros_like(data[6]).to(self.device), # decoder_target, set to random + data[7].to(self.device), # decoder_lengths, leave unchanged + data[8].to(self.device), # decoder_time_idx, unchanged + data[9].to(self.device), # groups, leave unchanged + data[10].to(self.device), # target_scale, leave unchanged + ) + return data, baselines + + def explantation_captum(self, test_loader, method, local_index=None, log_dir=".", plot=False, local=False, **kwargs): # Initialize lists to store attribution values for all instances all_attrs = [] + score = [] + r_score = [] method_name = method.__name__ # Loop through the test_loader to compute attributions for all instances for batch in test_loader: + for key, value in batch[0].items(): batch[0][key] = batch[0][key].to(self.device) x = batch[0] - data = ( - x["encoder_cat"].float().requires_grad_(), - x["encoder_cont"].requires_grad_(), - x["encoder_target"].float().requires_grad_(), - x["encoder_lengths"].float().requires_grad_(), - x["decoder_cat"].float().requires_grad_(), - x["decoder_cont"].requires_grad_(), - x["decoder_target"].float().requires_grad_(), - x["decoder_lengths"].float().requires_grad_(), - x["decoder_time_idx"].float().requires_grad_(), - x["groups"].float().requires_grad_(), - x["target_scale"].requires_grad_(), - ) - baselines = ( - data[0].to(self.device), # encoder_cat, no cat variables - torch.randn_like(data[1]).to(self.device), # encoder_cont, set to random - torch.randn_like(data[2]).to(self.device), # encoder_target, set to random - data[3].to(self.device), # encoder_lengths, leave unchanged - data[4].to(self.device), # decoder_cat, no cat variables - torch.randn_like(data[5]).to(self.device), # decoder_cont, set to random - torch.randn_like(data[6]).to(self.device), # decoder_target, set to random - data[7].to(self.device), # decoder_lengths, leave unchanged - data[8].to(self.device), # decoder_time_idx, unchanged - data[9].to(self.device), # groups, leave unchanged - data[10].to(self.device), # target_scale, leave unchanged - ) + data, baselines = self.prep_data_captum(x) explantation = method(self.forward_captum) - # Reformat attributions. - # attr_all_timesteps = [] - # for time_step in range(0, 24): + if method is not captum.attr.Saliency: attr = explantation.attribute(data, baselines=baselines, **kwargs) else: attr = explantation.attribute(data, **kwargs) - print(attr) - print(attr[0].shape) # Convert attributions to numpy array and append to the list - all_attrs.append(attr[0].cpu().detach().numpy()) + print(attr.shape, 'shape out of captum should be 64x24x53') + stacked_attr = torch.stack(attr).cpu().detach().numpy() + + # Compute the mean along the specified axis (axis=0) and keep it as a numpy array + """ score.append(self.Faithfulness_Correlation2(x, stacked_attr, + pertrub="Noise", feature_timestep=True, subset_size=[4, 9], nr_runs=10)) + r_score.append(self.Faithfulness_Correlation2(x, np.random.normal( + size=[64, 24, 53]), pertrub="Noise", feature_timestep=True, subset_size=[4, 9], nr_runs=10)) """ + print(np.shape(stacked_attr), 'shape after stacking') + attr = np.mean(stacked_attr, axis=0) + print(np.shape(attr), 'shape after averaging over batch should 24x53') + all_attrs.append(attr) all_attrs = np.array(all_attrs).mean(axis=(0)) @@ -561,7 +565,8 @@ def explantation_captum(self, test_loader, method, log_dir=".", plot=False, **kw plt.xlabel("Time Step") plt.ylabel("{} Attribution".format(method_name)) plt.title("{} Attribution Values".format(method_name)) - plt.xticks(x_values) # Set x-ticks to match the number of features + plt.xticks(x_values, ['height', 'weight', 'age', 'sex', 'time_idx', 'alb', 'alp', 'alt', 'ast', 'be', 'bicar', 'bili', 'bili_dir', 'bnd', 'bun', 'ca', 'cai', 'ck', 'ckmb', 'cl', 'crea', 'crp', 'dbp', 'fgn', 'fio2', 'glu', 'hgb', 'hr', 'inr_pt', 'k', 'lact', 'lymph', 'map', 'mch', 'mchc', 'mcv', 'methb', 'mg', 'na', 'neut', 'o2sat', 'pco2', 'ph', 'phos', 'plt', 'po2', 'ptt', 'resp', 'sbp', 'temp', 'tnt', 'urine', 'wbc'], rotation=90 + ) # Set x-ticks to match the number of features plt.tight_layout() plt.savefig(log_dir / "{}_attribution_features_plot.png".format(method_name), bbox_inches="tight") plt.figure(figsize=(8, 6)) @@ -583,10 +588,27 @@ def explantation_captum(self, test_loader, method, log_dir=".", plot=False, **kw plt.savefig(log_dir / "{}_attribution_plot.png".format(method_name), bbox_inches="tight") return all_attrs, features_attrs, timestep_attrs + def prep_data(self, x): + data = ( + x["encoder_cat"], + x["encoder_cont"], + x["encoder_target"], + x["encoder_lengths"], + x["decoder_cat"], + x["decoder_cont"], + x["decoder_target"], + x["decoder_lengths"], + x["decoder_time_idx"], + x["groups"], + x["target_scale"], + ) + return data + def Faithfulness_Correlation( self, test_loader, attribution, + local_index=None, similarity_func=None, nr_runs=100, pertrub=None, @@ -594,6 +616,7 @@ def Faithfulness_Correlation( feature=False, time_step=False, feature_timestep=False, + local=False, ): """ Implementation of faithfulness correlation by Bhatt et al., 2020. @@ -617,93 +640,197 @@ def Faithfulness_Correlation( explanations." IJCAI (2020): 3016-3022. 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. """ - - if torch.is_tensor(attribution): - # Convert the tensor to a NumPy array - attribution = attribution.cpu().detach().numpy() + def add_noise(x, indices, time_step, feature_timestep): + noise = torch.randn_like(x["encoder_cont"]) + # In-place modification to save memory + if time_step: + x["encoder_cont"][:, indices, :] += noise[:, indices, :] + elif feature_timestep: + x["encoder_cont"][:, indices[0], :][:, :, indices[1]] += noise[:, indices[0], :][:, :, indices[1]] + + def apply_baseline(x, indices, time_step, feature_timestep): + mask = torch.ones_like(x["encoder_cont"]) + if time_step: + + mask[:, indices, :] -= mask[:, indices, :] + elif feature_timestep: + mask[:, indices[0], :][:, :, indices[1]] -= mask[:, indices[0], :][:, :, indices[1]] + # Apply mask in-place + x["encoder_cont"] *= mask + + # Assuming 'attribution' is already a GPU tensor + if not torch.is_tensor(attribution): + attribution = torch.tensor(attribution).to(self.device) + + # Other initializations if similarity_func is None: - similarity_func = correlation_spearman + similarity_func = correlation_pearson # Ensure this function is compatible with GPU tensors if pertrub is None: pertrub = "baseline" similarities = [] - for batch in test_loader: - for key, value in batch[0].items(): - batch[0][key] = batch[0][key].to(self.device) + if local: + for key, value in test_loader[0].items(): + batch[0][key] = value.to(self.device) x = batch[0] - data = ( - x["encoder_cat"], - x["encoder_cont"], - x["encoder_target"], - x["encoder_lengths"], - x["decoder_cat"], - x["decoder_cont"], - x["decoder_target"], - x["decoder_lengths"], - x["decoder_time_idx"], - x["groups"], - x["target_scale"], - ) - y_pred = self(data).detach().cpu().numpy() - pred_deltas = [] - att_sums = [] - - def add_noise(x, indices, time_step, feature_timestep): - noise = torch.randn_like(x["encoder_cont"]) - if time_step: - x["encoder_cont"][:, indices, :] += noise[:, indices, :] - elif feature_timestep: - x["encoder_cont"][:, indices[0], :][:, :, indices[1]] += noise[:, indices[0], :][:, :, indices[1]] - - def apply_baseline(x, indices, time_step, feature_timestep): - mask = torch.ones_like(x["encoder_cont"]).cpu() - if time_step: - mask[:, indices, :] = 0 - elif feature_timestep: - mask[:, indices[0], :][:, :, indices[1]] = 0 - mask = mask.to(x["encoder_cont"].device) - x["encoder_cont"] *= mask - for i_ix in range(nr_runs): - # Randomly mask by subset size. + else: - if time_step: - a_ix = np.random.choice(24, subset_size, replace=False) - elif feature_timestep: - timesteps_idx = np.random.choice(24, subset_size[0], replace=False) - variables_idx = np.random.choice(53, subset_size[1], replace=False) - a_ix = [timesteps_idx, variables_idx] + for batch in test_loader: + # Move batch to GPU + for key, value in batch[0].items(): + batch[0][key] = value.to(self.device) + x = batch[0] + # Assuming this is a method to prepare your data + + y_pred = self((self.prep_data(x))).detach() # Keep on GPU + pred_deltas = [] + att_sums = [] + + for i_ix in range(nr_runs): + if time_step: + a_ix = np.random.choice(24, subset_size, replace=False) + elif feature_timestep: + timesteps_idx = np.random.choice(24, subset_size[0], replace=False) + variables_idx = np.random.choice(53, subset_size[1], replace=False) + a_ix = [timesteps_idx, variables_idx] + + # Apply perturbation + if pertrub == "Noise": + add_noise(x, a_ix, time_step, feature_timestep) + elif pertrub == "baseline": + apply_baseline(x, a_ix, time_step, feature_timestep) + + # Predict on perturbed input and calculate deltas + y_pred_perturb = (self(self.prep_data(x))).detach() # Keep on GPU + # Change dims as per your requirement + pred_deltas.append((y_pred - y_pred_perturb).mean(dim=[0, 1, 2])) + + # Sum attributions of the random subset and keep on GPU + if time_step: + att_sums.append(attribution[a_ix].sum()) + elif feature_timestep: + row_indices = a_ix[0] + col_indices = a_ix[1] + att_sums.append(attribution[row_indices][:, col_indices].sum()) + + # Convert to CPU for numpy operations + pred_deltas_cpu = torch.stack(pred_deltas).cpu().numpy() + att_sums_cpu = torch.tensor(att_sums).cpu().numpy() + similarities.append(similarity_func(pred_deltas_cpu, att_sums_cpu)) - if pertrub == "Noise": - add_noise(x, a_ix, time_step, feature_timestep) - elif pertrub == "baseline": - apply_baseline(x, a_ix, time_step, feature_timestep) + score = np.nanmean(similarities) + return score - data = ( - x["encoder_cat"], - x["encoder_cont"], - x["encoder_target"], - x["encoder_lengths"], - x["decoder_cat"], - x["decoder_cont"], - x["decoder_target"], - x["decoder_lengths"], - x["decoder_time_idx"], - x["groups"], - x["target_scale"], - ) - # Predict on perturbed input x. - y_pred_perturb = self(data).detach().cpu().numpy() - pred_deltas.append((y_pred - y_pred_perturb).mean()) - - # Sum attributions of the random subset. - if time_step: - att_sums.append(np.sum(attribution[a_ix])) - elif feature_timestep: - row_indices = a_ix[0] - col_indices = a_ix[1] - att_sums.append(np.sum(attribution[np.ix_(row_indices, col_indices)])) - - similarities.append(similarity_func(pred_deltas, att_sums)) + def Faithfulness_Correlation2( + self, + x, + attribution, + local_index=None, + similarity_func=None, + nr_runs=100, + pertrub=None, + subset_size=3, + feature=False, + time_step=False, + feature_timestep=False, + local=False, + ): + """ + Implementation of faithfulness correlation by Bhatt et al., 2020. + + The Faithfulness Correlation metric intend to capture an explanation's relative faithfulness + (or 'fidelity') with respect to the model behaviour. + + Faithfulness correlation scores shows to what extent the predicted logits of each modified test point and + the average explanation attribution for only the subset of features are (linearly) correlated, taking the + average over multiple runs and test samples. The metric returns one float per input-attribution pair that + ranges between -1 and 1, where higher scores are better. + + For each test sample, |S| features are randomly selected and replace them with baseline values (zero baseline + or average of set). Thereafter, Pearson’s correlation coefficient between the predicted logits of each modified + test point and the average explanation attribution for only the subset of features is calculated. Results is + average over multiple runs and several test samples. + This code is adapted from the quantus libray to suit our use case + + References: + 1) Umang Bhatt et al.: "Evaluating and aggregating feature-based model + explanations." IJCAI (2020): 3016-3022. + 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. + """ + def add_noise(x, indices, time_step, feature_timestep): + noise = torch.randn_like(x["encoder_cont"]) + # In-place modification to save memory + if time_step: + x["encoder_cont"][indices[0], :, :][:, indices[1], :] += noise[indices[0], :, :][:, indices[1], :] + elif feature_timestep: + x["encoder_cont"][indices[0], :, :][:, indices[1], :][:, :, indices[2] + ] += noise[indices[0], :, :][:, indices[1], :][:, :, indices[2]] + + def apply_baseline(x, indices, time_step, feature_timestep): + mask = torch.ones_like(x["encoder_cont"]) + if time_step: + mask[indices[0], :, :][:, indices[1], :] = 0 + elif feature_timestep: + mask[indices[0], :, :][:, indices[1], :][:, :, indices[2] + ] = 0 + with torch.no_grad(): + x["encoder_cont"] *= mask + # Assuming 'attribution' is already a GPU tensor + if not torch.is_tensor(attribution): + attribution = torch.tensor(attribution).to(self.device) + + # Other initializations + if similarity_func is None: + similarity_func = correlation_pearson # Ensure this function is compatible with GPU tensors + if pertrub is None: + pertrub = "baseline" + similarities = [] + + # Assuming this is a method to prepare your data + + y_pred = self(self.prep_data(x)).detach() # Keep on GPU + pred_deltas = [] + att_sums = [] + + for i_ix in range(nr_runs): + if time_step: + timesteps_idx = np.random.choice(24, subset_size, replace=False) + patient_idx = np.random.choice(64, subset_size, replace=False) + a_ix = [patient_idx, timesteps_idx] + elif feature_timestep: + timesteps_idx = np.random.choice(24, subset_size[0], replace=False) + variables_idx = np.random.choice(53, subset_size[1], replace=False) + patient_idx = np.random.choice(64, 1, replace=False) + a_ix = [patient_idx, timesteps_idx, variables_idx] + + # Apply perturbation + if pertrub == "Noise": + add_noise(x, a_ix, time_step, feature_timestep) + elif pertrub == "baseline": + apply_baseline(x, a_ix, time_step, feature_timestep) + + # Predict on perturbed input and calculate deltas + y_pred_perturb = (self(self.prep_data(x))).detach() # Keep on GPU + pred_deltas.append((y_pred - y_pred_perturb).mean(dim=[0, 1, 2])) # Change dims as per your requirement + + # Sum attributions of the random subset and keep on GPU + if time_step: + patient_indices = a_ix[0] + timestep_indices = a_ix[1] + att_sums.append(attribution[patient_indices, :, :][:, timestep_indices, :].sum()) + + elif feature_timestep: + patient_indices = a_ix[0] + timestep_indices = a_ix[1] + variable_indices = a_ix[2] + att_sums.append(attribution[patient_indices, :, :] + [:, timestep_indices, :][:, :, variable_indices].sum()) + + # Convert to CPU for numpy operations + + pred_deltas_cpu = torch.stack(pred_deltas).cpu().numpy() + att_sums_cpu = torch.tensor(att_sums).cpu().numpy() + similarities.append(similarity_func(pred_deltas_cpu, att_sums_cpu)) score = np.nanmean(similarities) return score From ab115fdee0b4f2a685ac219978fb83c0a0b2e67c Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Tue, 28 Nov 2023 18:16:13 +0100 Subject: [PATCH 114/142] cleaned and fixed aggreagtion along batch --- configs/prediction_models/DeepARpytorch.gin | 6 +- configs/prediction_models/RNNpytorch.gin | 8 +- configs/prediction_models/TFTpytorch.gin | 15 +- configs/prediction_models/common/DLCommon.gin | 2 + configs/prediction_models/common/DLTuning.gin | 2 +- icu_benchmarks/models/custom_metrics.py | 2 + icu_benchmarks/models/metrics.py | 5 + icu_benchmarks/models/train.py | 99 ++-- icu_benchmarks/models/wrappers.py | 458 ++++++++++-------- icu_benchmarks/run_utils.py | 8 +- 10 files changed, 320 insertions(+), 285 deletions(-) diff --git a/configs/prediction_models/DeepARpytorch.gin b/configs/prediction_models/DeepARpytorch.gin index 1b7a9ca0..9a61ca84 100644 --- a/configs/prediction_models/DeepARpytorch.gin +++ b/configs/prediction_models/DeepARpytorch.gin @@ -8,17 +8,17 @@ train_common.model = @DeepARpytorch optimizer/hyperparameter.class_to_tune = @Adam optimizer/hyperparameter.weight_decay = 1e-6 - +optimizer/hyperparameter.lr = (0.0001, 0.001,0.01) # Model params model/hyperparameter.class_to_tune = @DeepARpytorch -model/hyperparameter.hidden = (4, 64, "log-uniform", 2) +model/hyperparameter.hidden = (32, 256, "log-uniform", 2) model/hyperparameter.rnn_layers=(1, 3) model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.cell_type='LSTM' model/hyperparameter.dropout = (0.0, 0.4) -model/hyperparameter.lr_scheduler = "exponential" +#model/hyperparameter.lr_scheduler = "exponential" # Dataset params diff --git a/configs/prediction_models/RNNpytorch.gin b/configs/prediction_models/RNNpytorch.gin index 3969df0f..aa2398cb 100644 --- a/configs/prediction_models/RNNpytorch.gin +++ b/configs/prediction_models/RNNpytorch.gin @@ -8,17 +8,17 @@ train_common.model = @RNNpytorch optimizer/hyperparameter.class_to_tune = @Adam optimizer/hyperparameter.weight_decay = 1e-6 - +optimizer/hyperparameter.lr = (0.0001, 0.001,0.01) # Model params model/hyperparameter.class_to_tune = @RNNpytorch -model/hyperparameter.hidden = (4, 64, "log-uniform", 2) +model/hyperparameter.hidden = (32, 256, "log-uniform", 2) model/hyperparameter.rnn_layers=(1,3) model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.cell_type='LSTM' -model/hyperparameter.dropout = (0.0, 0.4) -model/hyperparameter.lr_scheduler = "exponential" +model/hyperparameter.dropout = (0.0, 0.9) +#model/hyperparameter.lr_scheduler = "exponential" # Dataset params diff --git a/configs/prediction_models/TFTpytorch.gin b/configs/prediction_models/TFTpytorch.gin index 94a42740..4f2d078e 100644 --- a/configs/prediction_models/TFTpytorch.gin +++ b/configs/prediction_models/TFTpytorch.gin @@ -8,16 +8,21 @@ train_common.model = @TFTpytorch optimizer/hyperparameter.class_to_tune = @Adam optimizer/hyperparameter.weight_decay = 1e-6 +optimizer/hyperparameter.lr = (0.0001, 0.001,0.01) # Model params model/hyperparameter.class_to_tune = @TFTpytorch -model/hyperparameter.hidden = (16,32,64) +model/hyperparameter.hidden = (10,20,40,80,160,240,320) model/hyperparameter.num_classes = %NUM_CLASSES -model/hyperparameter.dropout = (0.0, 0.4) -model/hyperparameter.dropout_att = (0.0, 0.4) -model/hyperparameter.n_heads =(1,2,4) -model/hyperparameter.lr_scheduler = "exponential" +model/hyperparameter.dropout =(0.1, 0.9) +model/hyperparameter.dropout_att = (0.1, 0.9) +model/hyperparameter.n_heads =(1,4) + + + + + # Dataset params diff --git a/configs/prediction_models/common/DLCommon.gin b/configs/prediction_models/common/DLCommon.gin index fa4b440f..2760e187 100644 --- a/configs/prediction_models/common/DLCommon.gin +++ b/configs/prediction_models/common/DLCommon.gin @@ -19,5 +19,7 @@ train_common.batch_size = 64 train_common.patience = 20 train_common.min_delta = 1e-4 + + # Hyperparameter tuning settings include "configs/prediction_models/common/DLTuning.gin" \ No newline at end of file diff --git a/configs/prediction_models/common/DLTuning.gin b/configs/prediction_models/common/DLTuning.gin index ec0240e2..0c56677c 100644 --- a/configs/prediction_models/common/DLTuning.gin +++ b/configs/prediction_models/common/DLTuning.gin @@ -1,5 +1,5 @@ # Hyperparameter tuner settings for Deep Learning. tune_hyperparameters.scopes = ["model", "optimizer"] tune_hyperparameters.n_initial_points = 5 -tune_hyperparameters.n_calls = 15 +tune_hyperparameters.n_calls = 20 tune_hyperparameters.folds_to_tune_on = 2 \ No newline at end of file diff --git a/icu_benchmarks/models/custom_metrics.py b/icu_benchmarks/models/custom_metrics.py index ddb5d37e..d1dbc271 100644 --- a/icu_benchmarks/models/custom_metrics.py +++ b/icu_benchmarks/models/custom_metrics.py @@ -67,8 +67,10 @@ def __init__( ) def mae_with_invert_compute_fn(y_preds: torch.Tensor, y_targets: torch.Tensor, invert_fn=Callable) -> float: + y_true = invert_fn(y_targets.numpy().reshape(-1, 1))[:, 0] y_pred = invert_fn(y_preds.numpy().reshape(-1, 1))[:, 0] + return mean_absolute_error(y_true, y_pred) diff --git a/icu_benchmarks/models/metrics.py b/icu_benchmarks/models/metrics.py index 5428db61..7301d8d9 100644 --- a/icu_benchmarks/models/metrics.py +++ b/icu_benchmarks/models/metrics.py @@ -45,8 +45,13 @@ def ece_curve_compute_fn(y_preds: torch.Tensor, y_targets: torch.Tensor) -> floa def mae_with_invert_compute_fn(y_preds: torch.Tensor, y_targets: torch.Tensor, invert_fn=Callable) -> float: + print('y_true', y_true) + print('y_pred', y_pred) y_true = invert_fn(y_targets.numpy().reshape(-1, 1))[:, 0] y_pred = invert_fn(y_preds.numpy().reshape(-1, 1))[:, 0] + print('y_true', y_true) + print('y_pred', y_pred) + print('mae', mean_absolute_error(y_true, y_pred)) return mean_absolute_error(y_true, y_pred) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 4a18deff..1c739b90 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -24,11 +24,12 @@ ImputationDataset, PredictionDatasetpytorch, ) + from icu_benchmarks.models.utils import save_config_file, JSONMetricsLogger from icu_benchmarks.contants import RunMode from icu_benchmarks.data.constants import DataSplit as Split from collections import OrderedDict -from captum.attr import IntegratedGradients, ShapleyValueSampling, Saliency, GuidedBackprop, LRP, FeatureAblation, Lime +from captum.attr import IntegratedGradients, Saliency, FeatureAblation, Lime cpu_core_count = len(os.sched_getaffinity(0)) if hasattr(os, "sched_getaffinity") else os.cpu_count() @@ -57,6 +58,7 @@ def train_common( epochs=1000, patience=20, min_delta=1e-5, + gradient_clip_val=0, test_on: str = Split.test, dataset_names=None, use_wandb: bool = False, @@ -242,6 +244,7 @@ def train_common( logger=loggers, num_sanity_val_steps=-1, log_every_n_steps=5, + ) if not eval_only: if model.requires_backprop: @@ -260,82 +263,50 @@ def train_common( return 0 if explain: - # local_index = np.random.randint(low=0, high=batch_size, size=1) - attributions_dict = {} - - model.train() - XAI_dict = {} - # if XAI_metric: - - # random attribution per timestep - random_attributions_ts = np.random.normal(size=24) - F_baseline_ts = model.Faithfulness_Correlation( - test_loader, random_attributions_ts, pertrub='baseline', subset_size=4, time_step=True, nr_runs=10) - print('Random normal faithfulness correlation for timesteps', F_baseline_ts) - - # random attribution per variable per timestep - random_attributions_v_ts = np.random.normal(size=[24, 53]) - F_baseline_v_ts = model.Faithfulness_Correlation( - test_loader, random_attributions_v_ts, pertrub='baseline', feature_timestep=True, nr_runs=10, subset_size=[4, 9]) - print('Random normal faithfulness correlation for variables per timesteps', F_baseline_v_ts) - - XAI_dict["random_faith_timestep"] = random_attributions_ts.tolist() - XAI_dict["random_faith_var_timestep"] = random_attributions_v_ts.tolist() + + XAI_dict = {} # dictrionary to log attributions metrics + + # choose which methods to get attributions methods = { "Saliency": Saliency, # "Lime": Lime, - # "IG": IntegratedGradients, - # "FA": FeatureAblation + "IG": IntegratedGradients, + # "FA": FeatureAblation, + "Random": "Random", + "Attention": "Attention" } for key, item in methods.items(): + # If conditions needed here as different explantations require different inputs if key == "IG": - all_attrs, features_attrs, timestep_attrs = model.explantation_captum( - test_loader=test_loader, method=item, log_dir=log_dir, plot=True, n_steps=10 + all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score = model.explantation( + dataloader=test_loader, method=item, log_dir=log_dir, plot=True, n_steps=50, XAI_metric=XAI_metric, ) - if key == "Lime": - all_attrs, features_attrs, timestep_attrs = model.explantation_captum( - test_loader=test_loader, method=item, log_dir=log_dir, plot=True, return_input_shape=True + elif key == "Lime": + all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score = model.explantation( + dataloader=test_loader, method=item, log_dir=log_dir, plot=True, return_input_shape=True, XAI_metric=XAI_metric, ) else: - all_attrs, features_attrs, timestep_attrs = model.explantation_captum( - test_loader=test_loader, method=item, log_dir=log_dir, plot=True + all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score = model.explantation( + dataloader=test_loader, method=item, log_dir=log_dir, plot=True, XAI_metric=XAI_metric, ) - attributions_dict["{}_all".format(key)] = all_attrs.tolist() - attributions_dict["{}_timesteps".format(key)] = timestep_attrs.tolist() - attributions_dict["{}_features".format(key)] = features_attrs.tolist() - print(np.shape(all_attrs), 'shape of attr all shoulld be 24x53') - print(np.shape(timestep_attrs), 'shape of attr ts shoulld be 24') - print(np.shape(features_attrs), 'shape of attr var shoulld be 53') - # print("{}".format(key), all_attrs, features_attrs, timestep_attrs) + if XAI_metric: - faithfulness_timesteps = model.Faithfulness_Correlation( - test_loader, timestep_attrs, pertrub="baseline", time_step=True, subset_size=4, nr_runs=10 - ) - print("{} Attributions faithfulness timesteps correlation".format(key), faithfulness_timesteps) - XAI_dict["{}_faith_timesteps".format(key)] = faithfulness_timesteps.tolist() - random_attributions = np.random.normal(np.shape(all_attrs)) + # logging metric scores + print("{} Attributions faithfulness timesteps ".format(key), ts_score) + XAI_dict["{}_faith_timesteps".format(key)] = ts_score + if key == "Attention": + print("Variable selection weights faithfulness featrues ".format(key), v_score) + XAI_dict["Variable_selection_weights_faith_features".format(key)] = v_score - faithfulness_timesteps_variable = model.Faithfulness_Correlation( - test_loader, all_attrs, pertrub="baseline", feature_timestep=True, subset_size=[4, 9], nr_runs=10 - ) - print("{}_Attributions faithfulness variable per timestep correlation".format( - key), faithfulness_timesteps_variable) - XAI_dict["{}_faith_variable_per_timestep".format(key)] = faithfulness_timesteps_variable.tolist() - Interpertations = model.interpertations(test_loader, log_dir, plot=True) - attributions_dict["attention_weights"] = Interpertations["attention"].tolist() - attributions_dict["static_variables"] = Interpertations["static_variables"].tolist() - attributions_dict["encoder_variables"] = Interpertations["encoder_variables"].tolist() - print("attention", Interpertations) - F_attention = model.Faithfulness_Correlation( - test_loader, Interpertations["attention"], pertrub='baseline', subset_size=4, time_step=True, nr_runs=10) - print('Attention faithfulness correlation', F_attention) - XAI_dict["attention_faith"] = F_attention.tolist() - # Path to the JSON file in log_dir - json_file_path = f"{log_dir}/Attributions.json" + else: + print("{} Attributions faithfulness featrues ".format(key), v_score) + XAI_dict["{}_faith_features".format(key)] = v_score - # Write the dictionary to a JSON file - with open(json_file_path, "w") as json_file: - json.dump(attributions_dict, json_file) + print("{}_Attributions faithfulness variable per timestep ".format( + key), ts_v_score) + XAI_dict["{}_faith_variable_per_timestep".format(key)] = ts_v_score + + # Getting the interpertations using pytorch forecasting native methods # Path to the JSON file in log_dir json_file_path = f"{log_dir}/XAI_metrics.json" diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index dacfbfb3..db2f77b8 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -21,6 +21,7 @@ import matplotlib.pyplot as plt from icu_benchmarks.models.similarity_func import correlation_spearman, distance_euclidean, correlation_pearson import captum +from captum._utils.models.linear_model import SkLearnLasso gin.config.external_configurable(nn.functional.nll_loss, module="torch.nn.functional") gin.config.external_configurable(nn.functional.cross_entropy, module="torch.nn.functional") @@ -478,6 +479,16 @@ def step_fn(self, element, step_prefix=""): return loss def prep_data_captum(self, x): + """ + Prepares data to be fed into captum and generates baseline as well. + + Args: + - x:Batch from dataloader + Returns: + - data:batch data in a tuple after being prepared + - baselines:Basically zero tensors in the input + """ + # captum requires gradient and float values data = ( x["encoder_cat"].float().requires_grad_(), x["encoder_cont"].requires_grad_(), @@ -493,12 +504,12 @@ def prep_data_captum(self, x): ) baselines = ( data[0].to(self.device), # encoder_cat, no cat variables - torch.zeros_like(data[1]).to(self.device), # encoder_cont, set to random - torch.zeros_like(data[2]).to(self.device), # encoder_target, set to random + torch.zeros_like(data[1]).to(self.device), # encoder_cont, set to zero + torch.zeros_like(data[2]).to(self.device), # encoder_target, set to zero data[3].to(self.device), # encoder_lengths, leave unchanged data[4].to(self.device), # decoder_cat, no cat variables - torch.zeros_like(data[5]).to(self.device), # decoder_cont, set to random - torch.zeros_like(data[6]).to(self.device), # decoder_target, set to random + torch.zeros_like(data[5]).to(self.device), # decoder_cont, set to zero + torch.zeros_like(data[6]).to(self.device), # decoder_target, set to zero data[7].to(self.device), # decoder_lengths, leave unchanged data[8].to(self.device), # decoder_time_idx, unchanged data[9].to(self.device), # groups, leave unchanged @@ -506,14 +517,126 @@ def prep_data_captum(self, x): ) return data, baselines - def explantation_captum(self, test_loader, method, local_index=None, log_dir=".", plot=False, local=False, **kwargs): + def plot_attributions(self, features_attrs, timestep_attrs, method_name, log_dir): + """ + Plots the attribution values for features and timesteps. + + Args: + - features_attrs: Array of feature attribution values. + - timestep_attrs: Array of timestep attribution values. + - method_name: Name of the attribution method. + - log_dir: Directory to save the plots. + Returns: + Nothing + """ + + # Plot for feature attributions + x_values = np.arange(1, len(features_attrs) + 1) + plt.figure(figsize=(8, 6)) + plt.plot( + x_values, + features_attrs, + marker="o", + color="skyblue", + linestyle="-", + linewidth=2, + markersize=8, + ) + plt.xlabel("Time Step") + plt.ylabel("{} Attribution".format(method_name)) + plt.title("{} Attribution Values".format(method_name)) + plt.xticks(x_values, ['height', 'weight', 'age', 'sex', 'time_idx', 'alb', 'alp', 'alt', 'ast', 'be', 'bicar', 'bili', 'bili_dir', 'bnd', 'bun', 'ca', 'cai', 'ck', 'ckmb', 'cl', 'crea', 'crp', 'dbp', 'fgn', 'fio2', 'glu', + 'hgb', 'hr', 'inr_pt', 'k', 'lact', 'lymph', 'map', 'mch', 'mchc', 'mcv', 'methb', 'mg', 'na', 'neut', 'o2sat', 'pco2', 'ph', 'phos', 'plt', 'po2', 'ptt', 'resp', 'sbp', 'temp', 'tnt', 'urine', 'wbc'], rotation=90) + plt.tight_layout() + plt.savefig(log_dir / "{}_attribution_features_plot.png".format(method_name), bbox_inches="tight") + + # Plot for timestep attributions + x_values = np.arange(1, len(timestep_attrs) + 1) + plt.figure(figsize=(8, 6)) + plt.plot( + x_values, + timestep_attrs, + marker="o", + color="skyblue", + linestyle="-", + linewidth=2, + markersize=8, + ) + plt.xlabel("Time Step") + plt.ylabel("{} Attribution".format(method_name)) + plt.title("{} Attribution Values".format(method_name)) + plt.xticks(x_values) + plt.tight_layout() + plt.savefig(log_dir / "{}_attribution_plot.png".format(method_name), bbox_inches="tight") + + def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=False, **kwargs): + """ + Generic method to combine pytorchforecasting data loading , interpertations and captum to generate attributions + + Args: + - dataloader: pytorchforecasting data loader + - method: The explantation method chosen + - log_dir= The directory to output the plots + - plot= Determines if plots should be done or not + - XAI_metric=Determines if XAI metrics should be calculated or not + Returns: + - all_attrs : Attribtuons of features per timesteps + - features_attrs : Attribtuons of features averaged over timesteps + - timestep_attrs : Attribtuons of timesteps averaged over features + - ts_v_score: Faithfulness score for attribtuons of features per timesteps + - ts_score: Faithfulness score for attribtuons of timesteps averaged over features + """ # Initialize lists to store attribution values for all instances all_attrs = [] - score = [] - r_score = [] - method_name = method.__name__ - # Loop through the test_loader to compute attributions for all instances - for batch in test_loader: + ts_score = [] + r_ts_score = [] + ts_v_score = [] + r_ts_v_score = [] + v_score = [] + + method_name = method if (method == "Random") or (method == "Attention") else ( + method.__name__) + if (method_name == "Random") or (method_name == "Attention"): + if method_name == "Attention": + Interpertations = self.interpertations(dataloader=dataloader, log_dir=log_dir, plot=plot) + timestep_attrs = Interpertations["attention"] + features_attrs = Interpertations["static_variables"].tolist() + features_attrs.extend(Interpertations["encoder_variables"].tolist()) + + elif method_name == "Random": + # Generate random attributions for baseline comparison + all_attrs = np.random.normal(size=[64, 24, 53]) + features_attrs = all_attrs.mean(axis=(1)) + timestep_attrs = all_attrs.mean(axis=(2)) + if XAI_metric: + for batch in dataloader: + + for key, value in batch[0].items(): + batch[0][key] = batch[0][key].to(self.device) + x = batch[0] + + if method_name == "Random": + ts_v_score.append(self.Faithfulness_Correlation(x, all_attrs, + pertrub="baseline", feature_timestep=True, subset_size=[4, 9], nr_runs=100)) + ts_score.append(self.Faithfulness_Correlation(x, all_attrs, + pertrub="baseline", time_step=True, subset_size=4, nr_runs=100)) + v_score.append(self.Faithfulness_Correlation(x, all_attrs, + pertrub="baseline", feature=True, subset_size=9, nr_runs=100)) + else: + ts_score.append(self.Faithfulness_Correlation(x, timestep_attrs, + pertrub="baseline", time_step=True, subset_size=4, nr_runs=100)) + v_score.append(self.Faithfulness_Correlation(x, features_attrs, + pertrub="baseline", feature=True, subset_size=9, nr_runs=100)) + + # Faithfulness score for attribtuons of features per timesteps + ts_v_score = np.mean(ts_v_score) + # Faithfulness score for attribtuons of timesteps averaged over features + ts_score = np.mean(ts_score) + v_score = np.mean(v_score) + return all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score + + # Loop through the dataloader to compute attributions for all instances + for batch in dataloader: for key, value in batch[0].items(): batch[0][key] = batch[0][key].to(self.device) @@ -521,74 +644,61 @@ def explantation_captum(self, test_loader, method, local_index=None, log_dir="." data, baselines = self.prep_data_captum(x) - explantation = method(self.forward_captum) + # Initialize the explanation method + explanation = method(self.forward_captum, interpretable_model=SkLearnLasso( + alpha=0.4)) if method_name == 'Lime' else method(self.forward_captum) + # Calculate attributions using the selected method if method is not captum.attr.Saliency: - attr = explantation.attribute(data, baselines=baselines, **kwargs) + attr = explanation.attribute(data, baselines=baselines, **kwargs) else: - attr = explantation.attribute(data, **kwargs) - - # Convert attributions to numpy array and append to the list - print(attr.shape, 'shape out of captum should be 64x24x53') - stacked_attr = torch.stack(attr).cpu().detach().numpy() - - # Compute the mean along the specified axis (axis=0) and keep it as a numpy array - """ score.append(self.Faithfulness_Correlation2(x, stacked_attr, - pertrub="Noise", feature_timestep=True, subset_size=[4, 9], nr_runs=10)) - r_score.append(self.Faithfulness_Correlation2(x, np.random.normal( - size=[64, 24, 53]), pertrub="Noise", feature_timestep=True, subset_size=[4, 9], nr_runs=10)) """ - print(np.shape(stacked_attr), 'shape after stacking') + attr = explanation.attribute(data, **kwargs) + + # Process and store the calculated attributions + stacked_attr = attr[1].cpu().detach().numpy() if method_name in [ + 'Lime', 'FeatureAblation'] else torch.stack(attr).cpu().detach().numpy() + if XAI_metric: + + ts_v_score.append(self.Faithfulness_Correlation(x, stacked_attr, + pertrub="baseline", feature_timestep=True, subset_size=[4, 9], nr_runs=100)) + + ts_score.append(self.Faithfulness_Correlation(x, stacked_attr, + pertrub="baseline", time_step=True, subset_size=4, nr_runs=100)) + v_score.append(self.Faithfulness_Correlation(x, stacked_attr, + pertrub="baseline", feature=True, subset_size=9, nr_runs=100)) + # aggregate over batch attr = np.mean(stacked_attr, axis=0) - print(np.shape(attr), 'shape after averaging over batch should 24x53') all_attrs.append(attr) - + # aggregate over all batches all_attrs = np.array(all_attrs).mean(axis=(0)) - + # aggregate over all timesteps features_attrs = all_attrs.mean(axis=(0)) - + # aggregate over all features timestep_attrs = all_attrs.mean(axis=(1)) + # Faithfulness score for attribtuons of features per timesteps + ts_v_score = np.mean(ts_v_score) + # Faithfulness score for attribtuons of timesteps averaged over features + ts_score = np.mean(ts_score) + # Faithfulness score for attribtuons of timesteps averaged over timesteps + v_score = np.mean(v_score) - # normalized_means = (means - means.min()) / (means.max() - means.min()) if plot: - x_values = np.arange(1, 54) - # Plotting the featrue means - plt.figure(figsize=(8, 6)) - plt.plot( - x_values, - features_attrs, - marker="o", - color="skyblue", - linestyle="-", - linewidth=2, - markersize=8, - ) - plt.xlabel("Time Step") - plt.ylabel("{} Attribution".format(method_name)) - plt.title("{} Attribution Values".format(method_name)) - plt.xticks(x_values, ['height', 'weight', 'age', 'sex', 'time_idx', 'alb', 'alp', 'alt', 'ast', 'be', 'bicar', 'bili', 'bili_dir', 'bnd', 'bun', 'ca', 'cai', 'ck', 'ckmb', 'cl', 'crea', 'crp', 'dbp', 'fgn', 'fio2', 'glu', 'hgb', 'hr', 'inr_pt', 'k', 'lact', 'lymph', 'map', 'mch', 'mchc', 'mcv', 'methb', 'mg', 'na', 'neut', 'o2sat', 'pco2', 'ph', 'phos', 'plt', 'po2', 'ptt', 'resp', 'sbp', 'temp', 'tnt', 'urine', 'wbc'], rotation=90 - ) # Set x-ticks to match the number of features - plt.tight_layout() - plt.savefig(log_dir / "{}_attribution_features_plot.png".format(method_name), bbox_inches="tight") - plt.figure(figsize=(8, 6)) - x_values = np.arange(1, 25) - plt.plot( - x_values, - timestep_attrs, - marker="o", - color="skyblue", - linestyle="-", - linewidth=2, - markersize=8, - ) - plt.xlabel("Time Step") - plt.ylabel("{} Attribution ".format(method_name)) - plt.title("{} Attribution Values".format(method_name)) - plt.xticks(x_values) # Set x-ticks to match the number of features - plt.tight_layout() - plt.savefig(log_dir / "{}_attribution_plot.png".format(method_name), bbox_inches="tight") - return all_attrs, features_attrs, timestep_attrs + # Plot attributions for features + self.plot_attributions(features_attrs, timestep_attrs, method_name, log_dir) + + # Return computed attributions and metrics + return all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score + # normalized_means = (means - means.min()) / (means.max() - means.min()) def prep_data(self, x): + """ + Prepares data for custom forward method + + Args: + - x:Batch returned from dataloader + Returns: + data:Tuple consisting of the tensors of X in the format the forward method needs + """ data = ( x["encoder_cat"], x["encoder_cont"], @@ -606,9 +716,8 @@ def prep_data(self, x): def Faithfulness_Correlation( self, - test_loader, + x, attribution, - local_index=None, similarity_func=None, nr_runs=100, pertrub=None, @@ -616,9 +725,24 @@ def Faithfulness_Correlation( feature=False, time_step=False, feature_timestep=False, - local=False, + ): """ + Calculates faithfulness scores for captum attributions + + Args: + - x:Batch input + -attribution: attribution generated by captum, + - similarity_func:function to determine similarity between sum of attributions and difference in prediction + - nr_runs: How many times to repeat the experiment, + - pertrub: What change to do to the input, + - subset_size: The size of the subset of featrues to alter , + - feature: Determines if to calcualte faithfulness of feature attributions, + - time_step: Determines if to calcualte faithfulness of timesteps attributions, + - feature_timestep: Determines if to calcualte faithfulness of featrues per timesteps attributions, + Returns: + score: similarity score between sum of attributions and difference in prediction averaged over nr_runs + Implementation of faithfulness correlation by Bhatt et al., 2020. The Faithfulness Correlation metric intend to capture an explanation's relative faithfulness @@ -642,137 +766,41 @@ def Faithfulness_Correlation( """ def add_noise(x, indices, time_step, feature_timestep): noise = torch.randn_like(x["encoder_cont"]) - # In-place modification to save memory - if time_step: - x["encoder_cont"][:, indices, :] += noise[:, indices, :] - elif feature_timestep: - x["encoder_cont"][:, indices[0], :][:, :, indices[1]] += noise[:, indices[0], :][:, :, indices[1]] - - def apply_baseline(x, indices, time_step, feature_timestep): - mask = torch.ones_like(x["encoder_cont"]) if time_step: + idx0, idx1 = np.meshgrid(indices[0], indices[1], indexing='ij') - mask[:, indices, :] -= mask[:, indices, :] - elif feature_timestep: - mask[:, indices[0], :][:, :, indices[1]] -= mask[:, indices[0], :][:, :, indices[1]] - # Apply mask in-place - x["encoder_cont"] *= mask + with torch.no_grad(): + x["encoder_cont"][idx0, idx1, :] += noise[idx0, idx1, :] - # Assuming 'attribution' is already a GPU tensor - if not torch.is_tensor(attribution): - attribution = torch.tensor(attribution).to(self.device) + elif feature: + idx0, idx1 = np.meshgrid(indices[0], indices[1], indexing='ij') - # Other initializations - if similarity_func is None: - similarity_func = correlation_pearson # Ensure this function is compatible with GPU tensors - if pertrub is None: - pertrub = "baseline" - similarities = [] - if local: - for key, value in test_loader[0].items(): - batch[0][key] = value.to(self.device) - x = batch[0] + with torch.no_grad(): + x["encoder_cont"][idx0, :, idx1] += noise[idx0, :, idx1] - else: - - for batch in test_loader: - # Move batch to GPU - for key, value in batch[0].items(): - batch[0][key] = value.to(self.device) - x = batch[0] - # Assuming this is a method to prepare your data - - y_pred = self((self.prep_data(x))).detach() # Keep on GPU - pred_deltas = [] - att_sums = [] - - for i_ix in range(nr_runs): - if time_step: - a_ix = np.random.choice(24, subset_size, replace=False) - elif feature_timestep: - timesteps_idx = np.random.choice(24, subset_size[0], replace=False) - variables_idx = np.random.choice(53, subset_size[1], replace=False) - a_ix = [timesteps_idx, variables_idx] - - # Apply perturbation - if pertrub == "Noise": - add_noise(x, a_ix, time_step, feature_timestep) - elif pertrub == "baseline": - apply_baseline(x, a_ix, time_step, feature_timestep) - - # Predict on perturbed input and calculate deltas - y_pred_perturb = (self(self.prep_data(x))).detach() # Keep on GPU - # Change dims as per your requirement - pred_deltas.append((y_pred - y_pred_perturb).mean(dim=[0, 1, 2])) - - # Sum attributions of the random subset and keep on GPU - if time_step: - att_sums.append(attribution[a_ix].sum()) - elif feature_timestep: - row_indices = a_ix[0] - col_indices = a_ix[1] - att_sums.append(attribution[row_indices][:, col_indices].sum()) - - # Convert to CPU for numpy operations - pred_deltas_cpu = torch.stack(pred_deltas).cpu().numpy() - att_sums_cpu = torch.tensor(att_sums).cpu().numpy() - similarities.append(similarity_func(pred_deltas_cpu, att_sums_cpu)) + elif feature_timestep: + idx0, idx1, idx2 = np.meshgrid(indices[0], indices[1], indices[2], indexing='ij') - score = np.nanmean(similarities) - return score + with torch.no_grad(): + x["encoder_cont"][idx0, idx1, idx2] += noise[idx0, idx1, idx2] - def Faithfulness_Correlation2( - self, - x, - attribution, - local_index=None, - similarity_func=None, - nr_runs=100, - pertrub=None, - subset_size=3, - feature=False, - time_step=False, - feature_timestep=False, - local=False, - ): - """ - Implementation of faithfulness correlation by Bhatt et al., 2020. + def apply_baseline(x, indices, time_step, feature_timestep): + mask = torch.ones_like(x["encoder_cont"]) + if time_step: - The Faithfulness Correlation metric intend to capture an explanation's relative faithfulness - (or 'fidelity') with respect to the model behaviour. + idx0, idx1, = np.meshgrid(indices[0], indices[1], indexing='ij') - Faithfulness correlation scores shows to what extent the predicted logits of each modified test point and - the average explanation attribution for only the subset of features are (linearly) correlated, taking the - average over multiple runs and test samples. The metric returns one float per input-attribution pair that - ranges between -1 and 1, where higher scores are better. + mask[idx0, idx1, :] -= mask[idx0, idx1, :] + elif feature: + idx0, idx1, = np.meshgrid(indices[0], indices[1], indexing='ij') - For each test sample, |S| features are randomly selected and replace them with baseline values (zero baseline - or average of set). Thereafter, Pearson’s correlation coefficient between the predicted logits of each modified - test point and the average explanation attribution for only the subset of features is calculated. Results is - average over multiple runs and several test samples. - This code is adapted from the quantus libray to suit our use case + mask[idx0, :, idx1] -= mask[idx0, :, idx1] - References: - 1) Umang Bhatt et al.: "Evaluating and aggregating feature-based model - explanations." IJCAI (2020): 3016-3022. - 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. - """ - def add_noise(x, indices, time_step, feature_timestep): - noise = torch.randn_like(x["encoder_cont"]) - # In-place modification to save memory - if time_step: - x["encoder_cont"][indices[0], :, :][:, indices[1], :] += noise[indices[0], :, :][:, indices[1], :] elif feature_timestep: - x["encoder_cont"][indices[0], :, :][:, indices[1], :][:, :, indices[2] - ] += noise[indices[0], :, :][:, indices[1], :][:, :, indices[2]] + idx0, idx1, idx2 = np.meshgrid(indices[0], indices[1], indices[2], indexing='ij') + + mask[idx0, idx1, idx2] -= mask[idx0, idx1, idx2] - def apply_baseline(x, indices, time_step, feature_timestep): - mask = torch.ones_like(x["encoder_cont"]) - if time_step: - mask[indices[0], :, :][:, indices[1], :] = 0 - elif feature_timestep: - mask[indices[0], :, :][:, indices[1], :][:, :, indices[2] - ] = 0 with torch.no_grad(): x["encoder_cont"] *= mask # Assuming 'attribution' is already a GPU tensor @@ -781,7 +809,7 @@ def apply_baseline(x, indices, time_step, feature_timestep): # Other initializations if similarity_func is None: - similarity_func = correlation_pearson # Ensure this function is compatible with GPU tensors + similarity_func = correlation_spearman if pertrub is None: pertrub = "baseline" similarities = [] @@ -795,13 +823,18 @@ def apply_baseline(x, indices, time_step, feature_timestep): for i_ix in range(nr_runs): if time_step: timesteps_idx = np.random.choice(24, subset_size, replace=False) - patient_idx = np.random.choice(64, subset_size, replace=False) + patient_idx = np.random.choice(64, 1, replace=False) a_ix = [patient_idx, timesteps_idx] + + elif feature: + feature_idx = np.random.choice(53, subset_size, replace=False) + patient_idx = np.random.choice(64, 1, replace=False) + a_ix = [patient_idx, feature_idx] elif feature_timestep: timesteps_idx = np.random.choice(24, subset_size[0], replace=False) - variables_idx = np.random.choice(53, subset_size[1], replace=False) + feature_idx = np.random.choice(53, subset_size[1], replace=False) patient_idx = np.random.choice(64, 1, replace=False) - a_ix = [patient_idx, timesteps_idx, variables_idx] + a_ix = [patient_idx, timesteps_idx, feature_idx] # Apply perturbation if pertrub == "Noise": @@ -811,32 +844,45 @@ def apply_baseline(x, indices, time_step, feature_timestep): # Predict on perturbed input and calculate deltas y_pred_perturb = (self(self.prep_data(x))).detach() # Keep on GPU - pred_deltas.append((y_pred - y_pred_perturb).mean(dim=[0, 1, 2])) # Change dims as per your requirement - # Sum attributions of the random subset and keep on GPU if time_step: - patient_indices = a_ix[0] - timestep_indices = a_ix[1] - att_sums.append(attribution[patient_indices, :, :][:, timestep_indices, :].sum()) - + # patient_indices = a_ix[0] + # timestep_indices = a_ix[1] + if attribution.size() == torch.Size([24]): + att_sums.append((attribution[timesteps_idx]).sum()) + else: + att_sums.append((attribution[patient_idx, :][:, timesteps_idx]).sum()) + elif feature: + # patient_indices = a_ix[0] + # feature_indices = a_ix[1] + if len(attribution) == 53: + att_sums.append((attribution[feature_idx]).sum()) + else: + att_sums.append((attribution[patient_idx, :][:, feature_idx]).sum()) elif feature_timestep: - patient_indices = a_ix[0] - timestep_indices = a_ix[1] - variable_indices = a_ix[2] - att_sums.append(attribution[patient_indices, :, :] - [:, timestep_indices, :][:, :, variable_indices].sum()) + # patient_indices = a_ix[0] + # timestep_indices = a_ix[1] + # variable_indices = a_ix[2] + + att_sums.append((attribution[patient_idx, :, :] + [:, timesteps_idx, :][:, :, feature_idx]).sum()) + print(y_pred.shape) + print(y_pred_perturb.shape) + print((y_pred - y_pred_perturb)[patient_idx], 'error') + pred_deltas.append((y_pred - y_pred_perturb)[patient_idx].item()) # Convert to CPU for numpy operations - pred_deltas_cpu = torch.stack(pred_deltas).cpu().numpy() + pred_deltas_cpu = torch.tensor(pred_deltas).cpu().numpy() att_sums_cpu = torch.tensor(att_sums).cpu().numpy() + similarities.append(similarity_func(pred_deltas_cpu, att_sums_cpu)) score = np.nanmean(similarities) return score def Data_Randomization( - self, test_loader, attribution, explain_method, random_model, similarity_func=None, test_dataset=None, **kwargs + self, dataloader, attribution, explain_method, random_model, similarity_func=None, test_dataset=None, **kwargs ): """ Implementation of the Random Logit Metric by Sixt et al., 2020. @@ -855,11 +901,11 @@ def Data_Randomization( if similarity_func is None: similarity_func = distance_euclidean if explain_method == "Attention": - Attention_weights = random_model.interpertations(test_loader) + Attention_weights = random_model.interpertations(dataloader) score = similarity_func(Attention_weights["attention"].cpu().numpy(), attribution.cpu().numpy()) else: - for batch in test_loader: + for batch in dataloader: for key, value in batch[0].items(): batch[0][key] = batch[0][key].to(random_model.device) x = batch[0] diff --git a/icu_benchmarks/run_utils.py b/icu_benchmarks/run_utils.py index 944295ea..7a67f3c5 100644 --- a/icu_benchmarks/run_utils.py +++ b/icu_benchmarks/run_utils.py @@ -15,6 +15,7 @@ from statistics import mean, pstdev from icu_benchmarks.models.utils import JsonResultLoggingEncoder from icu_benchmarks.wandb_utils import wandb_log +import numpy as np def build_parser() -> ArgumentParser: @@ -238,7 +239,9 @@ def aggregate_results(log_dir: Path, execution_time: timedelta = None): # Calculate the population standard deviation over aggregated results over folds/iterations # Divide by sqrt(n) to get standard deviation. - std_scores = {metric: (pstdev(list) / sqrt(len(list))) for metric, list in list_scores.items()} + + std_scores = {metric: (pstdev(list) / sqrt(len(list))) + for metric, list in list_scores.items() if not (np.isnan(list).all())} confidence_interval = { metric: (stats.t.interval(0.95, len(list) - 1, loc=mean(list), scale=stats.sem(list))) @@ -305,7 +308,8 @@ def load_pretrained_imputation_model(use_pretrained_imputation): pretrained_imputation_model_checkpoint = torch.load(use_pretrained_imputation, map_location=torch.device("cpu")) if isinstance(pretrained_imputation_model_checkpoint, dict): imputation_model_class = pretrained_imputation_model_checkpoint["class"] - pretrained_imputation_model = imputation_model_class(**pretrained_imputation_model_checkpoint["hyper_parameters"]) + pretrained_imputation_model = imputation_model_class( + **pretrained_imputation_model_checkpoint["hyper_parameters"]) pretrained_imputation_model.set_trained_columns(pretrained_imputation_model_checkpoint["trained_columns"]) pretrained_imputation_model.load_state_dict(pretrained_imputation_model_checkpoint["state_dict"]) else: From 64fb77f823d84537eb58814791a1f32fafba13eb Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Wed, 29 Nov 2023 13:53:41 +0100 Subject: [PATCH 115/142] added data randomization test --- icu_benchmarks/models/train.py | 40 ++++---- icu_benchmarks/models/wrappers.py | 158 +++++++++++++----------------- 2 files changed, 91 insertions(+), 107 deletions(-) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 1c739b90..1775c0a7 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -263,6 +263,18 @@ def train_common( return 0 if explain: + if random_model_dir is None: + random_model = None + else: + path = Path(random_model_dir) + + random_model = load_model( + model, + source_dir=path, + pl_model=pl_model, + train_dataset=train_dataset, + optimizer=optimizer, + ) XAI_dict = {} # dictrionary to log attributions metrics @@ -270,24 +282,24 @@ def train_common( methods = { "Saliency": Saliency, # "Lime": Lime, - "IG": IntegratedGradients, + # "IG": IntegratedGradients, # "FA": FeatureAblation, "Random": "Random", - "Attention": "Attention" + # "Attention": "Attention" } for key, item in methods.items(): # If conditions needed here as different explantations require different inputs if key == "IG": - all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score = model.explantation( - dataloader=test_loader, method=item, log_dir=log_dir, plot=True, n_steps=50, XAI_metric=XAI_metric, + all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score, r_score = model.explantation( + dataloader=test_loader, method=item, log_dir=log_dir, plot=True, n_steps=50, XAI_metric=XAI_metric, random_model=random_model ) elif key == "Lime": - all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score = model.explantation( - dataloader=test_loader, method=item, log_dir=log_dir, plot=True, return_input_shape=True, XAI_metric=XAI_metric, + all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score, r_score = model.explantation( + dataloader=test_loader, method=item, log_dir=log_dir, plot=True, return_input_shape=True, XAI_metric=XAI_metric, random_model=random_model ) else: - all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score = model.explantation( - dataloader=test_loader, method=item, log_dir=log_dir, plot=True, XAI_metric=XAI_metric, + all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score, r_score = model.explantation( + dataloader=test_loader, method=item, log_dir=log_dir, plot=True, XAI_metric=XAI_metric, random_model=random_model ) if XAI_metric: @@ -305,6 +317,9 @@ def train_common( print("{}_Attributions faithfulness variable per timestep ".format( key), ts_v_score) XAI_dict["{}_faith_variable_per_timestep".format(key)] = ts_v_score + print("{}_Data Randomization distance ".format( + key), r_score) + XAI_dict["{}_Data Randomization distance".format(key)] = r_score # Getting the interpertations using pytorch forecasting native methods @@ -315,15 +330,6 @@ def train_common( with open(json_file_path, "w") as json_file: json.dump(XAI_dict, json_file) - # path = Path(random_model_dir) - - # random_model = load_model( - # model, - # source_dir=path, - # pl_model=pl_model, - # train_dataset=train_dataset, - # optimizer=optimizer, - # ) # R_attribution = model.Data_Randomization( # test_loader, attributions_IG, IntegratedGradients, random_model) # print('Distance Data randmoization score attribution', R_attribution) diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index db2f77b8..ada7d150 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -569,7 +569,7 @@ def plot_attributions(self, features_attrs, timestep_attrs, method_name, log_dir plt.tight_layout() plt.savefig(log_dir / "{}_attribution_plot.png".format(method_name), bbox_inches="tight") - def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=False, **kwargs): + def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=False, random_model=None, test_dataset=None, **kwargs): """ Generic method to combine pytorchforecasting data loading , interpertations and captum to generate attributions @@ -583,16 +583,15 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F - all_attrs : Attribtuons of features per timesteps - features_attrs : Attribtuons of features averaged over timesteps - timestep_attrs : Attribtuons of timesteps averaged over features - - ts_v_score: Faithfulness score for attribtuons of features per timesteps - - ts_score: Faithfulness score for attribtuons of timesteps averaged over features + - f_ts_v_score: Faithfulness score for attribtuons of features per timesteps + - f_ts_score: Faithfulness score for attribtuons of timesteps averaged over features """ # Initialize lists to store attribution values for all instances all_attrs = [] - ts_score = [] - r_ts_score = [] - ts_v_score = [] - r_ts_v_score = [] - v_score = [] + f_ts_score = [] + f_ts_v_score = [] + f_v_score = [] + r_score = [] method_name = method if (method == "Random") or (method == "Attention") else ( method.__name__) @@ -602,6 +601,8 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F timestep_attrs = Interpertations["attention"] features_attrs = Interpertations["static_variables"].tolist() features_attrs.extend(Interpertations["encoder_variables"].tolist()) + r_score = self.Data_Randomization(x=None, attribution=timestep_attrs, + explain_method=method, random_model=random_model, dataloader=dataloader, method_name=method_name) elif method_name == "Random": # Generate random attributions for baseline comparison @@ -616,24 +617,29 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F x = batch[0] if method_name == "Random": - ts_v_score.append(self.Faithfulness_Correlation(x, all_attrs, - pertrub="baseline", feature_timestep=True, subset_size=[4, 9], nr_runs=100)) - ts_score.append(self.Faithfulness_Correlation(x, all_attrs, - pertrub="baseline", time_step=True, subset_size=4, nr_runs=100)) - v_score.append(self.Faithfulness_Correlation(x, all_attrs, - pertrub="baseline", feature=True, subset_size=9, nr_runs=100)) + + f_ts_v_score.append(self.Faithfulness_Correlation(x, all_attrs, + pertrub="baseline", feature_timestep=True, subset_size=[4, 9], nr_runs=100)) + f_ts_score.append(self.Faithfulness_Correlation(x, all_attrs, + pertrub="baseline", time_step=True, subset_size=4, nr_runs=100)) + f_v_score.append(self.Faithfulness_Correlation(x, all_attrs, + pertrub="baseline", feature=True, subset_size=9, nr_runs=100)) + + r_score.append(self.Data_Randomization(x, attribution=stacked_attr, + explain_method=method, random_model=random_model, method_name=method_name)) else: - ts_score.append(self.Faithfulness_Correlation(x, timestep_attrs, - pertrub="baseline", time_step=True, subset_size=4, nr_runs=100)) - v_score.append(self.Faithfulness_Correlation(x, features_attrs, - pertrub="baseline", feature=True, subset_size=9, nr_runs=100)) + f_ts_score.append(self.Faithfulness_Correlation(x, timestep_attrs, + pertrub="baseline", time_step=True, subset_size=4, nr_runs=100)) + f_v_score.append(self.Faithfulness_Correlation(x, features_attrs, + pertrub="baseline", feature=True, subset_size=9, nr_runs=100)) # Faithfulness score for attribtuons of features per timesteps - ts_v_score = np.mean(ts_v_score) + f_ts_v_score = np.mean(f_ts_v_score) # Faithfulness score for attribtuons of timesteps averaged over features - ts_score = np.mean(ts_score) - v_score = np.mean(v_score) - return all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score + f_ts_score = np.mean(f_ts_score) + f_v_score = np.mean(f_v_score) + r_score = np.mean(r_score) + return all_attrs, features_attrs, timestep_attrs, f_ts_v_score, f_ts_score, f_v_score, r_score # Loop through the dataloader to compute attributions for all instances for batch in dataloader: @@ -659,13 +665,15 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F 'Lime', 'FeatureAblation'] else torch.stack(attr).cpu().detach().numpy() if XAI_metric: - ts_v_score.append(self.Faithfulness_Correlation(x, stacked_attr, - pertrub="baseline", feature_timestep=True, subset_size=[4, 9], nr_runs=100)) + f_ts_v_score.append(self.Faithfulness_Correlation(x, stacked_attr, + pertrub="baseline", feature_timestep=True, subset_size=[4, 9], nr_runs=100)) - ts_score.append(self.Faithfulness_Correlation(x, stacked_attr, - pertrub="baseline", time_step=True, subset_size=4, nr_runs=100)) - v_score.append(self.Faithfulness_Correlation(x, stacked_attr, - pertrub="baseline", feature=True, subset_size=9, nr_runs=100)) + f_ts_score.append(self.Faithfulness_Correlation(x, stacked_attr, + pertrub="baseline", time_step=True, subset_size=4, nr_runs=100)) + f_v_score.append(self.Faithfulness_Correlation(x, stacked_attr, + pertrub="baseline", feature=True, subset_size=9, nr_runs=100)) + r_score.append(self.Data_Randomization(x, attribution=stacked_attr, + explain_method=method, random_model=random_model, method_name=method_name)) # aggregate over batch attr = np.mean(stacked_attr, axis=0) all_attrs.append(attr) @@ -676,18 +684,24 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F # aggregate over all features timestep_attrs = all_attrs.mean(axis=(1)) # Faithfulness score for attribtuons of features per timesteps - ts_v_score = np.mean(ts_v_score) + f_ts_v_score = np.mean(f_ts_v_score) # Faithfulness score for attribtuons of timesteps averaged over features - ts_score = np.mean(ts_score) + f_ts_score = np.mean(f_ts_score) # Faithfulness score for attribtuons of timesteps averaged over timesteps - v_score = np.mean(v_score) + f_v_score = np.mean(f_v_score) + + min_val = np.min(r_score) + max_val = np.max(r_score) + + r_score = (r_score - min_val) / (max_val - min_val) + r_score = np.mean(r_score) if plot: # Plot attributions for features self.plot_attributions(features_attrs, timestep_attrs, method_name, log_dir) # Return computed attributions and metrics - return all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score + return all_attrs, features_attrs, timestep_attrs, f_ts_v_score, f_ts_score, f_v_score, r_score # normalized_means = (means - means.min()) / (means.max() - means.min()) def prep_data(self, x): @@ -846,29 +860,22 @@ def apply_baseline(x, indices, time_step, feature_timestep): y_pred_perturb = (self(self.prep_data(x))).detach() # Keep on GPU if time_step: - # patient_indices = a_ix[0] - # timestep_indices = a_ix[1] + if attribution.size() == torch.Size([24]): att_sums.append((attribution[timesteps_idx]).sum()) else: - att_sums.append((attribution[patient_idx, :][:, timesteps_idx]).sum()) + att_sums.append((attribution[patient_idx, :, :][:, timesteps_idx, :]).sum()) elif feature: - # patient_indices = a_ix[0] - # feature_indices = a_ix[1] + if len(attribution) == 53: att_sums.append((attribution[feature_idx]).sum()) else: - att_sums.append((attribution[patient_idx, :][:, feature_idx]).sum()) + + att_sums.append((attribution[patient_idx, :, :][:, :, feature_idx]).sum()) elif feature_timestep: - # patient_indices = a_ix[0] - # timestep_indices = a_ix[1] - # variable_indices = a_ix[2] att_sums.append((attribution[patient_idx, :, :] [:, timesteps_idx, :][:, :, feature_idx]).sum()) - print(y_pred.shape) - print(y_pred_perturb.shape) - print((y_pred - y_pred_perturb)[patient_idx], 'error') pred_deltas.append((y_pred - y_pred_perturb)[patient_idx].item()) # Convert to CPU for numpy operations @@ -882,7 +889,8 @@ def apply_baseline(x, indices, time_step, feature_timestep): return score def Data_Randomization( - self, dataloader, attribution, explain_method, random_model, similarity_func=None, test_dataset=None, **kwargs + self, x, + attribution, explain_method, random_model, similarity_func=None, dataloader=None, method_name="", **kwargs ): """ Implementation of the Random Logit Metric by Sixt et al., 2020. @@ -897,61 +905,31 @@ def Data_Randomization( 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. """ - a_perturbed = [] + if similarity_func is None: similarity_func = distance_euclidean if explain_method == "Attention": Attention_weights = random_model.interpertations(dataloader) score = similarity_func(Attention_weights["attention"].cpu().numpy(), attribution.cpu().numpy()) + elif explain_method == "Random": + score = similarity_func(np.random.normal(size=[64, 24, 53]), attribution) else: - for batch in dataloader: - for key, value in batch[0].items(): - batch[0][key] = batch[0][key].to(random_model.device) - x = batch[0] - y = batch[1][0] - data = ( - x["encoder_cat"], - x["encoder_cont"], - x["encoder_target"], - x["encoder_lengths"], - x["decoder_cat"], - x["decoder_cont"], - x["decoder_target"], - x["decoder_lengths"], - x["decoder_time_idx"], - x["groups"], - x["target_scale"], - ) - - baselines = ( - data[0].to(self.device), # encoder_cat, set to random - torch.randn_like(data[1]).to(self.device), # encoder_cont, set to random - torch.randn_like(data[2]).to(self.device), # encoder_target, set to random - data[3].to(self.device), # encoder_lengths, leave unchanged - torch.randn_like(data[4]).to(self.device), # decoder_cat, set to random - torch.randn_like(data[5]).to(self.device), # decoder_cont, set to random - torch.randn_like(data[6]).to(self.device), # decoder_target, set to random - data[7].to(self.device), # decoder_lengths, leave unchanged - data[8].to(self.device), # decoder_time_idx, unchanged - data[9].to(self.device), # groups, leave unchanged - data[10].to(self.device), # target_scale, leave unchanged - ) + data, baselines = self.prep_data_captum(x) + y_pred = self(data).detach() - explantation = explain_method(random_model.forward_captum) - # Reformat attributions. - if explain_method is not captum.attr.Saliency: - attr = explantation.attribute(data, baselines=baselines, **kwargs) - else: - attr = explantation.attribute(data, **kwargs) - # Convert attributions to numpy array and append to the list - a_perturbed.append(attr[0].cpu().detach().numpy()) + explantation = explain_method(random_model.forward_captum) + # Reformat attributions. + if explain_method is not captum.attr.Saliency: + attr = explantation.attribute(data, baselines=baselines, **kwargs) + else: + attr = explantation.attribute(data, **kwargs) - a_perturbed = np.mean(a_perturbed, axis=(0)) + # Process and store the calculated attributions + stacked_attr = attr[1].cpu().detach().numpy() if method_name in [ + 'Lime', 'FeatureAblation'] else torch.stack(attr).cpu().detach().numpy() - # Normalize the means values to range [0, 1] - # normalized_a_perturbed = (a_perturbed - a_perturbed.min()) / (a_perturbed.max() - a_perturbed.min()) - score = similarity_func(a_perturbed, attribution) + score = similarity_func(stacked_attr.flatten(), attribution.flatten()) return score From 42ef29946c9c2b3322ac567302a5eb1b0e35462b Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Wed, 29 Nov 2023 16:29:10 +0100 Subject: [PATCH 116/142] added mask for feature ablation --- icu_benchmarks/models/train.py | 44 ++++++++++++++++++++++++++++--- icu_benchmarks/models/wrappers.py | 11 +++++--- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 1775c0a7..ac144025 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -280,11 +280,11 @@ def train_common( # choose which methods to get attributions methods = { - "Saliency": Saliency, - # "Lime": Lime, + # "Saliency": Saliency, + "Lime": Lime, # "IG": IntegratedGradients, # "FA": FeatureAblation, - "Random": "Random", + # "Random": "Random", # "Attention": "Attention" } for key, item in methods.items(): @@ -297,6 +297,44 @@ def train_common( all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score, r_score = model.explantation( dataloader=test_loader, method=item, log_dir=log_dir, plot=True, return_input_shape=True, XAI_metric=XAI_metric, random_model=random_model ) + elif key == "FA": + # basically need to specify a mask on which input values are considered as a feature + shapes = [ + torch.Size([64, 24, 0]), + torch.Size([64, 24, 53]), + torch.Size([64, 24]), + torch.Size([64]), + torch.Size([64, 1, 0]), + torch.Size([64, 1, 53]), + torch.Size([64, 1]), + torch.Size([64]), + torch.Size([64, 1]), + torch.Size([64, 1]), + torch.Size([64, 2]) + ] + + # Create a default mask for non-targeted tensors + def create_default_mask(shape): + if len(shape) == 3: + return torch.zeros(shape[0], shape[1], max(1, shape[2]), dtype=torch.int32) + elif len(shape) == 2: + return torch.zeros(shape[0], max(1, shape[1]), dtype=torch.int32) + else: # len(shape) == 1 + return torch.zeros(shape[0], dtype=torch.int32) + + # Create a feature mask for the second tensor + num_features_second_tensor = shapes[1][2] + feature_mask_second = torch.arange(num_features_second_tensor).repeat(shapes[1][1], 1) + feature_mask_second = feature_mask_second.unsqueeze(0).repeat(shapes[1][0], 1, 1) + + # Create a tuple of masks + feature_masks = tuple([create_default_mask(shape) if i != + 1 else feature_mask_second for i, shape in enumerate(shapes)]) + + all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score, r_score = model.explantation( + dataloader=test_loader, method=item, log_dir=log_dir, plot=True, feature_mask=feature_masks, XAI_metric=XAI_metric, random_model=random_model + ) + else: all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score, r_score = model.explantation( dataloader=test_loader, method=item, log_dir=log_dir, plot=True, XAI_metric=XAI_metric, random_model=random_model diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index ada7d150..cb928a02 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -489,6 +489,7 @@ def prep_data_captum(self, x): - baselines:Basically zero tensors in the input """ # captum requires gradient and float values + data = ( x["encoder_cat"].float().requires_grad_(), x["encoder_cont"].requires_grad_(), @@ -625,7 +626,7 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F f_v_score.append(self.Faithfulness_Correlation(x, all_attrs, pertrub="baseline", feature=True, subset_size=9, nr_runs=100)) - r_score.append(self.Data_Randomization(x, attribution=stacked_attr, + r_score.append(self.Data_Randomization(x, attribution=all_attrs, explain_method=method, random_model=random_model, method_name=method_name)) else: f_ts_score.append(self.Faithfulness_Correlation(x, timestep_attrs, @@ -638,6 +639,10 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F # Faithfulness score for attribtuons of timesteps averaged over features f_ts_score = np.mean(f_ts_score) f_v_score = np.mean(f_v_score) + min_val = np.min(r_score) + max_val = np.max(r_score) + + r_score = (r_score - min_val) / (max_val - min_val) r_score = np.mean(r_score) return all_attrs, features_attrs, timestep_attrs, f_ts_v_score, f_ts_score, f_v_score, r_score @@ -647,7 +652,7 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F for key, value in batch[0].items(): batch[0][key] = batch[0][key].to(self.device) x = batch[0] - + data, baselines = self.prep_data_captum(x) # Initialize the explanation method @@ -913,7 +918,7 @@ def Data_Randomization( score = similarity_func(Attention_weights["attention"].cpu().numpy(), attribution.cpu().numpy()) elif explain_method == "Random": - score = similarity_func(np.random.normal(size=[64, 24, 53]), attribution) + score = similarity_func(np.random.normal(size=[64, 24, 53]).flatten(), attribution.flatten()) else: data, baselines = self.prep_data_captum(x) y_pred = self(data).detach() From 7c87164d1289d3ca4976f8691586b7df15b3fb20 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Mon, 4 Dec 2023 16:30:39 +0100 Subject: [PATCH 117/142] plotting , added HP range , fixed distance range --- configs/prediction_models/DeepARpytorch.gin | 8 +- configs/prediction_models/RNNpytorch.gin | 8 +- configs/prediction_models/TFTpytorch.gin | 4 + configs/prediction_models/common/DLCommon.gin | 2 +- configs/prediction_models/common/DLTuning.gin | 4 +- configs/tasks/Regression.gin | 4 +- configs/tasks/common/CrossValidation.gin | 2 +- icu_benchmarks/models/train.py | 60 +-- icu_benchmarks/models/wrappers.py | 55 +- icu_benchmarks/run_utils.py | 60 +++ output.txt | 475 ------------------ 11 files changed, 142 insertions(+), 540 deletions(-) delete mode 100644 output.txt diff --git a/configs/prediction_models/DeepARpytorch.gin b/configs/prediction_models/DeepARpytorch.gin index 9a61ca84..5135035f 100644 --- a/configs/prediction_models/DeepARpytorch.gin +++ b/configs/prediction_models/DeepARpytorch.gin @@ -8,19 +8,21 @@ train_common.model = @DeepARpytorch optimizer/hyperparameter.class_to_tune = @Adam optimizer/hyperparameter.weight_decay = 1e-6 -optimizer/hyperparameter.lr = (0.0001, 0.001,0.01) +optimizer/hyperparameter.lr = (1e-5, 1e-3) # Model params model/hyperparameter.class_to_tune = @DeepARpytorch -model/hyperparameter.hidden = (32, 256, "log-uniform", 2) +model/hyperparameter.hidden = (40, 120, "log-uniform", 2) model/hyperparameter.rnn_layers=(1, 3) model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.cell_type='LSTM' model/hyperparameter.dropout = (0.0, 0.4) #model/hyperparameter.lr_scheduler = "exponential" - +train_common/hyperparameter.class_to_tune = @train_common +train_common/hyperparameter.batch_size=(32,64,128,256,512) +train_common/hyperparameter.gradient_clip_val=(0,0.01, 1.0, 100.0) # Dataset params PredictionDatasetpytorch.max_encoder_length = 24 PredictionDatasetpytorch.max_prediction_length = 1 diff --git a/configs/prediction_models/RNNpytorch.gin b/configs/prediction_models/RNNpytorch.gin index aa2398cb..0b88c670 100644 --- a/configs/prediction_models/RNNpytorch.gin +++ b/configs/prediction_models/RNNpytorch.gin @@ -8,7 +8,7 @@ train_common.model = @RNNpytorch optimizer/hyperparameter.class_to_tune = @Adam optimizer/hyperparameter.weight_decay = 1e-6 -optimizer/hyperparameter.lr = (0.0001, 0.001,0.01) +optimizer/hyperparameter.lr = (1e-5, 3e-4) # Model params @@ -17,10 +17,12 @@ model/hyperparameter.hidden = (32, 256, "log-uniform", 2) model/hyperparameter.rnn_layers=(1,3) model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.cell_type='LSTM' -model/hyperparameter.dropout = (0.0, 0.9) +model/hyperparameter.dropout = (0.0, 0.4) #model/hyperparameter.lr_scheduler = "exponential" - +train_common/hyperparameter.class_to_tune = @train_common +train_common/hyperparameter.batch_size=(32,64,128,256,512) +train_common/hyperparameter.gradient_clip_val=(0,0.01, 1.0, 100.0) # Dataset params PredictionDatasetpytorch.max_encoder_length = 24 PredictionDatasetpytorch.max_prediction_length = 1 diff --git a/configs/prediction_models/TFTpytorch.gin b/configs/prediction_models/TFTpytorch.gin index 4f2d078e..18eabb6a 100644 --- a/configs/prediction_models/TFTpytorch.gin +++ b/configs/prediction_models/TFTpytorch.gin @@ -19,8 +19,12 @@ model/hyperparameter.dropout =(0.1, 0.9) model/hyperparameter.dropout_att = (0.1, 0.9) model/hyperparameter.n_heads =(1,4) +#dataloader +train_common/hyperparameter.class_to_tune = @train_common +train_common/hyperparameter.batch_size=(32,64,128,256,512) +train_common/hyperparameter.gradient_clip_val=(0,0.01, 1.0, 100.0) diff --git a/configs/prediction_models/common/DLCommon.gin b/configs/prediction_models/common/DLCommon.gin index 2760e187..bfc48ef5 100644 --- a/configs/prediction_models/common/DLCommon.gin +++ b/configs/prediction_models/common/DLCommon.gin @@ -15,7 +15,7 @@ base_regression_preprocessor.generate_features = False # Train params train_common.optimizer = @Adam train_common.epochs = 1000 -train_common.batch_size = 64 +#train_common.batch_size = 64 train_common.patience = 20 train_common.min_delta = 1e-4 diff --git a/configs/prediction_models/common/DLTuning.gin b/configs/prediction_models/common/DLTuning.gin index 0c56677c..b631dc89 100644 --- a/configs/prediction_models/common/DLTuning.gin +++ b/configs/prediction_models/common/DLTuning.gin @@ -1,5 +1,5 @@ # Hyperparameter tuner settings for Deep Learning. -tune_hyperparameters.scopes = ["model", "optimizer"] +tune_hyperparameters.scopes = ["model", "optimizer","train_common"] tune_hyperparameters.n_initial_points = 5 -tune_hyperparameters.n_calls = 20 +tune_hyperparameters.n_calls = 15 tune_hyperparameters.folds_to_tune_on = 2 \ No newline at end of file diff --git a/configs/tasks/Regression.gin b/configs/tasks/Regression.gin index 75041763..1234f707 100644 --- a/configs/tasks/Regression.gin +++ b/configs/tasks/Regression.gin @@ -15,8 +15,8 @@ train_common.weight = "balanced" train_common.ram_cache = True # LOSS FUNCTION -DLPredictionWrapper.loss = @l1_loss -DLPredictionPytorchForecastingWrapper.loss = @l1_loss +DLPredictionWrapper.loss = @mse_loss +DLPredictionPytorchForecastingWrapper.loss = @mse_loss MLWrapper.loss = @mean_squared_error # SELECTING PREPROCESSOR diff --git a/configs/tasks/common/CrossValidation.gin b/configs/tasks/common/CrossValidation.gin index 519ad3dd..d0c4a704 100644 --- a/configs/tasks/common/CrossValidation.gin +++ b/configs/tasks/common/CrossValidation.gin @@ -1,3 +1,3 @@ # CROSS-VALIDATION SETTINGS -execute_repeated_cv.cv_repetitions = 2 +execute_repeated_cv.cv_repetitions = 1 execute_repeated_cv.cv_folds = 5 \ No newline at end of file diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index ac144025..14517da7 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -77,6 +77,7 @@ def train_common( XAI_metric: bool = False, random_labels: bool = False, random_model_dir: str = None, + ): """Common wrapper to train all benchmarked models. @@ -127,7 +128,7 @@ def train_common( f"Training on {train_dataset.name} with {len(train_dataset)} samples and validating on {val_dataset.name} with" f" {len(val_dataset)} samples." ) - + batch_size = int(batch_size) logging.info(f"Using {num_workers} workers for data loading.") if pytorch_forecasting: train_loader = train_dataset.to_dataloader( @@ -244,7 +245,7 @@ def train_common( logger=loggers, num_sanity_val_steps=-1, log_every_n_steps=5, - + gradient_clip_val=gradient_clip_val ) if not eval_only: if model.requires_backprop: @@ -280,12 +281,12 @@ def train_common( # choose which methods to get attributions methods = { - # "Saliency": Saliency, - "Lime": Lime, - # "IG": IntegratedGradients, - # "FA": FeatureAblation, - # "Random": "Random", - # "Attention": "Attention" + "G": Saliency, + "L": Lime, + "IG": IntegratedGradients, + "FA": FeatureAblation, + "R": "Random", + "Att": "Attention" } for key, item in methods.items(): # If conditions needed here as different explantations require different inputs @@ -293,12 +294,8 @@ def train_common( all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score, r_score = model.explantation( dataloader=test_loader, method=item, log_dir=log_dir, plot=True, n_steps=50, XAI_metric=XAI_metric, random_model=random_model ) - elif key == "Lime": - all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score, r_score = model.explantation( - dataloader=test_loader, method=item, log_dir=log_dir, plot=True, return_input_shape=True, XAI_metric=XAI_metric, random_model=random_model - ) - elif key == "FA": - # basically need to specify a mask on which input values are considered as a feature + elif key == "L" or key == "FA": + # for Lime and feature ablation we need to define what is a feature we define each variable per timestep as a feature shapes = [ torch.Size([64, 24, 0]), torch.Size([64, 24, 53]), @@ -322,17 +319,17 @@ def create_default_mask(shape): else: # len(shape) == 1 return torch.zeros(shape[0], dtype=torch.int32) - # Create a feature mask for the second tensor - num_features_second_tensor = shapes[1][2] - feature_mask_second = torch.arange(num_features_second_tensor).repeat(shapes[1][1], 1) + # Create a feature mask for the second tensor that includes both features and timesteps + num_timesteps = shapes[1][1] + num_features = shapes[1][2] + feature_mask_second = torch.arange(num_timesteps * num_features).reshape(num_timesteps, num_features) feature_mask_second = feature_mask_second.unsqueeze(0).repeat(shapes[1][0], 1, 1) # Create a tuple of masks feature_masks = tuple([create_default_mask(shape) if i != 1 else feature_mask_second for i, shape in enumerate(shapes)]) - all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score, r_score = model.explantation( - dataloader=test_loader, method=item, log_dir=log_dir, plot=True, feature_mask=feature_masks, XAI_metric=XAI_metric, random_model=random_model + dataloader=test_loader, method=item, log_dir=log_dir, plot=True, return_input_shape=True, XAI_metric=XAI_metric, random_model=random_model ) else: @@ -342,22 +339,22 @@ def create_default_mask(shape): if XAI_metric: # logging metric scores - print("{} Attributions faithfulness timesteps ".format(key), ts_score) - XAI_dict["{}_faith_timesteps".format(key)] = ts_score - if key == "Attention": + print("{} Attributions Faithfulness Timesteps ".format(key), ts_score) + XAI_dict["{}_Faith Timesteps".format(key)] = ts_score + if key == "Att": print("Variable selection weights faithfulness featrues ".format(key), v_score) - XAI_dict["Variable_selection_weights_faith_features".format(key)] = v_score + XAI_dict["VSN_Faith Features".format(key)] = v_score else: print("{} Attributions faithfulness featrues ".format(key), v_score) - XAI_dict["{}_faith_features".format(key)] = v_score + XAI_dict["{}_Faith Features".format(key)] = v_score - print("{}_Attributions faithfulness variable per timestep ".format( + print("{}_Attributions Faithfulness Variable Per Timestep ".format( key), ts_v_score) - XAI_dict["{}_faith_variable_per_timestep".format(key)] = ts_v_score - print("{}_Data Randomization distance ".format( + XAI_dict["{}_Faith Variable Per Timestep".format(key)] = ts_v_score + print("{}_Data Randomization Distance ".format( key), r_score) - XAI_dict["{}_Data Randomization distance".format(key)] = r_score + XAI_dict["{}_Data Randomization Distance".format(key)] = r_score # Getting the interpertations using pytorch forecasting native methods @@ -368,13 +365,6 @@ def create_default_mask(shape): with open(json_file_path, "w") as json_file: json.dump(XAI_dict, json_file) - # R_attribution = model.Data_Randomization( - # test_loader, attributions_IG, IntegratedGradients, random_model) - # print('Distance Data randmoization score attribution', R_attribution) - # R_attention = model.Data_Randomization( - # test_loader, Attention_weights["attention"], "Attention", random_model) - # print('Distance Data randmoization score attention', R_attention) - model.set_weight("balanced", train_dataset) test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0]["test/loss"] save_config_file(log_dir) diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index cb928a02..9fe0c682 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -19,9 +19,10 @@ from icu_benchmarks.models.constants import MLMetrics, DLMetrics from icu_benchmarks.contants import RunMode import matplotlib.pyplot as plt -from icu_benchmarks.models.similarity_func import correlation_spearman, distance_euclidean, correlation_pearson +from icu_benchmarks.models.similarity_func import correlation_spearman, distance_euclidean, correlation_pearson, cosine import captum from captum._utils.models.linear_model import SkLearnLasso +import os gin.config.external_configurable(nn.functional.nll_loss, module="torch.nn.functional") gin.config.external_configurable(nn.functional.cross_entropy, module="torch.nn.functional") @@ -543,7 +544,7 @@ def plot_attributions(self, features_attrs, timestep_attrs, method_name, log_dir linewidth=2, markersize=8, ) - plt.xlabel("Time Step") + plt.xlabel("Feature") plt.ylabel("{} Attribution".format(method_name)) plt.title("{} Attribution Values".format(method_name)) plt.xticks(x_values, ['height', 'weight', 'age', 'sex', 'time_idx', 'alb', 'alp', 'alt', 'ast', 'be', 'bicar', 'bili', 'bili_dir', 'bnd', 'bun', 'ca', 'cai', 'ck', 'ckmb', 'cl', 'crea', 'crp', 'dbp', 'fgn', 'fio2', 'glu', @@ -604,7 +605,7 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F features_attrs.extend(Interpertations["encoder_variables"].tolist()) r_score = self.Data_Randomization(x=None, attribution=timestep_attrs, explain_method=method, random_model=random_model, dataloader=dataloader, method_name=method_name) - + print(r_score) elif method_name == "Random": # Generate random attributions for baseline comparison all_attrs = np.random.normal(size=[64, 24, 53]) @@ -641,9 +642,9 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F f_v_score = np.mean(f_v_score) min_val = np.min(r_score) max_val = np.max(r_score) - - r_score = (r_score - min_val) / (max_val - min_val) - r_score = np.mean(r_score) + if method_name != "Attention": + r_score = (r_score - min_val) / (max_val - min_val) + r_score = np.mean(r_score) return all_attrs, features_attrs, timestep_attrs, f_ts_v_score, f_ts_score, f_v_score, r_score # Loop through the dataloader to compute attributions for all instances @@ -652,7 +653,7 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F for key, value in batch[0].items(): batch[0][key] = batch[0][key].to(self.device) x = batch[0] - + data, baselines = self.prep_data_captum(x) # Initialize the explanation method @@ -695,15 +696,16 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F # Faithfulness score for attribtuons of timesteps averaged over timesteps f_v_score = np.mean(f_v_score) - min_val = np.min(r_score) - max_val = np.max(r_score) - - r_score = (r_score - min_val) / (max_val - min_val) + # Random data score r_score = np.mean(r_score) if plot: - # Plot attributions for features - self.plot_attributions(features_attrs, timestep_attrs, method_name, log_dir) + log_dir_plots = log_dir / 'plots' + if not (log_dir_plots.exists()): + log_dir_plots.mkdir(parents=True) + # Plot attributions for features and timesteps + + self.plot_attributions(features_attrs, timestep_attrs, method_name, log_dir_plots) # Return computed attributions and metrics return all_attrs, features_attrs, timestep_attrs, f_ts_v_score, f_ts_score, f_v_score, r_score @@ -912,11 +914,19 @@ def Data_Randomization( """ if similarity_func is None: - similarity_func = distance_euclidean + similarity_func = cosine if explain_method == "Attention": Attention_weights = random_model.interpertations(dataloader) - - score = similarity_func(Attention_weights["attention"].cpu().numpy(), attribution.cpu().numpy()) + attribution = attribution.cpu().numpy() + min_val = np.min(attribution) + max_val = np.max(attribution) + + attribution = (attribution - min_val) / (max_val - min_val) + random_attr = Attention_weights["attention"].cpu().numpy() + min_val = np.min(random_attr) + max_val = np.max(random_attr) + random_attr = (random_attr - min_val) / (max_val - min_val) + score = similarity_func(random_attr, attribution)*53 elif explain_method == "Random": score = similarity_func(np.random.normal(size=[64, 24, 53]).flatten(), attribution.flatten()) else: @@ -931,10 +941,19 @@ def Data_Randomization( attr = explantation.attribute(data, **kwargs) # Process and store the calculated attributions - stacked_attr = attr[1].cpu().detach().numpy() if method_name in [ + random_attr = attr[1].cpu().detach().numpy() if method_name in [ 'Lime', 'FeatureAblation'] else torch.stack(attr).cpu().detach().numpy() - score = similarity_func(stacked_attr.flatten(), attribution.flatten()) + attribution = attribution.flatten() + min_val = np.min(attribution) + max_val = np.max(attribution) + attribution = (attribution - min_val) / (max_val - min_val) + random_attr = random_attr.flatten() + min_val = np.min(random_attr) + max_val = np.max(random_attr) + random_attr = (random_attr - min_val) / (max_val - min_val) + + score = similarity_func(random_attr, attribution) return score diff --git a/icu_benchmarks/run_utils.py b/icu_benchmarks/run_utils.py index 7a67f3c5..1fb89843 100644 --- a/icu_benchmarks/run_utils.py +++ b/icu_benchmarks/run_utils.py @@ -16,6 +16,7 @@ from icu_benchmarks.models.utils import JsonResultLoggingEncoder from icu_benchmarks.wandb_utils import wandb_log import numpy as np +import matplotlib.pyplot as plt def build_parser() -> ArgumentParser: @@ -254,6 +255,10 @@ def aggregate_results(log_dir: Path, execution_time: timedelta = None): "CI_0.95": confidence_interval, "execution_time": execution_time.total_seconds() if execution_time is not None else 0.0, } + log_dir_plots = log_dir / 'plots' + if not (log_dir_plots.exists()): + log_dir_plots.mkdir(parents=True) + plot_XAI_Metrics(accumulated_metrics, log_dir_plots=log_dir_plots) with open(log_dir / "aggregated_test_metrics.json", "w") as f: json.dump(aggregated, f, cls=JsonResultLoggingEncoder) @@ -266,6 +271,61 @@ def aggregate_results(log_dir: Path, execution_time: timedelta = None): wandb_log(json.loads(json.dumps(accumulated_metrics, cls=JsonResultLoggingEncoder))) +def plot_XAI_Metrics(accumulated_metrics, log_dir_plots): + groups = {} + for key in accumulated_metrics['avg']: + if key in ['loss', 'MAE']: + continue + suffix = key.split('_')[-1] + if suffix not in groups: + groups[suffix] = [] + groups[suffix].append(key) + + # Define a dictionary for legend labels + legend_labels = { + 'IG': 'Integrated Gradient', + 'G': 'Gradient', + 'R': 'Random', + 'FA': 'Feature Ablation', + 'Att': 'Attention', + 'VSN': 'Variable Selection Network', + 'L': 'Lime' + } + + # Plotting + num_groups = len(groups) + fig, axs = plt.subplots(num_groups, 1, figsize=(10, num_groups * 5)) + + # Custom handles for the legend + handles = [plt.Rectangle((0, 0), 1, 1, color='none', label=f'{key}: {value}') + for key, value in legend_labels.items()] + + for i, (suffix, keys) in enumerate(groups.items()): + + ax = axs[i] if num_groups > 1 else axs + avg_values = [accumulated_metrics['avg'][key] for key in keys] + ci_lower = [accumulated_metrics['CI_0.95'][key][0] for key in keys] + ci_upper = [accumulated_metrics['CI_0.95'][key][1] for key in keys] + ci_error = [np.abs([a - b, c - a]) for a, b, c in zip(avg_values, ci_lower, ci_upper)] + + bars = ax.bar(keys, np.abs(avg_values), yerr=np.array(ci_error).T, capsize=5) + # Modify the title to use the second suffix + title_suffix = keys[0].split('_')[1] + ax.set_title(f'Metric: "{title_suffix}"') + ax.set_ylabel('Values') + ax.axhline(0, color='grey', linewidth=0.8) + ax.grid(axis='y') + # Modify x-axis labels to show only the prefix + ax.set_xticks(keys) + ax.set_xticklabels([key.split('_')[0] for key in keys], rotation=45, ha="right") + # Create a custom legend for each subplot + custom_labels = [legend_labels[key.split('_')[0]] for key in keys] + ax.legend(bars, custom_labels, loc='upper right') + + plt.tight_layout() + plt.savefig(log_dir_plots / "metrics_plot.png", bbox_inches="tight") + + def name_datasets(train="default", val="default", test="default"): """Names the datasets for logging (optional).""" gin.bind_parameter("train_common.dataset_names", {"train": train, "val": val, "test": test}) diff --git a/output.txt b/output.txt deleted file mode 100644 index c197bcbd..00000000 --- a/output.txt +++ /dev/null @@ -1,475 +0,0 @@ -{'encoder_cat': tensor([], size=(64, 6, 0), dtype=torch.int64), 'encoder_cont': tensor([[[ 0.1205, -1.3078, -2.2605, ..., 0.0569, 0.5067, 0.2127], - [ 0.1205, -1.3078, -2.2605, ..., 0.0569, 0.5067, 0.2127], - [ 0.1205, -1.3078, -2.2605, ..., 0.0569, 0.5067, 0.2127], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000]], - - [[-0.9524, -0.5048, -0.2978, ..., 0.0569, 0.5067, 0.2127], - [-0.9524, -0.5048, -0.2978, ..., 0.0569, 0.5067, 0.2127], - [-0.9524, -0.5048, -0.2978, ..., 0.0569, 0.5067, 0.2127], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000]], - - [[ 0.1205, -0.6483, -2.2605, ..., 0.0569, 0.5067, 0.2127], - [ 0.1205, -0.6483, -2.2605, ..., 0.0569, 0.5067, 0.2127], - [ 0.1205, -0.6483, -2.2605, ..., 0.0569, 0.5067, 0.2127], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000]], - - ..., - - [[ 0.3433, 0.4882, 1.6016, ..., 0.0569, 0.5067, 0.2127], - [ 0.3433, 0.4882, 1.6016, ..., 0.0569, 0.5067, 0.2127], - [ 0.3433, 0.4882, 1.6016, ..., 0.0569, 0.5067, 0.2127], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000]], - - [[ 0.1205, 0.4882, -1.0575, ..., 0.0569, 0.5067, 0.2127], - [ 0.1205, 0.4882, -1.0575, ..., 0.0569, -1.9736, -4.7018], - [ 0.1205, 0.4882, -1.0575, ..., 0.0569, 0.5067, 0.2127], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000]], - - [[-0.5397, 1.7179, 0.7785, ..., 0.0569, 0.5067, -4.7018], - [-0.5397, 1.7179, 0.7785, ..., 0.0569, 0.5067, 0.2127], - [-0.5397, 1.7179, 0.7785, ..., 0.0569, -1.9736, 0.2127], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000]]]), 'encoder_target': tensor([[0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 1, 1, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [1, 1, 1, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0]]), 'encoder_lengths': tensor([3, 3, 3, 3, 3, 6, 6, 6, 6, 6, 3, 6, 6, 6, 6, 3, 3, 3, 3, 5, 6, 6, 3, 3, - 6, 6, 6, 6, 3, 6, 6, 6, 6, 6, 3, 6, 6, 6, 6, 3, 6, 6, 6, 3, 6, 3, 6, 6, - 6, 6, 3, 3, 6, 3, 3, 5, 6, 6, 5, 3, 6, 3, 3, 3]), 'decoder_cat': tensor([], size=(64, 12, 0), dtype=torch.int64), 'decoder_cont': tensor([[[ 0.1205, -1.3078, -2.2605, ..., 0.0569, 0.5067, 0.2127], - [ 0.1205, -1.3078, -2.2605, ..., 0.0569, 0.5067, 0.2127], - [ 0.1205, -1.3078, -2.2605, ..., 0.0569, 0.5067, 0.2127], - ..., - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000]], - - [[-0.9524, -0.5048, -0.2978, ..., 0.0569, 0.5067, 0.2127], - [-0.9524, -0.5048, -0.2978, ..., 0.0569, 0.5067, 0.2127], - [-0.9524, -0.5048, -0.2978, ..., 0.0569, 0.5067, 0.2127], - ..., - [-0.9524, -0.5048, -0.2978, ..., 0.0569, 0.5067, 0.2127], - [-0.9524, -0.5048, -0.2978, ..., 0.0569, 0.5067, -4.7018], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000]], - - [[ 0.1205, -0.6483, -2.2605, ..., 0.0569, 0.5067, 0.2127], - [ 0.1205, -0.6483, -2.2605, ..., 0.0569, 0.5067, 0.2127], - [ 0.1205, -0.6483, -2.2605, ..., 0.0569, 0.5067, 0.2127], - ..., - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000]], - - ..., - - [[ 0.3433, 0.4882, 1.6016, ..., 0.0569, 0.5067, 0.2127], - [ 0.3433, 0.4882, 1.6016, ..., 0.0569, 0.5067, 0.2127], - [ 0.3433, 0.4882, 1.6016, ..., 0.0569, 0.5067, 0.2127], - ..., - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000]], - - [[ 0.1205, 0.4882, -1.0575, ..., 0.0569, 0.5067, 0.2127], - [ 0.1205, 0.4882, -1.0575, ..., 0.0569, 0.5067, 0.2127], - [ 0.1205, 0.4882, -1.0575, ..., 0.0569, 0.5067, 0.2127], - ..., - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000]], - - [[-0.5397, 1.7179, 0.7785, ..., 0.0569, 0.5067, 0.2127], - [-0.5397, 1.7179, 0.7785, ..., 0.0569, 0.5067, 0.2127], - [-0.5397, 1.7179, 0.7785, ..., 0.0569, 0.5067, 0.2127], - ..., - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000], - [ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000]]]), 'decoder_target': tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]), 'decoder_lengths': tensor([ 3, 11, 8, 8, 9, 12, 12, 12, 12, 12, 10, 12, 12, 12, 12, 7, 6, 3, - 8, 12, 12, 12, 2, 12, 12, 12, 12, 12, 5, 12, 12, 12, 12, 12, 11, 12, - 12, 12, 12, 8, 12, 12, 12, 3, 12, 7, 12, 12, 12, 12, 9, 6, 12, 12, - 4, 12, 12, 12, 12, 7, 12, 7, 5, 4]), 'decoder_time_idx': tensor([[ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21], - [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], - [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], - [ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28], - [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], - [ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26], - [ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56], - [ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59], - [ 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66], - [ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22], - [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], - [ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47], - [128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139], - [ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17], - [ 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83], - [ 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97], - [106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117], - [ 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35], - [ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78], - [ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], - [ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50], - [ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49], - [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], - [ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19], - [ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18], - [ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73], - [ 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45], - [ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37], - [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], - [114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125], - [ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49], - [125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136], - [ 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44], - [109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120], - [ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17], - [ 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34], - [ 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72], - [ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49], - [ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21], - [ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25], - [ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17], - [ 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70], - [ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46], - [ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49], - [ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36], - [ 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68], - [ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19], - [ 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53], - [ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80], - [ 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39], - [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], - [ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57], - [ 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48], - [ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41], - [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], - [ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], - [ 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68], - [ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91], - [ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55], - [ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21], - [ 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103], - [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], - [ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21], - [113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124]]), 'groups': tensor([[173], - [283], - [107], - [ 34], - [388], - [429], - [126], - [376], - [265], - [213], - [314], - [431], - [185], - [360], - [247], - [203], - [245], - [222], - [299], - [ 43], - [190], - [181], - [345], - [ 16], - [278], - [452], - [395], - [518], - [564], - [266], - [ 76], - [187], - [464], - [266], - [527], - [ 11], - [315], - [ 42], - [513], - [105], - [468], - [313], - [188], - [525], - [201], - [554], - [559], - [551], - [181], - [549], - [335], - [382], - [369], - [436], - [389], - [145], - [475], - [442], - [ 43], - [527], - [207], - [554], - [331], - [239]]), 'target_scale': tensor([[0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.], - [0., 0.]])} -(tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]), None) From 08c3c31a3179e49e4906dbff13b23f60e4e946b8 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Mon, 4 Dec 2023 18:20:22 +0100 Subject: [PATCH 118/142] added stability metric --- icu_benchmarks/models/train.py | 22 +++-- icu_benchmarks/models/wrappers.py | 151 ++++++++++++++++++++++++++++-- 2 files changed, 155 insertions(+), 18 deletions(-) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 14517da7..b22a6438 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -281,17 +281,17 @@ def train_common( # choose which methods to get attributions methods = { - "G": Saliency, - "L": Lime, - "IG": IntegratedGradients, - "FA": FeatureAblation, - "R": "Random", + # "G": Saliency, + # "L": Lime, + # "IG": IntegratedGradients, + # "FA": FeatureAblation, + # "R": "Random", "Att": "Attention" } for key, item in methods.items(): # If conditions needed here as different explantations require different inputs if key == "IG": - all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score, r_score = model.explantation( + all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score, r_score, st_i_score, st_o_score = model.explantation( dataloader=test_loader, method=item, log_dir=log_dir, plot=True, n_steps=50, XAI_metric=XAI_metric, random_model=random_model ) elif key == "L" or key == "FA": @@ -328,12 +328,12 @@ def create_default_mask(shape): # Create a tuple of masks feature_masks = tuple([create_default_mask(shape) if i != 1 else feature_mask_second for i, shape in enumerate(shapes)]) - all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score, r_score = model.explantation( + all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score, r_score, st_i_score, st_o_score = model.explantation( dataloader=test_loader, method=item, log_dir=log_dir, plot=True, return_input_shape=True, XAI_metric=XAI_metric, random_model=random_model ) else: - all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score, r_score = model.explantation( + all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score, r_score, st_i_score, st_o_score = model.explantation( dataloader=test_loader, method=item, log_dir=log_dir, plot=True, XAI_metric=XAI_metric, random_model=random_model ) @@ -341,6 +341,12 @@ def create_default_mask(shape): # logging metric scores print("{} Attributions Faithfulness Timesteps ".format(key), ts_score) XAI_dict["{}_Faith Timesteps".format(key)] = ts_score + print("{}_ROS ".format( + key), st_o_score) + XAI_dict["{}_ROS".format(key)] = st_o_score + print("{}_RIS ".format( + key), st_i_score) + XAI_dict["{}_RIS".format(key)] = st_i_score if key == "Att": print("Variable selection weights faithfulness featrues ".format(key), v_score) XAI_dict["VSN_Faith Features".format(key)] = v_score diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 9fe0c682..96ffe0ce 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -594,6 +594,8 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F f_ts_v_score = [] f_v_score = [] r_score = [] + st_i_score = [] + st_o_score = [] method_name = method if (method == "Random") or (method == "Attention") else ( method.__name__) @@ -605,7 +607,9 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F features_attrs.extend(Interpertations["encoder_variables"].tolist()) r_score = self.Data_Randomization(x=None, attribution=timestep_attrs, explain_method=method, random_model=random_model, dataloader=dataloader, method_name=method_name) - print(r_score) + st_i_score, st_o_score = self.Relative_Stability(x=None, + attribution=timestep_attrs, explain_method=method, method_name=method_name, dataloader=dataloader, **kwargs + ) elif method_name == "Random": # Generate random attributions for baseline comparison all_attrs = np.random.normal(size=[64, 24, 53]) @@ -620,7 +624,7 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F if method_name == "Random": - f_ts_v_score.append(self.Faithfulness_Correlation(x, all_attrs, + """ f_ts_v_score.append(self.Faithfulness_Correlation(x, all_attrs, pertrub="baseline", feature_timestep=True, subset_size=[4, 9], nr_runs=100)) f_ts_score.append(self.Faithfulness_Correlation(x, all_attrs, pertrub="baseline", time_step=True, subset_size=4, nr_runs=100)) @@ -628,7 +632,12 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F pertrub="baseline", feature=True, subset_size=9, nr_runs=100)) r_score.append(self.Data_Randomization(x, attribution=all_attrs, - explain_method=method, random_model=random_model, method_name=method_name)) + explain_method=method, random_model=random_model, method_name=method_name)) """ + res1, res2 = self.Relative_Stability(x, + all_attrs, explain_method=method, method_name=method_name, dataloader=None, **kwargs + ) + st_i_score.append(res1) + st_o_score.append(res2) else: f_ts_score.append(self.Faithfulness_Correlation(x, timestep_attrs, pertrub="baseline", time_step=True, subset_size=4, nr_runs=100)) @@ -640,12 +649,14 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F # Faithfulness score for attribtuons of timesteps averaged over features f_ts_score = np.mean(f_ts_score) f_v_score = np.mean(f_v_score) - min_val = np.min(r_score) - max_val = np.max(r_score) + # min_val = np.min(r_score) + # max_val = np.max(r_score) if method_name != "Attention": - r_score = (r_score - min_val) / (max_val - min_val) + # r_score = (r_score - min_val) / (max_val - min_val) r_score = np.mean(r_score) - return all_attrs, features_attrs, timestep_attrs, f_ts_v_score, f_ts_score, f_v_score, r_score + st_i_score = np.max(st_i_score) + st_o_score = np.max(st_o_score) + return all_attrs, features_attrs, timestep_attrs, f_ts_v_score, f_ts_score, f_v_score, r_score, st_i_score, st_o_score # Loop through the dataloader to compute attributions for all instances for batch in dataloader: @@ -671,7 +682,7 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F 'Lime', 'FeatureAblation'] else torch.stack(attr).cpu().detach().numpy() if XAI_metric: - f_ts_v_score.append(self.Faithfulness_Correlation(x, stacked_attr, + """ f_ts_v_score.append(self.Faithfulness_Correlation(x, stacked_attr, pertrub="baseline", feature_timestep=True, subset_size=[4, 9], nr_runs=100)) f_ts_score.append(self.Faithfulness_Correlation(x, stacked_attr, @@ -679,7 +690,12 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F f_v_score.append(self.Faithfulness_Correlation(x, stacked_attr, pertrub="baseline", feature=True, subset_size=9, nr_runs=100)) r_score.append(self.Data_Randomization(x, attribution=stacked_attr, - explain_method=method, random_model=random_model, method_name=method_name)) + explain_method=method, random_model=random_model, method_name=method_name)) """ + res1, res2 = self.Relative_Stability(x, + stacked_attr, explain_method=method, method_name=method_name, dataloader=None, **kwargs + ) + st_i_score.append(res1) + st_o_score.append(res2) # aggregate over batch attr = np.mean(stacked_attr, axis=0) all_attrs.append(attr) @@ -698,6 +714,8 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F # Random data score r_score = np.mean(r_score) + st_i_score = np.max(st_i_score) + st_o_score = np.max(st_o_score) if plot: log_dir_plots = log_dir / 'plots' @@ -708,7 +726,7 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F self.plot_attributions(features_attrs, timestep_attrs, method_name, log_dir_plots) # Return computed attributions and metrics - return all_attrs, features_attrs, timestep_attrs, f_ts_v_score, f_ts_score, f_v_score, r_score + return all_attrs, features_attrs, timestep_attrs, f_ts_v_score, f_ts_score, f_v_score, r_score, st_i_score, st_o_score # normalized_means = (means - means.min()) / (means.max() - means.min()) def prep_data(self, x): @@ -956,6 +974,119 @@ def Data_Randomization( score = similarity_func(random_attr, attribution) return score + def Relative_Stability(self, x, + attribution, explain_method, method_name, dataloader=None, **kwargs + ): + """ + Computes relative input and output stabilities maximization objective + as defined here : + ref:`https://arxiv.org/pdf/2203.06877.pdf` by the authors. + Code mainly by: + Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. + """ + + def relative_stability_objective( + x: np.ndarray, xs: np.ndarray, e_x: np.ndarray, e_xs: np.ndarray, eps_min=0.0001, input=False + ) -> np.ndarray: + """ + Computes relative input stabilities maximization objective + as defined here :ref:`https://arxiv.org/pdf/2203.06877.pdf` by the authors. + + Parameters + ---------- + x: np.ndarray + Batch of images. + xs: np.ndarray + Batch of perturbed images. + e_x: np.ndarray + Explanations for x. + e_xs: np.ndarray + Explanations for xs. + + Returns + ------- + ris_obj: np.ndarray + RIS maximization objective. + """ + + if input: + num_dim = x.ndim + else: + num_dim = e_x.ndim + + if num_dim == 3: + def norm_function(arr): return np.linalg.norm(arr, axis=(-1, -2)) # noqa + elif num_dim == 2: + def norm_function(arr): return np.linalg.norm(arr, axis=-1) + else: + raise ValueError( + "Relative Input Stability only supports 4D, 3D and 2D inputs (batch dimension inclusive)." + ) + + # fmt: off + + nominator = (e_x - e_xs) / (e_x + (e_x == 0) * eps_min) # prevent division by 0 + nominator = norm_function(nominator) + # fmt: on + if input: + denominator = x - xs + denominator /= x + (x == 0) * eps_min + # fmt: off + denominator = norm_function(denominator) + # fmt: on + denominator += (denominator == 0) * eps_min + else: + + denominator = np.squeeze(x) - np.squeeze(xs) + denominator = np.linalg.norm(denominator, axis=-1) + denominator += (denominator == 0) * eps_min # prevent division by 0 + + return nominator / denominator + + y_pred = self(self.prep_data(x)).detach() + + if explain_method == "Attention": + with torch.no_grad(): + noise = torch.randn_like(self.dataset["encoder_cont"])*0.1 + self.dataset += noise + + Attention_weights = self.interpertations(dataloader) + att_preturb = Attention_weights["attention"].cpu().numpy() + + else: + y_pred = self(self.prep_data(x)).detach() + x_original = x["encoder_cont"].detach().clone() + y_pred_preturb = self(self.prep_data(x)).detach() + with torch.no_grad(): + noise = torch.randn_like(x["encoder_cont"])*0.01 + x["encoder_cont"] += noise + + if explain_method == "Random": + att_preturb = np.random.normal(size=[64, 24, 53]) + + else: + + data, baselines = self.prep_data_captum(x) + + explantation = explain_method(self.forward_captum) + # Reformat attributions. + if explain_method is not captum.attr.Saliency: + att_preturb = explantation.attribute(data, baselines=baselines, **kwargs) + else: + att_preturb = explantation.attribute(data, **kwargs) + + # Process and store the calculated attributions + att_preturb = att_preturb[1].cpu().detach().numpy() if method_name in [ + 'Lime', 'FeatureAblation'] else torch.stack(att_preturb).cpu().detach().numpy() + RIS = relative_stability_objective( + x_original.detach().cpu().numpy(), x["encoder_cont"].detach().cpu().numpy(), attribution, att_preturb, input=True + ) + ROS = relative_stability_objective( + y_pred.cpu().numpy(), y_pred_preturb.cpu().numpy(), attribution, att_preturb, input=False + ) + + return np.max(RIS), np.max(ROS) + @gin.configurable("MLWrapper") class MLWrapper(BaseModule, ABC): From 4893f3ea976c37a4a1ea95659d718308dc7bb9b4 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Tue, 5 Dec 2023 17:32:38 +0100 Subject: [PATCH 119/142] attention ROS RIS --- configs/prediction_models/DeepARpytorch.gin | 2 +- configs/prediction_models/RNNpytorch.gin | 2 +- icu_benchmarks/data/loader.py | 6 ++- icu_benchmarks/models/dl_models.py | 2 +- icu_benchmarks/models/train.py | 4 +- icu_benchmarks/models/wrappers.py | 55 ++++++++++++--------- icu_benchmarks/run_utils.py | 25 +++++++--- 7 files changed, 61 insertions(+), 35 deletions(-) diff --git a/configs/prediction_models/DeepARpytorch.gin b/configs/prediction_models/DeepARpytorch.gin index 5135035f..e01083ff 100644 --- a/configs/prediction_models/DeepARpytorch.gin +++ b/configs/prediction_models/DeepARpytorch.gin @@ -13,7 +13,7 @@ optimizer/hyperparameter.lr = (1e-5, 1e-3) # Model params model/hyperparameter.class_to_tune = @DeepARpytorch -model/hyperparameter.hidden = (40, 120, "log-uniform", 2) +model/hyperparameter.hidden = (4, 64, "log-uniform", 2) model/hyperparameter.rnn_layers=(1, 3) model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.cell_type='LSTM' diff --git a/configs/prediction_models/RNNpytorch.gin b/configs/prediction_models/RNNpytorch.gin index 0b88c670..486e5b21 100644 --- a/configs/prediction_models/RNNpytorch.gin +++ b/configs/prediction_models/RNNpytorch.gin @@ -13,7 +13,7 @@ optimizer/hyperparameter.lr = (1e-5, 3e-4) # Model params model/hyperparameter.class_to_tune = @RNNpytorch -model/hyperparameter.hidden = (32, 256, "log-uniform", 2) +model/hyperparameter.hidden = (2, 64, "log-uniform", 2) model/hyperparameter.rnn_layers=(1,3) model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.cell_type='LSTM' diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 96ba9695..1b3e72ad 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -3,7 +3,7 @@ import pandas as pd import gin import numpy as np -from torch import Tensor, cat, from_numpy, float32, min, max +from torch import Tensor, cat, from_numpy, float32, min, max, randn_like from torch.utils.data import Dataset import logging from typing import Dict, Tuple, Union @@ -531,3 +531,7 @@ def randomize_labels(self, num_classes=None, min=None, max=None): else: random_target = np.random.randint(num_classes, size=len(self.data["target"][0])) self.data["target"][0] = Tensor(random_target) + + def add_noise(self, num_classes=None, min=None, max=None): + noise = randn_like(self.data["reals"])*0.1 + self.data["reals"] += noise diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 57107b60..ff66c4da 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -476,7 +476,7 @@ def __init__( **kwargs, ): super().__init__(optimizer=optimizer, pytorch_forecasting=True, *args, **kwargs) - + self.dataset = dataset self.model = TemporalFusionTransformer.from_dataset( dataset=dataset, hidden_size=hidden, diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index b22a6438..943a533e 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -342,10 +342,10 @@ def create_default_mask(shape): print("{} Attributions Faithfulness Timesteps ".format(key), ts_score) XAI_dict["{}_Faith Timesteps".format(key)] = ts_score print("{}_ROS ".format( - key), st_o_score) + key), st_o_score, type(st_o_score)) XAI_dict["{}_ROS".format(key)] = st_o_score print("{}_RIS ".format( - key), st_i_score) + key), st_i_score, type(st_i_score)) XAI_dict["{}_RIS".format(key)] = st_i_score if key == "Att": print("Variable selection weights faithfulness featrues ".format(key), v_score) diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 96ffe0ce..50ac9c4d 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -605,8 +605,8 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F timestep_attrs = Interpertations["attention"] features_attrs = Interpertations["static_variables"].tolist() features_attrs.extend(Interpertations["encoder_variables"].tolist()) - r_score = self.Data_Randomization(x=None, attribution=timestep_attrs, - explain_method=method, random_model=random_model, dataloader=dataloader, method_name=method_name) + """ r_score = self.Data_Randomization(x=None, attribution=timestep_attrs, + explain_method=method, random_model=random_model, dataloader=dataloader, method_name=method_name) """ st_i_score, st_o_score = self.Relative_Stability(x=None, attribution=timestep_attrs, explain_method=method, method_name=method_name, dataloader=dataloader, **kwargs ) @@ -649,8 +649,7 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F # Faithfulness score for attribtuons of timesteps averaged over features f_ts_score = np.mean(f_ts_score) f_v_score = np.mean(f_v_score) - # min_val = np.min(r_score) - # max_val = np.max(r_score) + if method_name != "Attention": # r_score = (r_score - min_val) / (max_val - min_val) r_score = np.mean(r_score) @@ -944,7 +943,7 @@ def Data_Randomization( min_val = np.min(random_attr) max_val = np.max(random_attr) random_attr = (random_attr - min_val) / (max_val - min_val) - score = similarity_func(random_attr, attribution)*53 + score = similarity_func(random_attr, attribution) elif explain_method == "Random": score = similarity_func(np.random.normal(size=[64, 24, 53]).flatten(), attribution.flatten()) else: @@ -989,7 +988,7 @@ def relative_stability_objective( x: np.ndarray, xs: np.ndarray, e_x: np.ndarray, e_xs: np.ndarray, eps_min=0.0001, input=False ) -> np.ndarray: """ - Computes relative input stabilities maximization objective + Computes relative input and output stabilities maximization objective as defined here :ref:`https://arxiv.org/pdf/2203.06877.pdf` by the authors. Parameters @@ -1019,9 +1018,7 @@ def norm_function(arr): return np.linalg.norm(arr, axis=(-1, -2)) # noqa elif num_dim == 2: def norm_function(arr): return np.linalg.norm(arr, axis=-1) else: - raise ValueError( - "Relative Input Stability only supports 4D, 3D and 2D inputs (batch dimension inclusive)." - ) + def norm_function(arr): return np.linalg.norm(arr) # fmt: off @@ -1043,16 +1040,30 @@ def norm_function(arr): return np.linalg.norm(arr, axis=-1) return nominator / denominator - y_pred = self(self.prep_data(x)).detach() - if explain_method == "Attention": - with torch.no_grad(): - noise = torch.randn_like(self.dataset["encoder_cont"])*0.1 - self.dataset += noise - + y_pred = self.model.predict(dataloader) + x_original = dataloader.dataset.data["reals"].clone() + + dataloader.dataset.add_noise() + x_preturb = dataloader.dataset.data["reals"].clone() + """ dataloader = self.dataset.to_dataloader( + train=False, + batch_size=64, + num_workers=8, + pin_memory=False, + drop_last=True, + shuffle=False, + ) """ + + y_pred_preturb = self.model.predict(dataloader) Attention_weights = self.interpertations(dataloader) att_preturb = Attention_weights["attention"].cpu().numpy() - + RIS = relative_stability_objective( + x_original.detach().cpu().numpy(), x_preturb.detach().cpu().numpy(), attribution.cpu().numpy(), att_preturb, input=True + ) + ROS = relative_stability_objective( + y_pred.cpu().numpy(), y_pred_preturb.cpu().numpy(), attribution.cpu().numpy(), att_preturb, input=False + ) else: y_pred = self(self.prep_data(x)).detach() x_original = x["encoder_cont"].detach().clone() @@ -1078,12 +1089,12 @@ def norm_function(arr): return np.linalg.norm(arr, axis=-1) # Process and store the calculated attributions att_preturb = att_preturb[1].cpu().detach().numpy() if method_name in [ 'Lime', 'FeatureAblation'] else torch.stack(att_preturb).cpu().detach().numpy() - RIS = relative_stability_objective( - x_original.detach().cpu().numpy(), x["encoder_cont"].detach().cpu().numpy(), attribution, att_preturb, input=True - ) - ROS = relative_stability_objective( - y_pred.cpu().numpy(), y_pred_preturb.cpu().numpy(), attribution, att_preturb, input=False - ) + RIS = relative_stability_objective( + x_original.detach().cpu().numpy(), x["encoder_cont"].detach().cpu().numpy(), attribution, att_preturb, input=True + ) + ROS = relative_stability_objective( + y_pred.cpu().numpy(), y_pred_preturb.cpu().numpy(), attribution, att_preturb, input=False + ) return np.max(RIS), np.max(ROS) diff --git a/icu_benchmarks/run_utils.py b/icu_benchmarks/run_utils.py index 1fb89843..577607ff 100644 --- a/icu_benchmarks/run_utils.py +++ b/icu_benchmarks/run_utils.py @@ -291,6 +291,7 @@ def plot_XAI_Metrics(accumulated_metrics, log_dir_plots): 'VSN': 'Variable Selection Network', 'L': 'Lime' } + colors = ['navy', 'skyblue', 'crimson', 'salmon', 'teal', 'orange', 'darkgreen', 'lightgreen'] # Plotting num_groups = len(groups) @@ -303,23 +304,33 @@ def plot_XAI_Metrics(accumulated_metrics, log_dir_plots): for i, (suffix, keys) in enumerate(groups.items()): ax = axs[i] if num_groups > 1 else axs + # Extract values and errors avg_values = [accumulated_metrics['avg'][key] for key in keys] ci_lower = [accumulated_metrics['CI_0.95'][key][0] for key in keys] ci_upper = [accumulated_metrics['CI_0.95'][key][1] for key in keys] ci_error = [np.abs([a - b, c - a]) for a, b, c in zip(avg_values, ci_lower, ci_upper)] - bars = ax.bar(keys, np.abs(avg_values), yerr=np.array(ci_error).T, capsize=5) - # Modify the title to use the second suffix - title_suffix = keys[0].split('_')[1] + # Sort by absolute values of avg_values + sorted_indices = np.argsort([np.abs(val) for val in avg_values])[::-1] # Indices to sort in descending order + sorted_keys = np.array(keys)[sorted_indices] + sorted_avg_values = np.array(avg_values)[sorted_indices] + sorted_ci_error = np.array(ci_error)[sorted_indices] + + # Plot bars + bars = ax.bar(sorted_keys, np.abs(sorted_avg_values), yerr=np.array(sorted_ci_error).T, capsize=5, color=colors) + + # Set titles and labels + title_suffix = sorted_keys[0].split('_')[1] ax.set_title(f'Metric: "{title_suffix}"') ax.set_ylabel('Values') ax.axhline(0, color='grey', linewidth=0.8) ax.grid(axis='y') - # Modify x-axis labels to show only the prefix - ax.set_xticks(keys) - ax.set_xticklabels([key.split('_')[0] for key in keys], rotation=45, ha="right") + + # Set x-ticks + ax.set_xticks(sorted_keys) + ax.set_xticklabels([key.split('_')[0] for key in sorted_keys], rotation=45, ha="right") # Create a custom legend for each subplot - custom_labels = [legend_labels[key.split('_')[0]] for key in keys] + custom_labels = [legend_labels[key.split('_')[0]] for key in sorted_keys] ax.legend(bars, custom_labels, loc='upper right') plt.tight_layout() From 38ed6e335727f58fe5a36f8fad7525889f626b70 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Wed, 13 Dec 2023 15:06:31 +0100 Subject: [PATCH 120/142] added condition for ROS and RIS --- configs/prediction_models/DeepARpytorch.gin | 19 ++- configs/prediction_models/RNNpytorch.gin | 18 ++- configs/prediction_models/TFTpytorch.gin | 20 ++- configs/tasks/Regression.gin | 4 +- configs/tasks/common/CrossValidation.gin | 2 +- icu_benchmarks/data/loader.py | 2 +- icu_benchmarks/models/train.py | 8 +- icu_benchmarks/models/wrappers.py | 160 ++++++++++++-------- icu_benchmarks/run_utils.py | 2 +- 9 files changed, 138 insertions(+), 97 deletions(-) diff --git a/configs/prediction_models/DeepARpytorch.gin b/configs/prediction_models/DeepARpytorch.gin index e01083ff..b093027e 100644 --- a/configs/prediction_models/DeepARpytorch.gin +++ b/configs/prediction_models/DeepARpytorch.gin @@ -5,24 +5,27 @@ include "configs/prediction_models/common/DLCommon.gin" # Optimizer params train_common.model = @DeepARpytorch - + optimizer/hyperparameter.class_to_tune = @Adam optimizer/hyperparameter.weight_decay = 1e-6 -optimizer/hyperparameter.lr = (1e-5, 1e-3) - +optimizer/hyperparameter.lr = 0.00046 +#(1e-5, 1e-3) # Model params model/hyperparameter.class_to_tune = @DeepARpytorch -model/hyperparameter.hidden = (4, 64, "log-uniform", 2) -model/hyperparameter.rnn_layers=(1, 3) +model/hyperparameter.hidden = 116 +#(4, 64, "log-uniform", 2) +model/hyperparameter.rnn_layers=1 + model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.cell_type='LSTM' -model/hyperparameter.dropout = (0.0, 0.4) +model/hyperparameter.dropout = (0.286968842146375, 0.29) #model/hyperparameter.lr_scheduler = "exponential" train_common/hyperparameter.class_to_tune = @train_common -train_common/hyperparameter.batch_size=(32,64,128,256,512) -train_common/hyperparameter.gradient_clip_val=(0,0.01, 1.0, 100.0) +train_common/hyperparameter.batch_size=256 +train_common/hyperparameter.gradient_clip_val=0.01 +#(0,0.01, 1.0, 100.0) # Dataset params PredictionDatasetpytorch.max_encoder_length = 24 PredictionDatasetpytorch.max_prediction_length = 1 diff --git a/configs/prediction_models/RNNpytorch.gin b/configs/prediction_models/RNNpytorch.gin index 486e5b21..050f11a5 100644 --- a/configs/prediction_models/RNNpytorch.gin +++ b/configs/prediction_models/RNNpytorch.gin @@ -8,21 +8,25 @@ train_common.model = @RNNpytorch optimizer/hyperparameter.class_to_tune = @Adam optimizer/hyperparameter.weight_decay = 1e-6 -optimizer/hyperparameter.lr = (1e-5, 3e-4) +optimizer/hyperparameter.lr = 0.00016 # Model params - model/hyperparameter.class_to_tune = @RNNpytorch -model/hyperparameter.hidden = (2, 64, "log-uniform", 2) -model/hyperparameter.rnn_layers=(1,3) +model/hyperparameter.hidden = 42 +#(2, 64, "log-uniform", 2) +model/hyperparameter.rnn_layers=2 +#(1,3) model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.cell_type='LSTM' -model/hyperparameter.dropout = (0.0, 0.4) +model/hyperparameter.dropout = (0.3976, 0.3978) +#(0.0, 0.4) #model/hyperparameter.lr_scheduler = "exponential" train_common/hyperparameter.class_to_tune = @train_common -train_common/hyperparameter.batch_size=(32,64,128,256,512) -train_common/hyperparameter.gradient_clip_val=(0,0.01, 1.0, 100.0) +train_common/hyperparameter.batch_size=128 +#(32,64,128,256,512) +train_common/hyperparameter.gradient_clip_val=0.01 +#(0,0.01, 1.0, 100.0) # Dataset params PredictionDatasetpytorch.max_encoder_length = 24 PredictionDatasetpytorch.max_prediction_length = 1 diff --git a/configs/prediction_models/TFTpytorch.gin b/configs/prediction_models/TFTpytorch.gin index 18eabb6a..19130460 100644 --- a/configs/prediction_models/TFTpytorch.gin +++ b/configs/prediction_models/TFTpytorch.gin @@ -8,23 +8,27 @@ train_common.model = @TFTpytorch optimizer/hyperparameter.class_to_tune = @Adam optimizer/hyperparameter.weight_decay = 1e-6 -optimizer/hyperparameter.lr = (0.0001, 0.001,0.01) - +optimizer/hyperparameter.lr = 0.001 +#(0.0001, 0.001,0.01) # Model params model/hyperparameter.class_to_tune = @TFTpytorch -model/hyperparameter.hidden = (10,20,40,80,160,240,320) +model/hyperparameter.hidden = 32 +#(10,20,40,80,160,240,320) model/hyperparameter.num_classes = %NUM_CLASSES -model/hyperparameter.dropout =(0.1, 0.9) -model/hyperparameter.dropout_att = (0.1, 0.9) -model/hyperparameter.n_heads =(1,4) +model/hyperparameter.dropout =(0.37, 0.38) +model/hyperparameter.dropout_att = 0.0589 +#(0.1, 0.9) +model/hyperparameter.n_heads =2 #dataloader train_common/hyperparameter.class_to_tune = @train_common -train_common/hyperparameter.batch_size=(32,64,128,256,512) +train_common/hyperparameter.batch_size=64 +#(32,64,128,256,512) -train_common/hyperparameter.gradient_clip_val=(0,0.01, 1.0, 100.0) +train_common/hyperparameter.gradient_clip_val=0.0 +#(0,0.01, 1.0, 100.0) diff --git a/configs/tasks/Regression.gin b/configs/tasks/Regression.gin index 1234f707..75041763 100644 --- a/configs/tasks/Regression.gin +++ b/configs/tasks/Regression.gin @@ -15,8 +15,8 @@ train_common.weight = "balanced" train_common.ram_cache = True # LOSS FUNCTION -DLPredictionWrapper.loss = @mse_loss -DLPredictionPytorchForecastingWrapper.loss = @mse_loss +DLPredictionWrapper.loss = @l1_loss +DLPredictionPytorchForecastingWrapper.loss = @l1_loss MLWrapper.loss = @mean_squared_error # SELECTING PREPROCESSOR diff --git a/configs/tasks/common/CrossValidation.gin b/configs/tasks/common/CrossValidation.gin index d0c4a704..519ad3dd 100644 --- a/configs/tasks/common/CrossValidation.gin +++ b/configs/tasks/common/CrossValidation.gin @@ -1,3 +1,3 @@ # CROSS-VALIDATION SETTINGS -execute_repeated_cv.cv_repetitions = 1 +execute_repeated_cv.cv_repetitions = 2 execute_repeated_cv.cv_folds = 5 \ No newline at end of file diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 1b3e72ad..a0e51dbc 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -533,5 +533,5 @@ def randomize_labels(self, num_classes=None, min=None, max=None): self.data["target"][0] = Tensor(random_target) def add_noise(self, num_classes=None, min=None, max=None): - noise = randn_like(self.data["reals"])*0.1 + noise = randn_like(self.data["reals"])*0.01 self.data["reals"] += noise diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 943a533e..7097571b 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -285,8 +285,8 @@ def train_common( # "L": Lime, # "IG": IntegratedGradients, # "FA": FeatureAblation, - # "R": "Random", - "Att": "Attention" + "R": "Random", + # "Att": "Attention" } for key, item in methods.items(): # If conditions needed here as different explantations require different inputs @@ -342,10 +342,10 @@ def create_default_mask(shape): print("{} Attributions Faithfulness Timesteps ".format(key), ts_score) XAI_dict["{}_Faith Timesteps".format(key)] = ts_score print("{}_ROS ".format( - key), st_o_score, type(st_o_score)) + key), st_o_score) XAI_dict["{}_ROS".format(key)] = st_o_score print("{}_RIS ".format( - key), st_i_score, type(st_i_score)) + key), st_i_score) XAI_dict["{}_RIS".format(key)] = st_i_score if key == "Att": print("Variable selection weights faithfulness featrues ".format(key), v_score) diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 50ac9c4d..b3c76a40 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -605,8 +605,8 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F timestep_attrs = Interpertations["attention"] features_attrs = Interpertations["static_variables"].tolist() features_attrs.extend(Interpertations["encoder_variables"].tolist()) - """ r_score = self.Data_Randomization(x=None, attribution=timestep_attrs, - explain_method=method, random_model=random_model, dataloader=dataloader, method_name=method_name) """ + r_score = self.Data_Randomization(x=None, attribution=timestep_attrs, + explain_method=method, random_model=random_model, dataloader=dataloader, method_name=method_name) st_i_score, st_o_score = self.Relative_Stability(x=None, attribution=timestep_attrs, explain_method=method, method_name=method_name, dataloader=dataloader, **kwargs ) @@ -624,15 +624,15 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F if method_name == "Random": - """ f_ts_v_score.append(self.Faithfulness_Correlation(x, all_attrs, + """ f_ts_v_score.append(self.Faithfulness_Correlation(x, all_attrs, pertrub="baseline", feature_timestep=True, subset_size=[4, 9], nr_runs=100)) f_ts_score.append(self.Faithfulness_Correlation(x, all_attrs, pertrub="baseline", time_step=True, subset_size=4, nr_runs=100)) f_v_score.append(self.Faithfulness_Correlation(x, all_attrs, - pertrub="baseline", feature=True, subset_size=9, nr_runs=100)) + pertrub="baseline", feature=True, subset_size=9, nr_runs=100)) r_score.append(self.Data_Randomization(x, attribution=all_attrs, - explain_method=method, random_model=random_model, method_name=method_name)) """ + explain_method=method, random_model=random_model, method_name=method_name))""" res1, res2 = self.Relative_Stability(x, all_attrs, explain_method=method, method_name=method_name, dataloader=None, **kwargs ) @@ -681,7 +681,7 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F 'Lime', 'FeatureAblation'] else torch.stack(attr).cpu().detach().numpy() if XAI_metric: - """ f_ts_v_score.append(self.Faithfulness_Correlation(x, stacked_attr, + f_ts_v_score.append(self.Faithfulness_Correlation(x, stacked_attr, pertrub="baseline", feature_timestep=True, subset_size=[4, 9], nr_runs=100)) f_ts_score.append(self.Faithfulness_Correlation(x, stacked_attr, @@ -689,7 +689,7 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F f_v_score.append(self.Faithfulness_Correlation(x, stacked_attr, pertrub="baseline", feature=True, subset_size=9, nr_runs=100)) r_score.append(self.Data_Randomization(x, attribution=stacked_attr, - explain_method=method, random_model=random_model, method_name=method_name)) """ + explain_method=method, random_model=random_model, method_name=method_name)) res1, res2 = self.Relative_Stability(x, stacked_attr, explain_method=method, method_name=method_name, dataloader=None, **kwargs ) @@ -914,9 +914,22 @@ def apply_baseline(x, indices, time_step, feature_timestep): def Data_Randomization( self, x, - attribution, explain_method, random_model, similarity_func=None, dataloader=None, method_name="", **kwargs + attribution, explain_method, random_model, similarity_func=cosine, dataloader=None, method_name="", **kwargs ): """ + + Args: + - x:Batch input + -attribution: attribution + - explain_method:function to generate explantations + - random_model: Reference to model trained on random labels + - similarity_func: Function to measure similiarity + - dataloader:In case of using Attention as the explain method need to pass the dataloader instead of the batch , + - method_name: Name of the explantation + + Returns: + score: similarity score between attributions of model trained on random data and model trained on real data + Implementation of the Random Logit Metric by Sixt et al., 2020. The Random Logit Metric computes the distance between the original explanation and a reference explanation of @@ -930,8 +943,6 @@ def Data_Randomization( """ - if similarity_func is None: - similarity_func = cosine if explain_method == "Attention": Attention_weights = random_model.interpertations(dataloader) attribution = attribution.cpu().numpy() @@ -974,39 +985,58 @@ def Data_Randomization( return score def Relative_Stability(self, x, - attribution, explain_method, method_name, dataloader=None, **kwargs + attribution, explain_method, method_name, dataloader=None, thershold=0.5, **kwargs ): """ - Computes relative input and output stabilities maximization objective - as defined here : - ref:`https://arxiv.org/pdf/2203.06877.pdf` by the authors. - Code mainly by: - Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. + Args: + - x:Batch input + -attribution: attribution + - explain_method:function to generate explantations + - method_name: Name of the explantation + - dataloader:In case of using Attention as the explain method need to pass the dataloader instead of the batch , + + + Returns: + RIS : relative distance between the explantation and the input + ROS: relative distance between the explantation and the output + + + References: + 1) `https://arxiv.org/pdf/2203.06877.pdf + 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. + """ def relative_stability_objective( - x: np.ndarray, xs: np.ndarray, e_x: np.ndarray, e_xs: np.ndarray, eps_min=0.0001, input=False - ) -> np.ndarray: + x, xs, e_x, e_xs, eps_min=0.0001, input=False, device='cuda' + ) -> torch.Tensor: """ Computes relative input and output stabilities maximization objective as defined here :ref:`https://arxiv.org/pdf/2203.06877.pdf` by the authors. - Parameters - ---------- - x: np.ndarray - Batch of images. - xs: np.ndarray - Batch of perturbed images. - e_x: np.ndarray - Explanations for x. - e_xs: np.ndarray - Explanations for xs. - - Returns - ------- - ris_obj: np.ndarray - RIS maximization objective. + Args: + + x: Input tensor + xs: perturbed tensor. + e_x: Explanations for x. + e_xs: Explanations for xs. + eps_min:Value to avoid division by zero if needed + input:Boolean to indicate if this is an input or an output + device: the device to keep the tensors on + + Returns: + + ris_obj: Tensor + RIS maximization objective. """ + # Function to convert inputs to tensors if they are numpy arrays + def to_tensor(input_array): + if isinstance(input_array, np.ndarray): + return torch.tensor(input_array).to(device) + return input_array.to(device) + + # Convert all inputs to tensors and move to GPU + x, xs, e_x, e_xs = map(to_tensor, [x, xs, e_x, e_xs]) if input: num_dim = x.ndim @@ -1014,29 +1044,24 @@ def relative_stability_objective( num_dim = e_x.ndim if num_dim == 3: - def norm_function(arr): return np.linalg.norm(arr, axis=(-1, -2)) # noqa + def norm_function(arr): return torch.norm(arr, dim=(-1, -2)) elif num_dim == 2: - def norm_function(arr): return np.linalg.norm(arr, axis=-1) + def norm_function(arr): return torch.norm(arr, dim=-1) else: - def norm_function(arr): return np.linalg.norm(arr) - - # fmt: off + def norm_function(arr): return torch.norm(arr) - nominator = (e_x - e_xs) / (e_x + (e_x == 0) * eps_min) # prevent division by 0 + nominator = (e_x - e_xs) / (e_x + (e_x == 0) * eps_min) nominator = norm_function(nominator) - # fmt: on + if input: denominator = x - xs denominator /= x + (x == 0) * eps_min - # fmt: off denominator = norm_function(denominator) - # fmt: on denominator += (denominator == 0) * eps_min else: - - denominator = np.squeeze(x) - np.squeeze(xs) - denominator = np.linalg.norm(denominator, axis=-1) - denominator += (denominator == 0) * eps_min # prevent division by 0 + denominator = torch.squeeze(x) - torch.squeeze(xs) + denominator = torch.norm(denominator, dim=-1) + denominator += (denominator == 0) * eps_min return nominator / denominator @@ -1046,32 +1071,29 @@ def norm_function(arr): return np.linalg.norm(arr) dataloader.dataset.add_noise() x_preturb = dataloader.dataset.data["reals"].clone() - """ dataloader = self.dataset.to_dataloader( - train=False, - batch_size=64, - num_workers=8, - pin_memory=False, - drop_last=True, - shuffle=False, - ) """ - y_pred_preturb = self.model.predict(dataloader) Attention_weights = self.interpertations(dataloader) - att_preturb = Attention_weights["attention"].cpu().numpy() + att_preturb = Attention_weights["attention"] + # Calculate the absolute difference + difference = torch.abs(y_pred_preturb - y_pred) + + # Find where the difference is less than or equal to a thershold + close_indices = torch.nonzero(difference <= thershold).squeeze() RIS = relative_stability_objective( - x_original.detach().cpu().numpy(), x_preturb.detach().cpu().numpy(), attribution.cpu().numpy(), att_preturb, input=True + x_original[close_indices, :, :].detach(), x_preturb[close_indices, :, :].detach(), attribution, att_preturb, input=True ) ROS = relative_stability_objective( - y_pred.cpu().numpy(), y_pred_preturb.cpu().numpy(), attribution.cpu().numpy(), att_preturb, input=False + y_pred[close_indices], y_pred_preturb[close_indices], attribution, att_preturb, input=False ) + else: y_pred = self(self.prep_data(x)).detach() x_original = x["encoder_cont"].detach().clone() - y_pred_preturb = self(self.prep_data(x)).detach() + with torch.no_grad(): noise = torch.randn_like(x["encoder_cont"])*0.01 x["encoder_cont"] += noise - + y_pred_preturb = self(self.prep_data(x)).detach() if explain_method == "Random": att_preturb = np.random.normal(size=[64, 24, 53]) @@ -1087,16 +1109,24 @@ def norm_function(arr): return np.linalg.norm(arr) att_preturb = explantation.attribute(data, **kwargs) # Process and store the calculated attributions - att_preturb = att_preturb[1].cpu().detach().numpy() if method_name in [ - 'Lime', 'FeatureAblation'] else torch.stack(att_preturb).cpu().detach().numpy() + att_preturb = att_preturb[1].detach() if method_name in [ + 'Lime', 'FeatureAblation'] else torch.stack(att_preturb).detach() + # Calculate the absolute difference + difference = torch.abs(y_pred_preturb - y_pred) + + # Find where the difference is less than or equal to a thershold + close_indices = torch.nonzero(difference <= thershold).squeeze() + print(close_indices) + RIS = relative_stability_objective( - x_original.detach().cpu().numpy(), x["encoder_cont"].detach().cpu().numpy(), attribution, att_preturb, input=True + x_original[close_indices, :, :].detach(), x["encoder_cont"][close_indices, :, :].detach(), attribution[close_indices, :, :], att_preturb[close_indices, :, :], input=True ) ROS = relative_stability_objective( - y_pred.cpu().numpy(), y_pred_preturb.cpu().numpy(), attribution, att_preturb, input=False + y_pred[close_indices], y_pred_preturb[close_indices], attribution[close_indices, + :, :], att_preturb[close_indices, :, :], input=False ) - return np.max(RIS), np.max(ROS) + return np.max(RIS.cpu().numpy()).astype(np.float64), np.max(ROS.cpu().numpy()).astype(np.float64) @gin.configurable("MLWrapper") diff --git a/icu_benchmarks/run_utils.py b/icu_benchmarks/run_utils.py index 577607ff..c46f7b0f 100644 --- a/icu_benchmarks/run_utils.py +++ b/icu_benchmarks/run_utils.py @@ -258,7 +258,7 @@ def aggregate_results(log_dir: Path, execution_time: timedelta = None): log_dir_plots = log_dir / 'plots' if not (log_dir_plots.exists()): log_dir_plots.mkdir(parents=True) - plot_XAI_Metrics(accumulated_metrics, log_dir_plots=log_dir_plots) + # plot_XAI_Metrics(accumulated_metrics, log_dir_plots=log_dir_plots) with open(log_dir / "aggregated_test_metrics.json", "w") as f: json.dump(aggregated, f, cls=JsonResultLoggingEncoder) From a39b5c418b62fe163bbd621502f0cbabb6fdc191 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Wed, 13 Dec 2023 15:52:02 +0100 Subject: [PATCH 121/142] added condition for ROS and RIS for attention --- icu_benchmarks/models/train.py | 4 +-- icu_benchmarks/models/wrappers.py | 43 +++++++++++++++++-------------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 7097571b..b22a6438 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -285,8 +285,8 @@ def train_common( # "L": Lime, # "IG": IntegratedGradients, # "FA": FeatureAblation, - "R": "Random", - # "Att": "Attention" + # "R": "Random", + "Att": "Attention" } for key, item in methods.items(): # If conditions needed here as different explantations require different inputs diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index b3c76a40..5c0cde89 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -605,8 +605,8 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F timestep_attrs = Interpertations["attention"] features_attrs = Interpertations["static_variables"].tolist() features_attrs.extend(Interpertations["encoder_variables"].tolist()) - r_score = self.Data_Randomization(x=None, attribution=timestep_attrs, - explain_method=method, random_model=random_model, dataloader=dataloader, method_name=method_name) + """ r_score = self.Data_Randomization(x=None, attribution=timestep_attrs, + explain_method=method, random_model=random_model, dataloader=dataloader, method_name=method_name) """ st_i_score, st_o_score = self.Relative_Stability(x=None, attribution=timestep_attrs, explain_method=method, method_name=method_name, dataloader=dataloader, **kwargs ) @@ -638,11 +638,11 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F ) st_i_score.append(res1) st_o_score.append(res2) - else: + """ else: f_ts_score.append(self.Faithfulness_Correlation(x, timestep_attrs, pertrub="baseline", time_step=True, subset_size=4, nr_runs=100)) f_v_score.append(self.Faithfulness_Correlation(x, features_attrs, - pertrub="baseline", feature=True, subset_size=9, nr_runs=100)) + pertrub="baseline", feature=True, subset_size=9, nr_runs=100)) """ # Faithfulness score for attribtuons of features per timesteps f_ts_v_score = np.mean(f_ts_v_score) @@ -681,7 +681,7 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F 'Lime', 'FeatureAblation'] else torch.stack(attr).cpu().detach().numpy() if XAI_metric: - f_ts_v_score.append(self.Faithfulness_Correlation(x, stacked_attr, + """ f_ts_v_score.append(self.Faithfulness_Correlation(x, stacked_attr, pertrub="baseline", feature_timestep=True, subset_size=[4, 9], nr_runs=100)) f_ts_score.append(self.Faithfulness_Correlation(x, stacked_attr, @@ -689,7 +689,7 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F f_v_score.append(self.Faithfulness_Correlation(x, stacked_attr, pertrub="baseline", feature=True, subset_size=9, nr_runs=100)) r_score.append(self.Data_Randomization(x, attribution=stacked_attr, - explain_method=method, random_model=random_model, method_name=method_name)) + explain_method=method, random_model=random_model, method_name=method_name)) """ res1, res2 = self.Relative_Stability(x, stacked_attr, explain_method=method, method_name=method_name, dataloader=None, **kwargs ) @@ -985,7 +985,7 @@ def Data_Randomization( return score def Relative_Stability(self, x, - attribution, explain_method, method_name, dataloader=None, thershold=0.5, **kwargs + attribution, explain_method, method_name, dataloader=None, thershold=0.5, device='cuda', **kwargs ): """ Args: @@ -1008,7 +1008,7 @@ def Relative_Stability(self, x, """ def relative_stability_objective( - x, xs, e_x, e_xs, eps_min=0.0001, input=False, device='cuda' + x, xs, e_x, e_xs, close_indices, eps_min=0.0001, input=False, attention=False, device='cuda' ) -> torch.Tensor: """ Computes relative input and output stabilities maximization objective @@ -1032,11 +1032,15 @@ def relative_stability_objective( # Function to convert inputs to tensors if they are numpy arrays def to_tensor(input_array): if isinstance(input_array, np.ndarray): - return torch.tensor(input_array).to(device) - return input_array.to(device) + return torch.index_select(torch.tensor(input_array).to(device), 0, close_indices) + + return torch.index_select(input_array.to(device), 0, close_indices) # Convert all inputs to tensors and move to GPU - x, xs, e_x, e_xs = map(to_tensor, [x, xs, e_x, e_xs]) + if attention: + x, xs = map(to_tensor, [x, xs]) + else: + x, xs, e_x, e_xs = map(to_tensor, [x, xs, e_x, e_xs]) if input: num_dim = x.ndim @@ -1078,12 +1082,13 @@ def norm_function(arr): return torch.norm(arr) difference = torch.abs(y_pred_preturb - y_pred) # Find where the difference is less than or equal to a thershold - close_indices = torch.nonzero(difference <= thershold).squeeze() + close_indices = torch.nonzero(difference <= thershold).squeeze()[:, 0].to(device) + RIS = relative_stability_objective( - x_original[close_indices, :, :].detach(), x_preturb[close_indices, :, :].detach(), attribution, att_preturb, input=True + x_original.detach(), x_preturb.detach(), attribution, att_preturb, close_indices=close_indices, input=True, attention=True ) ROS = relative_stability_objective( - y_pred[close_indices], y_pred_preturb[close_indices], attribution, att_preturb, input=False + y_pred, y_pred_preturb, attribution, att_preturb, close_indices=close_indices, input=False, attention=True ) else: @@ -1115,15 +1120,15 @@ def norm_function(arr): return torch.norm(arr) difference = torch.abs(y_pred_preturb - y_pred) # Find where the difference is less than or equal to a thershold - close_indices = torch.nonzero(difference <= thershold).squeeze() - print(close_indices) + close_indices = torch.nonzero(difference <= thershold).squeeze()[:, 0].to(device) RIS = relative_stability_objective( - x_original[close_indices, :, :].detach(), x["encoder_cont"][close_indices, :, :].detach(), attribution[close_indices, :, :], att_preturb[close_indices, :, :], input=True + x_original.detach(), + x["encoder_cont"].detach(), + attribution, att_preturb, close_indices=close_indices, input=True ) ROS = relative_stability_objective( - y_pred[close_indices], y_pred_preturb[close_indices], attribution[close_indices, - :, :], att_preturb[close_indices, :, :], input=False + y_pred, y_pred_preturb, attribution, att_preturb, close_indices=close_indices, input=False ) return np.max(RIS.cpu().numpy()).astype(np.float64), np.max(ROS.cpu().numpy()).astype(np.float64) From daffaa2b41bae8078eb7de8eb1544c0508e9a32c Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Mon, 25 Dec 2023 00:25:38 +0100 Subject: [PATCH 122/142] added flags to add explantation methods and metrics --- configs/prediction_models/DeepARpytorch.gin | 2 +- configs/prediction_models/RNNpytorch.gin | 12 +++++------ configs/prediction_models/TFTpytorch.gin | 2 +- icu_benchmarks/models/train.py | 13 +++++------ icu_benchmarks/models/wrappers.py | 24 +++++++++++++-------- icu_benchmarks/run.py | 7 ++++-- icu_benchmarks/run_utils.py | 22 +++++++++++++++---- 7 files changed, 53 insertions(+), 29 deletions(-) diff --git a/configs/prediction_models/DeepARpytorch.gin b/configs/prediction_models/DeepARpytorch.gin index b093027e..bcc6abf6 100644 --- a/configs/prediction_models/DeepARpytorch.gin +++ b/configs/prediction_models/DeepARpytorch.gin @@ -14,7 +14,7 @@ optimizer/hyperparameter.lr = 0.00046 model/hyperparameter.class_to_tune = @DeepARpytorch model/hyperparameter.hidden = 116 -#(4, 64, "log-uniform", 2) +#(4, 120, "log-uniform", 2) model/hyperparameter.rnn_layers=1 model/hyperparameter.num_classes = %NUM_CLASSES diff --git a/configs/prediction_models/RNNpytorch.gin b/configs/prediction_models/RNNpytorch.gin index 050f11a5..6c0f2e15 100644 --- a/configs/prediction_models/RNNpytorch.gin +++ b/configs/prediction_models/RNNpytorch.gin @@ -8,24 +8,24 @@ train_common.model = @RNNpytorch optimizer/hyperparameter.class_to_tune = @Adam optimizer/hyperparameter.weight_decay = 1e-6 -optimizer/hyperparameter.lr = 0.00016 +optimizer/hyperparameter.lr = 0.00041 # Model params model/hyperparameter.class_to_tune = @RNNpytorch -model/hyperparameter.hidden = 42 +model/hyperparameter.hidden = 214 #(2, 64, "log-uniform", 2) -model/hyperparameter.rnn_layers=2 +model/hyperparameter.rnn_layers=1 #(1,3) model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.cell_type='LSTM' -model/hyperparameter.dropout = (0.3976, 0.3978) +model/hyperparameter.dropout = (0.244, 0.2441) #(0.0, 0.4) #model/hyperparameter.lr_scheduler = "exponential" train_common/hyperparameter.class_to_tune = @train_common -train_common/hyperparameter.batch_size=128 +train_common/hyperparameter.batch_size=256 #(32,64,128,256,512) -train_common/hyperparameter.gradient_clip_val=0.01 +train_common/hyperparameter.gradient_clip_val=100.0 #(0,0.01, 1.0, 100.0) # Dataset params PredictionDatasetpytorch.max_encoder_length = 24 diff --git a/configs/prediction_models/TFTpytorch.gin b/configs/prediction_models/TFTpytorch.gin index 19130460..c7264ed5 100644 --- a/configs/prediction_models/TFTpytorch.gin +++ b/configs/prediction_models/TFTpytorch.gin @@ -27,7 +27,7 @@ train_common/hyperparameter.class_to_tune = @train_common train_common/hyperparameter.batch_size=64 #(32,64,128,256,512) -train_common/hyperparameter.gradient_clip_val=0.0 +train_common/hyperparameter.gradient_clip_val=0.01 #(0,0.01, 1.0, 100.0) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index b22a6438..73116f46 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -25,6 +25,7 @@ PredictionDatasetpytorch, ) + from icu_benchmarks.models.utils import save_config_file, JSONMetricsLogger from icu_benchmarks.contants import RunMode from icu_benchmarks.data.constants import DataSplit as Split @@ -72,9 +73,9 @@ def train_common( torch.cuda.device_count() * 8 * int(torch.cuda.is_available()), 32, ), - explain: bool = False, + explain: list = [], pytorch_forecasting: bool = False, - XAI_metric: bool = False, + XAI_metric: list = [], random_labels: bool = False, random_model_dir: str = None, @@ -245,7 +246,7 @@ def train_common( logger=loggers, num_sanity_val_steps=-1, log_every_n_steps=5, - gradient_clip_val=gradient_clip_val + # gradient_clip_val=gradient_clip_val ) if not eval_only: if model.requires_backprop: @@ -281,11 +282,11 @@ def train_common( # choose which methods to get attributions methods = { - # "G": Saliency, + "G": Saliency, # "L": Lime, - # "IG": IntegratedGradients, + "IG": IntegratedGradients, # "FA": FeatureAblation, - # "R": "Random", + "R": "Random", "Att": "Attention" } for key, item in methods.items(): diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 5c0cde89..b97d1306 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -242,6 +242,8 @@ def __init__( input_size: Tensor = None, initialization_method: str = "normal", pytorch_forecasting: bool = False, + explain: list = [], + XAI_metric: list = [], **kwargs, ): super().__init__( @@ -262,6 +264,8 @@ def __init__( self.output_transform = None self.loss_weights = None self.pytorch_forecasting = pytorch_forecasting + self.explain = explain + self.XAI_metric = XAI_metric def set_weight(self, weight, dataset): """Set the weight for the loss function.""" @@ -605,8 +609,8 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F timestep_attrs = Interpertations["attention"] features_attrs = Interpertations["static_variables"].tolist() features_attrs.extend(Interpertations["encoder_variables"].tolist()) - """ r_score = self.Data_Randomization(x=None, attribution=timestep_attrs, - explain_method=method, random_model=random_model, dataloader=dataloader, method_name=method_name) """ + r_score = self.Data_Randomization(x=None, attribution=timestep_attrs, + explain_method=method, random_model=random_model, dataloader=dataloader, method_name=method_name) st_i_score, st_o_score = self.Relative_Stability(x=None, attribution=timestep_attrs, explain_method=method, method_name=method_name, dataloader=dataloader, **kwargs ) @@ -624,25 +628,25 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F if method_name == "Random": - """ f_ts_v_score.append(self.Faithfulness_Correlation(x, all_attrs, + f_ts_v_score.append(self.Faithfulness_Correlation(x, all_attrs, pertrub="baseline", feature_timestep=True, subset_size=[4, 9], nr_runs=100)) f_ts_score.append(self.Faithfulness_Correlation(x, all_attrs, pertrub="baseline", time_step=True, subset_size=4, nr_runs=100)) f_v_score.append(self.Faithfulness_Correlation(x, all_attrs, - pertrub="baseline", feature=True, subset_size=9, nr_runs=100)) + pertrub="baseline", feature=True, subset_size=9, nr_runs=100)) r_score.append(self.Data_Randomization(x, attribution=all_attrs, - explain_method=method, random_model=random_model, method_name=method_name))""" + explain_method=method, random_model=random_model, method_name=method_name)) res1, res2 = self.Relative_Stability(x, all_attrs, explain_method=method, method_name=method_name, dataloader=None, **kwargs ) st_i_score.append(res1) st_o_score.append(res2) - """ else: + else: f_ts_score.append(self.Faithfulness_Correlation(x, timestep_attrs, pertrub="baseline", time_step=True, subset_size=4, nr_runs=100)) f_v_score.append(self.Faithfulness_Correlation(x, features_attrs, - pertrub="baseline", feature=True, subset_size=9, nr_runs=100)) """ + pertrub="baseline", feature=True, subset_size=9, nr_runs=100)) # Faithfulness score for attribtuons of features per timesteps f_ts_v_score = np.mean(f_ts_v_score) @@ -681,7 +685,7 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F 'Lime', 'FeatureAblation'] else torch.stack(attr).cpu().detach().numpy() if XAI_metric: - """ f_ts_v_score.append(self.Faithfulness_Correlation(x, stacked_attr, + f_ts_v_score.append(self.Faithfulness_Correlation(x, stacked_attr, pertrub="baseline", feature_timestep=True, subset_size=[4, 9], nr_runs=100)) f_ts_score.append(self.Faithfulness_Correlation(x, stacked_attr, @@ -689,12 +693,14 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F f_v_score.append(self.Faithfulness_Correlation(x, stacked_attr, pertrub="baseline", feature=True, subset_size=9, nr_runs=100)) r_score.append(self.Data_Randomization(x, attribution=stacked_attr, - explain_method=method, random_model=random_model, method_name=method_name)) """ + explain_method=method, random_model=random_model, method_name=method_name)) + res1, res2 = self.Relative_Stability(x, stacked_attr, explain_method=method, method_name=method_name, dataloader=None, **kwargs ) st_i_score.append(res1) st_o_score.append(res2) + # aggregate over batch attr = np.mean(stacked_attr, axis=0) all_attrs.append(attr) diff --git a/icu_benchmarks/run.py b/icu_benchmarks/run.py index 1faadab6..bd65c935 100644 --- a/icu_benchmarks/run.py +++ b/icu_benchmarks/run.py @@ -56,6 +56,7 @@ def main(my_args=tuple(sys.argv[1:])): pytorch_forecasting = args.pytorch_forecasting XAI_metric = args.XAI_metric random_labels = args.random_labels + # Load task config gin.parse_config_file(f"configs/tasks/{task}.gin") mode = get_mode() @@ -117,7 +118,8 @@ def main(my_args=tuple(sys.argv[1:])): name_datasets(args.name, args.name, args.name) run_dir = create_run_dir(log_dir) source_dir = args.source_dir - logging.info(f"Will load weights from {source_dir} and bind train gin-config. Note: this might override your config.") + logging.info( + f"Will load weights from {source_dir} and bind train gin-config. Note: this might override your config.") gin.parse_config_file(source_dir / "train_config.gin") elif args.samples and args.source_dir is not None: # Train model with limited samples and bind existing config logging.info("Binding train gin-config. Note: this might override your config.") @@ -130,7 +132,8 @@ def main(my_args=tuple(sys.argv[1:])): name_datasets(args.name, args.name, args.name) hp_checkpoint = log_dir / args.hp_checkpoint if args.hp_checkpoint else None model_path = ( - Path("configs") / ("imputation_models" if mode == RunMode.imputation else "prediction_models") / f"{model}.gin" + Path("configs") / ("imputation_models" if mode == + RunMode.imputation else "prediction_models") / f"{model}.gin" ) gin_config_files = ( [Path(f"configs/experiments/{args.experiment}.gin")] diff --git a/icu_benchmarks/run_utils.py b/icu_benchmarks/run_utils.py index c46f7b0f..020238e4 100644 --- a/icu_benchmarks/run_utils.py +++ b/icu_benchmarks/run_utils.py @@ -123,29 +123,43 @@ def build_parser() -> ArgumentParser: default=None, help="Number of samples to use for evaluation.", ) - parser.add_argument( + """ parser.add_argument( "--explain", default=False, action=BOA, help="Provide explaintations for predictions.", - ) + ) """ parser.add_argument( "--pytorch-forecasting", default=False, action=BOA, help="use pytorch forecasting library ", ) - parser.add_argument( + """ parser.add_argument( "--XAI_metric", default=False, action=BOA, help="Compare explantations ", + ) """ + parser.add_argument( + "-ex", + "--explain", + nargs='+', # '+' means one or more arguments + type=str, # or any type you need + help="List of XAI methods to run" + ) + parser.add_argument( + "-exm", + "--XAI_metric", + nargs='+', # '+' means one or more arguments + type=str, # or any type you need + help="List of XAI metrics to run" ) parser.add_argument( "--random_labels", default=False, action=BOA, - help="train model on random label dataset", + help="randmize target labels", ) parser.add_argument( From a9ec05eaf237e0f27ae3c91b7aa5e462d28193e5 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Wed, 27 Dec 2023 19:36:59 +0100 Subject: [PATCH 123/142] changes to allow easier run --- configs/prediction_models/DeepARpytorch.gin | 68 +++- configs/prediction_models/RNNpytorch.gin | 81 +++- configs/prediction_models/TFTpytorch.gin | 2 + icu_benchmarks/cross_validation.py | 4 +- icu_benchmarks/data/loader.py | 12 +- icu_benchmarks/models/constants.py | 14 + icu_benchmarks/models/custom_metrics.py | 421 ++++++++++++++++++++ icu_benchmarks/models/dl_models.py | 22 +- icu_benchmarks/models/train.py | 46 ++- icu_benchmarks/models/wrappers.py | 248 +++++++++++- 10 files changed, 857 insertions(+), 61 deletions(-) diff --git a/configs/prediction_models/DeepARpytorch.gin b/configs/prediction_models/DeepARpytorch.gin index bcc6abf6..04d8011a 100644 --- a/configs/prediction_models/DeepARpytorch.gin +++ b/configs/prediction_models/DeepARpytorch.gin @@ -29,16 +29,63 @@ train_common/hyperparameter.gradient_clip_val=0.01 # Dataset params PredictionDatasetpytorch.max_encoder_length = 24 PredictionDatasetpytorch.max_prediction_length = 1 -PredictionDatasetpytorch.time_varying_known_reals=['alb_lag_1', 'alp_lag_1', 'alt_lag_1', 'ast_lag_1', 'be_lag_1', 'bicar_lag_1', 'bili_lag_1', 'bili_dir_lag_1', 'bnd_lag_1', 'bun_lag_1', 'ca_lag_1', 'cai_lag_1', 'ck_lag_1', 'ckmb_lag_1', 'cl_lag_1', 'crea_lag_1', 'crp_lag_1', 'dbp_lag_1', 'fgn_lag_1', 'fio2_lag_1', 'glu_lag_1', 'hgb_lag_1', 'hr_lag_1', 'inr_pt_lag_1', 'k_lag_1', 'lact_lag_1', 'lymph_lag_1', 'map_lag_1', 'mch_lag_1', 'mchc_lag_1', 'mcv_lag_1', 'methb_lag_1', 'mg_lag_1', 'na_lag_1', 'neut_lag_1', 'o2sat_lag_1', 'pco2_lag_1', 'ph_lag_1', 'phos_lag_1', 'plt_lag_1', 'po2_lag_1', 'ptt_lag_1', 'resp_lag_1', 'sbp_lag_1', 'temp_lag_1', 'tnt_lag_1', 'urine_lag_1', 'wbc_lag_1', 'alb_lag_2', 'alp_lag_2', 'alt_lag_2', 'ast_lag_2', 'be_lag_2', 'bicar_lag_2', 'bili_lag_2', 'bili_dir_lag_2', 'bnd_lag_2', 'bun_lag_2', 'ca_lag_2', 'cai_lag_2', 'ck_lag_2', 'ckmb_lag_2', 'cl_lag_2', 'crea_lag_2', 'crp_lag_2', 'dbp_lag_2', 'fgn_lag_2', 'fio2_lag_2', 'glu_lag_2', 'hgb_lag_2', 'hr_lag_2', 'inr_pt_lag_2', 'k_lag_2', 'lact_lag_2', 'lymph_lag_2', 'map_lag_2', 'mch_lag_2', 'mchc_lag_2', 'mcv_lag_2', 'methb_lag_2', 'mg_lag_2', 'na_lag_2', 'neut_lag_2', 'o2sat_lag_2', 'pco2_lag_2', 'ph_lag_2', 'phos_lag_2', 'plt_lag_2', 'po2_lag_2', 'ptt_lag_2', 'resp_lag_2', 'sbp_lag_2', 'temp_lag_2', 'tnt_lag_2', 'urine_lag_2', 'wbc_lag_2', 'alb_lag_3', 'alp_lag_3', 'alt_lag_3', 'ast_lag_3', 'be_lag_3', 'bicar_lag_3', 'bili_lag_3', 'bili_dir_lag_3', 'bnd_lag_3', 'bun_lag_3', 'ca_lag_3', 'cai_lag_3', 'ck_lag_3', 'ckmb_lag_3', 'cl_lag_3', 'crea_lag_3', 'crp_lag_3', 'dbp_lag_3', 'fgn_lag_3', 'fio2_lag_3', 'glu_lag_3', 'hgb_lag_3', 'hr_lag_3', 'inr_pt_lag_3', 'k_lag_3', 'lact_lag_3', 'lymph_lag_3', 'map_lag_3', 'mch_lag_3', 'mchc_lag_3', 'mcv_lag_3', 'methb_lag_3', 'mg_lag_3', 'na_lag_3', 'neut_lag_3', 'o2sat_lag_3', 'pco2_lag_3', 'ph_lag_3', 'phos_lag_3', 'plt_lag_3', 'po2_lag_3', 'ptt_lag_3', 'resp_lag_3', 'sbp_lag_3', 'temp_lag_3', 'tnt_lag_3', 'urine_lag_3', 'wbc_lag_3', 'alb_lag_4', 'alp_lag_4', 'alt_lag_4', 'ast_lag_4', 'be_lag_4', 'bicar_lag_4', 'bili_lag_4', 'bili_dir_lag_4', 'bnd_lag_4', 'bun_lag_4', 'ca_lag_4', 'cai_lag_4', 'ck_lag_4', 'ckmb_lag_4', 'cl_lag_4', 'crea_lag_4', 'crp_lag_4', 'dbp_lag_4', 'fgn_lag_4', 'fio2_lag_4', 'glu_lag_4', 'hgb_lag_4', 'hr_lag_4', 'inr_pt_lag_4', 'k_lag_4', 'lact_lag_4', 'lymph_lag_4', 'map_lag_4', 'mch_lag_4', 'mchc_lag_4', 'mcv_lag_4', 'methb_lag_4', 'mg_lag_4', 'na_lag_4', 'neut_lag_4', 'o2sat_lag_4', 'pco2_lag_4', 'ph_lag_4', 'phos_lag_4', 'plt_lag_4', 'po2_lag_4', 'ptt_lag_4', 'resp_lag_4', 'sbp_lag_4', 'temp_lag_4', 'tnt_lag_4', 'urine_lag_4', 'wbc_lag_4', 'alb_lag_5', 'alp_lag_5', 'alt_lag_5', 'ast_lag_5', 'be_lag_5', 'bicar_lag_5', 'bili_lag_5', 'bili_dir_lag_5', 'bnd_lag_5', 'bun_lag_5', 'ca_lag_5', 'cai_lag_5', 'ck_lag_5', 'ckmb_lag_5', 'cl_lag_5', 'crea_lag_5', 'crp_lag_5', 'dbp_lag_5', 'fgn_lag_5', 'fio2_lag_5', 'glu_lag_5', 'hgb_lag_5', 'hr_lag_5', 'inr_pt_lag_5', 'k_lag_5', 'lact_lag_5', 'lymph_lag_5', 'map_lag_5', 'mch_lag_5', 'mchc_lag_5', 'mcv_lag_5', 'methb_lag_5', 'mg_lag_5', 'na_lag_5', 'neut_lag_5', 'o2sat_lag_5', 'pco2_lag_5', 'ph_lag_5', 'phos_lag_5', 'plt_lag_5', 'po2_lag_5', 'ptt_lag_5', 'resp_lag_5', 'sbp_lag_5', 'temp_lag_5', 'tnt_lag_5', 'urine_lag_5', 'wbc_lag_5', 'alb_lag_6', 'alp_lag_6', 'alt_lag_6', 'ast_lag_6', 'be_lag_6', 'bicar_lag_6', 'bili_lag_6', 'bili_dir_lag_6', 'bnd_lag_6', 'bun_lag_6', 'ca_lag_6', 'cai_lag_6', 'ck_lag_6', 'ckmb_lag_6', 'cl_lag_6', 'crea_lag_6', 'crp_lag_6', 'dbp_lag_6', 'fgn_lag_6', 'fio2_lag_6', 'glu_lag_6', 'hgb_lag_6', 'hr_lag_6', 'inr_pt_lag_6', 'k_lag_6', 'lact_lag_6', 'lymph_lag_6', 'map_lag_6', 'mch_lag_6', 'mchc_lag_6', 'mcv_lag_6', 'methb_lag_6', 'mg_lag_6', 'na_lag_6', 'neut_lag_6', 'o2sat_lag_6', 'pco2_lag_6', 'ph_lag_6', 'phos_lag_6', 'plt_lag_6', 'po2_lag_6', 'ptt_lag_6', 'resp_lag_6', 'sbp_lag_6', 'temp_lag_6', 'tnt_lag_6', 'urine_lag_6', 'wbc_lag_6', 'alb_lag_7', 'alp_lag_7', 'alt_lag_7', 'ast_lag_7', 'be_lag_7', 'bicar_lag_7', 'bili_lag_7', 'bili_dir_lag_7', 'bnd_lag_7', 'bun_lag_7', 'ca_lag_7', 'cai_lag_7', 'ck_lag_7', 'ckmb_lag_7', 'cl_lag_7', 'crea_lag_7', 'crp_lag_7', 'dbp_lag_7', 'fgn_lag_7', 'fio2_lag_7', 'glu_lag_7', 'hgb_lag_7', 'hr_lag_7', 'inr_pt_lag_7', 'k_lag_7', 'lact_lag_7', 'lymph_lag_7', 'map_lag_7', 'mch_lag_7', 'mchc_lag_7', 'mcv_lag_7', 'methb_lag_7', 'mg_lag_7', 'na_lag_7', 'neut_lag_7', 'o2sat_lag_7', 'pco2_lag_7', 'ph_lag_7', 'phos_lag_7', 'plt_lag_7', 'po2_lag_7', 'ptt_lag_7', 'resp_lag_7', 'sbp_lag_7', 'temp_lag_7', 'tnt_lag_7', 'urine_lag_7', 'wbc_lag_7', 'alb_lag_8', 'alp_lag_8', 'alt_lag_8', 'ast_lag_8', 'be_lag_8', 'bicar_lag_8', 'bili_lag_8', 'bili_dir_lag_8', 'bnd_lag_8', 'bun_lag_8', 'ca_lag_8', 'cai_lag_8', 'ck_lag_8', 'ckmb_lag_8', 'cl_lag_8', 'crea_lag_8', 'crp_lag_8', 'dbp_lag_8', 'fgn_lag_8', 'fio2_lag_8', 'glu_lag_8', 'hgb_lag_8', 'hr_lag_8', 'inr_pt_lag_8', 'k_lag_8', 'lact_lag_8', 'lymph_lag_8', 'map_lag_8', 'mch_lag_8', 'mchc_lag_8', 'mcv_lag_8', 'methb_lag_8', 'mg_lag_8', 'na_lag_8', 'neut_lag_8', 'o2sat_lag_8', 'pco2_lag_8', 'ph_lag_8', 'phos_lag_8', 'plt_lag_8', 'po2_lag_8', 'ptt_lag_8', 'resp_lag_8', 'sbp_lag_8', 'temp_lag_8', 'tnt_lag_8', 'urine_lag_8', 'wbc_lag_8', 'alb_lag_9', 'alp_lag_9', 'alt_lag_9', 'ast_lag_9', 'be_lag_9', 'bicar_lag_9', 'bili_lag_9', 'bili_dir_lag_9', 'bnd_lag_9', 'bun_lag_9', 'ca_lag_9', 'cai_lag_9', 'ck_lag_9', 'ckmb_lag_9', 'cl_lag_9', 'crea_lag_9', 'crp_lag_9', 'dbp_lag_9', 'fgn_lag_9', 'fio2_lag_9', 'glu_lag_9', 'hgb_lag_9', 'hr_lag_9', 'inr_pt_lag_9', 'k_lag_9', 'lact_lag_9', 'lymph_lag_9', 'map_lag_9', 'mch_lag_9', 'mchc_lag_9', 'mcv_lag_9', 'methb_lag_9', 'mg_lag_9', 'na_lag_9', 'neut_lag_9', 'o2sat_lag_9', 'pco2_lag_9', 'ph_lag_9', 'phos_lag_9', 'plt_lag_9', 'po2_lag_9', 'ptt_lag_9', 'resp_lag_9', 'sbp_lag_9', 'temp_lag_9', 'tnt_lag_9', 'urine_lag_9', 'wbc_lag_9', 'alb_lag_10', 'alp_lag_10', 'alt_lag_10', 'ast_lag_10', 'be_lag_10', 'bicar_lag_10', 'bili_lag_10', 'bili_dir_lag_10', 'bnd_lag_10', 'bun_lag_10', 'ca_lag_10', 'cai_lag_10', 'ck_lag_10', 'ckmb_lag_10', 'cl_lag_10', 'crea_lag_10', 'crp_lag_10', 'dbp_lag_10', 'fgn_lag_10', 'fio2_lag_10', 'glu_lag_10', 'hgb_lag_10', 'hr_lag_10', 'inr_pt_lag_10', 'k_lag_10', 'lact_lag_10', 'lymph_lag_10', 'map_lag_10', 'mch_lag_10', 'mchc_lag_10', 'mcv_lag_10', 'methb_lag_10', 'mg_lag_10', 'na_lag_10', 'neut_lag_10', 'o2sat_lag_10', 'pco2_lag_10', 'ph_lag_10', 'phos_lag_10', 'plt_lag_10', 'po2_lag_10', 'ptt_lag_10', 'resp_lag_10', 'sbp_lag_10', 'temp_lag_10', 'tnt_lag_10', 'urine_lag_10', 'wbc_lag_10', 'alb_lag_11', 'alp_lag_11', 'alt_lag_11', 'ast_lag_11', 'be_lag_11', 'bicar_lag_11', 'bili_lag_11', 'bili_dir_lag_11', 'bnd_lag_11', 'bun_lag_11', 'ca_lag_11', 'cai_lag_11', 'ck_lag_11', 'ckmb_lag_11', 'cl_lag_11', 'crea_lag_11', 'crp_lag_11', 'dbp_lag_11', 'fgn_lag_11', 'fio2_lag_11', 'glu_lag_11', 'hgb_lag_11', 'hr_lag_11', 'inr_pt_lag_11', 'k_lag_11', 'lact_lag_11', 'lymph_lag_11', 'map_lag_11', 'mch_lag_11', 'mchc_lag_11', 'mcv_lag_11', 'methb_lag_11', 'mg_lag_11', 'na_lag_11', 'neut_lag_11', 'o2sat_lag_11', 'pco2_lag_11', 'ph_lag_11', 'phos_lag_11', 'plt_lag_11', 'po2_lag_11', 'ptt_lag_11', 'resp_lag_11', 'sbp_lag_11', 'temp_lag_11', 'tnt_lag_11', 'urine_lag_11', 'wbc_lag_11', 'alb_lag_12', 'alp_lag_12', 'alt_lag_12', 'ast_lag_12', 'be_lag_12', 'bicar_lag_12', 'bili_lag_12', 'bili_dir_lag_12', 'bnd_lag_12', 'bun_lag_12', 'ca_lag_12', 'cai_lag_12', 'ck_lag_12', 'ckmb_lag_12', 'cl_lag_12', 'crea_lag_12', 'crp_lag_12', 'dbp_lag_12', 'fgn_lag_12', 'fio2_lag_12', 'glu_lag_12', 'hgb_lag_12', 'hr_lag_12', 'inr_pt_lag_12', 'k_lag_12', 'lact_lag_12', 'lymph_lag_12', 'map_lag_12', 'mch_lag_12', 'mchc_lag_12', 'mcv_lag_12', 'methb_lag_12', 'mg_lag_12', 'na_lag_12', 'neut_lag_12', 'o2sat_lag_12', 'pco2_lag_12', 'ph_lag_12', 'phos_lag_12', 'plt_lag_12', 'po2_lag_12', 'ptt_lag_12', 'resp_lag_12', 'sbp_lag_12', 'temp_lag_12', 'tnt_lag_12', 'urine_lag_12', 'wbc_lag_12', 'alb_lag_13', 'alp_lag_13', 'alt_lag_13', 'ast_lag_13', 'be_lag_13', 'bicar_lag_13', 'bili_lag_13', 'bili_dir_lag_13', 'bnd_lag_13', 'bun_lag_13', 'ca_lag_13', 'cai_lag_13', 'ck_lag_13', 'ckmb_lag_13', 'cl_lag_13', 'crea_lag_13', 'crp_lag_13', 'dbp_lag_13', 'fgn_lag_13', 'fio2_lag_13', 'glu_lag_13', 'hgb_lag_13', 'hr_lag_13', 'inr_pt_lag_13', 'k_lag_13', 'lact_lag_13', 'lymph_lag_13', 'map_lag_13', 'mch_lag_13', 'mchc_lag_13', 'mcv_lag_13', 'methb_lag_13', 'mg_lag_13', 'na_lag_13', 'neut_lag_13', 'o2sat_lag_13', 'pco2_lag_13', 'ph_lag_13', 'phos_lag_13', 'plt_lag_13', 'po2_lag_13', 'ptt_lag_13', 'resp_lag_13', 'sbp_lag_13', 'temp_lag_13', 'tnt_lag_13', 'urine_lag_13', 'wbc_lag_13', 'alb_lag_14', 'alp_lag_14', 'alt_lag_14', 'ast_lag_14', 'be_lag_14', 'bicar_lag_14', 'bili_lag_14', 'bili_dir_lag_14', 'bnd_lag_14', 'bun_lag_14', 'ca_lag_14', 'cai_lag_14', 'ck_lag_14', 'ckmb_lag_14', 'cl_lag_14', 'crea_lag_14', 'crp_lag_14', 'dbp_lag_14', 'fgn_lag_14', 'fio2_lag_14', 'glu_lag_14', 'hgb_lag_14', 'hr_lag_14', 'inr_pt_lag_14', 'k_lag_14', 'lact_lag_14', 'lymph_lag_14', 'map_lag_14', 'mch_lag_14', 'mchc_lag_14', 'mcv_lag_14', 'methb_lag_14', 'mg_lag_14', 'na_lag_14', 'neut_lag_14', 'o2sat_lag_14', 'pco2_lag_14', 'ph_lag_14', 'phos_lag_14', 'plt_lag_14', 'po2_lag_14', 'ptt_lag_14', 'resp_lag_14', 'sbp_lag_14', 'temp_lag_14', 'tnt_lag_14', 'urine_lag_14', 'wbc_lag_14', 'alb_lag_15', 'alp_lag_15', 'alt_lag_15', 'ast_lag_15', 'be_lag_15', 'bicar_lag_15', 'bili_lag_15', 'bili_dir_lag_15', 'bnd_lag_15', 'bun_lag_15', 'ca_lag_15', 'cai_lag_15', 'ck_lag_15', 'ckmb_lag_15', 'cl_lag_15', 'crea_lag_15', 'crp_lag_15', 'dbp_lag_15', 'fgn_lag_15', 'fio2_lag_15', 'glu_lag_15', 'hgb_lag_15', 'hr_lag_15', 'inr_pt_lag_15', 'k_lag_15', 'lact_lag_15', 'lymph_lag_15', 'map_lag_15', 'mch_lag_15', 'mchc_lag_15', 'mcv_lag_15', 'methb_lag_15', 'mg_lag_15', 'na_lag_15', 'neut_lag_15', 'o2sat_lag_15', 'pco2_lag_15', 'ph_lag_15', 'phos_lag_15', 'plt_lag_15', 'po2_lag_15', 'ptt_lag_15', 'resp_lag_15', 'sbp_lag_15', 'temp_lag_15', 'tnt_lag_15', 'urine_lag_15', 'wbc_lag_15', 'alb_lag_16', 'alp_lag_16', 'alt_lag_16', 'ast_lag_16', 'be_lag_16', 'bicar_lag_16', 'bili_lag_16', 'bili_dir_lag_16', 'bnd_lag_16', 'bun_lag_16', 'ca_lag_16', 'cai_lag_16', 'ck_lag_16', 'ckmb_lag_16', 'cl_lag_16', 'crea_lag_16', 'crp_lag_16', 'dbp_lag_16', 'fgn_lag_16', 'fio2_lag_16', 'glu_lag_16', 'hgb_lag_16', 'hr_lag_16', 'inr_pt_lag_16', 'k_lag_16', 'lact_lag_16', 'lymph_lag_16', 'map_lag_16', 'mch_lag_16', 'mchc_lag_16', 'mcv_lag_16', 'methb_lag_16', 'mg_lag_16', 'na_lag_16', 'neut_lag_16', 'o2sat_lag_16', 'pco2_lag_16', 'ph_lag_16', 'phos_lag_16', 'plt_lag_16', 'po2_lag_16', 'ptt_lag_16', 'resp_lag_16', 'sbp_lag_16', 'temp_lag_16', 'tnt_lag_16', 'urine_lag_16', 'wbc_lag_16', 'alb_lag_17', 'alp_lag_17', 'alt_lag_17', 'ast_lag_17', 'be_lag_17', 'bicar_lag_17', 'bili_lag_17', 'bili_dir_lag_17', 'bnd_lag_17', 'bun_lag_17', 'ca_lag_17', 'cai_lag_17', 'ck_lag_17', 'ckmb_lag_17', 'cl_lag_17', 'crea_lag_17', 'crp_lag_17', 'dbp_lag_17', 'fgn_lag_17', 'fio2_lag_17', 'glu_lag_17', 'hgb_lag_17', 'hr_lag_17', 'inr_pt_lag_17', 'k_lag_17', 'lact_lag_17', 'lymph_lag_17', 'map_lag_17', 'mch_lag_17', 'mchc_lag_17', 'mcv_lag_17', 'methb_lag_17', 'mg_lag_17', 'na_lag_17', 'neut_lag_17', 'o2sat_lag_17', 'pco2_lag_17', 'ph_lag_17', 'phos_lag_17', 'plt_lag_17', 'po2_lag_17', 'ptt_lag_17', 'resp_lag_17', 'sbp_lag_17', 'temp_lag_17', 'tnt_lag_17', 'urine_lag_17', 'wbc_lag_17', 'alb_lag_18', 'alp_lag_18', 'alt_lag_18', 'ast_lag_18', 'be_lag_18', 'bicar_lag_18', 'bili_lag_18', 'bili_dir_lag_18', 'bnd_lag_18', 'bun_lag_18', 'ca_lag_18', 'cai_lag_18', 'ck_lag_18', 'ckmb_lag_18', 'cl_lag_18', 'crea_lag_18', 'crp_lag_18', 'dbp_lag_18', 'fgn_lag_18', 'fio2_lag_18', 'glu_lag_18', 'hgb_lag_18', 'hr_lag_18', 'inr_pt_lag_18', 'k_lag_18', 'lact_lag_18', 'lymph_lag_18', 'map_lag_18', 'mch_lag_18', 'mchc_lag_18', 'mcv_lag_18', 'methb_lag_18', 'mg_lag_18', 'na_lag_18', 'neut_lag_18', 'o2sat_lag_18', 'pco2_lag_18', 'ph_lag_18', 'phos_lag_18', 'plt_lag_18', 'po2_lag_18', 'ptt_lag_18', 'resp_lag_18', 'sbp_lag_18', 'temp_lag_18', 'tnt_lag_18', 'urine_lag_18', 'wbc_lag_18', 'alb_lag_19', 'alp_lag_19', 'alt_lag_19', 'ast_lag_19', 'be_lag_19', 'bicar_lag_19', 'bili_lag_19', 'bili_dir_lag_19', 'bnd_lag_19', 'bun_lag_19', 'ca_lag_19', 'cai_lag_19', 'ck_lag_19', 'ckmb_lag_19', 'cl_lag_19', 'crea_lag_19', 'crp_lag_19', 'dbp_lag_19', 'fgn_lag_19', 'fio2_lag_19', 'glu_lag_19', 'hgb_lag_19', 'hr_lag_19', 'inr_pt_lag_19', 'k_lag_19', 'lact_lag_19', 'lymph_lag_19', 'map_lag_19', 'mch_lag_19', 'mchc_lag_19', 'mcv_lag_19', 'methb_lag_19', 'mg_lag_19', 'na_lag_19', 'neut_lag_19', 'o2sat_lag_19', 'pco2_lag_19', 'ph_lag_19', 'phos_lag_19', 'plt_lag_19', 'po2_lag_19', 'ptt_lag_19', 'resp_lag_19', 'sbp_lag_19', 'temp_lag_19', 'tnt_lag_19', 'urine_lag_19', 'wbc_lag_19', 'alb_lag_20', 'alp_lag_20', 'alt_lag_20', 'ast_lag_20', 'be_lag_20', 'bicar_lag_20', 'bili_lag_20', 'bili_dir_lag_20', 'bnd_lag_20', 'bun_lag_20', 'ca_lag_20', 'cai_lag_20', 'ck_lag_20', 'ckmb_lag_20', 'cl_lag_20', 'crea_lag_20', 'crp_lag_20', 'dbp_lag_20', 'fgn_lag_20', 'fio2_lag_20', 'glu_lag_20', 'hgb_lag_20', 'hr_lag_20', 'inr_pt_lag_20', 'k_lag_20', 'lact_lag_20', 'lymph_lag_20', 'map_lag_20', 'mch_lag_20', 'mchc_lag_20', 'mcv_lag_20', 'methb_lag_20', 'mg_lag_20', 'na_lag_20', 'neut_lag_20', 'o2sat_lag_20', 'pco2_lag_20', 'ph_lag_20', 'phos_lag_20', 'plt_lag_20', 'po2_lag_20', 'ptt_lag_20', 'resp_lag_20', 'sbp_lag_20', 'temp_lag_20', 'tnt_lag_20', 'urine_lag_20', 'wbc_lag_20', 'alb_lag_21', 'alp_lag_21', 'alt_lag_21', 'ast_lag_21', 'be_lag_21', 'bicar_lag_21', 'bili_lag_21', 'bili_dir_lag_21', 'bnd_lag_21', 'bun_lag_21', 'ca_lag_21', 'cai_lag_21', 'ck_lag_21', 'ckmb_lag_21', 'cl_lag_21', 'crea_lag_21', 'crp_lag_21', 'dbp_lag_21', 'fgn_lag_21', 'fio2_lag_21', 'glu_lag_21', 'hgb_lag_21', 'hr_lag_21', 'inr_pt_lag_21', 'k_lag_21', 'lact_lag_21', 'lymph_lag_21', 'map_lag_21', 'mch_lag_21', 'mchc_lag_21', 'mcv_lag_21', 'methb_lag_21', 'mg_lag_21', 'na_lag_21', 'neut_lag_21', 'o2sat_lag_21', 'pco2_lag_21', 'ph_lag_21', 'phos_lag_21', 'plt_lag_21', 'po2_lag_21', 'ptt_lag_21', 'resp_lag_21', 'sbp_lag_21', 'temp_lag_21', 'tnt_lag_21', 'urine_lag_21', 'wbc_lag_21', 'alb_lag_22', 'alp_lag_22', 'alt_lag_22', 'ast_lag_22', 'be_lag_22', 'bicar_lag_22', 'bili_lag_22', 'bili_dir_lag_22', 'bnd_lag_22', 'bun_lag_22', 'ca_lag_22', 'cai_lag_22', 'ck_lag_22', 'ckmb_lag_22', 'cl_lag_22', 'crea_lag_22', 'crp_lag_22', 'dbp_lag_22', 'fgn_lag_22', 'fio2_lag_22', 'glu_lag_22', 'hgb_lag_22', 'hr_lag_22', 'inr_pt_lag_22', 'k_lag_22', 'lact_lag_22', 'lymph_lag_22', 'map_lag_22', 'mch_lag_22', 'mchc_lag_22', 'mcv_lag_22', 'methb_lag_22', 'mg_lag_22', 'na_lag_22', 'neut_lag_22', 'o2sat_lag_22', 'pco2_lag_22', 'ph_lag_22', 'phos_lag_22', 'plt_lag_22', 'po2_lag_22', 'ptt_lag_22', 'resp_lag_22', 'sbp_lag_22', 'temp_lag_22', 'tnt_lag_22', 'urine_lag_22', 'wbc_lag_22', 'alb_lag_23', 'alp_lag_23', 'alt_lag_23', 'ast_lag_23', 'be_lag_23', 'bicar_lag_23', 'bili_lag_23', 'bili_dir_lag_23', 'bnd_lag_23', 'bun_lag_23', 'ca_lag_23', 'cai_lag_23', 'ck_lag_23', 'ckmb_lag_23', 'cl_lag_23', 'crea_lag_23', 'crp_lag_23', 'dbp_lag_23', 'fgn_lag_23', 'fio2_lag_23', 'glu_lag_23', 'hgb_lag_23', 'hr_lag_23', 'inr_pt_lag_23', 'k_lag_23', 'lact_lag_23', 'lymph_lag_23', 'map_lag_23', 'mch_lag_23', 'mchc_lag_23', 'mcv_lag_23', 'methb_lag_23', 'mg_lag_23', 'na_lag_23', 'neut_lag_23', 'o2sat_lag_23', 'pco2_lag_23', 'ph_lag_23', 'phos_lag_23', 'plt_lag_23', 'po2_lag_23', 'ptt_lag_23', 'resp_lag_23', 'sbp_lag_23', 'temp_lag_23', 'tnt_lag_23', 'urine_lag_23', 'wbc_lag_23', 'alb_lag_24', 'alp_lag_24', 'alt_lag_24', 'ast_lag_24', 'be_lag_24', 'bicar_lag_24', 'bili_lag_24', 'bili_dir_lag_24', 'bnd_lag_24', 'bun_lag_24', 'ca_lag_24', 'cai_lag_24', 'ck_lag_24', 'ckmb_lag_24', 'cl_lag_24', 'crea_lag_24', 'crp_lag_24', 'dbp_lag_24', 'fgn_lag_24', 'fio2_lag_24', 'glu_lag_24', 'hgb_lag_24', 'hr_lag_24', 'inr_pt_lag_24', 'k_lag_24', 'lact_lag_24', 'lymph_lag_24', 'map_lag_24', 'mch_lag_24', 'mchc_lag_24', 'mcv_lag_24', 'methb_lag_24', 'mg_lag_24', 'na_lag_24', 'neut_lag_24', 'o2sat_lag_24', 'pco2_lag_24', 'ph_lag_24', 'phos_lag_24', 'plt_lag_24', 'po2_lag_24', 'ptt_lag_24', 'resp_lag_24', 'sbp_lag_24', 'temp_lag_24', 'tnt_lag_24', 'urine_lag_24', 'wbc_lag_24' ] PredictionDatasetpytorch.add_relative_time_idx=False -PredictionDatasetpytorch.target="label" - -PredictionDatasetpytorch.time_varying_unknown_reals=[ +PredictionDatasetpytorch.target=[ + "alb", + "alp", + "alt", + "ast", + "be", + "bicar", + "bili", + "bili_dir", + "bnd", + "bun", + "ca", + "cai", + "ck", + "ckmb", + "cl", + "crea", + "crp", + "dbp", + "fgn", + "fio2", + "glu", + "hgb", + "hr", + "inr_pt", + "k", + "lact", + "lymph", + "map", + "mch", + "mchc", + "mcv", + "methb", + "mg", + "na", + "neut", + "o2sat", + "pco2", + "ph", + "phos", + "plt", + "po2", + "ptt", + "resp", + "sbp", + "temp", + "tnt", + "urine", + "wbc", "label", ] -PredictionDatasetpytorch.time_varying_unknown_categoricals=[] -PredictionDatasetpytorch.lagged_variables=[ "alb", + +PredictionDatasetpytorch.time_varying_known_reals=[] +PredictionDatasetpytorch.time_varying_unknown_reals=[ + "alb", "alp", "alt", "ast", @@ -85,6 +132,11 @@ PredictionDatasetpytorch.lagged_variables=[ "alb", "temp", "tnt", "urine", - "wbc"] + "wbc", + "label", + + ] +PredictionDatasetpytorch.time_varying_unknown_categoricals=[] +PredictionDatasetpytorch.lagged_variables=[] - \ No newline at end of file +PredictionDatasetpytorch.target_normalizer='multi' \ No newline at end of file diff --git a/configs/prediction_models/RNNpytorch.gin b/configs/prediction_models/RNNpytorch.gin index 6c0f2e15..a1804a1d 100644 --- a/configs/prediction_models/RNNpytorch.gin +++ b/configs/prediction_models/RNNpytorch.gin @@ -5,42 +5,89 @@ include "configs/prediction_models/common/DLCommon.gin" # Optimizer params train_common.model = @RNNpytorch - optimizer/hyperparameter.class_to_tune = @Adam optimizer/hyperparameter.weight_decay = 1e-6 -optimizer/hyperparameter.lr = 0.00041 +optimizer/hyperparameter.lr = 4.4815375287835764e-05 # Model params model/hyperparameter.class_to_tune = @RNNpytorch -model/hyperparameter.hidden = 214 +model/hyperparameter.hidden = 42 #(2, 64, "log-uniform", 2) -model/hyperparameter.rnn_layers=1 +model/hyperparameter.rnn_layers=2 #(1,3) model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.cell_type='LSTM' -model/hyperparameter.dropout = (0.244, 0.2441) +model/hyperparameter.dropout = (0.397, 0.3971) #(0.0, 0.4) #model/hyperparameter.lr_scheduler = "exponential" train_common/hyperparameter.class_to_tune = @train_common -train_common/hyperparameter.batch_size=256 +train_common/hyperparameter.batch_size=128 #(32,64,128,256,512) -train_common/hyperparameter.gradient_clip_val=100.0 +train_common/hyperparameter.gradient_clip_val=0.01 #(0,0.01, 1.0, 100.0) # Dataset params PredictionDatasetpytorch.max_encoder_length = 24 PredictionDatasetpytorch.max_prediction_length = 1 -PredictionDatasetpytorch.time_varying_known_reals=['alb_lag_1', 'alp_lag_1', 'alt_lag_1', 'ast_lag_1', 'be_lag_1', 'bicar_lag_1', 'bili_lag_1', 'bili_dir_lag_1', 'bnd_lag_1', 'bun_lag_1', 'ca_lag_1', 'cai_lag_1', 'ck_lag_1', 'ckmb_lag_1', 'cl_lag_1', 'crea_lag_1', 'crp_lag_1', 'dbp_lag_1', 'fgn_lag_1', 'fio2_lag_1', 'glu_lag_1', 'hgb_lag_1', 'hr_lag_1', 'inr_pt_lag_1', 'k_lag_1', 'lact_lag_1', 'lymph_lag_1', 'map_lag_1', 'mch_lag_1', 'mchc_lag_1', 'mcv_lag_1', 'methb_lag_1', 'mg_lag_1', 'na_lag_1', 'neut_lag_1', 'o2sat_lag_1', 'pco2_lag_1', 'ph_lag_1', 'phos_lag_1', 'plt_lag_1', 'po2_lag_1', 'ptt_lag_1', 'resp_lag_1', 'sbp_lag_1', 'temp_lag_1', 'tnt_lag_1', 'urine_lag_1', 'wbc_lag_1', 'alb_lag_2', 'alp_lag_2', 'alt_lag_2', 'ast_lag_2', 'be_lag_2', 'bicar_lag_2', 'bili_lag_2', 'bili_dir_lag_2', 'bnd_lag_2', 'bun_lag_2', 'ca_lag_2', 'cai_lag_2', 'ck_lag_2', 'ckmb_lag_2', 'cl_lag_2', 'crea_lag_2', 'crp_lag_2', 'dbp_lag_2', 'fgn_lag_2', 'fio2_lag_2', 'glu_lag_2', 'hgb_lag_2', 'hr_lag_2', 'inr_pt_lag_2', 'k_lag_2', 'lact_lag_2', 'lymph_lag_2', 'map_lag_2', 'mch_lag_2', 'mchc_lag_2', 'mcv_lag_2', 'methb_lag_2', 'mg_lag_2', 'na_lag_2', 'neut_lag_2', 'o2sat_lag_2', 'pco2_lag_2', 'ph_lag_2', 'phos_lag_2', 'plt_lag_2', 'po2_lag_2', 'ptt_lag_2', 'resp_lag_2', 'sbp_lag_2', 'temp_lag_2', 'tnt_lag_2', 'urine_lag_2', 'wbc_lag_2', 'alb_lag_3', 'alp_lag_3', 'alt_lag_3', 'ast_lag_3', 'be_lag_3', 'bicar_lag_3', 'bili_lag_3', 'bili_dir_lag_3', 'bnd_lag_3', 'bun_lag_3', 'ca_lag_3', 'cai_lag_3', 'ck_lag_3', 'ckmb_lag_3', 'cl_lag_3', 'crea_lag_3', 'crp_lag_3', 'dbp_lag_3', 'fgn_lag_3', 'fio2_lag_3', 'glu_lag_3', 'hgb_lag_3', 'hr_lag_3', 'inr_pt_lag_3', 'k_lag_3', 'lact_lag_3', 'lymph_lag_3', 'map_lag_3', 'mch_lag_3', 'mchc_lag_3', 'mcv_lag_3', 'methb_lag_3', 'mg_lag_3', 'na_lag_3', 'neut_lag_3', 'o2sat_lag_3', 'pco2_lag_3', 'ph_lag_3', 'phos_lag_3', 'plt_lag_3', 'po2_lag_3', 'ptt_lag_3', 'resp_lag_3', 'sbp_lag_3', 'temp_lag_3', 'tnt_lag_3', 'urine_lag_3', 'wbc_lag_3', 'alb_lag_4', 'alp_lag_4', 'alt_lag_4', 'ast_lag_4', 'be_lag_4', 'bicar_lag_4', 'bili_lag_4', 'bili_dir_lag_4', 'bnd_lag_4', 'bun_lag_4', 'ca_lag_4', 'cai_lag_4', 'ck_lag_4', 'ckmb_lag_4', 'cl_lag_4', 'crea_lag_4', 'crp_lag_4', 'dbp_lag_4', 'fgn_lag_4', 'fio2_lag_4', 'glu_lag_4', 'hgb_lag_4', 'hr_lag_4', 'inr_pt_lag_4', 'k_lag_4', 'lact_lag_4', 'lymph_lag_4', 'map_lag_4', 'mch_lag_4', 'mchc_lag_4', 'mcv_lag_4', 'methb_lag_4', 'mg_lag_4', 'na_lag_4', 'neut_lag_4', 'o2sat_lag_4', 'pco2_lag_4', 'ph_lag_4', 'phos_lag_4', 'plt_lag_4', 'po2_lag_4', 'ptt_lag_4', 'resp_lag_4', 'sbp_lag_4', 'temp_lag_4', 'tnt_lag_4', 'urine_lag_4', 'wbc_lag_4', 'alb_lag_5', 'alp_lag_5', 'alt_lag_5', 'ast_lag_5', 'be_lag_5', 'bicar_lag_5', 'bili_lag_5', 'bili_dir_lag_5', 'bnd_lag_5', 'bun_lag_5', 'ca_lag_5', 'cai_lag_5', 'ck_lag_5', 'ckmb_lag_5', 'cl_lag_5', 'crea_lag_5', 'crp_lag_5', 'dbp_lag_5', 'fgn_lag_5', 'fio2_lag_5', 'glu_lag_5', 'hgb_lag_5', 'hr_lag_5', 'inr_pt_lag_5', 'k_lag_5', 'lact_lag_5', 'lymph_lag_5', 'map_lag_5', 'mch_lag_5', 'mchc_lag_5', 'mcv_lag_5', 'methb_lag_5', 'mg_lag_5', 'na_lag_5', 'neut_lag_5', 'o2sat_lag_5', 'pco2_lag_5', 'ph_lag_5', 'phos_lag_5', 'plt_lag_5', 'po2_lag_5', 'ptt_lag_5', 'resp_lag_5', 'sbp_lag_5', 'temp_lag_5', 'tnt_lag_5', 'urine_lag_5', 'wbc_lag_5', 'alb_lag_6', 'alp_lag_6', 'alt_lag_6', 'ast_lag_6', 'be_lag_6', 'bicar_lag_6', 'bili_lag_6', 'bili_dir_lag_6', 'bnd_lag_6', 'bun_lag_6', 'ca_lag_6', 'cai_lag_6', 'ck_lag_6', 'ckmb_lag_6', 'cl_lag_6', 'crea_lag_6', 'crp_lag_6', 'dbp_lag_6', 'fgn_lag_6', 'fio2_lag_6', 'glu_lag_6', 'hgb_lag_6', 'hr_lag_6', 'inr_pt_lag_6', 'k_lag_6', 'lact_lag_6', 'lymph_lag_6', 'map_lag_6', 'mch_lag_6', 'mchc_lag_6', 'mcv_lag_6', 'methb_lag_6', 'mg_lag_6', 'na_lag_6', 'neut_lag_6', 'o2sat_lag_6', 'pco2_lag_6', 'ph_lag_6', 'phos_lag_6', 'plt_lag_6', 'po2_lag_6', 'ptt_lag_6', 'resp_lag_6', 'sbp_lag_6', 'temp_lag_6', 'tnt_lag_6', 'urine_lag_6', 'wbc_lag_6', 'alb_lag_7', 'alp_lag_7', 'alt_lag_7', 'ast_lag_7', 'be_lag_7', 'bicar_lag_7', 'bili_lag_7', 'bili_dir_lag_7', 'bnd_lag_7', 'bun_lag_7', 'ca_lag_7', 'cai_lag_7', 'ck_lag_7', 'ckmb_lag_7', 'cl_lag_7', 'crea_lag_7', 'crp_lag_7', 'dbp_lag_7', 'fgn_lag_7', 'fio2_lag_7', 'glu_lag_7', 'hgb_lag_7', 'hr_lag_7', 'inr_pt_lag_7', 'k_lag_7', 'lact_lag_7', 'lymph_lag_7', 'map_lag_7', 'mch_lag_7', 'mchc_lag_7', 'mcv_lag_7', 'methb_lag_7', 'mg_lag_7', 'na_lag_7', 'neut_lag_7', 'o2sat_lag_7', 'pco2_lag_7', 'ph_lag_7', 'phos_lag_7', 'plt_lag_7', 'po2_lag_7', 'ptt_lag_7', 'resp_lag_7', 'sbp_lag_7', 'temp_lag_7', 'tnt_lag_7', 'urine_lag_7', 'wbc_lag_7', 'alb_lag_8', 'alp_lag_8', 'alt_lag_8', 'ast_lag_8', 'be_lag_8', 'bicar_lag_8', 'bili_lag_8', 'bili_dir_lag_8', 'bnd_lag_8', 'bun_lag_8', 'ca_lag_8', 'cai_lag_8', 'ck_lag_8', 'ckmb_lag_8', 'cl_lag_8', 'crea_lag_8', 'crp_lag_8', 'dbp_lag_8', 'fgn_lag_8', 'fio2_lag_8', 'glu_lag_8', 'hgb_lag_8', 'hr_lag_8', 'inr_pt_lag_8', 'k_lag_8', 'lact_lag_8', 'lymph_lag_8', 'map_lag_8', 'mch_lag_8', 'mchc_lag_8', 'mcv_lag_8', 'methb_lag_8', 'mg_lag_8', 'na_lag_8', 'neut_lag_8', 'o2sat_lag_8', 'pco2_lag_8', 'ph_lag_8', 'phos_lag_8', 'plt_lag_8', 'po2_lag_8', 'ptt_lag_8', 'resp_lag_8', 'sbp_lag_8', 'temp_lag_8', 'tnt_lag_8', 'urine_lag_8', 'wbc_lag_8', 'alb_lag_9', 'alp_lag_9', 'alt_lag_9', 'ast_lag_9', 'be_lag_9', 'bicar_lag_9', 'bili_lag_9', 'bili_dir_lag_9', 'bnd_lag_9', 'bun_lag_9', 'ca_lag_9', 'cai_lag_9', 'ck_lag_9', 'ckmb_lag_9', 'cl_lag_9', 'crea_lag_9', 'crp_lag_9', 'dbp_lag_9', 'fgn_lag_9', 'fio2_lag_9', 'glu_lag_9', 'hgb_lag_9', 'hr_lag_9', 'inr_pt_lag_9', 'k_lag_9', 'lact_lag_9', 'lymph_lag_9', 'map_lag_9', 'mch_lag_9', 'mchc_lag_9', 'mcv_lag_9', 'methb_lag_9', 'mg_lag_9', 'na_lag_9', 'neut_lag_9', 'o2sat_lag_9', 'pco2_lag_9', 'ph_lag_9', 'phos_lag_9', 'plt_lag_9', 'po2_lag_9', 'ptt_lag_9', 'resp_lag_9', 'sbp_lag_9', 'temp_lag_9', 'tnt_lag_9', 'urine_lag_9', 'wbc_lag_9', 'alb_lag_10', 'alp_lag_10', 'alt_lag_10', 'ast_lag_10', 'be_lag_10', 'bicar_lag_10', 'bili_lag_10', 'bili_dir_lag_10', 'bnd_lag_10', 'bun_lag_10', 'ca_lag_10', 'cai_lag_10', 'ck_lag_10', 'ckmb_lag_10', 'cl_lag_10', 'crea_lag_10', 'crp_lag_10', 'dbp_lag_10', 'fgn_lag_10', 'fio2_lag_10', 'glu_lag_10', 'hgb_lag_10', 'hr_lag_10', 'inr_pt_lag_10', 'k_lag_10', 'lact_lag_10', 'lymph_lag_10', 'map_lag_10', 'mch_lag_10', 'mchc_lag_10', 'mcv_lag_10', 'methb_lag_10', 'mg_lag_10', 'na_lag_10', 'neut_lag_10', 'o2sat_lag_10', 'pco2_lag_10', 'ph_lag_10', 'phos_lag_10', 'plt_lag_10', 'po2_lag_10', 'ptt_lag_10', 'resp_lag_10', 'sbp_lag_10', 'temp_lag_10', 'tnt_lag_10', 'urine_lag_10', 'wbc_lag_10', 'alb_lag_11', 'alp_lag_11', 'alt_lag_11', 'ast_lag_11', 'be_lag_11', 'bicar_lag_11', 'bili_lag_11', 'bili_dir_lag_11', 'bnd_lag_11', 'bun_lag_11', 'ca_lag_11', 'cai_lag_11', 'ck_lag_11', 'ckmb_lag_11', 'cl_lag_11', 'crea_lag_11', 'crp_lag_11', 'dbp_lag_11', 'fgn_lag_11', 'fio2_lag_11', 'glu_lag_11', 'hgb_lag_11', 'hr_lag_11', 'inr_pt_lag_11', 'k_lag_11', 'lact_lag_11', 'lymph_lag_11', 'map_lag_11', 'mch_lag_11', 'mchc_lag_11', 'mcv_lag_11', 'methb_lag_11', 'mg_lag_11', 'na_lag_11', 'neut_lag_11', 'o2sat_lag_11', 'pco2_lag_11', 'ph_lag_11', 'phos_lag_11', 'plt_lag_11', 'po2_lag_11', 'ptt_lag_11', 'resp_lag_11', 'sbp_lag_11', 'temp_lag_11', 'tnt_lag_11', 'urine_lag_11', 'wbc_lag_11', 'alb_lag_12', 'alp_lag_12', 'alt_lag_12', 'ast_lag_12', 'be_lag_12', 'bicar_lag_12', 'bili_lag_12', 'bili_dir_lag_12', 'bnd_lag_12', 'bun_lag_12', 'ca_lag_12', 'cai_lag_12', 'ck_lag_12', 'ckmb_lag_12', 'cl_lag_12', 'crea_lag_12', 'crp_lag_12', 'dbp_lag_12', 'fgn_lag_12', 'fio2_lag_12', 'glu_lag_12', 'hgb_lag_12', 'hr_lag_12', 'inr_pt_lag_12', 'k_lag_12', 'lact_lag_12', 'lymph_lag_12', 'map_lag_12', 'mch_lag_12', 'mchc_lag_12', 'mcv_lag_12', 'methb_lag_12', 'mg_lag_12', 'na_lag_12', 'neut_lag_12', 'o2sat_lag_12', 'pco2_lag_12', 'ph_lag_12', 'phos_lag_12', 'plt_lag_12', 'po2_lag_12', 'ptt_lag_12', 'resp_lag_12', 'sbp_lag_12', 'temp_lag_12', 'tnt_lag_12', 'urine_lag_12', 'wbc_lag_12', 'alb_lag_13', 'alp_lag_13', 'alt_lag_13', 'ast_lag_13', 'be_lag_13', 'bicar_lag_13', 'bili_lag_13', 'bili_dir_lag_13', 'bnd_lag_13', 'bun_lag_13', 'ca_lag_13', 'cai_lag_13', 'ck_lag_13', 'ckmb_lag_13', 'cl_lag_13', 'crea_lag_13', 'crp_lag_13', 'dbp_lag_13', 'fgn_lag_13', 'fio2_lag_13', 'glu_lag_13', 'hgb_lag_13', 'hr_lag_13', 'inr_pt_lag_13', 'k_lag_13', 'lact_lag_13', 'lymph_lag_13', 'map_lag_13', 'mch_lag_13', 'mchc_lag_13', 'mcv_lag_13', 'methb_lag_13', 'mg_lag_13', 'na_lag_13', 'neut_lag_13', 'o2sat_lag_13', 'pco2_lag_13', 'ph_lag_13', 'phos_lag_13', 'plt_lag_13', 'po2_lag_13', 'ptt_lag_13', 'resp_lag_13', 'sbp_lag_13', 'temp_lag_13', 'tnt_lag_13', 'urine_lag_13', 'wbc_lag_13', 'alb_lag_14', 'alp_lag_14', 'alt_lag_14', 'ast_lag_14', 'be_lag_14', 'bicar_lag_14', 'bili_lag_14', 'bili_dir_lag_14', 'bnd_lag_14', 'bun_lag_14', 'ca_lag_14', 'cai_lag_14', 'ck_lag_14', 'ckmb_lag_14', 'cl_lag_14', 'crea_lag_14', 'crp_lag_14', 'dbp_lag_14', 'fgn_lag_14', 'fio2_lag_14', 'glu_lag_14', 'hgb_lag_14', 'hr_lag_14', 'inr_pt_lag_14', 'k_lag_14', 'lact_lag_14', 'lymph_lag_14', 'map_lag_14', 'mch_lag_14', 'mchc_lag_14', 'mcv_lag_14', 'methb_lag_14', 'mg_lag_14', 'na_lag_14', 'neut_lag_14', 'o2sat_lag_14', 'pco2_lag_14', 'ph_lag_14', 'phos_lag_14', 'plt_lag_14', 'po2_lag_14', 'ptt_lag_14', 'resp_lag_14', 'sbp_lag_14', 'temp_lag_14', 'tnt_lag_14', 'urine_lag_14', 'wbc_lag_14', 'alb_lag_15', 'alp_lag_15', 'alt_lag_15', 'ast_lag_15', 'be_lag_15', 'bicar_lag_15', 'bili_lag_15', 'bili_dir_lag_15', 'bnd_lag_15', 'bun_lag_15', 'ca_lag_15', 'cai_lag_15', 'ck_lag_15', 'ckmb_lag_15', 'cl_lag_15', 'crea_lag_15', 'crp_lag_15', 'dbp_lag_15', 'fgn_lag_15', 'fio2_lag_15', 'glu_lag_15', 'hgb_lag_15', 'hr_lag_15', 'inr_pt_lag_15', 'k_lag_15', 'lact_lag_15', 'lymph_lag_15', 'map_lag_15', 'mch_lag_15', 'mchc_lag_15', 'mcv_lag_15', 'methb_lag_15', 'mg_lag_15', 'na_lag_15', 'neut_lag_15', 'o2sat_lag_15', 'pco2_lag_15', 'ph_lag_15', 'phos_lag_15', 'plt_lag_15', 'po2_lag_15', 'ptt_lag_15', 'resp_lag_15', 'sbp_lag_15', 'temp_lag_15', 'tnt_lag_15', 'urine_lag_15', 'wbc_lag_15', 'alb_lag_16', 'alp_lag_16', 'alt_lag_16', 'ast_lag_16', 'be_lag_16', 'bicar_lag_16', 'bili_lag_16', 'bili_dir_lag_16', 'bnd_lag_16', 'bun_lag_16', 'ca_lag_16', 'cai_lag_16', 'ck_lag_16', 'ckmb_lag_16', 'cl_lag_16', 'crea_lag_16', 'crp_lag_16', 'dbp_lag_16', 'fgn_lag_16', 'fio2_lag_16', 'glu_lag_16', 'hgb_lag_16', 'hr_lag_16', 'inr_pt_lag_16', 'k_lag_16', 'lact_lag_16', 'lymph_lag_16', 'map_lag_16', 'mch_lag_16', 'mchc_lag_16', 'mcv_lag_16', 'methb_lag_16', 'mg_lag_16', 'na_lag_16', 'neut_lag_16', 'o2sat_lag_16', 'pco2_lag_16', 'ph_lag_16', 'phos_lag_16', 'plt_lag_16', 'po2_lag_16', 'ptt_lag_16', 'resp_lag_16', 'sbp_lag_16', 'temp_lag_16', 'tnt_lag_16', 'urine_lag_16', 'wbc_lag_16', 'alb_lag_17', 'alp_lag_17', 'alt_lag_17', 'ast_lag_17', 'be_lag_17', 'bicar_lag_17', 'bili_lag_17', 'bili_dir_lag_17', 'bnd_lag_17', 'bun_lag_17', 'ca_lag_17', 'cai_lag_17', 'ck_lag_17', 'ckmb_lag_17', 'cl_lag_17', 'crea_lag_17', 'crp_lag_17', 'dbp_lag_17', 'fgn_lag_17', 'fio2_lag_17', 'glu_lag_17', 'hgb_lag_17', 'hr_lag_17', 'inr_pt_lag_17', 'k_lag_17', 'lact_lag_17', 'lymph_lag_17', 'map_lag_17', 'mch_lag_17', 'mchc_lag_17', 'mcv_lag_17', 'methb_lag_17', 'mg_lag_17', 'na_lag_17', 'neut_lag_17', 'o2sat_lag_17', 'pco2_lag_17', 'ph_lag_17', 'phos_lag_17', 'plt_lag_17', 'po2_lag_17', 'ptt_lag_17', 'resp_lag_17', 'sbp_lag_17', 'temp_lag_17', 'tnt_lag_17', 'urine_lag_17', 'wbc_lag_17', 'alb_lag_18', 'alp_lag_18', 'alt_lag_18', 'ast_lag_18', 'be_lag_18', 'bicar_lag_18', 'bili_lag_18', 'bili_dir_lag_18', 'bnd_lag_18', 'bun_lag_18', 'ca_lag_18', 'cai_lag_18', 'ck_lag_18', 'ckmb_lag_18', 'cl_lag_18', 'crea_lag_18', 'crp_lag_18', 'dbp_lag_18', 'fgn_lag_18', 'fio2_lag_18', 'glu_lag_18', 'hgb_lag_18', 'hr_lag_18', 'inr_pt_lag_18', 'k_lag_18', 'lact_lag_18', 'lymph_lag_18', 'map_lag_18', 'mch_lag_18', 'mchc_lag_18', 'mcv_lag_18', 'methb_lag_18', 'mg_lag_18', 'na_lag_18', 'neut_lag_18', 'o2sat_lag_18', 'pco2_lag_18', 'ph_lag_18', 'phos_lag_18', 'plt_lag_18', 'po2_lag_18', 'ptt_lag_18', 'resp_lag_18', 'sbp_lag_18', 'temp_lag_18', 'tnt_lag_18', 'urine_lag_18', 'wbc_lag_18', 'alb_lag_19', 'alp_lag_19', 'alt_lag_19', 'ast_lag_19', 'be_lag_19', 'bicar_lag_19', 'bili_lag_19', 'bili_dir_lag_19', 'bnd_lag_19', 'bun_lag_19', 'ca_lag_19', 'cai_lag_19', 'ck_lag_19', 'ckmb_lag_19', 'cl_lag_19', 'crea_lag_19', 'crp_lag_19', 'dbp_lag_19', 'fgn_lag_19', 'fio2_lag_19', 'glu_lag_19', 'hgb_lag_19', 'hr_lag_19', 'inr_pt_lag_19', 'k_lag_19', 'lact_lag_19', 'lymph_lag_19', 'map_lag_19', 'mch_lag_19', 'mchc_lag_19', 'mcv_lag_19', 'methb_lag_19', 'mg_lag_19', 'na_lag_19', 'neut_lag_19', 'o2sat_lag_19', 'pco2_lag_19', 'ph_lag_19', 'phos_lag_19', 'plt_lag_19', 'po2_lag_19', 'ptt_lag_19', 'resp_lag_19', 'sbp_lag_19', 'temp_lag_19', 'tnt_lag_19', 'urine_lag_19', 'wbc_lag_19', 'alb_lag_20', 'alp_lag_20', 'alt_lag_20', 'ast_lag_20', 'be_lag_20', 'bicar_lag_20', 'bili_lag_20', 'bili_dir_lag_20', 'bnd_lag_20', 'bun_lag_20', 'ca_lag_20', 'cai_lag_20', 'ck_lag_20', 'ckmb_lag_20', 'cl_lag_20', 'crea_lag_20', 'crp_lag_20', 'dbp_lag_20', 'fgn_lag_20', 'fio2_lag_20', 'glu_lag_20', 'hgb_lag_20', 'hr_lag_20', 'inr_pt_lag_20', 'k_lag_20', 'lact_lag_20', 'lymph_lag_20', 'map_lag_20', 'mch_lag_20', 'mchc_lag_20', 'mcv_lag_20', 'methb_lag_20', 'mg_lag_20', 'na_lag_20', 'neut_lag_20', 'o2sat_lag_20', 'pco2_lag_20', 'ph_lag_20', 'phos_lag_20', 'plt_lag_20', 'po2_lag_20', 'ptt_lag_20', 'resp_lag_20', 'sbp_lag_20', 'temp_lag_20', 'tnt_lag_20', 'urine_lag_20', 'wbc_lag_20', 'alb_lag_21', 'alp_lag_21', 'alt_lag_21', 'ast_lag_21', 'be_lag_21', 'bicar_lag_21', 'bili_lag_21', 'bili_dir_lag_21', 'bnd_lag_21', 'bun_lag_21', 'ca_lag_21', 'cai_lag_21', 'ck_lag_21', 'ckmb_lag_21', 'cl_lag_21', 'crea_lag_21', 'crp_lag_21', 'dbp_lag_21', 'fgn_lag_21', 'fio2_lag_21', 'glu_lag_21', 'hgb_lag_21', 'hr_lag_21', 'inr_pt_lag_21', 'k_lag_21', 'lact_lag_21', 'lymph_lag_21', 'map_lag_21', 'mch_lag_21', 'mchc_lag_21', 'mcv_lag_21', 'methb_lag_21', 'mg_lag_21', 'na_lag_21', 'neut_lag_21', 'o2sat_lag_21', 'pco2_lag_21', 'ph_lag_21', 'phos_lag_21', 'plt_lag_21', 'po2_lag_21', 'ptt_lag_21', 'resp_lag_21', 'sbp_lag_21', 'temp_lag_21', 'tnt_lag_21', 'urine_lag_21', 'wbc_lag_21', 'alb_lag_22', 'alp_lag_22', 'alt_lag_22', 'ast_lag_22', 'be_lag_22', 'bicar_lag_22', 'bili_lag_22', 'bili_dir_lag_22', 'bnd_lag_22', 'bun_lag_22', 'ca_lag_22', 'cai_lag_22', 'ck_lag_22', 'ckmb_lag_22', 'cl_lag_22', 'crea_lag_22', 'crp_lag_22', 'dbp_lag_22', 'fgn_lag_22', 'fio2_lag_22', 'glu_lag_22', 'hgb_lag_22', 'hr_lag_22', 'inr_pt_lag_22', 'k_lag_22', 'lact_lag_22', 'lymph_lag_22', 'map_lag_22', 'mch_lag_22', 'mchc_lag_22', 'mcv_lag_22', 'methb_lag_22', 'mg_lag_22', 'na_lag_22', 'neut_lag_22', 'o2sat_lag_22', 'pco2_lag_22', 'ph_lag_22', 'phos_lag_22', 'plt_lag_22', 'po2_lag_22', 'ptt_lag_22', 'resp_lag_22', 'sbp_lag_22', 'temp_lag_22', 'tnt_lag_22', 'urine_lag_22', 'wbc_lag_22', 'alb_lag_23', 'alp_lag_23', 'alt_lag_23', 'ast_lag_23', 'be_lag_23', 'bicar_lag_23', 'bili_lag_23', 'bili_dir_lag_23', 'bnd_lag_23', 'bun_lag_23', 'ca_lag_23', 'cai_lag_23', 'ck_lag_23', 'ckmb_lag_23', 'cl_lag_23', 'crea_lag_23', 'crp_lag_23', 'dbp_lag_23', 'fgn_lag_23', 'fio2_lag_23', 'glu_lag_23', 'hgb_lag_23', 'hr_lag_23', 'inr_pt_lag_23', 'k_lag_23', 'lact_lag_23', 'lymph_lag_23', 'map_lag_23', 'mch_lag_23', 'mchc_lag_23', 'mcv_lag_23', 'methb_lag_23', 'mg_lag_23', 'na_lag_23', 'neut_lag_23', 'o2sat_lag_23', 'pco2_lag_23', 'ph_lag_23', 'phos_lag_23', 'plt_lag_23', 'po2_lag_23', 'ptt_lag_23', 'resp_lag_23', 'sbp_lag_23', 'temp_lag_23', 'tnt_lag_23', 'urine_lag_23', 'wbc_lag_23', 'alb_lag_24', 'alp_lag_24', 'alt_lag_24', 'ast_lag_24', 'be_lag_24', 'bicar_lag_24', 'bili_lag_24', 'bili_dir_lag_24', 'bnd_lag_24', 'bun_lag_24', 'ca_lag_24', 'cai_lag_24', 'ck_lag_24', 'ckmb_lag_24', 'cl_lag_24', 'crea_lag_24', 'crp_lag_24', 'dbp_lag_24', 'fgn_lag_24', 'fio2_lag_24', 'glu_lag_24', 'hgb_lag_24', 'hr_lag_24', 'inr_pt_lag_24', 'k_lag_24', 'lact_lag_24', 'lymph_lag_24', 'map_lag_24', 'mch_lag_24', 'mchc_lag_24', 'mcv_lag_24', 'methb_lag_24', 'mg_lag_24', 'na_lag_24', 'neut_lag_24', 'o2sat_lag_24', 'pco2_lag_24', 'ph_lag_24', 'phos_lag_24', 'plt_lag_24', 'po2_lag_24', 'ptt_lag_24', 'resp_lag_24', 'sbp_lag_24', 'temp_lag_24', 'tnt_lag_24', 'urine_lag_24', 'wbc_lag_24' ] +PredictionDatasetpytorch.time_varying_known_reals=[] PredictionDatasetpytorch.add_relative_time_idx=False -PredictionDatasetpytorch.target="label" - -PredictionDatasetpytorch.time_varying_unknown_reals=[ +PredictionDatasetpytorch.target=[ + "alb", + "alp", + "alt", + "ast", + "be", + "bicar", + "bili", + "bili_dir", + "bnd", + "bun", + "ca", + "cai", + "ck", + "ckmb", + "cl", + "crea", + "crp", + "dbp", + "fgn", + "fio2", + "glu", + "hgb", + "hr", + "inr_pt", + "k", + "lact", + "lymph", + "map", + "mch", + "mchc", + "mcv", + "methb", + "mg", + "na", + "neut", + "o2sat", + "pco2", + "ph", + "phos", + "plt", + "po2", + "ptt", + "resp", + "sbp", + "temp", + "tnt", + "urine", + "wbc", "label", ] -PredictionDatasetpytorch.time_varying_unknown_categoricals=[] -PredictionDatasetpytorch.lagged_variables=[ "alb", + + +PredictionDatasetpytorch.time_varying_unknown_reals=[ + "alb", "alp", "alt", "ast", @@ -87,4 +134,10 @@ PredictionDatasetpytorch.lagged_variables=[ "alb", "temp", "tnt", "urine", - "wbc"] \ No newline at end of file + "wbc", + "label", + + ] +PredictionDatasetpytorch.time_varying_unknown_categoricals=[] +PredictionDatasetpytorch.lagged_variables=[ ] +PredictionDatasetpytorch.target_normalizer='multi' \ No newline at end of file diff --git a/configs/prediction_models/TFTpytorch.gin b/configs/prediction_models/TFTpytorch.gin index c7264ed5..de79f61f 100644 --- a/configs/prediction_models/TFTpytorch.gin +++ b/configs/prediction_models/TFTpytorch.gin @@ -89,3 +89,5 @@ PredictionDatasetpytorch.time_varying_unknown_reals=["alb", "urine", "wbc",] PredictionDatasetpytorch.lagged_variables=[] +PredictionDatasetpytorch.lagged_variables=[] +PredictionDatasetpytorch.target_normalizer='single' \ No newline at end of file diff --git a/icu_benchmarks/cross_validation.py b/icu_benchmarks/cross_validation.py index 535d9619..69db2960 100644 --- a/icu_benchmarks/cross_validation.py +++ b/icu_benchmarks/cross_validation.py @@ -38,9 +38,9 @@ def execute_repeated_cv( verbose: bool = False, wandb: bool = False, complete_train: bool = False, - explain: bool = False, + explain: list = None, pytorch_forecasting: bool = False, - XAI_metric: bool = False, + XAI_metric: list = None, random_labels: bool = False, random_model_dir: str = None, ) -> float: diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index a0e51dbc..49c898b7 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -10,7 +10,7 @@ from icu_benchmarks.imputation.amputations import ampute_data from .constants import DataSegment as Segment from .constants import DataSplit as Split -from pytorch_forecasting import TimeSeriesDataSet, GroupNormalizer +from pytorch_forecasting import TimeSeriesDataSet, GroupNormalizer, MultiNormalizer, EncoderNormalizer class CommonDataset(Dataset): @@ -450,6 +450,7 @@ def __init__( time_varying_known_reals: List[str], time_varying_unknown_categoricals: List[str], lagged_variables: List[str], + # target_normalizer: str, *args, ram_cache: bool = False, add_relative_time_idx: bool = False, @@ -482,7 +483,13 @@ def __init__( self.ram_cache = ram_cache self.kwargs = kwargs self.column_names = features.columns - + """ if target_normalizer == 'multi': + target_normalizer = MultiNormalizer([EncoderNormalizer(transformation='relu') for _ in range(len(target)-1)] + [GroupNormalizer(groups=["stay_id"], transformation="relu")] + ) + else: + target_normalizer = GroupNormalizer( + groups=["stay_id"], transformation="relu" + ) """ super().__init__( data=self.data, time_idx="time_idx", @@ -502,6 +509,7 @@ def __init__( # add_target_scales=True, # add_encoder_length=True, predict_mode=True, + target_normalizer=GroupNormalizer( groups=["stay_id"], transformation="relu" ), diff --git a/icu_benchmarks/models/constants.py b/icu_benchmarks/models/constants.py index 093d15bd..702fe7f7 100644 --- a/icu_benchmarks/models/constants.py +++ b/icu_benchmarks/models/constants.py @@ -26,6 +26,9 @@ MAE, JSD, BinaryFairnessWrapper, + Faithfulness, + Stability, + Randomization ) @@ -85,6 +88,17 @@ class DLMetrics: "mae": MAE, "jsd": JSD, } + Faithfulness = { + "Faithfulness_timesteps": Faithfulness, + "Faithfulness_features": Faithfulness, + "Faithfulness_feature_timestep": Faithfulness, + } + Stability = { + "Stability": Stability + } + Randmoization = { + "DDR": Randomization, + } class ImputationInit(str, Enum): diff --git a/icu_benchmarks/models/custom_metrics.py b/icu_benchmarks/models/custom_metrics.py index d1dbc271..ee942a2d 100644 --- a/icu_benchmarks/models/custom_metrics.py +++ b/icu_benchmarks/models/custom_metrics.py @@ -6,6 +6,8 @@ from sklearn.calibration import calibration_curve from scipy.spatial.distance import jensenshannon from torchmetrics.classification import BinaryFairness +from icu_benchmarks.models.similarity_func import correlation_spearman, distance_euclidean, correlation_pearson, cosine + """" This file contains custom metrics that can be added to YAIB. @@ -132,3 +134,422 @@ def feature_helper(self, trainer, step_prefix): else: feature_names = trainer.test_dataloaders.dataset.features return feature_names + +# XAI Metrics + + +class Faithfulness(EpochMetric): + def __init__( + self, + output_transform: Callable = lambda x: x, + check_compute_fn: bool = False, + *args, **kwargs + ) -> None: + super().__init__(output_transform, + check_compute_fn, *args, **kwargs + ) + + def update( + self, + x, + attribution, + model, + similarity_func=None, + nr_runs=100, + pertrub=None, + subset_size=3, + feature=False, + time_step=False, + feature_timestep=False, + device='cuda' + + ): + """ + Calculates faithfulness scores for captum attributions + + Args: + - x:Batch input + -attribution: attribution generated by captum, + - similarity_func:function to determine similarity between sum of attributions and difference in prediction + - nr_runs: How many times to repeat the experiment, + - pertrub: What change to do to the input, + - subset_size: The size of the subset of featrues to alter , + - feature: Determines if to calcualte faithfulness of feature attributions, + - time_step: Determines if to calcualte faithfulness of timesteps attributions, + - feature_timestep: Determines if to calcualte faithfulness of featrues per timesteps attributions, + Returns: + score: similarity score between sum of attributions and difference in prediction averaged over nr_runs + + Implementation of faithfulness correlation by Bhatt et al., 2020. + + The Faithfulness Correlation metric intend to capture an explanation's relative faithfulness + (or 'fidelity') with respect to the model behaviour. + + Faithfulness correlation scores shows to what extent the predicted logits of each modified test point and + the average explanation attribution for only the subset of features are (linearly) correlated, taking the + average over multiple runs and test samples. The metric returns one float per input-attribution pair that + ranges between -1 and 1, where higher scores are better. + + For each test sample, |S| features are randomly selected and replace them with baseline values (zero baseline + or average of set). Thereafter, Pearson’s correlation coefficient between the predicted logits of each modified + test point and the average explanation attribution for only the subset of features is calculated. Results is + average over multiple runs and several test samples. + This code is adapted from the quantus libray to suit our use case + + References: + 1) Umang Bhatt et al.: "Evaluating and aggregating feature-based model + explanations." IJCAI (2020): 3016-3022. + 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. + """ + def add_noise(x, indices, time_step, feature_timestep): + noise = torch.randn_like(x["encoder_cont"]) + if time_step: + idx0, idx1 = np.meshgrid(indices[0], indices[1], indexing='ij') + + with torch.no_grad(): + x["encoder_cont"][idx0, idx1, :] += noise[idx0, idx1, :] + + elif feature: + idx0, idx1 = np.meshgrid(indices[0], indices[1], indexing='ij') + + with torch.no_grad(): + x["encoder_cont"][idx0, :, idx1] += noise[idx0, :, idx1] + + elif feature_timestep: + idx0, idx1, idx2 = np.meshgrid(indices[0], indices[1], indices[2], indexing='ij') + + with torch.no_grad(): + x["encoder_cont"][idx0, idx1, idx2] += noise[idx0, idx1, idx2] + + def apply_baseline(x, indices, time_step, feature_timestep): + mask = torch.ones_like(x["encoder_cont"]) + if time_step: + + idx0, idx1, = np.meshgrid(indices[0], indices[1], indexing='ij') + + mask[idx0, idx1, :] -= mask[idx0, idx1, :] + elif feature: + idx0, idx1, = np.meshgrid(indices[0], indices[1], indexing='ij') + + mask[idx0, :, idx1] -= mask[idx0, :, idx1] + + elif feature_timestep: + idx0, idx1, idx2 = np.meshgrid(indices[0], indices[1], indices[2], indexing='ij') + + mask[idx0, idx1, idx2] -= mask[idx0, idx1, idx2] + + with torch.no_grad(): + x["encoder_cont"] *= mask + # Assuming 'attribution' is already a GPU tensor + if not torch.is_tensor(attribution): + attribution = torch.tensor(attribution).to(device) + + # Other initializations + if similarity_func is None: + similarity_func = correlation_spearman + if pertrub is None: + pertrub = "baseline" + similarities = [] + + # Assuming this is a method to prepare your data + + y_pred = model(model.prep_data(x)).detach() # Keep on GPU + pred_deltas = [] + att_sums = [] + + for i_ix in range(nr_runs): + if time_step: + timesteps_idx = np.random.choice(24, subset_size, replace=False) + patient_idx = np.random.choice(64, 1, replace=False) + a_ix = [patient_idx, timesteps_idx] + + elif feature: + feature_idx = np.random.choice(53, subset_size, replace=False) + patient_idx = np.random.choice(64, 1, replace=False) + a_ix = [patient_idx, feature_idx] + elif feature_timestep: + timesteps_idx = np.random.choice(24, subset_size[0], replace=False) + feature_idx = np.random.choice(53, subset_size[1], replace=False) + patient_idx = np.random.choice(64, 1, replace=False) + a_ix = [patient_idx, timesteps_idx, feature_idx] + + # Apply perturbation + if pertrub == "Noise": + add_noise(x, a_ix, time_step, feature_timestep) + elif pertrub == "baseline": + apply_baseline(x, a_ix, time_step, feature_timestep) + + # Predict on perturbed input and calculate deltas + y_pred_perturb = (model(model.prep_data(x))).detach() # Keep on GPU + + if time_step: + + if attribution.size() == torch.Size([24]): + att_sums.append((attribution[timesteps_idx]).sum()) + else: + att_sums.append((attribution[patient_idx, :, :][:, timesteps_idx, :]).sum()) + elif feature: + + if len(attribution) == 53: + att_sums.append((attribution[feature_idx]).sum()) + else: + + att_sums.append((attribution[patient_idx, :, :][:, :, feature_idx]).sum()) + elif feature_timestep: + + att_sums.append((attribution[patient_idx, :, :] + [:, timesteps_idx, :][:, :, feature_idx]).sum()) + + pred_deltas.append((y_pred - y_pred_perturb)[patient_idx].item()) + # Convert to CPU for numpy operations + + pred_deltas_cpu = torch.tensor(pred_deltas).cpu().numpy() + att_sums_cpu = torch.tensor(att_sums).cpu().numpy() + + similarities.append(similarity_func(pred_deltas_cpu, att_sums_cpu)) + + score = np.nanmean(similarities) + return score + + +class Stability(EpochMetric): + def __init__( + self, output_transform: Callable = lambda x: x, + check_compute_fn: bool = False, + *args, **kwargs + ) -> None: + super().__init__(output_transform, + check_compute_fn, *args, **kwargs + ) + + def update(self, x, + attribution, model, explain_method, method_name, dataloader=None, thershold=0.5, device='cuda', **kwargs + ): + """ + Args: + - x:Batch input + -attribution: attribution + - explain_method:function to generate explantations + - method_name: Name of the explantation + - dataloader:In case of using Attention as the explain method need to pass the dataloader instead of the batch , + + + Returns: + RIS : relative distance between the explantation and the input + ROS: relative distance between the explantation and the output + + + References: + 1) `https://arxiv.org/pdf/2203.06877.pdf + 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. + + """ + + def relative_stability_objective( + x, xs, e_x, e_xs, close_indices, eps_min=0.0001, input=False, attention=False, device='cuda' + ) -> torch.Tensor: + """ + Computes relative input and output stabilities maximization objective + as defined here :ref:`https://arxiv.org/pdf/2203.06877.pdf` by the authors. + + Args: + + x: Input tensor + xs: perturbed tensor. + e_x: Explanations for x. + e_xs: Explanations for xs. + eps_min:Value to avoid division by zero if needed + input:Boolean to indicate if this is an input or an output + device: the device to keep the tensors on + + Returns: + + ris_obj: Tensor + RIS maximization objective. + """ + # Function to convert inputs to tensors if they are numpy arrays + def to_tensor(input_array): + if isinstance(input_array, np.ndarray): + return torch.index_select(torch.tensor(input_array).to(device), 0, close_indices) + + return torch.index_select(input_array.to(device), 0, close_indices) + + # Convert all inputs to tensors and move to GPU + if attention: + x, xs = map(to_tensor, [x, xs]) + else: + x, xs, e_x, e_xs = map(to_tensor, [x, xs, e_x, e_xs]) + + if input: + num_dim = x.ndim + else: + num_dim = e_x.ndim + + if num_dim == 3: + def norm_function(arr): return torch.norm(arr, dim=(-1, -2)) + elif num_dim == 2: + def norm_function(arr): return torch.norm(arr, dim=-1) + else: + def norm_function(arr): return torch.norm(arr) + + nominator = (e_x - e_xs) / (e_x + (e_x == 0) * eps_min) + nominator = norm_function(nominator) + + if input: + denominator = x - xs + denominator /= x + (x == 0) * eps_min + denominator = norm_function(denominator) + denominator += (denominator == 0) * eps_min + else: + denominator = torch.squeeze(x) - torch.squeeze(xs) + denominator = torch.norm(denominator, dim=-1) + denominator += (denominator == 0) * eps_min + + return nominator / denominator + + if explain_method == "Attention": + y_pred = model.model.predict(dataloader) + x_original = dataloader.dataset.data["reals"].clone() + + dataloader.dataset.add_noise() + x_preturb = dataloader.dataset.data["reals"].clone() + y_pred_preturb = model.model.predict(dataloader) + Attention_weights = model.interpertations(dataloader) + att_preturb = Attention_weights["attention"] + # Calculate the absolute difference + difference = torch.abs(y_pred_preturb - y_pred) + + # Find where the difference is less than or equal to a thershold + close_indices = torch.nonzero(difference <= thershold).squeeze()[:, 0].to(device) + + RIS = relative_stability_objective( + x_original.detach(), x_preturb.detach(), attribution, att_preturb, close_indices=close_indices, input=True, attention=True + ) + ROS = relative_stability_objective( + y_pred, y_pred_preturb, attribution, att_preturb, close_indices=close_indices, input=False, attention=True + ) + + else: + y_pred = model(model.prep_data(x)).detach() + x_original = x["encoder_cont"].detach().clone() + + with torch.no_grad(): + noise = torch.randn_like(x["encoder_cont"])*0.01 + x["encoder_cont"] += noise + y_pred_preturb = model(model.prep_data(x)).detach() + if explain_method == "Random": + att_preturb = np.random.normal(size=[64, 24, 53]) + + else: + + data, baselines = model.prep_data_captum(x) + + explantation = explain_method(model.forward_captum) + # Reformat attributions. + if explain_method is not captum.attr.Saliency: + att_preturb = explantation.attribute(data, baselines=baselines, **kwargs) + else: + att_preturb = explantation.attribute(data, **kwargs) + + # Process and store the calculated attributions + att_preturb = att_preturb[1].detach() if method_name in [ + 'Lime', 'FeatureAblation'] else torch.stack(att_preturb).detach() + # Calculate the absolute difference + difference = torch.abs(y_pred_preturb - y_pred) + + # Find where the difference is less than or equal to a thershold + close_indices = torch.nonzero(difference <= thershold).squeeze()[:, 0].to(device) + + RIS = relative_stability_objective( + x_original.detach(), + x["encoder_cont"].detach(), + attribution, att_preturb, close_indices=close_indices, input=True + ) + ROS = relative_stability_objective( + y_pred, y_pred_preturb, attribution, att_preturb, close_indices=close_indices, input=False + ) + + return np.max(RIS.cpu().numpy()).astype(np.float64), np.max(ROS.cpu().numpy()).astype(np.float64) + + +class Randomization(EpochMetric): + def __init__( + self, output_transform: Callable = lambda x: x, + check_compute_fn: bool = False, + *args, **kwargs + ) -> None: + super().__init__(output_transform, + check_compute_fn, *args, **kwargs + ) + + def update( + self, x, + attribution, model, explain_method, random_model, similarity_func=cosine, dataloader=None, method_name="", **kwargs + ): + """ + + Args: + - x:Batch input + -attribution: attribution + - explain_method:function to generate explantations + - random_model: Reference to model trained on random labels + - similarity_func: Function to measure similiarity + - dataloader:In case of using Attention as the explain method need to pass the dataloader instead of the batch , + - method_name: Name of the explantation + + Returns: + score: similarity score between attributions of model trained on random data and model trained on real data + + Implementation of the Random Logit Metric by Sixt et al., 2020. + + The Random Logit Metric computes the distance between the original explanation and a reference explanation of + a randomly chosen non-target class. + This code is adapted from the quantus libray to suit our use case + + References: + 1) Leon Sixt et al.: "When Explanations Lie: Why Many Modified BP + Attributions Fail." ICML (2020): 9046-9057. + 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. + + """ + + if explain_method == "Attention": + Attention_weights = random_model.interpertations(dataloader) + attribution = attribution.cpu().numpy() + min_val = np.min(attribution) + max_val = np.max(attribution) + + attribution = (attribution - min_val) / (max_val - min_val) + random_attr = Attention_weights["attention"].cpu().numpy() + min_val = np.min(random_attr) + max_val = np.max(random_attr) + random_attr = (random_attr - min_val) / (max_val - min_val) + score = similarity_func(random_attr, attribution) + elif explain_method == "Random": + score = similarity_func(np.random.normal(size=[64, 24, 53]).flatten(), attribution.flatten()) + else: + data, baselines = model.prep_data_captum(x) + y_pred = model(data).detach() + + explantation = explain_method(random_model.forward_captum) + # Reformat attributions. + if explain_method is not captum.attr.Saliency: + attr = explantation.attribute(data, baselines=baselines, **kwargs) + else: + attr = explantation.attribute(data, **kwargs) + + # Process and store the calculated attributions + random_attr = attr[1].cpu().detach().numpy() if method_name in [ + 'Lime', 'FeatureAblation'] else torch.stack(attr).cpu().detach().numpy() + + attribution = attribution.flatten() + min_val = np.min(attribution) + max_val = np.max(attribution) + attribution = (attribution - min_val) / (max_val - min_val) + random_attr = random_attr.flatten() + min_val = np.min(random_attr) + max_val = np.max(random_attr) + random_attr = (random_attr - min_val) / (max_val - min_val) + + score = similarity_func(random_attr, attribution) + return score diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index ff66c4da..0966dec7 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -12,7 +12,7 @@ ) import matplotlib.pyplot as plt from icu_benchmarks.models.wrappers import DLPredictionWrapper, DLPredictionPytorchForecastingWrapper -from torch import Tensor, FloatTensor, zeros_like, ones_like, randn_like, is_tensor +from torch import Tensor, FloatTensor, zeros_like, ones_like, randn_like, is_tensor, autograd from pytorch_forecasting import TemporalFusionTransformer, RecurrentNetwork, DeepAR from pytorch_forecasting.metrics import QuantileLoss, MAE import matplotlib.pyplot as plt @@ -475,8 +475,8 @@ def __init__( *args, **kwargs, ): - super().__init__(optimizer=optimizer, pytorch_forecasting=True, *args, **kwargs) - self.dataset = dataset + super().__init__(optimizer=optimizer, pytorch_forecasting=True, dataset=dataset, * args, **kwargs) + self.model = TemporalFusionTransformer.from_dataset( dataset=dataset, hidden_size=hidden, @@ -486,6 +486,7 @@ def __init__( loss=QuantileLoss(), hidden_continuous_size=hidden, ) + self.dataset = dataset self.num_classes = num_classes self.logit = nn.Linear(7, num_classes) @@ -575,7 +576,10 @@ def forward_captum( target_scale, ) - return self.forward(tuple_x) + # with autograd.set_grad_enabled(True): + result = self.forward(tuple_x) + + return result @gin.configurable @@ -634,9 +638,11 @@ def forward( out = self.model(x_dict) - pred = self.logit(out["prediction"]) + out = ((out["prediction"][-1]).squeeze(dim=-1)) - return pred + pred = self.logit(out) + + return pred.unsqueeze(1) @gin.configurable @@ -694,6 +700,8 @@ def forward( x_dict["encoder_cont"][:, :, -1] = 0.0 out = self.model(x_dict) - pred = self.logit(out["prediction"]) + out = ((out["prediction"][-1])) + + pred = self.logit(out) return pred diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 73116f46..e1566713 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -73,9 +73,9 @@ def train_common( torch.cuda.device_count() * 8 * int(torch.cuda.is_available()), 32, ), - explain: list = [], + explain: list = None, pytorch_forecasting: bool = False, - XAI_metric: list = [], + XAI_metric: list = None, random_labels: bool = False, random_model_dir: str = None, @@ -154,6 +154,12 @@ def train_common( drop_last=True, shuffle=False, ) + if random_model_dir is None: + random_model = None + else: + + random_model = Path(random_model_dir) + if load_weights: model = load_model( model, @@ -161,6 +167,11 @@ def train_common( pl_model=pl_model, train_dataset=train_dataset, optimizer=optimizer, + explain=explain, + XAI_metric=XAI_metric, + random_model=random_model, + test_loader=test_loader + ) else: @@ -170,6 +181,11 @@ def train_common( epochs=epochs, run_mode=mode, batch_size=batch_size, + explain=explain, + XAI_metric=XAI_metric, + random_model=random_model, + test_loader=test_loader + ) if random_labels: train_dataset.randomize_labels(num_classes=model.num_classes) @@ -246,7 +262,8 @@ def train_common( logger=loggers, num_sanity_val_steps=-1, log_every_n_steps=5, - # gradient_clip_val=gradient_clip_val + gradient_clip_val=gradient_clip_val, + inference_mode=False ) if not eval_only: if model.requires_backprop: @@ -264,21 +281,7 @@ def train_common( save_config_file(log_dir) return 0 - if explain: - if random_model_dir is None: - random_model = None - else: - path = Path(random_model_dir) - - random_model = load_model( - model, - source_dir=path, - pl_model=pl_model, - train_dataset=train_dataset, - optimizer=optimizer, - ) - - XAI_dict = {} # dictrionary to log attributions metrics + """ XAI_dict = {} # dictrionary to log attributions metrics # choose which methods to get attributions methods = { @@ -370,7 +373,7 @@ def create_default_mask(shape): # Write the dictionary to a JSON file with open(json_file_path, "w") as json_file: - json.dump(XAI_dict, json_file) + json.dump(XAI_dict, json_file) """ model.set_weight("balanced", train_dataset) test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0]["test/loss"] @@ -378,7 +381,7 @@ def create_default_mask(shape): return test_loss -def load_model(model, source_dir, pl_model=True, train_dataset=None, optimizer=None): +def load_model(model, source_dir, pl_model=True, train_dataset=None, optimizer=None, explain=None, XAI_metric=None, random_model=None, test_loader=None): if source_dir.exists(): if model.requires_backprop: if (source_dir / "model.ckpt").exists(): @@ -391,7 +394,8 @@ def load_model(model, source_dir, pl_model=True, train_dataset=None, optimizer=N return Exception(f"No weights to load at path : {source_dir}") if pl_model: if train_dataset is not None: - model = model.load_from_checkpoint(model_path, dataset=train_dataset, optimizer=optimizer) + model = model.load_from_checkpoint(model_path, dataset=train_dataset, optimizer=optimizer, + explain=explain, XAI_metric=XAI_metric, test_loader=test_loader) else: model = model.load_from_checkpoint(model_path) diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index b97d1306..b0cd933a 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -18,9 +18,10 @@ from pytorch_lightning import LightningModule from icu_benchmarks.models.constants import MLMetrics, DLMetrics from icu_benchmarks.contants import RunMode +from icu_benchmarks.models.train import load_model import matplotlib.pyplot as plt from icu_benchmarks.models.similarity_func import correlation_spearman, distance_euclidean, correlation_pearson, cosine -import captum +from captum.attr import IntegratedGradients, Saliency, FeatureAblation, Lime from captum._utils.models.linear_model import SkLearnLasso import os @@ -242,8 +243,7 @@ def __init__( input_size: Tensor = None, initialization_method: str = "normal", pytorch_forecasting: bool = False, - explain: list = [], - XAI_metric: list = [], + **kwargs, ): super().__init__( @@ -264,8 +264,6 @@ def __init__( self.output_transform = None self.loss_weights = None self.pytorch_forecasting = pytorch_forecasting - self.explain = explain - self.XAI_metric = XAI_metric def set_weight(self, weight, dataset): """Set the weight for the loss function.""" @@ -408,6 +406,11 @@ def __init__( input_size: Tensor = None, initialization_method: str = "normal", pytorch_forecasting: bool = False, + explain: list = None, + XAI_metric: list = None, + random_model=None, + test_loader=None, + dataset=None, **kwargs, ): super().__init__( @@ -425,6 +428,55 @@ def __init__( initialization_method=initialization_method, kwargs=kwargs, ) + self.explain = explain + self.XAI_metric = XAI_metric + self.methods = { + "G": Saliency, + "L": Lime, + "IG": IntegratedGradients, + "FA": FeatureAblation, + "R": "Random", + "Att": "Attention" + } + self.random_model = random_model + self.test_loader = test_loader + + if random_model is not None: + self.random_model = load_model( + model=self, + source_dir=random_model, + pl_model=True, + train_dataset=dataset, + optimizer=optimizer, + ) + else: + self.random_model = None + + def set_metrics(self, *args): + """Set the evaluation metrics for the prediction model.""" + + metrics = super().set_metrics(*args) + + if self.XAI_metric is not None: + for metric in self.XAI_metric: + if metric == "Faithfulness": + metrics.update(DLMetrics.Faithfulness) + elif metric == "Stability": + metrics.update(DLMetrics.Stability) + elif metric == "Randmoization": + metrics.update(DLMetrics.Randmoization) + + return metrics + + def on_train_start(self): + self.metrics = { + step_name: { + metric_name: (metric() if isinstance(metric, type) else metric) + for metric_name, metric in self.set_metrics().items() + } + for step_name in ["train", "val", "test"] + } + return super().on_train_start() def step_fn(self, element, step_prefix=""): """Perform a step in the DL prediction model training loop. @@ -449,7 +501,6 @@ def step_fn(self, element, step_prefix=""): else: aux_loss = 0 # Get prediction and target - prediction = out.to(self.device).squeeze(-1) target = labels.to(self.device) @@ -460,13 +511,112 @@ def step_fn(self, element, step_prefix=""): # Returns torch.long because negative log likelihood loss elif self.run_mode == RunMode.regression: # Regression task - loss = self.loss(prediction[:, 0], target.float()) + aux_loss else: raise ValueError(f"Run mode {self.run_mode} not yet supported. Please implement it.") transformed_output = self.output_transform((prediction, target)) + if self.explain is not None: + + for method in self.explain: + if method == "Attention": + all_attrs, features_attrs, timestep_attrs = self.explantation2(dic, method) + else: + all_attrs, features_attrs, timestep_attrs = self.explantation2(dic, method) + for key, value in self.metrics[step_prefix].items(): + if key == "Faithfulness_timesteps": + value.update( + dic, + all_attrs, + self, + correlation_pearson, + 100, + "baseline", + 4, + False, + True, + False + + ) + elif key == "Faithfulness_features": + value.update( + dic, + all_attrs, + self, + correlation_pearson, + 100, + "baseline", + 9, + True, + False, + False + + ) + elif key == "Faithfulness_feature_timestep": + value.update( + dic, + all_attrs, + self, + correlation_pearson, + 100, + "baseline", + [4, 9], + False, + False, + True + + ) + + elif key == "Stability": + if method == "Attention": + + value.update( + dic, + timestep_attrs, + self, + self.methods[method], + method, + self.test_loader, + 0.5, + + ) + else: + value.update( + dic, + all_attrs, + self, + self.methods[method], + method, + None, + 0.5, + ) + elif key == "Randomization": + + if method == "Attention": + value.update( + dic, + timestep_attrs, + self, + self.methods[method], + self.random_model, + cosine, + None, + method + ) + else: + + value.update( + dic, + all_attrs, + self, + self.methods[method], + self.random_model, + cosine, + self.test_loader, + method + ) for key, value in self.metrics[step_prefix].items(): + if isinstance(value, torchmetrics.Metric): if key == "Binary_Fairness": feature_names = self.metrics[step_prefix][key].feature_helper(self.trainer, step_prefix) @@ -476,6 +626,10 @@ def step_fn(self, element, step_prefix=""): data, feature_names, ) + elif (key == "Faithfulness_timesteps" or key == "Faithfulness_features" or key == "Faithfulness_feature_timestep" + or key == "Stability" or key == "Randomization"): + continue + else: value.update(transformed_output[0], transformed_output[1].int()) else: @@ -575,6 +729,86 @@ def plot_attributions(self, features_attrs, timestep_attrs, method_name, log_dir plt.tight_layout() plt.savefig(log_dir / "{}_attribution_plot.png".format(method_name), bbox_inches="tight") + def explantation2(self, dic, method, dataloader=None, log_dir=".", plot=False, **kwargs): + """ + Generic method to combine pytorchforecasting data loading , interpertations and captum to generate attributions + + Args: + - dataloader: pytorchforecasting data loader + - method: The explantation method chosen + - log_dir= The directory to output the plots + - plot= Determines if plots should be done or not + - XAI_metric=Determines if XAI metrics should be calculated or not + Returns: + - all_attrs : Attribtuons of features per timesteps + - features_attrs : Attribtuons of features averaged over timesteps + - timestep_attrs : Attribtuons of timesteps averaged over features + - f_ts_v_score: Faithfulness score for attribtuons of features per timesteps + - f_ts_score: Faithfulness score for attribtuons of timesteps averaged over features + """ + # Initialize lists to store attribution values for all instances + + all_attrs = [] + method = self.methods[method] + for key, value in dic.items(): + dic[key] = dic[key].to(self.device) + + method_name = method if (method == "Random") or (method == "Attention") else ( + method.__name__) + if (method_name == "Random") or (method_name == "Attention"): + if method_name == "Attention": + Interpertations = self.interpertations(dataloader=dataloader, log_dir=log_dir, plot=plot) + timestep_attrs = Interpertations["attention"] + features_attrs = Interpertations["static_variables"].tolist() + features_attrs.extend(Interpertations["encoder_variables"].tolist()) + + elif method_name == "Random": + # Generate random attributions for baseline comparison + all_attrs = np.random.normal(size=[64, 24, 53]) + features_attrs = all_attrs.mean(axis=(1)) + timestep_attrs = all_attrs.mean(axis=(2)) + + return all_attrs, features_attrs, timestep_attrs + # Loop through the dataloader to compute attributions for all instances + + data, baselines = self.prep_data_captum(dic) + + # Initialize the explanation method + self.train() + explanation = method(self.forward_captum, interpretable_model=SkLearnLasso( + alpha=0.4)) if method_name == 'Lime' else method(self.forward_captum) + + # Calculate attributions using the selected method + if method is not Saliency: + attr = explanation.attribute(data, baselines=baselines, **kwargs) + else: + attr = explanation.attribute(data, **kwargs) + self.eval() + # Process and store the calculated attributions + stacked_attr = attr[1].cpu().detach().numpy() if method_name in [ + 'Lime', 'FeatureAblation'] else torch.stack(attr).cpu().detach().numpy() + + # aggregate over batch + attr = np.mean(stacked_attr, axis=0) + all_attrs.append(attr) + # aggregate over all batches + all_attrs = np.array(all_attrs).mean(axis=(0)) + # aggregate over all timesteps + features_attrs = all_attrs.mean(axis=(0)) + # aggregate over all features + timestep_attrs = all_attrs.mean(axis=(1)) + + if plot: + log_dir_plots = log_dir / 'plots' + if not (log_dir_plots.exists()): + log_dir_plots.mkdir(parents=True) + # Plot attributions for features and timesteps + + self.plot_attributions(features_attrs, timestep_attrs, method_name, log_dir_plots) + + # Return computed attributions and metrics + return all_attrs, features_attrs, timestep_attrs + def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=False, random_model=None, test_dataset=None, **kwargs): """ Generic method to combine pytorchforecasting data loading , interpertations and captum to generate attributions From 1534c13ebe15532e6c282f26e18e94c2b9bea1b0 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Wed, 27 Dec 2023 20:32:50 +0100 Subject: [PATCH 124/142] fixes --- icu_benchmarks/models/custom_metrics.py | 35 +++--------- icu_benchmarks/models/wrappers.py | 76 ++++++++++++++++++------- 2 files changed, 62 insertions(+), 49 deletions(-) diff --git a/icu_benchmarks/models/custom_metrics.py b/icu_benchmarks/models/custom_metrics.py index ee942a2d..9b84a18d 100644 --- a/icu_benchmarks/models/custom_metrics.py +++ b/icu_benchmarks/models/custom_metrics.py @@ -243,7 +243,6 @@ def apply_baseline(x, indices, time_step, feature_timestep): # Assuming 'attribution' is already a GPU tensor if not torch.is_tensor(attribution): attribution = torch.tensor(attribution).to(device) - # Other initializations if similarity_func is None: similarity_func = correlation_spearman @@ -287,16 +286,15 @@ def apply_baseline(x, indices, time_step, feature_timestep): if attribution.size() == torch.Size([24]): att_sums.append((attribution[timesteps_idx]).sum()) else: - att_sums.append((attribution[patient_idx, :, :][:, timesteps_idx, :]).sum()) + att_sums.append((attribution[patient_idx, :][:, timesteps_idx]).sum()) elif feature: if len(attribution) == 53: att_sums.append((attribution[feature_idx]).sum()) else: - att_sums.append((attribution[patient_idx, :, :][:, :, feature_idx]).sum()) + att_sums.append((attribution[patient_idx, :][:, feature_idx]).sum()) elif feature_timestep: - att_sums.append((attribution[patient_idx, :, :] [:, timesteps_idx, :][:, :, feature_idx]).sum()) @@ -323,7 +321,7 @@ def __init__( ) def update(self, x, - attribution, model, explain_method, method_name, dataloader=None, thershold=0.5, device='cuda', **kwargs + attribution, model, explain_method, dataloader=None, thershold=0.5, device='cuda', **kwargs ): """ Args: @@ -441,19 +439,9 @@ def norm_function(arr): return torch.norm(arr) att_preturb = np.random.normal(size=[64, 24, 53]) else: + att_preturb, features_attrs, timestep_attrs = model.explantation2(x, explain_method) - data, baselines = model.prep_data_captum(x) - - explantation = explain_method(model.forward_captum) - # Reformat attributions. - if explain_method is not captum.attr.Saliency: - att_preturb = explantation.attribute(data, baselines=baselines, **kwargs) - else: - att_preturb = explantation.attribute(data, **kwargs) - - # Process and store the calculated attributions - att_preturb = att_preturb[1].detach() if method_name in [ - 'Lime', 'FeatureAblation'] else torch.stack(att_preturb).detach() + # # Calculate the absolute difference difference = torch.abs(y_pred_preturb - y_pred) @@ -484,7 +472,7 @@ def __init__( def update( self, x, - attribution, model, explain_method, random_model, similarity_func=cosine, dataloader=None, method_name="", **kwargs + attribution, model, explain_method, random_model, similarity_func=cosine, dataloader=None, **kwargs ): """ @@ -531,16 +519,7 @@ def update( data, baselines = model.prep_data_captum(x) y_pred = model(data).detach() - explantation = explain_method(random_model.forward_captum) - # Reformat attributions. - if explain_method is not captum.attr.Saliency: - attr = explantation.attribute(data, baselines=baselines, **kwargs) - else: - attr = explantation.attribute(data, **kwargs) - - # Process and store the calculated attributions - random_attr = attr[1].cpu().detach().numpy() if method_name in [ - 'Lime', 'FeatureAblation'] else torch.stack(attr).cpu().detach().numpy() + random_attr, features_attrs, timestep_attrs = model.explantation2(x, explain_method) attribution = attribution.flatten() min_val = np.min(attribution) diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index b0cd933a..92a364a2 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -518,15 +518,16 @@ def step_fn(self, element, step_prefix=""): if self.explain is not None: for method in self.explain: + print(method) if method == "Attention": - all_attrs, features_attrs, timestep_attrs = self.explantation2(dic, method) + all_attrs, features_attrs, timestep_attrs = self.explantation2(dic, method, self.test_loader) else: all_attrs, features_attrs, timestep_attrs = self.explantation2(dic, method) for key, value in self.metrics[step_prefix].items(): if key == "Faithfulness_timesteps": value.update( dic, - all_attrs, + timestep_attrs, self, correlation_pearson, 100, @@ -540,7 +541,7 @@ def step_fn(self, element, step_prefix=""): elif key == "Faithfulness_features": value.update( dic, - all_attrs, + features_attrs, self, correlation_pearson, 100, @@ -573,7 +574,6 @@ def step_fn(self, element, step_prefix=""): dic, timestep_attrs, self, - self.methods[method], method, self.test_loader, 0.5, @@ -584,7 +584,6 @@ def step_fn(self, element, step_prefix=""): dic, all_attrs, self, - self.methods[method], method, None, 0.5, @@ -596,11 +595,11 @@ def step_fn(self, element, step_prefix=""): dic, timestep_attrs, self, - self.methods[method], + method, self.random_model, cosine, None, - method + ) else: @@ -608,15 +607,17 @@ def step_fn(self, element, step_prefix=""): dic, all_attrs, self, - self.methods[method], + method, self.random_model, cosine, self.test_loader, - method + ) for key, value in self.metrics[step_prefix].items(): - + if (key == "Faithfulness_timesteps" or key == "Faithfulness_features" or key == "Faithfulness_feature_timestep" + or key == "Stability" or key == "Randomization"): + continue if isinstance(value, torchmetrics.Metric): if key == "Binary_Fairness": feature_names = self.metrics[step_prefix][key].feature_helper(self.trainer, step_prefix) @@ -626,9 +627,6 @@ def step_fn(self, element, step_prefix=""): data, feature_names, ) - elif (key == "Faithfulness_timesteps" or key == "Faithfulness_features" or key == "Faithfulness_feature_timestep" - or key == "Stability" or key == "Randomization"): - continue else: value.update(transformed_output[0], transformed_output[1].int()) @@ -780,23 +778,59 @@ def explantation2(self, dic, method, dataloader=None, log_dir=".", plot=False, * # Calculate attributions using the selected method if method is not Saliency: - attr = explanation.attribute(data, baselines=baselines, **kwargs) + if method_name == "IG": + attr = explanation.attribute(data, baselines=baselines, n_steps=50, **kwargs) + elif method_name == "L" or method_name == "FA": + # for Lime and feature ablation we need to define what is a feature we define each variable per timestep as a feature + shapes = [ + torch.Size([64, 24, 0]), + torch.Size([64, 24, 53]), + torch.Size([64, 24]), + torch.Size([64]), + torch.Size([64, 1, 0]), + torch.Size([64, 1, 53]), + torch.Size([64, 1]), + torch.Size([64]), + torch.Size([64, 1]), + torch.Size([64, 1]), + torch.Size([64, 2]) + ] + + # Create a default mask for non-targeted tensors + def create_default_mask(shape): + if len(shape) == 3: + return torch.zeros(shape[0], shape[1], max(1, shape[2]), dtype=torch.int32) + elif len(shape) == 2: + return torch.zeros(shape[0], max(1, shape[1]), dtype=torch.int32) + else: # len(shape) == 1 + return torch.zeros(shape[0], dtype=torch.int32) + + # Create a feature mask for the second tensor that includes both features and timesteps + num_timesteps = shapes[1][1] + num_features = shapes[1][2] + feature_mask_second = torch.arange(num_timesteps * num_features).reshape(num_timesteps, num_features) + feature_mask_second = feature_mask_second.unsqueeze(0).repeat(shapes[1][0], 1, 1) + + # Create a tuple of masks + feature_masks = tuple([create_default_mask(shape) if i != + 1 else feature_mask_second for i, shape in enumerate(shapes)]) + + attr = explanation.attribute(data, baselines=baselines, feature_mask=feature_masks, **kwargs) else: attr = explanation.attribute(data, **kwargs) self.eval() # Process and store the calculated attributions stacked_attr = attr[1].cpu().detach().numpy() if method_name in [ 'Lime', 'FeatureAblation'] else torch.stack(attr).cpu().detach().numpy() - # aggregate over batch - attr = np.mean(stacked_attr, axis=0) - all_attrs.append(attr) + # attr = np.mean(stacked_attr, axis=0) + # aggregate over all batches - all_attrs = np.array(all_attrs).mean(axis=(0)) + # all_attrs = np.array(all_attrs).mean(axis=(0)) # aggregate over all timesteps - features_attrs = all_attrs.mean(axis=(0)) + features_attrs = stacked_attr.mean(axis=(1)) # aggregate over all features - timestep_attrs = all_attrs.mean(axis=(1)) + timestep_attrs = stacked_attr.mean(axis=(2)) if plot: log_dir_plots = log_dir / 'plots' @@ -807,7 +841,7 @@ def explantation2(self, dic, method, dataloader=None, log_dir=".", plot=False, * self.plot_attributions(features_attrs, timestep_attrs, method_name, log_dir_plots) # Return computed attributions and metrics - return all_attrs, features_attrs, timestep_attrs + return stacked_attr, features_attrs, timestep_attrs def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=False, random_model=None, test_dataset=None, **kwargs): """ From 1586fbb583d6661ee771f5080f9a2681d4b579a9 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Thu, 28 Dec 2023 15:53:25 +0100 Subject: [PATCH 125/142] :Revert "added condition for ROS and RIS for attention" This reverts commit a39b5c418b62fe163bbd621502f0cbabb6fdc191. --- icu_benchmarks/models/train.py | 4 ++++ icu_benchmarks/models/wrappers.py | 34 +++++++++++++++---------------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index e1566713..cf94843c 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -290,7 +290,11 @@ def train_common( "IG": IntegratedGradients, # "FA": FeatureAblation, "R": "Random", +<<<<<<< HEAD "Att": "Attention" +======= + # "Att": "Attention" +>>>>>>> parent of a39b5c4... added condition for ROS and RIS for attention } for key, item in methods.items(): # If conditions needed here as different explantations require different inputs diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 92a364a2..0f86e6cc 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -962,7 +962,10 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F pertrub="baseline", feature=True, subset_size=9, nr_runs=100)) r_score.append(self.Data_Randomization(x, attribution=stacked_attr, explain_method=method, random_model=random_model, method_name=method_name)) +<<<<<<< HEAD +======= +>>>>>>> parent of a39b5c4... added condition for ROS and RIS for attention res1, res2 = self.Relative_Stability(x, stacked_attr, explain_method=method, method_name=method_name, dataloader=None, **kwargs ) @@ -1259,7 +1262,7 @@ def Data_Randomization( return score def Relative_Stability(self, x, - attribution, explain_method, method_name, dataloader=None, thershold=0.5, device='cuda', **kwargs + attribution, explain_method, method_name, dataloader=None, thershold=0.5, **kwargs ): """ Args: @@ -1282,7 +1285,7 @@ def Relative_Stability(self, x, """ def relative_stability_objective( - x, xs, e_x, e_xs, close_indices, eps_min=0.0001, input=False, attention=False, device='cuda' + x, xs, e_x, e_xs, eps_min=0.0001, input=False, device='cuda' ) -> torch.Tensor: """ Computes relative input and output stabilities maximization objective @@ -1306,15 +1309,11 @@ def relative_stability_objective( # Function to convert inputs to tensors if they are numpy arrays def to_tensor(input_array): if isinstance(input_array, np.ndarray): - return torch.index_select(torch.tensor(input_array).to(device), 0, close_indices) - - return torch.index_select(input_array.to(device), 0, close_indices) + return torch.tensor(input_array).to(device) + return input_array.to(device) # Convert all inputs to tensors and move to GPU - if attention: - x, xs = map(to_tensor, [x, xs]) - else: - x, xs, e_x, e_xs = map(to_tensor, [x, xs, e_x, e_xs]) + x, xs, e_x, e_xs = map(to_tensor, [x, xs, e_x, e_xs]) if input: num_dim = x.ndim @@ -1356,13 +1355,12 @@ def norm_function(arr): return torch.norm(arr) difference = torch.abs(y_pred_preturb - y_pred) # Find where the difference is less than or equal to a thershold - close_indices = torch.nonzero(difference <= thershold).squeeze()[:, 0].to(device) - + close_indices = torch.nonzero(difference <= thershold).squeeze() RIS = relative_stability_objective( - x_original.detach(), x_preturb.detach(), attribution, att_preturb, close_indices=close_indices, input=True, attention=True + x_original[close_indices, :, :].detach(), x_preturb[close_indices, :, :].detach(), attribution, att_preturb, input=True ) ROS = relative_stability_objective( - y_pred, y_pred_preturb, attribution, att_preturb, close_indices=close_indices, input=False, attention=True + y_pred[close_indices], y_pred_preturb[close_indices], attribution, att_preturb, input=False ) else: @@ -1394,15 +1392,15 @@ def norm_function(arr): return torch.norm(arr) difference = torch.abs(y_pred_preturb - y_pred) # Find where the difference is less than or equal to a thershold - close_indices = torch.nonzero(difference <= thershold).squeeze()[:, 0].to(device) + close_indices = torch.nonzero(difference <= thershold).squeeze() + print(close_indices) RIS = relative_stability_objective( - x_original.detach(), - x["encoder_cont"].detach(), - attribution, att_preturb, close_indices=close_indices, input=True + x_original[close_indices, :, :].detach(), x["encoder_cont"][close_indices, :, :].detach(), attribution[close_indices, :, :], att_preturb[close_indices, :, :], input=True ) ROS = relative_stability_objective( - y_pred, y_pred_preturb, attribution, att_preturb, close_indices=close_indices, input=False + y_pred[close_indices], y_pred_preturb[close_indices], attribution[close_indices, + :, :], att_preturb[close_indices, :, :], input=False ) return np.max(RIS.cpu().numpy()).astype(np.float64), np.max(ROS.cpu().numpy()).astype(np.float64) From 8b39c6ebbd3d3a606ddf6dd422d37d60cb41e995 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Thu, 28 Dec 2023 15:59:26 +0100 Subject: [PATCH 126/142] reverted changes --- configs/prediction_models/DeepARpytorch.gin | 68 +---- configs/prediction_models/RNNpytorch.gin | 81 +----- configs/prediction_models/TFTpytorch.gin | 2 - icu_benchmarks/cross_validation.py | 4 +- icu_benchmarks/data/loader.py | 12 +- icu_benchmarks/models/constants.py | 14 - icu_benchmarks/models/custom_metrics.py | 3 +- icu_benchmarks/models/dl_models.py | 22 +- icu_benchmarks/models/train.py | 51 ++-- icu_benchmarks/models/wrappers.py | 285 +------------------- icu_benchmarks/run_utils.py | 20 +- 11 files changed, 67 insertions(+), 495 deletions(-) diff --git a/configs/prediction_models/DeepARpytorch.gin b/configs/prediction_models/DeepARpytorch.gin index 04d8011a..bcc6abf6 100644 --- a/configs/prediction_models/DeepARpytorch.gin +++ b/configs/prediction_models/DeepARpytorch.gin @@ -29,63 +29,16 @@ train_common/hyperparameter.gradient_clip_val=0.01 # Dataset params PredictionDatasetpytorch.max_encoder_length = 24 PredictionDatasetpytorch.max_prediction_length = 1 +PredictionDatasetpytorch.time_varying_known_reals=['alb_lag_1', 'alp_lag_1', 'alt_lag_1', 'ast_lag_1', 'be_lag_1', 'bicar_lag_1', 'bili_lag_1', 'bili_dir_lag_1', 'bnd_lag_1', 'bun_lag_1', 'ca_lag_1', 'cai_lag_1', 'ck_lag_1', 'ckmb_lag_1', 'cl_lag_1', 'crea_lag_1', 'crp_lag_1', 'dbp_lag_1', 'fgn_lag_1', 'fio2_lag_1', 'glu_lag_1', 'hgb_lag_1', 'hr_lag_1', 'inr_pt_lag_1', 'k_lag_1', 'lact_lag_1', 'lymph_lag_1', 'map_lag_1', 'mch_lag_1', 'mchc_lag_1', 'mcv_lag_1', 'methb_lag_1', 'mg_lag_1', 'na_lag_1', 'neut_lag_1', 'o2sat_lag_1', 'pco2_lag_1', 'ph_lag_1', 'phos_lag_1', 'plt_lag_1', 'po2_lag_1', 'ptt_lag_1', 'resp_lag_1', 'sbp_lag_1', 'temp_lag_1', 'tnt_lag_1', 'urine_lag_1', 'wbc_lag_1', 'alb_lag_2', 'alp_lag_2', 'alt_lag_2', 'ast_lag_2', 'be_lag_2', 'bicar_lag_2', 'bili_lag_2', 'bili_dir_lag_2', 'bnd_lag_2', 'bun_lag_2', 'ca_lag_2', 'cai_lag_2', 'ck_lag_2', 'ckmb_lag_2', 'cl_lag_2', 'crea_lag_2', 'crp_lag_2', 'dbp_lag_2', 'fgn_lag_2', 'fio2_lag_2', 'glu_lag_2', 'hgb_lag_2', 'hr_lag_2', 'inr_pt_lag_2', 'k_lag_2', 'lact_lag_2', 'lymph_lag_2', 'map_lag_2', 'mch_lag_2', 'mchc_lag_2', 'mcv_lag_2', 'methb_lag_2', 'mg_lag_2', 'na_lag_2', 'neut_lag_2', 'o2sat_lag_2', 'pco2_lag_2', 'ph_lag_2', 'phos_lag_2', 'plt_lag_2', 'po2_lag_2', 'ptt_lag_2', 'resp_lag_2', 'sbp_lag_2', 'temp_lag_2', 'tnt_lag_2', 'urine_lag_2', 'wbc_lag_2', 'alb_lag_3', 'alp_lag_3', 'alt_lag_3', 'ast_lag_3', 'be_lag_3', 'bicar_lag_3', 'bili_lag_3', 'bili_dir_lag_3', 'bnd_lag_3', 'bun_lag_3', 'ca_lag_3', 'cai_lag_3', 'ck_lag_3', 'ckmb_lag_3', 'cl_lag_3', 'crea_lag_3', 'crp_lag_3', 'dbp_lag_3', 'fgn_lag_3', 'fio2_lag_3', 'glu_lag_3', 'hgb_lag_3', 'hr_lag_3', 'inr_pt_lag_3', 'k_lag_3', 'lact_lag_3', 'lymph_lag_3', 'map_lag_3', 'mch_lag_3', 'mchc_lag_3', 'mcv_lag_3', 'methb_lag_3', 'mg_lag_3', 'na_lag_3', 'neut_lag_3', 'o2sat_lag_3', 'pco2_lag_3', 'ph_lag_3', 'phos_lag_3', 'plt_lag_3', 'po2_lag_3', 'ptt_lag_3', 'resp_lag_3', 'sbp_lag_3', 'temp_lag_3', 'tnt_lag_3', 'urine_lag_3', 'wbc_lag_3', 'alb_lag_4', 'alp_lag_4', 'alt_lag_4', 'ast_lag_4', 'be_lag_4', 'bicar_lag_4', 'bili_lag_4', 'bili_dir_lag_4', 'bnd_lag_4', 'bun_lag_4', 'ca_lag_4', 'cai_lag_4', 'ck_lag_4', 'ckmb_lag_4', 'cl_lag_4', 'crea_lag_4', 'crp_lag_4', 'dbp_lag_4', 'fgn_lag_4', 'fio2_lag_4', 'glu_lag_4', 'hgb_lag_4', 'hr_lag_4', 'inr_pt_lag_4', 'k_lag_4', 'lact_lag_4', 'lymph_lag_4', 'map_lag_4', 'mch_lag_4', 'mchc_lag_4', 'mcv_lag_4', 'methb_lag_4', 'mg_lag_4', 'na_lag_4', 'neut_lag_4', 'o2sat_lag_4', 'pco2_lag_4', 'ph_lag_4', 'phos_lag_4', 'plt_lag_4', 'po2_lag_4', 'ptt_lag_4', 'resp_lag_4', 'sbp_lag_4', 'temp_lag_4', 'tnt_lag_4', 'urine_lag_4', 'wbc_lag_4', 'alb_lag_5', 'alp_lag_5', 'alt_lag_5', 'ast_lag_5', 'be_lag_5', 'bicar_lag_5', 'bili_lag_5', 'bili_dir_lag_5', 'bnd_lag_5', 'bun_lag_5', 'ca_lag_5', 'cai_lag_5', 'ck_lag_5', 'ckmb_lag_5', 'cl_lag_5', 'crea_lag_5', 'crp_lag_5', 'dbp_lag_5', 'fgn_lag_5', 'fio2_lag_5', 'glu_lag_5', 'hgb_lag_5', 'hr_lag_5', 'inr_pt_lag_5', 'k_lag_5', 'lact_lag_5', 'lymph_lag_5', 'map_lag_5', 'mch_lag_5', 'mchc_lag_5', 'mcv_lag_5', 'methb_lag_5', 'mg_lag_5', 'na_lag_5', 'neut_lag_5', 'o2sat_lag_5', 'pco2_lag_5', 'ph_lag_5', 'phos_lag_5', 'plt_lag_5', 'po2_lag_5', 'ptt_lag_5', 'resp_lag_5', 'sbp_lag_5', 'temp_lag_5', 'tnt_lag_5', 'urine_lag_5', 'wbc_lag_5', 'alb_lag_6', 'alp_lag_6', 'alt_lag_6', 'ast_lag_6', 'be_lag_6', 'bicar_lag_6', 'bili_lag_6', 'bili_dir_lag_6', 'bnd_lag_6', 'bun_lag_6', 'ca_lag_6', 'cai_lag_6', 'ck_lag_6', 'ckmb_lag_6', 'cl_lag_6', 'crea_lag_6', 'crp_lag_6', 'dbp_lag_6', 'fgn_lag_6', 'fio2_lag_6', 'glu_lag_6', 'hgb_lag_6', 'hr_lag_6', 'inr_pt_lag_6', 'k_lag_6', 'lact_lag_6', 'lymph_lag_6', 'map_lag_6', 'mch_lag_6', 'mchc_lag_6', 'mcv_lag_6', 'methb_lag_6', 'mg_lag_6', 'na_lag_6', 'neut_lag_6', 'o2sat_lag_6', 'pco2_lag_6', 'ph_lag_6', 'phos_lag_6', 'plt_lag_6', 'po2_lag_6', 'ptt_lag_6', 'resp_lag_6', 'sbp_lag_6', 'temp_lag_6', 'tnt_lag_6', 'urine_lag_6', 'wbc_lag_6', 'alb_lag_7', 'alp_lag_7', 'alt_lag_7', 'ast_lag_7', 'be_lag_7', 'bicar_lag_7', 'bili_lag_7', 'bili_dir_lag_7', 'bnd_lag_7', 'bun_lag_7', 'ca_lag_7', 'cai_lag_7', 'ck_lag_7', 'ckmb_lag_7', 'cl_lag_7', 'crea_lag_7', 'crp_lag_7', 'dbp_lag_7', 'fgn_lag_7', 'fio2_lag_7', 'glu_lag_7', 'hgb_lag_7', 'hr_lag_7', 'inr_pt_lag_7', 'k_lag_7', 'lact_lag_7', 'lymph_lag_7', 'map_lag_7', 'mch_lag_7', 'mchc_lag_7', 'mcv_lag_7', 'methb_lag_7', 'mg_lag_7', 'na_lag_7', 'neut_lag_7', 'o2sat_lag_7', 'pco2_lag_7', 'ph_lag_7', 'phos_lag_7', 'plt_lag_7', 'po2_lag_7', 'ptt_lag_7', 'resp_lag_7', 'sbp_lag_7', 'temp_lag_7', 'tnt_lag_7', 'urine_lag_7', 'wbc_lag_7', 'alb_lag_8', 'alp_lag_8', 'alt_lag_8', 'ast_lag_8', 'be_lag_8', 'bicar_lag_8', 'bili_lag_8', 'bili_dir_lag_8', 'bnd_lag_8', 'bun_lag_8', 'ca_lag_8', 'cai_lag_8', 'ck_lag_8', 'ckmb_lag_8', 'cl_lag_8', 'crea_lag_8', 'crp_lag_8', 'dbp_lag_8', 'fgn_lag_8', 'fio2_lag_8', 'glu_lag_8', 'hgb_lag_8', 'hr_lag_8', 'inr_pt_lag_8', 'k_lag_8', 'lact_lag_8', 'lymph_lag_8', 'map_lag_8', 'mch_lag_8', 'mchc_lag_8', 'mcv_lag_8', 'methb_lag_8', 'mg_lag_8', 'na_lag_8', 'neut_lag_8', 'o2sat_lag_8', 'pco2_lag_8', 'ph_lag_8', 'phos_lag_8', 'plt_lag_8', 'po2_lag_8', 'ptt_lag_8', 'resp_lag_8', 'sbp_lag_8', 'temp_lag_8', 'tnt_lag_8', 'urine_lag_8', 'wbc_lag_8', 'alb_lag_9', 'alp_lag_9', 'alt_lag_9', 'ast_lag_9', 'be_lag_9', 'bicar_lag_9', 'bili_lag_9', 'bili_dir_lag_9', 'bnd_lag_9', 'bun_lag_9', 'ca_lag_9', 'cai_lag_9', 'ck_lag_9', 'ckmb_lag_9', 'cl_lag_9', 'crea_lag_9', 'crp_lag_9', 'dbp_lag_9', 'fgn_lag_9', 'fio2_lag_9', 'glu_lag_9', 'hgb_lag_9', 'hr_lag_9', 'inr_pt_lag_9', 'k_lag_9', 'lact_lag_9', 'lymph_lag_9', 'map_lag_9', 'mch_lag_9', 'mchc_lag_9', 'mcv_lag_9', 'methb_lag_9', 'mg_lag_9', 'na_lag_9', 'neut_lag_9', 'o2sat_lag_9', 'pco2_lag_9', 'ph_lag_9', 'phos_lag_9', 'plt_lag_9', 'po2_lag_9', 'ptt_lag_9', 'resp_lag_9', 'sbp_lag_9', 'temp_lag_9', 'tnt_lag_9', 'urine_lag_9', 'wbc_lag_9', 'alb_lag_10', 'alp_lag_10', 'alt_lag_10', 'ast_lag_10', 'be_lag_10', 'bicar_lag_10', 'bili_lag_10', 'bili_dir_lag_10', 'bnd_lag_10', 'bun_lag_10', 'ca_lag_10', 'cai_lag_10', 'ck_lag_10', 'ckmb_lag_10', 'cl_lag_10', 'crea_lag_10', 'crp_lag_10', 'dbp_lag_10', 'fgn_lag_10', 'fio2_lag_10', 'glu_lag_10', 'hgb_lag_10', 'hr_lag_10', 'inr_pt_lag_10', 'k_lag_10', 'lact_lag_10', 'lymph_lag_10', 'map_lag_10', 'mch_lag_10', 'mchc_lag_10', 'mcv_lag_10', 'methb_lag_10', 'mg_lag_10', 'na_lag_10', 'neut_lag_10', 'o2sat_lag_10', 'pco2_lag_10', 'ph_lag_10', 'phos_lag_10', 'plt_lag_10', 'po2_lag_10', 'ptt_lag_10', 'resp_lag_10', 'sbp_lag_10', 'temp_lag_10', 'tnt_lag_10', 'urine_lag_10', 'wbc_lag_10', 'alb_lag_11', 'alp_lag_11', 'alt_lag_11', 'ast_lag_11', 'be_lag_11', 'bicar_lag_11', 'bili_lag_11', 'bili_dir_lag_11', 'bnd_lag_11', 'bun_lag_11', 'ca_lag_11', 'cai_lag_11', 'ck_lag_11', 'ckmb_lag_11', 'cl_lag_11', 'crea_lag_11', 'crp_lag_11', 'dbp_lag_11', 'fgn_lag_11', 'fio2_lag_11', 'glu_lag_11', 'hgb_lag_11', 'hr_lag_11', 'inr_pt_lag_11', 'k_lag_11', 'lact_lag_11', 'lymph_lag_11', 'map_lag_11', 'mch_lag_11', 'mchc_lag_11', 'mcv_lag_11', 'methb_lag_11', 'mg_lag_11', 'na_lag_11', 'neut_lag_11', 'o2sat_lag_11', 'pco2_lag_11', 'ph_lag_11', 'phos_lag_11', 'plt_lag_11', 'po2_lag_11', 'ptt_lag_11', 'resp_lag_11', 'sbp_lag_11', 'temp_lag_11', 'tnt_lag_11', 'urine_lag_11', 'wbc_lag_11', 'alb_lag_12', 'alp_lag_12', 'alt_lag_12', 'ast_lag_12', 'be_lag_12', 'bicar_lag_12', 'bili_lag_12', 'bili_dir_lag_12', 'bnd_lag_12', 'bun_lag_12', 'ca_lag_12', 'cai_lag_12', 'ck_lag_12', 'ckmb_lag_12', 'cl_lag_12', 'crea_lag_12', 'crp_lag_12', 'dbp_lag_12', 'fgn_lag_12', 'fio2_lag_12', 'glu_lag_12', 'hgb_lag_12', 'hr_lag_12', 'inr_pt_lag_12', 'k_lag_12', 'lact_lag_12', 'lymph_lag_12', 'map_lag_12', 'mch_lag_12', 'mchc_lag_12', 'mcv_lag_12', 'methb_lag_12', 'mg_lag_12', 'na_lag_12', 'neut_lag_12', 'o2sat_lag_12', 'pco2_lag_12', 'ph_lag_12', 'phos_lag_12', 'plt_lag_12', 'po2_lag_12', 'ptt_lag_12', 'resp_lag_12', 'sbp_lag_12', 'temp_lag_12', 'tnt_lag_12', 'urine_lag_12', 'wbc_lag_12', 'alb_lag_13', 'alp_lag_13', 'alt_lag_13', 'ast_lag_13', 'be_lag_13', 'bicar_lag_13', 'bili_lag_13', 'bili_dir_lag_13', 'bnd_lag_13', 'bun_lag_13', 'ca_lag_13', 'cai_lag_13', 'ck_lag_13', 'ckmb_lag_13', 'cl_lag_13', 'crea_lag_13', 'crp_lag_13', 'dbp_lag_13', 'fgn_lag_13', 'fio2_lag_13', 'glu_lag_13', 'hgb_lag_13', 'hr_lag_13', 'inr_pt_lag_13', 'k_lag_13', 'lact_lag_13', 'lymph_lag_13', 'map_lag_13', 'mch_lag_13', 'mchc_lag_13', 'mcv_lag_13', 'methb_lag_13', 'mg_lag_13', 'na_lag_13', 'neut_lag_13', 'o2sat_lag_13', 'pco2_lag_13', 'ph_lag_13', 'phos_lag_13', 'plt_lag_13', 'po2_lag_13', 'ptt_lag_13', 'resp_lag_13', 'sbp_lag_13', 'temp_lag_13', 'tnt_lag_13', 'urine_lag_13', 'wbc_lag_13', 'alb_lag_14', 'alp_lag_14', 'alt_lag_14', 'ast_lag_14', 'be_lag_14', 'bicar_lag_14', 'bili_lag_14', 'bili_dir_lag_14', 'bnd_lag_14', 'bun_lag_14', 'ca_lag_14', 'cai_lag_14', 'ck_lag_14', 'ckmb_lag_14', 'cl_lag_14', 'crea_lag_14', 'crp_lag_14', 'dbp_lag_14', 'fgn_lag_14', 'fio2_lag_14', 'glu_lag_14', 'hgb_lag_14', 'hr_lag_14', 'inr_pt_lag_14', 'k_lag_14', 'lact_lag_14', 'lymph_lag_14', 'map_lag_14', 'mch_lag_14', 'mchc_lag_14', 'mcv_lag_14', 'methb_lag_14', 'mg_lag_14', 'na_lag_14', 'neut_lag_14', 'o2sat_lag_14', 'pco2_lag_14', 'ph_lag_14', 'phos_lag_14', 'plt_lag_14', 'po2_lag_14', 'ptt_lag_14', 'resp_lag_14', 'sbp_lag_14', 'temp_lag_14', 'tnt_lag_14', 'urine_lag_14', 'wbc_lag_14', 'alb_lag_15', 'alp_lag_15', 'alt_lag_15', 'ast_lag_15', 'be_lag_15', 'bicar_lag_15', 'bili_lag_15', 'bili_dir_lag_15', 'bnd_lag_15', 'bun_lag_15', 'ca_lag_15', 'cai_lag_15', 'ck_lag_15', 'ckmb_lag_15', 'cl_lag_15', 'crea_lag_15', 'crp_lag_15', 'dbp_lag_15', 'fgn_lag_15', 'fio2_lag_15', 'glu_lag_15', 'hgb_lag_15', 'hr_lag_15', 'inr_pt_lag_15', 'k_lag_15', 'lact_lag_15', 'lymph_lag_15', 'map_lag_15', 'mch_lag_15', 'mchc_lag_15', 'mcv_lag_15', 'methb_lag_15', 'mg_lag_15', 'na_lag_15', 'neut_lag_15', 'o2sat_lag_15', 'pco2_lag_15', 'ph_lag_15', 'phos_lag_15', 'plt_lag_15', 'po2_lag_15', 'ptt_lag_15', 'resp_lag_15', 'sbp_lag_15', 'temp_lag_15', 'tnt_lag_15', 'urine_lag_15', 'wbc_lag_15', 'alb_lag_16', 'alp_lag_16', 'alt_lag_16', 'ast_lag_16', 'be_lag_16', 'bicar_lag_16', 'bili_lag_16', 'bili_dir_lag_16', 'bnd_lag_16', 'bun_lag_16', 'ca_lag_16', 'cai_lag_16', 'ck_lag_16', 'ckmb_lag_16', 'cl_lag_16', 'crea_lag_16', 'crp_lag_16', 'dbp_lag_16', 'fgn_lag_16', 'fio2_lag_16', 'glu_lag_16', 'hgb_lag_16', 'hr_lag_16', 'inr_pt_lag_16', 'k_lag_16', 'lact_lag_16', 'lymph_lag_16', 'map_lag_16', 'mch_lag_16', 'mchc_lag_16', 'mcv_lag_16', 'methb_lag_16', 'mg_lag_16', 'na_lag_16', 'neut_lag_16', 'o2sat_lag_16', 'pco2_lag_16', 'ph_lag_16', 'phos_lag_16', 'plt_lag_16', 'po2_lag_16', 'ptt_lag_16', 'resp_lag_16', 'sbp_lag_16', 'temp_lag_16', 'tnt_lag_16', 'urine_lag_16', 'wbc_lag_16', 'alb_lag_17', 'alp_lag_17', 'alt_lag_17', 'ast_lag_17', 'be_lag_17', 'bicar_lag_17', 'bili_lag_17', 'bili_dir_lag_17', 'bnd_lag_17', 'bun_lag_17', 'ca_lag_17', 'cai_lag_17', 'ck_lag_17', 'ckmb_lag_17', 'cl_lag_17', 'crea_lag_17', 'crp_lag_17', 'dbp_lag_17', 'fgn_lag_17', 'fio2_lag_17', 'glu_lag_17', 'hgb_lag_17', 'hr_lag_17', 'inr_pt_lag_17', 'k_lag_17', 'lact_lag_17', 'lymph_lag_17', 'map_lag_17', 'mch_lag_17', 'mchc_lag_17', 'mcv_lag_17', 'methb_lag_17', 'mg_lag_17', 'na_lag_17', 'neut_lag_17', 'o2sat_lag_17', 'pco2_lag_17', 'ph_lag_17', 'phos_lag_17', 'plt_lag_17', 'po2_lag_17', 'ptt_lag_17', 'resp_lag_17', 'sbp_lag_17', 'temp_lag_17', 'tnt_lag_17', 'urine_lag_17', 'wbc_lag_17', 'alb_lag_18', 'alp_lag_18', 'alt_lag_18', 'ast_lag_18', 'be_lag_18', 'bicar_lag_18', 'bili_lag_18', 'bili_dir_lag_18', 'bnd_lag_18', 'bun_lag_18', 'ca_lag_18', 'cai_lag_18', 'ck_lag_18', 'ckmb_lag_18', 'cl_lag_18', 'crea_lag_18', 'crp_lag_18', 'dbp_lag_18', 'fgn_lag_18', 'fio2_lag_18', 'glu_lag_18', 'hgb_lag_18', 'hr_lag_18', 'inr_pt_lag_18', 'k_lag_18', 'lact_lag_18', 'lymph_lag_18', 'map_lag_18', 'mch_lag_18', 'mchc_lag_18', 'mcv_lag_18', 'methb_lag_18', 'mg_lag_18', 'na_lag_18', 'neut_lag_18', 'o2sat_lag_18', 'pco2_lag_18', 'ph_lag_18', 'phos_lag_18', 'plt_lag_18', 'po2_lag_18', 'ptt_lag_18', 'resp_lag_18', 'sbp_lag_18', 'temp_lag_18', 'tnt_lag_18', 'urine_lag_18', 'wbc_lag_18', 'alb_lag_19', 'alp_lag_19', 'alt_lag_19', 'ast_lag_19', 'be_lag_19', 'bicar_lag_19', 'bili_lag_19', 'bili_dir_lag_19', 'bnd_lag_19', 'bun_lag_19', 'ca_lag_19', 'cai_lag_19', 'ck_lag_19', 'ckmb_lag_19', 'cl_lag_19', 'crea_lag_19', 'crp_lag_19', 'dbp_lag_19', 'fgn_lag_19', 'fio2_lag_19', 'glu_lag_19', 'hgb_lag_19', 'hr_lag_19', 'inr_pt_lag_19', 'k_lag_19', 'lact_lag_19', 'lymph_lag_19', 'map_lag_19', 'mch_lag_19', 'mchc_lag_19', 'mcv_lag_19', 'methb_lag_19', 'mg_lag_19', 'na_lag_19', 'neut_lag_19', 'o2sat_lag_19', 'pco2_lag_19', 'ph_lag_19', 'phos_lag_19', 'plt_lag_19', 'po2_lag_19', 'ptt_lag_19', 'resp_lag_19', 'sbp_lag_19', 'temp_lag_19', 'tnt_lag_19', 'urine_lag_19', 'wbc_lag_19', 'alb_lag_20', 'alp_lag_20', 'alt_lag_20', 'ast_lag_20', 'be_lag_20', 'bicar_lag_20', 'bili_lag_20', 'bili_dir_lag_20', 'bnd_lag_20', 'bun_lag_20', 'ca_lag_20', 'cai_lag_20', 'ck_lag_20', 'ckmb_lag_20', 'cl_lag_20', 'crea_lag_20', 'crp_lag_20', 'dbp_lag_20', 'fgn_lag_20', 'fio2_lag_20', 'glu_lag_20', 'hgb_lag_20', 'hr_lag_20', 'inr_pt_lag_20', 'k_lag_20', 'lact_lag_20', 'lymph_lag_20', 'map_lag_20', 'mch_lag_20', 'mchc_lag_20', 'mcv_lag_20', 'methb_lag_20', 'mg_lag_20', 'na_lag_20', 'neut_lag_20', 'o2sat_lag_20', 'pco2_lag_20', 'ph_lag_20', 'phos_lag_20', 'plt_lag_20', 'po2_lag_20', 'ptt_lag_20', 'resp_lag_20', 'sbp_lag_20', 'temp_lag_20', 'tnt_lag_20', 'urine_lag_20', 'wbc_lag_20', 'alb_lag_21', 'alp_lag_21', 'alt_lag_21', 'ast_lag_21', 'be_lag_21', 'bicar_lag_21', 'bili_lag_21', 'bili_dir_lag_21', 'bnd_lag_21', 'bun_lag_21', 'ca_lag_21', 'cai_lag_21', 'ck_lag_21', 'ckmb_lag_21', 'cl_lag_21', 'crea_lag_21', 'crp_lag_21', 'dbp_lag_21', 'fgn_lag_21', 'fio2_lag_21', 'glu_lag_21', 'hgb_lag_21', 'hr_lag_21', 'inr_pt_lag_21', 'k_lag_21', 'lact_lag_21', 'lymph_lag_21', 'map_lag_21', 'mch_lag_21', 'mchc_lag_21', 'mcv_lag_21', 'methb_lag_21', 'mg_lag_21', 'na_lag_21', 'neut_lag_21', 'o2sat_lag_21', 'pco2_lag_21', 'ph_lag_21', 'phos_lag_21', 'plt_lag_21', 'po2_lag_21', 'ptt_lag_21', 'resp_lag_21', 'sbp_lag_21', 'temp_lag_21', 'tnt_lag_21', 'urine_lag_21', 'wbc_lag_21', 'alb_lag_22', 'alp_lag_22', 'alt_lag_22', 'ast_lag_22', 'be_lag_22', 'bicar_lag_22', 'bili_lag_22', 'bili_dir_lag_22', 'bnd_lag_22', 'bun_lag_22', 'ca_lag_22', 'cai_lag_22', 'ck_lag_22', 'ckmb_lag_22', 'cl_lag_22', 'crea_lag_22', 'crp_lag_22', 'dbp_lag_22', 'fgn_lag_22', 'fio2_lag_22', 'glu_lag_22', 'hgb_lag_22', 'hr_lag_22', 'inr_pt_lag_22', 'k_lag_22', 'lact_lag_22', 'lymph_lag_22', 'map_lag_22', 'mch_lag_22', 'mchc_lag_22', 'mcv_lag_22', 'methb_lag_22', 'mg_lag_22', 'na_lag_22', 'neut_lag_22', 'o2sat_lag_22', 'pco2_lag_22', 'ph_lag_22', 'phos_lag_22', 'plt_lag_22', 'po2_lag_22', 'ptt_lag_22', 'resp_lag_22', 'sbp_lag_22', 'temp_lag_22', 'tnt_lag_22', 'urine_lag_22', 'wbc_lag_22', 'alb_lag_23', 'alp_lag_23', 'alt_lag_23', 'ast_lag_23', 'be_lag_23', 'bicar_lag_23', 'bili_lag_23', 'bili_dir_lag_23', 'bnd_lag_23', 'bun_lag_23', 'ca_lag_23', 'cai_lag_23', 'ck_lag_23', 'ckmb_lag_23', 'cl_lag_23', 'crea_lag_23', 'crp_lag_23', 'dbp_lag_23', 'fgn_lag_23', 'fio2_lag_23', 'glu_lag_23', 'hgb_lag_23', 'hr_lag_23', 'inr_pt_lag_23', 'k_lag_23', 'lact_lag_23', 'lymph_lag_23', 'map_lag_23', 'mch_lag_23', 'mchc_lag_23', 'mcv_lag_23', 'methb_lag_23', 'mg_lag_23', 'na_lag_23', 'neut_lag_23', 'o2sat_lag_23', 'pco2_lag_23', 'ph_lag_23', 'phos_lag_23', 'plt_lag_23', 'po2_lag_23', 'ptt_lag_23', 'resp_lag_23', 'sbp_lag_23', 'temp_lag_23', 'tnt_lag_23', 'urine_lag_23', 'wbc_lag_23', 'alb_lag_24', 'alp_lag_24', 'alt_lag_24', 'ast_lag_24', 'be_lag_24', 'bicar_lag_24', 'bili_lag_24', 'bili_dir_lag_24', 'bnd_lag_24', 'bun_lag_24', 'ca_lag_24', 'cai_lag_24', 'ck_lag_24', 'ckmb_lag_24', 'cl_lag_24', 'crea_lag_24', 'crp_lag_24', 'dbp_lag_24', 'fgn_lag_24', 'fio2_lag_24', 'glu_lag_24', 'hgb_lag_24', 'hr_lag_24', 'inr_pt_lag_24', 'k_lag_24', 'lact_lag_24', 'lymph_lag_24', 'map_lag_24', 'mch_lag_24', 'mchc_lag_24', 'mcv_lag_24', 'methb_lag_24', 'mg_lag_24', 'na_lag_24', 'neut_lag_24', 'o2sat_lag_24', 'pco2_lag_24', 'ph_lag_24', 'phos_lag_24', 'plt_lag_24', 'po2_lag_24', 'ptt_lag_24', 'resp_lag_24', 'sbp_lag_24', 'temp_lag_24', 'tnt_lag_24', 'urine_lag_24', 'wbc_lag_24' ] PredictionDatasetpytorch.add_relative_time_idx=False -PredictionDatasetpytorch.target=[ - "alb", - "alp", - "alt", - "ast", - "be", - "bicar", - "bili", - "bili_dir", - "bnd", - "bun", - "ca", - "cai", - "ck", - "ckmb", - "cl", - "crea", - "crp", - "dbp", - "fgn", - "fio2", - "glu", - "hgb", - "hr", - "inr_pt", - "k", - "lact", - "lymph", - "map", - "mch", - "mchc", - "mcv", - "methb", - "mg", - "na", - "neut", - "o2sat", - "pco2", - "ph", - "phos", - "plt", - "po2", - "ptt", - "resp", - "sbp", - "temp", - "tnt", - "urine", - "wbc", +PredictionDatasetpytorch.target="label" + +PredictionDatasetpytorch.time_varying_unknown_reals=[ "label", ] - -PredictionDatasetpytorch.time_varying_known_reals=[] -PredictionDatasetpytorch.time_varying_unknown_reals=[ - "alb", +PredictionDatasetpytorch.time_varying_unknown_categoricals=[] +PredictionDatasetpytorch.lagged_variables=[ "alb", "alp", "alt", "ast", @@ -132,11 +85,6 @@ PredictionDatasetpytorch.time_varying_unknown_reals=[ "temp", "tnt", "urine", - "wbc", - "label", - - ] -PredictionDatasetpytorch.time_varying_unknown_categoricals=[] -PredictionDatasetpytorch.lagged_variables=[] + "wbc"] -PredictionDatasetpytorch.target_normalizer='multi' \ No newline at end of file + \ No newline at end of file diff --git a/configs/prediction_models/RNNpytorch.gin b/configs/prediction_models/RNNpytorch.gin index a1804a1d..6c0f2e15 100644 --- a/configs/prediction_models/RNNpytorch.gin +++ b/configs/prediction_models/RNNpytorch.gin @@ -5,89 +5,42 @@ include "configs/prediction_models/common/DLCommon.gin" # Optimizer params train_common.model = @RNNpytorch + optimizer/hyperparameter.class_to_tune = @Adam optimizer/hyperparameter.weight_decay = 1e-6 -optimizer/hyperparameter.lr = 4.4815375287835764e-05 +optimizer/hyperparameter.lr = 0.00041 # Model params model/hyperparameter.class_to_tune = @RNNpytorch -model/hyperparameter.hidden = 42 +model/hyperparameter.hidden = 214 #(2, 64, "log-uniform", 2) -model/hyperparameter.rnn_layers=2 +model/hyperparameter.rnn_layers=1 #(1,3) model/hyperparameter.num_classes = %NUM_CLASSES model/hyperparameter.cell_type='LSTM' -model/hyperparameter.dropout = (0.397, 0.3971) +model/hyperparameter.dropout = (0.244, 0.2441) #(0.0, 0.4) #model/hyperparameter.lr_scheduler = "exponential" train_common/hyperparameter.class_to_tune = @train_common -train_common/hyperparameter.batch_size=128 +train_common/hyperparameter.batch_size=256 #(32,64,128,256,512) -train_common/hyperparameter.gradient_clip_val=0.01 +train_common/hyperparameter.gradient_clip_val=100.0 #(0,0.01, 1.0, 100.0) # Dataset params PredictionDatasetpytorch.max_encoder_length = 24 PredictionDatasetpytorch.max_prediction_length = 1 -PredictionDatasetpytorch.time_varying_known_reals=[] +PredictionDatasetpytorch.time_varying_known_reals=['alb_lag_1', 'alp_lag_1', 'alt_lag_1', 'ast_lag_1', 'be_lag_1', 'bicar_lag_1', 'bili_lag_1', 'bili_dir_lag_1', 'bnd_lag_1', 'bun_lag_1', 'ca_lag_1', 'cai_lag_1', 'ck_lag_1', 'ckmb_lag_1', 'cl_lag_1', 'crea_lag_1', 'crp_lag_1', 'dbp_lag_1', 'fgn_lag_1', 'fio2_lag_1', 'glu_lag_1', 'hgb_lag_1', 'hr_lag_1', 'inr_pt_lag_1', 'k_lag_1', 'lact_lag_1', 'lymph_lag_1', 'map_lag_1', 'mch_lag_1', 'mchc_lag_1', 'mcv_lag_1', 'methb_lag_1', 'mg_lag_1', 'na_lag_1', 'neut_lag_1', 'o2sat_lag_1', 'pco2_lag_1', 'ph_lag_1', 'phos_lag_1', 'plt_lag_1', 'po2_lag_1', 'ptt_lag_1', 'resp_lag_1', 'sbp_lag_1', 'temp_lag_1', 'tnt_lag_1', 'urine_lag_1', 'wbc_lag_1', 'alb_lag_2', 'alp_lag_2', 'alt_lag_2', 'ast_lag_2', 'be_lag_2', 'bicar_lag_2', 'bili_lag_2', 'bili_dir_lag_2', 'bnd_lag_2', 'bun_lag_2', 'ca_lag_2', 'cai_lag_2', 'ck_lag_2', 'ckmb_lag_2', 'cl_lag_2', 'crea_lag_2', 'crp_lag_2', 'dbp_lag_2', 'fgn_lag_2', 'fio2_lag_2', 'glu_lag_2', 'hgb_lag_2', 'hr_lag_2', 'inr_pt_lag_2', 'k_lag_2', 'lact_lag_2', 'lymph_lag_2', 'map_lag_2', 'mch_lag_2', 'mchc_lag_2', 'mcv_lag_2', 'methb_lag_2', 'mg_lag_2', 'na_lag_2', 'neut_lag_2', 'o2sat_lag_2', 'pco2_lag_2', 'ph_lag_2', 'phos_lag_2', 'plt_lag_2', 'po2_lag_2', 'ptt_lag_2', 'resp_lag_2', 'sbp_lag_2', 'temp_lag_2', 'tnt_lag_2', 'urine_lag_2', 'wbc_lag_2', 'alb_lag_3', 'alp_lag_3', 'alt_lag_3', 'ast_lag_3', 'be_lag_3', 'bicar_lag_3', 'bili_lag_3', 'bili_dir_lag_3', 'bnd_lag_3', 'bun_lag_3', 'ca_lag_3', 'cai_lag_3', 'ck_lag_3', 'ckmb_lag_3', 'cl_lag_3', 'crea_lag_3', 'crp_lag_3', 'dbp_lag_3', 'fgn_lag_3', 'fio2_lag_3', 'glu_lag_3', 'hgb_lag_3', 'hr_lag_3', 'inr_pt_lag_3', 'k_lag_3', 'lact_lag_3', 'lymph_lag_3', 'map_lag_3', 'mch_lag_3', 'mchc_lag_3', 'mcv_lag_3', 'methb_lag_3', 'mg_lag_3', 'na_lag_3', 'neut_lag_3', 'o2sat_lag_3', 'pco2_lag_3', 'ph_lag_3', 'phos_lag_3', 'plt_lag_3', 'po2_lag_3', 'ptt_lag_3', 'resp_lag_3', 'sbp_lag_3', 'temp_lag_3', 'tnt_lag_3', 'urine_lag_3', 'wbc_lag_3', 'alb_lag_4', 'alp_lag_4', 'alt_lag_4', 'ast_lag_4', 'be_lag_4', 'bicar_lag_4', 'bili_lag_4', 'bili_dir_lag_4', 'bnd_lag_4', 'bun_lag_4', 'ca_lag_4', 'cai_lag_4', 'ck_lag_4', 'ckmb_lag_4', 'cl_lag_4', 'crea_lag_4', 'crp_lag_4', 'dbp_lag_4', 'fgn_lag_4', 'fio2_lag_4', 'glu_lag_4', 'hgb_lag_4', 'hr_lag_4', 'inr_pt_lag_4', 'k_lag_4', 'lact_lag_4', 'lymph_lag_4', 'map_lag_4', 'mch_lag_4', 'mchc_lag_4', 'mcv_lag_4', 'methb_lag_4', 'mg_lag_4', 'na_lag_4', 'neut_lag_4', 'o2sat_lag_4', 'pco2_lag_4', 'ph_lag_4', 'phos_lag_4', 'plt_lag_4', 'po2_lag_4', 'ptt_lag_4', 'resp_lag_4', 'sbp_lag_4', 'temp_lag_4', 'tnt_lag_4', 'urine_lag_4', 'wbc_lag_4', 'alb_lag_5', 'alp_lag_5', 'alt_lag_5', 'ast_lag_5', 'be_lag_5', 'bicar_lag_5', 'bili_lag_5', 'bili_dir_lag_5', 'bnd_lag_5', 'bun_lag_5', 'ca_lag_5', 'cai_lag_5', 'ck_lag_5', 'ckmb_lag_5', 'cl_lag_5', 'crea_lag_5', 'crp_lag_5', 'dbp_lag_5', 'fgn_lag_5', 'fio2_lag_5', 'glu_lag_5', 'hgb_lag_5', 'hr_lag_5', 'inr_pt_lag_5', 'k_lag_5', 'lact_lag_5', 'lymph_lag_5', 'map_lag_5', 'mch_lag_5', 'mchc_lag_5', 'mcv_lag_5', 'methb_lag_5', 'mg_lag_5', 'na_lag_5', 'neut_lag_5', 'o2sat_lag_5', 'pco2_lag_5', 'ph_lag_5', 'phos_lag_5', 'plt_lag_5', 'po2_lag_5', 'ptt_lag_5', 'resp_lag_5', 'sbp_lag_5', 'temp_lag_5', 'tnt_lag_5', 'urine_lag_5', 'wbc_lag_5', 'alb_lag_6', 'alp_lag_6', 'alt_lag_6', 'ast_lag_6', 'be_lag_6', 'bicar_lag_6', 'bili_lag_6', 'bili_dir_lag_6', 'bnd_lag_6', 'bun_lag_6', 'ca_lag_6', 'cai_lag_6', 'ck_lag_6', 'ckmb_lag_6', 'cl_lag_6', 'crea_lag_6', 'crp_lag_6', 'dbp_lag_6', 'fgn_lag_6', 'fio2_lag_6', 'glu_lag_6', 'hgb_lag_6', 'hr_lag_6', 'inr_pt_lag_6', 'k_lag_6', 'lact_lag_6', 'lymph_lag_6', 'map_lag_6', 'mch_lag_6', 'mchc_lag_6', 'mcv_lag_6', 'methb_lag_6', 'mg_lag_6', 'na_lag_6', 'neut_lag_6', 'o2sat_lag_6', 'pco2_lag_6', 'ph_lag_6', 'phos_lag_6', 'plt_lag_6', 'po2_lag_6', 'ptt_lag_6', 'resp_lag_6', 'sbp_lag_6', 'temp_lag_6', 'tnt_lag_6', 'urine_lag_6', 'wbc_lag_6', 'alb_lag_7', 'alp_lag_7', 'alt_lag_7', 'ast_lag_7', 'be_lag_7', 'bicar_lag_7', 'bili_lag_7', 'bili_dir_lag_7', 'bnd_lag_7', 'bun_lag_7', 'ca_lag_7', 'cai_lag_7', 'ck_lag_7', 'ckmb_lag_7', 'cl_lag_7', 'crea_lag_7', 'crp_lag_7', 'dbp_lag_7', 'fgn_lag_7', 'fio2_lag_7', 'glu_lag_7', 'hgb_lag_7', 'hr_lag_7', 'inr_pt_lag_7', 'k_lag_7', 'lact_lag_7', 'lymph_lag_7', 'map_lag_7', 'mch_lag_7', 'mchc_lag_7', 'mcv_lag_7', 'methb_lag_7', 'mg_lag_7', 'na_lag_7', 'neut_lag_7', 'o2sat_lag_7', 'pco2_lag_7', 'ph_lag_7', 'phos_lag_7', 'plt_lag_7', 'po2_lag_7', 'ptt_lag_7', 'resp_lag_7', 'sbp_lag_7', 'temp_lag_7', 'tnt_lag_7', 'urine_lag_7', 'wbc_lag_7', 'alb_lag_8', 'alp_lag_8', 'alt_lag_8', 'ast_lag_8', 'be_lag_8', 'bicar_lag_8', 'bili_lag_8', 'bili_dir_lag_8', 'bnd_lag_8', 'bun_lag_8', 'ca_lag_8', 'cai_lag_8', 'ck_lag_8', 'ckmb_lag_8', 'cl_lag_8', 'crea_lag_8', 'crp_lag_8', 'dbp_lag_8', 'fgn_lag_8', 'fio2_lag_8', 'glu_lag_8', 'hgb_lag_8', 'hr_lag_8', 'inr_pt_lag_8', 'k_lag_8', 'lact_lag_8', 'lymph_lag_8', 'map_lag_8', 'mch_lag_8', 'mchc_lag_8', 'mcv_lag_8', 'methb_lag_8', 'mg_lag_8', 'na_lag_8', 'neut_lag_8', 'o2sat_lag_8', 'pco2_lag_8', 'ph_lag_8', 'phos_lag_8', 'plt_lag_8', 'po2_lag_8', 'ptt_lag_8', 'resp_lag_8', 'sbp_lag_8', 'temp_lag_8', 'tnt_lag_8', 'urine_lag_8', 'wbc_lag_8', 'alb_lag_9', 'alp_lag_9', 'alt_lag_9', 'ast_lag_9', 'be_lag_9', 'bicar_lag_9', 'bili_lag_9', 'bili_dir_lag_9', 'bnd_lag_9', 'bun_lag_9', 'ca_lag_9', 'cai_lag_9', 'ck_lag_9', 'ckmb_lag_9', 'cl_lag_9', 'crea_lag_9', 'crp_lag_9', 'dbp_lag_9', 'fgn_lag_9', 'fio2_lag_9', 'glu_lag_9', 'hgb_lag_9', 'hr_lag_9', 'inr_pt_lag_9', 'k_lag_9', 'lact_lag_9', 'lymph_lag_9', 'map_lag_9', 'mch_lag_9', 'mchc_lag_9', 'mcv_lag_9', 'methb_lag_9', 'mg_lag_9', 'na_lag_9', 'neut_lag_9', 'o2sat_lag_9', 'pco2_lag_9', 'ph_lag_9', 'phos_lag_9', 'plt_lag_9', 'po2_lag_9', 'ptt_lag_9', 'resp_lag_9', 'sbp_lag_9', 'temp_lag_9', 'tnt_lag_9', 'urine_lag_9', 'wbc_lag_9', 'alb_lag_10', 'alp_lag_10', 'alt_lag_10', 'ast_lag_10', 'be_lag_10', 'bicar_lag_10', 'bili_lag_10', 'bili_dir_lag_10', 'bnd_lag_10', 'bun_lag_10', 'ca_lag_10', 'cai_lag_10', 'ck_lag_10', 'ckmb_lag_10', 'cl_lag_10', 'crea_lag_10', 'crp_lag_10', 'dbp_lag_10', 'fgn_lag_10', 'fio2_lag_10', 'glu_lag_10', 'hgb_lag_10', 'hr_lag_10', 'inr_pt_lag_10', 'k_lag_10', 'lact_lag_10', 'lymph_lag_10', 'map_lag_10', 'mch_lag_10', 'mchc_lag_10', 'mcv_lag_10', 'methb_lag_10', 'mg_lag_10', 'na_lag_10', 'neut_lag_10', 'o2sat_lag_10', 'pco2_lag_10', 'ph_lag_10', 'phos_lag_10', 'plt_lag_10', 'po2_lag_10', 'ptt_lag_10', 'resp_lag_10', 'sbp_lag_10', 'temp_lag_10', 'tnt_lag_10', 'urine_lag_10', 'wbc_lag_10', 'alb_lag_11', 'alp_lag_11', 'alt_lag_11', 'ast_lag_11', 'be_lag_11', 'bicar_lag_11', 'bili_lag_11', 'bili_dir_lag_11', 'bnd_lag_11', 'bun_lag_11', 'ca_lag_11', 'cai_lag_11', 'ck_lag_11', 'ckmb_lag_11', 'cl_lag_11', 'crea_lag_11', 'crp_lag_11', 'dbp_lag_11', 'fgn_lag_11', 'fio2_lag_11', 'glu_lag_11', 'hgb_lag_11', 'hr_lag_11', 'inr_pt_lag_11', 'k_lag_11', 'lact_lag_11', 'lymph_lag_11', 'map_lag_11', 'mch_lag_11', 'mchc_lag_11', 'mcv_lag_11', 'methb_lag_11', 'mg_lag_11', 'na_lag_11', 'neut_lag_11', 'o2sat_lag_11', 'pco2_lag_11', 'ph_lag_11', 'phos_lag_11', 'plt_lag_11', 'po2_lag_11', 'ptt_lag_11', 'resp_lag_11', 'sbp_lag_11', 'temp_lag_11', 'tnt_lag_11', 'urine_lag_11', 'wbc_lag_11', 'alb_lag_12', 'alp_lag_12', 'alt_lag_12', 'ast_lag_12', 'be_lag_12', 'bicar_lag_12', 'bili_lag_12', 'bili_dir_lag_12', 'bnd_lag_12', 'bun_lag_12', 'ca_lag_12', 'cai_lag_12', 'ck_lag_12', 'ckmb_lag_12', 'cl_lag_12', 'crea_lag_12', 'crp_lag_12', 'dbp_lag_12', 'fgn_lag_12', 'fio2_lag_12', 'glu_lag_12', 'hgb_lag_12', 'hr_lag_12', 'inr_pt_lag_12', 'k_lag_12', 'lact_lag_12', 'lymph_lag_12', 'map_lag_12', 'mch_lag_12', 'mchc_lag_12', 'mcv_lag_12', 'methb_lag_12', 'mg_lag_12', 'na_lag_12', 'neut_lag_12', 'o2sat_lag_12', 'pco2_lag_12', 'ph_lag_12', 'phos_lag_12', 'plt_lag_12', 'po2_lag_12', 'ptt_lag_12', 'resp_lag_12', 'sbp_lag_12', 'temp_lag_12', 'tnt_lag_12', 'urine_lag_12', 'wbc_lag_12', 'alb_lag_13', 'alp_lag_13', 'alt_lag_13', 'ast_lag_13', 'be_lag_13', 'bicar_lag_13', 'bili_lag_13', 'bili_dir_lag_13', 'bnd_lag_13', 'bun_lag_13', 'ca_lag_13', 'cai_lag_13', 'ck_lag_13', 'ckmb_lag_13', 'cl_lag_13', 'crea_lag_13', 'crp_lag_13', 'dbp_lag_13', 'fgn_lag_13', 'fio2_lag_13', 'glu_lag_13', 'hgb_lag_13', 'hr_lag_13', 'inr_pt_lag_13', 'k_lag_13', 'lact_lag_13', 'lymph_lag_13', 'map_lag_13', 'mch_lag_13', 'mchc_lag_13', 'mcv_lag_13', 'methb_lag_13', 'mg_lag_13', 'na_lag_13', 'neut_lag_13', 'o2sat_lag_13', 'pco2_lag_13', 'ph_lag_13', 'phos_lag_13', 'plt_lag_13', 'po2_lag_13', 'ptt_lag_13', 'resp_lag_13', 'sbp_lag_13', 'temp_lag_13', 'tnt_lag_13', 'urine_lag_13', 'wbc_lag_13', 'alb_lag_14', 'alp_lag_14', 'alt_lag_14', 'ast_lag_14', 'be_lag_14', 'bicar_lag_14', 'bili_lag_14', 'bili_dir_lag_14', 'bnd_lag_14', 'bun_lag_14', 'ca_lag_14', 'cai_lag_14', 'ck_lag_14', 'ckmb_lag_14', 'cl_lag_14', 'crea_lag_14', 'crp_lag_14', 'dbp_lag_14', 'fgn_lag_14', 'fio2_lag_14', 'glu_lag_14', 'hgb_lag_14', 'hr_lag_14', 'inr_pt_lag_14', 'k_lag_14', 'lact_lag_14', 'lymph_lag_14', 'map_lag_14', 'mch_lag_14', 'mchc_lag_14', 'mcv_lag_14', 'methb_lag_14', 'mg_lag_14', 'na_lag_14', 'neut_lag_14', 'o2sat_lag_14', 'pco2_lag_14', 'ph_lag_14', 'phos_lag_14', 'plt_lag_14', 'po2_lag_14', 'ptt_lag_14', 'resp_lag_14', 'sbp_lag_14', 'temp_lag_14', 'tnt_lag_14', 'urine_lag_14', 'wbc_lag_14', 'alb_lag_15', 'alp_lag_15', 'alt_lag_15', 'ast_lag_15', 'be_lag_15', 'bicar_lag_15', 'bili_lag_15', 'bili_dir_lag_15', 'bnd_lag_15', 'bun_lag_15', 'ca_lag_15', 'cai_lag_15', 'ck_lag_15', 'ckmb_lag_15', 'cl_lag_15', 'crea_lag_15', 'crp_lag_15', 'dbp_lag_15', 'fgn_lag_15', 'fio2_lag_15', 'glu_lag_15', 'hgb_lag_15', 'hr_lag_15', 'inr_pt_lag_15', 'k_lag_15', 'lact_lag_15', 'lymph_lag_15', 'map_lag_15', 'mch_lag_15', 'mchc_lag_15', 'mcv_lag_15', 'methb_lag_15', 'mg_lag_15', 'na_lag_15', 'neut_lag_15', 'o2sat_lag_15', 'pco2_lag_15', 'ph_lag_15', 'phos_lag_15', 'plt_lag_15', 'po2_lag_15', 'ptt_lag_15', 'resp_lag_15', 'sbp_lag_15', 'temp_lag_15', 'tnt_lag_15', 'urine_lag_15', 'wbc_lag_15', 'alb_lag_16', 'alp_lag_16', 'alt_lag_16', 'ast_lag_16', 'be_lag_16', 'bicar_lag_16', 'bili_lag_16', 'bili_dir_lag_16', 'bnd_lag_16', 'bun_lag_16', 'ca_lag_16', 'cai_lag_16', 'ck_lag_16', 'ckmb_lag_16', 'cl_lag_16', 'crea_lag_16', 'crp_lag_16', 'dbp_lag_16', 'fgn_lag_16', 'fio2_lag_16', 'glu_lag_16', 'hgb_lag_16', 'hr_lag_16', 'inr_pt_lag_16', 'k_lag_16', 'lact_lag_16', 'lymph_lag_16', 'map_lag_16', 'mch_lag_16', 'mchc_lag_16', 'mcv_lag_16', 'methb_lag_16', 'mg_lag_16', 'na_lag_16', 'neut_lag_16', 'o2sat_lag_16', 'pco2_lag_16', 'ph_lag_16', 'phos_lag_16', 'plt_lag_16', 'po2_lag_16', 'ptt_lag_16', 'resp_lag_16', 'sbp_lag_16', 'temp_lag_16', 'tnt_lag_16', 'urine_lag_16', 'wbc_lag_16', 'alb_lag_17', 'alp_lag_17', 'alt_lag_17', 'ast_lag_17', 'be_lag_17', 'bicar_lag_17', 'bili_lag_17', 'bili_dir_lag_17', 'bnd_lag_17', 'bun_lag_17', 'ca_lag_17', 'cai_lag_17', 'ck_lag_17', 'ckmb_lag_17', 'cl_lag_17', 'crea_lag_17', 'crp_lag_17', 'dbp_lag_17', 'fgn_lag_17', 'fio2_lag_17', 'glu_lag_17', 'hgb_lag_17', 'hr_lag_17', 'inr_pt_lag_17', 'k_lag_17', 'lact_lag_17', 'lymph_lag_17', 'map_lag_17', 'mch_lag_17', 'mchc_lag_17', 'mcv_lag_17', 'methb_lag_17', 'mg_lag_17', 'na_lag_17', 'neut_lag_17', 'o2sat_lag_17', 'pco2_lag_17', 'ph_lag_17', 'phos_lag_17', 'plt_lag_17', 'po2_lag_17', 'ptt_lag_17', 'resp_lag_17', 'sbp_lag_17', 'temp_lag_17', 'tnt_lag_17', 'urine_lag_17', 'wbc_lag_17', 'alb_lag_18', 'alp_lag_18', 'alt_lag_18', 'ast_lag_18', 'be_lag_18', 'bicar_lag_18', 'bili_lag_18', 'bili_dir_lag_18', 'bnd_lag_18', 'bun_lag_18', 'ca_lag_18', 'cai_lag_18', 'ck_lag_18', 'ckmb_lag_18', 'cl_lag_18', 'crea_lag_18', 'crp_lag_18', 'dbp_lag_18', 'fgn_lag_18', 'fio2_lag_18', 'glu_lag_18', 'hgb_lag_18', 'hr_lag_18', 'inr_pt_lag_18', 'k_lag_18', 'lact_lag_18', 'lymph_lag_18', 'map_lag_18', 'mch_lag_18', 'mchc_lag_18', 'mcv_lag_18', 'methb_lag_18', 'mg_lag_18', 'na_lag_18', 'neut_lag_18', 'o2sat_lag_18', 'pco2_lag_18', 'ph_lag_18', 'phos_lag_18', 'plt_lag_18', 'po2_lag_18', 'ptt_lag_18', 'resp_lag_18', 'sbp_lag_18', 'temp_lag_18', 'tnt_lag_18', 'urine_lag_18', 'wbc_lag_18', 'alb_lag_19', 'alp_lag_19', 'alt_lag_19', 'ast_lag_19', 'be_lag_19', 'bicar_lag_19', 'bili_lag_19', 'bili_dir_lag_19', 'bnd_lag_19', 'bun_lag_19', 'ca_lag_19', 'cai_lag_19', 'ck_lag_19', 'ckmb_lag_19', 'cl_lag_19', 'crea_lag_19', 'crp_lag_19', 'dbp_lag_19', 'fgn_lag_19', 'fio2_lag_19', 'glu_lag_19', 'hgb_lag_19', 'hr_lag_19', 'inr_pt_lag_19', 'k_lag_19', 'lact_lag_19', 'lymph_lag_19', 'map_lag_19', 'mch_lag_19', 'mchc_lag_19', 'mcv_lag_19', 'methb_lag_19', 'mg_lag_19', 'na_lag_19', 'neut_lag_19', 'o2sat_lag_19', 'pco2_lag_19', 'ph_lag_19', 'phos_lag_19', 'plt_lag_19', 'po2_lag_19', 'ptt_lag_19', 'resp_lag_19', 'sbp_lag_19', 'temp_lag_19', 'tnt_lag_19', 'urine_lag_19', 'wbc_lag_19', 'alb_lag_20', 'alp_lag_20', 'alt_lag_20', 'ast_lag_20', 'be_lag_20', 'bicar_lag_20', 'bili_lag_20', 'bili_dir_lag_20', 'bnd_lag_20', 'bun_lag_20', 'ca_lag_20', 'cai_lag_20', 'ck_lag_20', 'ckmb_lag_20', 'cl_lag_20', 'crea_lag_20', 'crp_lag_20', 'dbp_lag_20', 'fgn_lag_20', 'fio2_lag_20', 'glu_lag_20', 'hgb_lag_20', 'hr_lag_20', 'inr_pt_lag_20', 'k_lag_20', 'lact_lag_20', 'lymph_lag_20', 'map_lag_20', 'mch_lag_20', 'mchc_lag_20', 'mcv_lag_20', 'methb_lag_20', 'mg_lag_20', 'na_lag_20', 'neut_lag_20', 'o2sat_lag_20', 'pco2_lag_20', 'ph_lag_20', 'phos_lag_20', 'plt_lag_20', 'po2_lag_20', 'ptt_lag_20', 'resp_lag_20', 'sbp_lag_20', 'temp_lag_20', 'tnt_lag_20', 'urine_lag_20', 'wbc_lag_20', 'alb_lag_21', 'alp_lag_21', 'alt_lag_21', 'ast_lag_21', 'be_lag_21', 'bicar_lag_21', 'bili_lag_21', 'bili_dir_lag_21', 'bnd_lag_21', 'bun_lag_21', 'ca_lag_21', 'cai_lag_21', 'ck_lag_21', 'ckmb_lag_21', 'cl_lag_21', 'crea_lag_21', 'crp_lag_21', 'dbp_lag_21', 'fgn_lag_21', 'fio2_lag_21', 'glu_lag_21', 'hgb_lag_21', 'hr_lag_21', 'inr_pt_lag_21', 'k_lag_21', 'lact_lag_21', 'lymph_lag_21', 'map_lag_21', 'mch_lag_21', 'mchc_lag_21', 'mcv_lag_21', 'methb_lag_21', 'mg_lag_21', 'na_lag_21', 'neut_lag_21', 'o2sat_lag_21', 'pco2_lag_21', 'ph_lag_21', 'phos_lag_21', 'plt_lag_21', 'po2_lag_21', 'ptt_lag_21', 'resp_lag_21', 'sbp_lag_21', 'temp_lag_21', 'tnt_lag_21', 'urine_lag_21', 'wbc_lag_21', 'alb_lag_22', 'alp_lag_22', 'alt_lag_22', 'ast_lag_22', 'be_lag_22', 'bicar_lag_22', 'bili_lag_22', 'bili_dir_lag_22', 'bnd_lag_22', 'bun_lag_22', 'ca_lag_22', 'cai_lag_22', 'ck_lag_22', 'ckmb_lag_22', 'cl_lag_22', 'crea_lag_22', 'crp_lag_22', 'dbp_lag_22', 'fgn_lag_22', 'fio2_lag_22', 'glu_lag_22', 'hgb_lag_22', 'hr_lag_22', 'inr_pt_lag_22', 'k_lag_22', 'lact_lag_22', 'lymph_lag_22', 'map_lag_22', 'mch_lag_22', 'mchc_lag_22', 'mcv_lag_22', 'methb_lag_22', 'mg_lag_22', 'na_lag_22', 'neut_lag_22', 'o2sat_lag_22', 'pco2_lag_22', 'ph_lag_22', 'phos_lag_22', 'plt_lag_22', 'po2_lag_22', 'ptt_lag_22', 'resp_lag_22', 'sbp_lag_22', 'temp_lag_22', 'tnt_lag_22', 'urine_lag_22', 'wbc_lag_22', 'alb_lag_23', 'alp_lag_23', 'alt_lag_23', 'ast_lag_23', 'be_lag_23', 'bicar_lag_23', 'bili_lag_23', 'bili_dir_lag_23', 'bnd_lag_23', 'bun_lag_23', 'ca_lag_23', 'cai_lag_23', 'ck_lag_23', 'ckmb_lag_23', 'cl_lag_23', 'crea_lag_23', 'crp_lag_23', 'dbp_lag_23', 'fgn_lag_23', 'fio2_lag_23', 'glu_lag_23', 'hgb_lag_23', 'hr_lag_23', 'inr_pt_lag_23', 'k_lag_23', 'lact_lag_23', 'lymph_lag_23', 'map_lag_23', 'mch_lag_23', 'mchc_lag_23', 'mcv_lag_23', 'methb_lag_23', 'mg_lag_23', 'na_lag_23', 'neut_lag_23', 'o2sat_lag_23', 'pco2_lag_23', 'ph_lag_23', 'phos_lag_23', 'plt_lag_23', 'po2_lag_23', 'ptt_lag_23', 'resp_lag_23', 'sbp_lag_23', 'temp_lag_23', 'tnt_lag_23', 'urine_lag_23', 'wbc_lag_23', 'alb_lag_24', 'alp_lag_24', 'alt_lag_24', 'ast_lag_24', 'be_lag_24', 'bicar_lag_24', 'bili_lag_24', 'bili_dir_lag_24', 'bnd_lag_24', 'bun_lag_24', 'ca_lag_24', 'cai_lag_24', 'ck_lag_24', 'ckmb_lag_24', 'cl_lag_24', 'crea_lag_24', 'crp_lag_24', 'dbp_lag_24', 'fgn_lag_24', 'fio2_lag_24', 'glu_lag_24', 'hgb_lag_24', 'hr_lag_24', 'inr_pt_lag_24', 'k_lag_24', 'lact_lag_24', 'lymph_lag_24', 'map_lag_24', 'mch_lag_24', 'mchc_lag_24', 'mcv_lag_24', 'methb_lag_24', 'mg_lag_24', 'na_lag_24', 'neut_lag_24', 'o2sat_lag_24', 'pco2_lag_24', 'ph_lag_24', 'phos_lag_24', 'plt_lag_24', 'po2_lag_24', 'ptt_lag_24', 'resp_lag_24', 'sbp_lag_24', 'temp_lag_24', 'tnt_lag_24', 'urine_lag_24', 'wbc_lag_24' ] PredictionDatasetpytorch.add_relative_time_idx=False -PredictionDatasetpytorch.target=[ - "alb", - "alp", - "alt", - "ast", - "be", - "bicar", - "bili", - "bili_dir", - "bnd", - "bun", - "ca", - "cai", - "ck", - "ckmb", - "cl", - "crea", - "crp", - "dbp", - "fgn", - "fio2", - "glu", - "hgb", - "hr", - "inr_pt", - "k", - "lact", - "lymph", - "map", - "mch", - "mchc", - "mcv", - "methb", - "mg", - "na", - "neut", - "o2sat", - "pco2", - "ph", - "phos", - "plt", - "po2", - "ptt", - "resp", - "sbp", - "temp", - "tnt", - "urine", - "wbc", +PredictionDatasetpytorch.target="label" + +PredictionDatasetpytorch.time_varying_unknown_reals=[ "label", ] - - -PredictionDatasetpytorch.time_varying_unknown_reals=[ - "alb", +PredictionDatasetpytorch.time_varying_unknown_categoricals=[] +PredictionDatasetpytorch.lagged_variables=[ "alb", "alp", "alt", "ast", @@ -134,10 +87,4 @@ PredictionDatasetpytorch.time_varying_unknown_reals=[ "temp", "tnt", "urine", - "wbc", - "label", - - ] -PredictionDatasetpytorch.time_varying_unknown_categoricals=[] -PredictionDatasetpytorch.lagged_variables=[ ] -PredictionDatasetpytorch.target_normalizer='multi' \ No newline at end of file + "wbc"] \ No newline at end of file diff --git a/configs/prediction_models/TFTpytorch.gin b/configs/prediction_models/TFTpytorch.gin index de79f61f..c7264ed5 100644 --- a/configs/prediction_models/TFTpytorch.gin +++ b/configs/prediction_models/TFTpytorch.gin @@ -89,5 +89,3 @@ PredictionDatasetpytorch.time_varying_unknown_reals=["alb", "urine", "wbc",] PredictionDatasetpytorch.lagged_variables=[] -PredictionDatasetpytorch.lagged_variables=[] -PredictionDatasetpytorch.target_normalizer='single' \ No newline at end of file diff --git a/icu_benchmarks/cross_validation.py b/icu_benchmarks/cross_validation.py index 69db2960..535d9619 100644 --- a/icu_benchmarks/cross_validation.py +++ b/icu_benchmarks/cross_validation.py @@ -38,9 +38,9 @@ def execute_repeated_cv( verbose: bool = False, wandb: bool = False, complete_train: bool = False, - explain: list = None, + explain: bool = False, pytorch_forecasting: bool = False, - XAI_metric: list = None, + XAI_metric: bool = False, random_labels: bool = False, random_model_dir: str = None, ) -> float: diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 49c898b7..a0e51dbc 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -10,7 +10,7 @@ from icu_benchmarks.imputation.amputations import ampute_data from .constants import DataSegment as Segment from .constants import DataSplit as Split -from pytorch_forecasting import TimeSeriesDataSet, GroupNormalizer, MultiNormalizer, EncoderNormalizer +from pytorch_forecasting import TimeSeriesDataSet, GroupNormalizer class CommonDataset(Dataset): @@ -450,7 +450,6 @@ def __init__( time_varying_known_reals: List[str], time_varying_unknown_categoricals: List[str], lagged_variables: List[str], - # target_normalizer: str, *args, ram_cache: bool = False, add_relative_time_idx: bool = False, @@ -483,13 +482,7 @@ def __init__( self.ram_cache = ram_cache self.kwargs = kwargs self.column_names = features.columns - """ if target_normalizer == 'multi': - target_normalizer = MultiNormalizer([EncoderNormalizer(transformation='relu') for _ in range(len(target)-1)] + [GroupNormalizer(groups=["stay_id"], transformation="relu")] - ) - else: - target_normalizer = GroupNormalizer( - groups=["stay_id"], transformation="relu" - ) """ + super().__init__( data=self.data, time_idx="time_idx", @@ -509,7 +502,6 @@ def __init__( # add_target_scales=True, # add_encoder_length=True, predict_mode=True, - target_normalizer=GroupNormalizer( groups=["stay_id"], transformation="relu" ), diff --git a/icu_benchmarks/models/constants.py b/icu_benchmarks/models/constants.py index 702fe7f7..093d15bd 100644 --- a/icu_benchmarks/models/constants.py +++ b/icu_benchmarks/models/constants.py @@ -26,9 +26,6 @@ MAE, JSD, BinaryFairnessWrapper, - Faithfulness, - Stability, - Randomization ) @@ -88,17 +85,6 @@ class DLMetrics: "mae": MAE, "jsd": JSD, } - Faithfulness = { - "Faithfulness_timesteps": Faithfulness, - "Faithfulness_features": Faithfulness, - "Faithfulness_feature_timestep": Faithfulness, - } - Stability = { - "Stability": Stability - } - Randmoization = { - "DDR": Randomization, - } class ImputationInit(str, Enum): diff --git a/icu_benchmarks/models/custom_metrics.py b/icu_benchmarks/models/custom_metrics.py index 9b84a18d..4e082d29 100644 --- a/icu_benchmarks/models/custom_metrics.py +++ b/icu_benchmarks/models/custom_metrics.py @@ -6,8 +6,6 @@ from sklearn.calibration import calibration_curve from scipy.spatial.distance import jensenshannon from torchmetrics.classification import BinaryFairness -from icu_benchmarks.models.similarity_func import correlation_spearman, distance_euclidean, correlation_pearson, cosine - """" This file contains custom metrics that can be added to YAIB. @@ -135,6 +133,7 @@ def feature_helper(self, trainer, step_prefix): feature_names = trainer.test_dataloaders.dataset.features return feature_names + # XAI Metrics diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 0966dec7..ff66c4da 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -12,7 +12,7 @@ ) import matplotlib.pyplot as plt from icu_benchmarks.models.wrappers import DLPredictionWrapper, DLPredictionPytorchForecastingWrapper -from torch import Tensor, FloatTensor, zeros_like, ones_like, randn_like, is_tensor, autograd +from torch import Tensor, FloatTensor, zeros_like, ones_like, randn_like, is_tensor from pytorch_forecasting import TemporalFusionTransformer, RecurrentNetwork, DeepAR from pytorch_forecasting.metrics import QuantileLoss, MAE import matplotlib.pyplot as plt @@ -475,8 +475,8 @@ def __init__( *args, **kwargs, ): - super().__init__(optimizer=optimizer, pytorch_forecasting=True, dataset=dataset, * args, **kwargs) - + super().__init__(optimizer=optimizer, pytorch_forecasting=True, *args, **kwargs) + self.dataset = dataset self.model = TemporalFusionTransformer.from_dataset( dataset=dataset, hidden_size=hidden, @@ -486,7 +486,6 @@ def __init__( loss=QuantileLoss(), hidden_continuous_size=hidden, ) - self.dataset = dataset self.num_classes = num_classes self.logit = nn.Linear(7, num_classes) @@ -576,10 +575,7 @@ def forward_captum( target_scale, ) - # with autograd.set_grad_enabled(True): - result = self.forward(tuple_x) - - return result + return self.forward(tuple_x) @gin.configurable @@ -638,11 +634,9 @@ def forward( out = self.model(x_dict) - out = ((out["prediction"][-1]).squeeze(dim=-1)) - - pred = self.logit(out) + pred = self.logit(out["prediction"]) - return pred.unsqueeze(1) + return pred @gin.configurable @@ -700,8 +694,6 @@ def forward( x_dict["encoder_cont"][:, :, -1] = 0.0 out = self.model(x_dict) - out = ((out["prediction"][-1])) - - pred = self.logit(out) + pred = self.logit(out["prediction"]) return pred diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index cf94843c..32358d29 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -73,9 +73,9 @@ def train_common( torch.cuda.device_count() * 8 * int(torch.cuda.is_available()), 32, ), - explain: list = None, + explain: bool = False, pytorch_forecasting: bool = False, - XAI_metric: list = None, + XAI_metric: bool = False, random_labels: bool = False, random_model_dir: str = None, @@ -154,12 +154,6 @@ def train_common( drop_last=True, shuffle=False, ) - if random_model_dir is None: - random_model = None - else: - - random_model = Path(random_model_dir) - if load_weights: model = load_model( model, @@ -167,11 +161,6 @@ def train_common( pl_model=pl_model, train_dataset=train_dataset, optimizer=optimizer, - explain=explain, - XAI_metric=XAI_metric, - random_model=random_model, - test_loader=test_loader - ) else: @@ -181,11 +170,6 @@ def train_common( epochs=epochs, run_mode=mode, batch_size=batch_size, - explain=explain, - XAI_metric=XAI_metric, - random_model=random_model, - test_loader=test_loader - ) if random_labels: train_dataset.randomize_labels(num_classes=model.num_classes) @@ -262,8 +246,7 @@ def train_common( logger=loggers, num_sanity_val_steps=-1, log_every_n_steps=5, - gradient_clip_val=gradient_clip_val, - inference_mode=False + # gradient_clip_val=gradient_clip_val ) if not eval_only: if model.requires_backprop: @@ -281,7 +264,21 @@ def train_common( save_config_file(log_dir) return 0 - """ XAI_dict = {} # dictrionary to log attributions metrics + if explain: + if random_model_dir is None: + random_model = None + else: + path = Path(random_model_dir) + + random_model = load_model( + model, + source_dir=path, + pl_model=pl_model, + train_dataset=train_dataset, + optimizer=optimizer, + ) + + XAI_dict = {} # dictrionary to log attributions metrics # choose which methods to get attributions methods = { @@ -290,11 +287,8 @@ def train_common( "IG": IntegratedGradients, # "FA": FeatureAblation, "R": "Random", -<<<<<<< HEAD "Att": "Attention" -======= - # "Att": "Attention" ->>>>>>> parent of a39b5c4... added condition for ROS and RIS for attention + } for key, item in methods.items(): # If conditions needed here as different explantations require different inputs @@ -377,7 +371,7 @@ def create_default_mask(shape): # Write the dictionary to a JSON file with open(json_file_path, "w") as json_file: - json.dump(XAI_dict, json_file) """ + json.dump(XAI_dict, json_file) model.set_weight("balanced", train_dataset) test_loss = trainer.test(model, dataloaders=test_loader, verbose=verbose)[0]["test/loss"] @@ -385,7 +379,7 @@ def create_default_mask(shape): return test_loss -def load_model(model, source_dir, pl_model=True, train_dataset=None, optimizer=None, explain=None, XAI_metric=None, random_model=None, test_loader=None): +def load_model(model, source_dir, pl_model=True, train_dataset=None, optimizer=None): if source_dir.exists(): if model.requires_backprop: if (source_dir / "model.ckpt").exists(): @@ -398,8 +392,7 @@ def load_model(model, source_dir, pl_model=True, train_dataset=None, optimizer=N return Exception(f"No weights to load at path : {source_dir}") if pl_model: if train_dataset is not None: - model = model.load_from_checkpoint(model_path, dataset=train_dataset, optimizer=optimizer, - explain=explain, XAI_metric=XAI_metric, test_loader=test_loader) + model = model.load_from_checkpoint(model_path, dataset=train_dataset, optimizer=optimizer) else: model = model.load_from_checkpoint(model_path) diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 0f86e6cc..b1844d3d 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -18,10 +18,9 @@ from pytorch_lightning import LightningModule from icu_benchmarks.models.constants import MLMetrics, DLMetrics from icu_benchmarks.contants import RunMode -from icu_benchmarks.models.train import load_model import matplotlib.pyplot as plt from icu_benchmarks.models.similarity_func import correlation_spearman, distance_euclidean, correlation_pearson, cosine -from captum.attr import IntegratedGradients, Saliency, FeatureAblation, Lime +import captum from captum._utils.models.linear_model import SkLearnLasso import os @@ -243,7 +242,8 @@ def __init__( input_size: Tensor = None, initialization_method: str = "normal", pytorch_forecasting: bool = False, - + explain: list = [], + XAI_metric: list = [], **kwargs, ): super().__init__( @@ -264,6 +264,8 @@ def __init__( self.output_transform = None self.loss_weights = None self.pytorch_forecasting = pytorch_forecasting + self.explain = explain + self.XAI_metric = XAI_metric def set_weight(self, weight, dataset): """Set the weight for the loss function.""" @@ -406,11 +408,6 @@ def __init__( input_size: Tensor = None, initialization_method: str = "normal", pytorch_forecasting: bool = False, - explain: list = None, - XAI_metric: list = None, - random_model=None, - test_loader=None, - dataset=None, **kwargs, ): super().__init__( @@ -428,55 +425,6 @@ def __init__( initialization_method=initialization_method, kwargs=kwargs, ) - self.explain = explain - self.XAI_metric = XAI_metric - self.methods = { - "G": Saliency, - "L": Lime, - "IG": IntegratedGradients, - "FA": FeatureAblation, - "R": "Random", - "Att": "Attention" - } - self.random_model = random_model - self.test_loader = test_loader - - if random_model is not None: - self.random_model = load_model( - model=self, - source_dir=random_model, - pl_model=True, - train_dataset=dataset, - optimizer=optimizer, - ) - else: - self.random_model = None - - def set_metrics(self, *args): - """Set the evaluation metrics for the prediction model.""" - - metrics = super().set_metrics(*args) - - if self.XAI_metric is not None: - for metric in self.XAI_metric: - if metric == "Faithfulness": - metrics.update(DLMetrics.Faithfulness) - elif metric == "Stability": - metrics.update(DLMetrics.Stability) - elif metric == "Randmoization": - metrics.update(DLMetrics.Randmoization) - - return metrics - - def on_train_start(self): - self.metrics = { - step_name: { - metric_name: (metric() if isinstance(metric, type) else metric) - for metric_name, metric in self.set_metrics().items() - } - for step_name in ["train", "val", "test"] - } - return super().on_train_start() def step_fn(self, element, step_prefix=""): """Perform a step in the DL prediction model training loop. @@ -501,6 +449,7 @@ def step_fn(self, element, step_prefix=""): else: aux_loss = 0 # Get prediction and target + prediction = out.to(self.device).squeeze(-1) target = labels.to(self.device) @@ -511,113 +460,14 @@ def step_fn(self, element, step_prefix=""): # Returns torch.long because negative log likelihood loss elif self.run_mode == RunMode.regression: # Regression task + loss = self.loss(prediction[:, 0], target.float()) + aux_loss else: raise ValueError(f"Run mode {self.run_mode} not yet supported. Please implement it.") transformed_output = self.output_transform((prediction, target)) - if self.explain is not None: - - for method in self.explain: - print(method) - if method == "Attention": - all_attrs, features_attrs, timestep_attrs = self.explantation2(dic, method, self.test_loader) - else: - all_attrs, features_attrs, timestep_attrs = self.explantation2(dic, method) - for key, value in self.metrics[step_prefix].items(): - if key == "Faithfulness_timesteps": - value.update( - dic, - timestep_attrs, - self, - correlation_pearson, - 100, - "baseline", - 4, - False, - True, - False - - ) - elif key == "Faithfulness_features": - value.update( - dic, - features_attrs, - self, - correlation_pearson, - 100, - "baseline", - 9, - True, - False, - False - - ) - elif key == "Faithfulness_feature_timestep": - value.update( - dic, - all_attrs, - self, - correlation_pearson, - 100, - "baseline", - [4, 9], - False, - False, - True - - ) - - elif key == "Stability": - if method == "Attention": - - value.update( - dic, - timestep_attrs, - self, - method, - self.test_loader, - 0.5, - - ) - else: - value.update( - dic, - all_attrs, - self, - method, - None, - 0.5, - ) - elif key == "Randomization": - - if method == "Attention": - value.update( - dic, - timestep_attrs, - self, - method, - self.random_model, - cosine, - None, - - ) - else: - - value.update( - dic, - all_attrs, - self, - method, - self.random_model, - cosine, - self.test_loader, - - ) for key, value in self.metrics[step_prefix].items(): - if (key == "Faithfulness_timesteps" or key == "Faithfulness_features" or key == "Faithfulness_feature_timestep" - or key == "Stability" or key == "Randomization"): - continue + if isinstance(value, torchmetrics.Metric): if key == "Binary_Fairness": feature_names = self.metrics[step_prefix][key].feature_helper(self.trainer, step_prefix) @@ -727,122 +577,6 @@ def plot_attributions(self, features_attrs, timestep_attrs, method_name, log_dir plt.tight_layout() plt.savefig(log_dir / "{}_attribution_plot.png".format(method_name), bbox_inches="tight") - def explantation2(self, dic, method, dataloader=None, log_dir=".", plot=False, **kwargs): - """ - Generic method to combine pytorchforecasting data loading , interpertations and captum to generate attributions - - Args: - - dataloader: pytorchforecasting data loader - - method: The explantation method chosen - - log_dir= The directory to output the plots - - plot= Determines if plots should be done or not - - XAI_metric=Determines if XAI metrics should be calculated or not - Returns: - - all_attrs : Attribtuons of features per timesteps - - features_attrs : Attribtuons of features averaged over timesteps - - timestep_attrs : Attribtuons of timesteps averaged over features - - f_ts_v_score: Faithfulness score for attribtuons of features per timesteps - - f_ts_score: Faithfulness score for attribtuons of timesteps averaged over features - """ - # Initialize lists to store attribution values for all instances - - all_attrs = [] - method = self.methods[method] - for key, value in dic.items(): - dic[key] = dic[key].to(self.device) - - method_name = method if (method == "Random") or (method == "Attention") else ( - method.__name__) - if (method_name == "Random") or (method_name == "Attention"): - if method_name == "Attention": - Interpertations = self.interpertations(dataloader=dataloader, log_dir=log_dir, plot=plot) - timestep_attrs = Interpertations["attention"] - features_attrs = Interpertations["static_variables"].tolist() - features_attrs.extend(Interpertations["encoder_variables"].tolist()) - - elif method_name == "Random": - # Generate random attributions for baseline comparison - all_attrs = np.random.normal(size=[64, 24, 53]) - features_attrs = all_attrs.mean(axis=(1)) - timestep_attrs = all_attrs.mean(axis=(2)) - - return all_attrs, features_attrs, timestep_attrs - # Loop through the dataloader to compute attributions for all instances - - data, baselines = self.prep_data_captum(dic) - - # Initialize the explanation method - self.train() - explanation = method(self.forward_captum, interpretable_model=SkLearnLasso( - alpha=0.4)) if method_name == 'Lime' else method(self.forward_captum) - - # Calculate attributions using the selected method - if method is not Saliency: - if method_name == "IG": - attr = explanation.attribute(data, baselines=baselines, n_steps=50, **kwargs) - elif method_name == "L" or method_name == "FA": - # for Lime and feature ablation we need to define what is a feature we define each variable per timestep as a feature - shapes = [ - torch.Size([64, 24, 0]), - torch.Size([64, 24, 53]), - torch.Size([64, 24]), - torch.Size([64]), - torch.Size([64, 1, 0]), - torch.Size([64, 1, 53]), - torch.Size([64, 1]), - torch.Size([64]), - torch.Size([64, 1]), - torch.Size([64, 1]), - torch.Size([64, 2]) - ] - - # Create a default mask for non-targeted tensors - def create_default_mask(shape): - if len(shape) == 3: - return torch.zeros(shape[0], shape[1], max(1, shape[2]), dtype=torch.int32) - elif len(shape) == 2: - return torch.zeros(shape[0], max(1, shape[1]), dtype=torch.int32) - else: # len(shape) == 1 - return torch.zeros(shape[0], dtype=torch.int32) - - # Create a feature mask for the second tensor that includes both features and timesteps - num_timesteps = shapes[1][1] - num_features = shapes[1][2] - feature_mask_second = torch.arange(num_timesteps * num_features).reshape(num_timesteps, num_features) - feature_mask_second = feature_mask_second.unsqueeze(0).repeat(shapes[1][0], 1, 1) - - # Create a tuple of masks - feature_masks = tuple([create_default_mask(shape) if i != - 1 else feature_mask_second for i, shape in enumerate(shapes)]) - - attr = explanation.attribute(data, baselines=baselines, feature_mask=feature_masks, **kwargs) - else: - attr = explanation.attribute(data, **kwargs) - self.eval() - # Process and store the calculated attributions - stacked_attr = attr[1].cpu().detach().numpy() if method_name in [ - 'Lime', 'FeatureAblation'] else torch.stack(attr).cpu().detach().numpy() - # aggregate over batch - # attr = np.mean(stacked_attr, axis=0) - - # aggregate over all batches - # all_attrs = np.array(all_attrs).mean(axis=(0)) - # aggregate over all timesteps - features_attrs = stacked_attr.mean(axis=(1)) - # aggregate over all features - timestep_attrs = stacked_attr.mean(axis=(2)) - - if plot: - log_dir_plots = log_dir / 'plots' - if not (log_dir_plots.exists()): - log_dir_plots.mkdir(parents=True) - # Plot attributions for features and timesteps - - self.plot_attributions(features_attrs, timestep_attrs, method_name, log_dir_plots) - - # Return computed attributions and metrics - return stacked_attr, features_attrs, timestep_attrs - def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=False, random_model=None, test_dataset=None, **kwargs): """ Generic method to combine pytorchforecasting data loading , interpertations and captum to generate attributions @@ -962,10 +696,7 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F pertrub="baseline", feature=True, subset_size=9, nr_runs=100)) r_score.append(self.Data_Randomization(x, attribution=stacked_attr, explain_method=method, random_model=random_model, method_name=method_name)) -<<<<<<< HEAD -======= ->>>>>>> parent of a39b5c4... added condition for ROS and RIS for attention res1, res2 = self.Relative_Stability(x, stacked_attr, explain_method=method, method_name=method_name, dataloader=None, **kwargs ) diff --git a/icu_benchmarks/run_utils.py b/icu_benchmarks/run_utils.py index 020238e4..969b4f4f 100644 --- a/icu_benchmarks/run_utils.py +++ b/icu_benchmarks/run_utils.py @@ -123,37 +123,23 @@ def build_parser() -> ArgumentParser: default=None, help="Number of samples to use for evaluation.", ) - """ parser.add_argument( + parser.add_argument( "--explain", default=False, action=BOA, help="Provide explaintations for predictions.", - ) """ + ) parser.add_argument( "--pytorch-forecasting", default=False, action=BOA, help="use pytorch forecasting library ", ) - """ parser.add_argument( + parser.add_argument( "--XAI_metric", default=False, action=BOA, help="Compare explantations ", - ) """ - parser.add_argument( - "-ex", - "--explain", - nargs='+', # '+' means one or more arguments - type=str, # or any type you need - help="List of XAI methods to run" - ) - parser.add_argument( - "-exm", - "--XAI_metric", - nargs='+', # '+' means one or more arguments - type=str, # or any type you need - help="List of XAI metrics to run" ) parser.add_argument( "--random_labels", From 21bf3513c4ce16e3f51114804f9c30029ccb2343 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Thu, 28 Dec 2023 16:27:55 +0100 Subject: [PATCH 127/142] added choice of normalizer --- configs/prediction_models/DeepARpytorch.gin | 66 +++++++++++++++++--- configs/prediction_models/RNNpytorch.gin | 68 ++++++++++++++++++--- configs/prediction_models/TFTpytorch.gin | 1 + environment.yml | 2 + icu_benchmarks/data/loader.py | 15 +++-- icu_benchmarks/models/custom_metrics.py | 2 +- icu_benchmarks/models/wrappers.py | 5 +- 7 files changed, 136 insertions(+), 23 deletions(-) diff --git a/configs/prediction_models/DeepARpytorch.gin b/configs/prediction_models/DeepARpytorch.gin index bcc6abf6..9407222a 100644 --- a/configs/prediction_models/DeepARpytorch.gin +++ b/configs/prediction_models/DeepARpytorch.gin @@ -29,16 +29,63 @@ train_common/hyperparameter.gradient_clip_val=0.01 # Dataset params PredictionDatasetpytorch.max_encoder_length = 24 PredictionDatasetpytorch.max_prediction_length = 1 -PredictionDatasetpytorch.time_varying_known_reals=['alb_lag_1', 'alp_lag_1', 'alt_lag_1', 'ast_lag_1', 'be_lag_1', 'bicar_lag_1', 'bili_lag_1', 'bili_dir_lag_1', 'bnd_lag_1', 'bun_lag_1', 'ca_lag_1', 'cai_lag_1', 'ck_lag_1', 'ckmb_lag_1', 'cl_lag_1', 'crea_lag_1', 'crp_lag_1', 'dbp_lag_1', 'fgn_lag_1', 'fio2_lag_1', 'glu_lag_1', 'hgb_lag_1', 'hr_lag_1', 'inr_pt_lag_1', 'k_lag_1', 'lact_lag_1', 'lymph_lag_1', 'map_lag_1', 'mch_lag_1', 'mchc_lag_1', 'mcv_lag_1', 'methb_lag_1', 'mg_lag_1', 'na_lag_1', 'neut_lag_1', 'o2sat_lag_1', 'pco2_lag_1', 'ph_lag_1', 'phos_lag_1', 'plt_lag_1', 'po2_lag_1', 'ptt_lag_1', 'resp_lag_1', 'sbp_lag_1', 'temp_lag_1', 'tnt_lag_1', 'urine_lag_1', 'wbc_lag_1', 'alb_lag_2', 'alp_lag_2', 'alt_lag_2', 'ast_lag_2', 'be_lag_2', 'bicar_lag_2', 'bili_lag_2', 'bili_dir_lag_2', 'bnd_lag_2', 'bun_lag_2', 'ca_lag_2', 'cai_lag_2', 'ck_lag_2', 'ckmb_lag_2', 'cl_lag_2', 'crea_lag_2', 'crp_lag_2', 'dbp_lag_2', 'fgn_lag_2', 'fio2_lag_2', 'glu_lag_2', 'hgb_lag_2', 'hr_lag_2', 'inr_pt_lag_2', 'k_lag_2', 'lact_lag_2', 'lymph_lag_2', 'map_lag_2', 'mch_lag_2', 'mchc_lag_2', 'mcv_lag_2', 'methb_lag_2', 'mg_lag_2', 'na_lag_2', 'neut_lag_2', 'o2sat_lag_2', 'pco2_lag_2', 'ph_lag_2', 'phos_lag_2', 'plt_lag_2', 'po2_lag_2', 'ptt_lag_2', 'resp_lag_2', 'sbp_lag_2', 'temp_lag_2', 'tnt_lag_2', 'urine_lag_2', 'wbc_lag_2', 'alb_lag_3', 'alp_lag_3', 'alt_lag_3', 'ast_lag_3', 'be_lag_3', 'bicar_lag_3', 'bili_lag_3', 'bili_dir_lag_3', 'bnd_lag_3', 'bun_lag_3', 'ca_lag_3', 'cai_lag_3', 'ck_lag_3', 'ckmb_lag_3', 'cl_lag_3', 'crea_lag_3', 'crp_lag_3', 'dbp_lag_3', 'fgn_lag_3', 'fio2_lag_3', 'glu_lag_3', 'hgb_lag_3', 'hr_lag_3', 'inr_pt_lag_3', 'k_lag_3', 'lact_lag_3', 'lymph_lag_3', 'map_lag_3', 'mch_lag_3', 'mchc_lag_3', 'mcv_lag_3', 'methb_lag_3', 'mg_lag_3', 'na_lag_3', 'neut_lag_3', 'o2sat_lag_3', 'pco2_lag_3', 'ph_lag_3', 'phos_lag_3', 'plt_lag_3', 'po2_lag_3', 'ptt_lag_3', 'resp_lag_3', 'sbp_lag_3', 'temp_lag_3', 'tnt_lag_3', 'urine_lag_3', 'wbc_lag_3', 'alb_lag_4', 'alp_lag_4', 'alt_lag_4', 'ast_lag_4', 'be_lag_4', 'bicar_lag_4', 'bili_lag_4', 'bili_dir_lag_4', 'bnd_lag_4', 'bun_lag_4', 'ca_lag_4', 'cai_lag_4', 'ck_lag_4', 'ckmb_lag_4', 'cl_lag_4', 'crea_lag_4', 'crp_lag_4', 'dbp_lag_4', 'fgn_lag_4', 'fio2_lag_4', 'glu_lag_4', 'hgb_lag_4', 'hr_lag_4', 'inr_pt_lag_4', 'k_lag_4', 'lact_lag_4', 'lymph_lag_4', 'map_lag_4', 'mch_lag_4', 'mchc_lag_4', 'mcv_lag_4', 'methb_lag_4', 'mg_lag_4', 'na_lag_4', 'neut_lag_4', 'o2sat_lag_4', 'pco2_lag_4', 'ph_lag_4', 'phos_lag_4', 'plt_lag_4', 'po2_lag_4', 'ptt_lag_4', 'resp_lag_4', 'sbp_lag_4', 'temp_lag_4', 'tnt_lag_4', 'urine_lag_4', 'wbc_lag_4', 'alb_lag_5', 'alp_lag_5', 'alt_lag_5', 'ast_lag_5', 'be_lag_5', 'bicar_lag_5', 'bili_lag_5', 'bili_dir_lag_5', 'bnd_lag_5', 'bun_lag_5', 'ca_lag_5', 'cai_lag_5', 'ck_lag_5', 'ckmb_lag_5', 'cl_lag_5', 'crea_lag_5', 'crp_lag_5', 'dbp_lag_5', 'fgn_lag_5', 'fio2_lag_5', 'glu_lag_5', 'hgb_lag_5', 'hr_lag_5', 'inr_pt_lag_5', 'k_lag_5', 'lact_lag_5', 'lymph_lag_5', 'map_lag_5', 'mch_lag_5', 'mchc_lag_5', 'mcv_lag_5', 'methb_lag_5', 'mg_lag_5', 'na_lag_5', 'neut_lag_5', 'o2sat_lag_5', 'pco2_lag_5', 'ph_lag_5', 'phos_lag_5', 'plt_lag_5', 'po2_lag_5', 'ptt_lag_5', 'resp_lag_5', 'sbp_lag_5', 'temp_lag_5', 'tnt_lag_5', 'urine_lag_5', 'wbc_lag_5', 'alb_lag_6', 'alp_lag_6', 'alt_lag_6', 'ast_lag_6', 'be_lag_6', 'bicar_lag_6', 'bili_lag_6', 'bili_dir_lag_6', 'bnd_lag_6', 'bun_lag_6', 'ca_lag_6', 'cai_lag_6', 'ck_lag_6', 'ckmb_lag_6', 'cl_lag_6', 'crea_lag_6', 'crp_lag_6', 'dbp_lag_6', 'fgn_lag_6', 'fio2_lag_6', 'glu_lag_6', 'hgb_lag_6', 'hr_lag_6', 'inr_pt_lag_6', 'k_lag_6', 'lact_lag_6', 'lymph_lag_6', 'map_lag_6', 'mch_lag_6', 'mchc_lag_6', 'mcv_lag_6', 'methb_lag_6', 'mg_lag_6', 'na_lag_6', 'neut_lag_6', 'o2sat_lag_6', 'pco2_lag_6', 'ph_lag_6', 'phos_lag_6', 'plt_lag_6', 'po2_lag_6', 'ptt_lag_6', 'resp_lag_6', 'sbp_lag_6', 'temp_lag_6', 'tnt_lag_6', 'urine_lag_6', 'wbc_lag_6', 'alb_lag_7', 'alp_lag_7', 'alt_lag_7', 'ast_lag_7', 'be_lag_7', 'bicar_lag_7', 'bili_lag_7', 'bili_dir_lag_7', 'bnd_lag_7', 'bun_lag_7', 'ca_lag_7', 'cai_lag_7', 'ck_lag_7', 'ckmb_lag_7', 'cl_lag_7', 'crea_lag_7', 'crp_lag_7', 'dbp_lag_7', 'fgn_lag_7', 'fio2_lag_7', 'glu_lag_7', 'hgb_lag_7', 'hr_lag_7', 'inr_pt_lag_7', 'k_lag_7', 'lact_lag_7', 'lymph_lag_7', 'map_lag_7', 'mch_lag_7', 'mchc_lag_7', 'mcv_lag_7', 'methb_lag_7', 'mg_lag_7', 'na_lag_7', 'neut_lag_7', 'o2sat_lag_7', 'pco2_lag_7', 'ph_lag_7', 'phos_lag_7', 'plt_lag_7', 'po2_lag_7', 'ptt_lag_7', 'resp_lag_7', 'sbp_lag_7', 'temp_lag_7', 'tnt_lag_7', 'urine_lag_7', 'wbc_lag_7', 'alb_lag_8', 'alp_lag_8', 'alt_lag_8', 'ast_lag_8', 'be_lag_8', 'bicar_lag_8', 'bili_lag_8', 'bili_dir_lag_8', 'bnd_lag_8', 'bun_lag_8', 'ca_lag_8', 'cai_lag_8', 'ck_lag_8', 'ckmb_lag_8', 'cl_lag_8', 'crea_lag_8', 'crp_lag_8', 'dbp_lag_8', 'fgn_lag_8', 'fio2_lag_8', 'glu_lag_8', 'hgb_lag_8', 'hr_lag_8', 'inr_pt_lag_8', 'k_lag_8', 'lact_lag_8', 'lymph_lag_8', 'map_lag_8', 'mch_lag_8', 'mchc_lag_8', 'mcv_lag_8', 'methb_lag_8', 'mg_lag_8', 'na_lag_8', 'neut_lag_8', 'o2sat_lag_8', 'pco2_lag_8', 'ph_lag_8', 'phos_lag_8', 'plt_lag_8', 'po2_lag_8', 'ptt_lag_8', 'resp_lag_8', 'sbp_lag_8', 'temp_lag_8', 'tnt_lag_8', 'urine_lag_8', 'wbc_lag_8', 'alb_lag_9', 'alp_lag_9', 'alt_lag_9', 'ast_lag_9', 'be_lag_9', 'bicar_lag_9', 'bili_lag_9', 'bili_dir_lag_9', 'bnd_lag_9', 'bun_lag_9', 'ca_lag_9', 'cai_lag_9', 'ck_lag_9', 'ckmb_lag_9', 'cl_lag_9', 'crea_lag_9', 'crp_lag_9', 'dbp_lag_9', 'fgn_lag_9', 'fio2_lag_9', 'glu_lag_9', 'hgb_lag_9', 'hr_lag_9', 'inr_pt_lag_9', 'k_lag_9', 'lact_lag_9', 'lymph_lag_9', 'map_lag_9', 'mch_lag_9', 'mchc_lag_9', 'mcv_lag_9', 'methb_lag_9', 'mg_lag_9', 'na_lag_9', 'neut_lag_9', 'o2sat_lag_9', 'pco2_lag_9', 'ph_lag_9', 'phos_lag_9', 'plt_lag_9', 'po2_lag_9', 'ptt_lag_9', 'resp_lag_9', 'sbp_lag_9', 'temp_lag_9', 'tnt_lag_9', 'urine_lag_9', 'wbc_lag_9', 'alb_lag_10', 'alp_lag_10', 'alt_lag_10', 'ast_lag_10', 'be_lag_10', 'bicar_lag_10', 'bili_lag_10', 'bili_dir_lag_10', 'bnd_lag_10', 'bun_lag_10', 'ca_lag_10', 'cai_lag_10', 'ck_lag_10', 'ckmb_lag_10', 'cl_lag_10', 'crea_lag_10', 'crp_lag_10', 'dbp_lag_10', 'fgn_lag_10', 'fio2_lag_10', 'glu_lag_10', 'hgb_lag_10', 'hr_lag_10', 'inr_pt_lag_10', 'k_lag_10', 'lact_lag_10', 'lymph_lag_10', 'map_lag_10', 'mch_lag_10', 'mchc_lag_10', 'mcv_lag_10', 'methb_lag_10', 'mg_lag_10', 'na_lag_10', 'neut_lag_10', 'o2sat_lag_10', 'pco2_lag_10', 'ph_lag_10', 'phos_lag_10', 'plt_lag_10', 'po2_lag_10', 'ptt_lag_10', 'resp_lag_10', 'sbp_lag_10', 'temp_lag_10', 'tnt_lag_10', 'urine_lag_10', 'wbc_lag_10', 'alb_lag_11', 'alp_lag_11', 'alt_lag_11', 'ast_lag_11', 'be_lag_11', 'bicar_lag_11', 'bili_lag_11', 'bili_dir_lag_11', 'bnd_lag_11', 'bun_lag_11', 'ca_lag_11', 'cai_lag_11', 'ck_lag_11', 'ckmb_lag_11', 'cl_lag_11', 'crea_lag_11', 'crp_lag_11', 'dbp_lag_11', 'fgn_lag_11', 'fio2_lag_11', 'glu_lag_11', 'hgb_lag_11', 'hr_lag_11', 'inr_pt_lag_11', 'k_lag_11', 'lact_lag_11', 'lymph_lag_11', 'map_lag_11', 'mch_lag_11', 'mchc_lag_11', 'mcv_lag_11', 'methb_lag_11', 'mg_lag_11', 'na_lag_11', 'neut_lag_11', 'o2sat_lag_11', 'pco2_lag_11', 'ph_lag_11', 'phos_lag_11', 'plt_lag_11', 'po2_lag_11', 'ptt_lag_11', 'resp_lag_11', 'sbp_lag_11', 'temp_lag_11', 'tnt_lag_11', 'urine_lag_11', 'wbc_lag_11', 'alb_lag_12', 'alp_lag_12', 'alt_lag_12', 'ast_lag_12', 'be_lag_12', 'bicar_lag_12', 'bili_lag_12', 'bili_dir_lag_12', 'bnd_lag_12', 'bun_lag_12', 'ca_lag_12', 'cai_lag_12', 'ck_lag_12', 'ckmb_lag_12', 'cl_lag_12', 'crea_lag_12', 'crp_lag_12', 'dbp_lag_12', 'fgn_lag_12', 'fio2_lag_12', 'glu_lag_12', 'hgb_lag_12', 'hr_lag_12', 'inr_pt_lag_12', 'k_lag_12', 'lact_lag_12', 'lymph_lag_12', 'map_lag_12', 'mch_lag_12', 'mchc_lag_12', 'mcv_lag_12', 'methb_lag_12', 'mg_lag_12', 'na_lag_12', 'neut_lag_12', 'o2sat_lag_12', 'pco2_lag_12', 'ph_lag_12', 'phos_lag_12', 'plt_lag_12', 'po2_lag_12', 'ptt_lag_12', 'resp_lag_12', 'sbp_lag_12', 'temp_lag_12', 'tnt_lag_12', 'urine_lag_12', 'wbc_lag_12', 'alb_lag_13', 'alp_lag_13', 'alt_lag_13', 'ast_lag_13', 'be_lag_13', 'bicar_lag_13', 'bili_lag_13', 'bili_dir_lag_13', 'bnd_lag_13', 'bun_lag_13', 'ca_lag_13', 'cai_lag_13', 'ck_lag_13', 'ckmb_lag_13', 'cl_lag_13', 'crea_lag_13', 'crp_lag_13', 'dbp_lag_13', 'fgn_lag_13', 'fio2_lag_13', 'glu_lag_13', 'hgb_lag_13', 'hr_lag_13', 'inr_pt_lag_13', 'k_lag_13', 'lact_lag_13', 'lymph_lag_13', 'map_lag_13', 'mch_lag_13', 'mchc_lag_13', 'mcv_lag_13', 'methb_lag_13', 'mg_lag_13', 'na_lag_13', 'neut_lag_13', 'o2sat_lag_13', 'pco2_lag_13', 'ph_lag_13', 'phos_lag_13', 'plt_lag_13', 'po2_lag_13', 'ptt_lag_13', 'resp_lag_13', 'sbp_lag_13', 'temp_lag_13', 'tnt_lag_13', 'urine_lag_13', 'wbc_lag_13', 'alb_lag_14', 'alp_lag_14', 'alt_lag_14', 'ast_lag_14', 'be_lag_14', 'bicar_lag_14', 'bili_lag_14', 'bili_dir_lag_14', 'bnd_lag_14', 'bun_lag_14', 'ca_lag_14', 'cai_lag_14', 'ck_lag_14', 'ckmb_lag_14', 'cl_lag_14', 'crea_lag_14', 'crp_lag_14', 'dbp_lag_14', 'fgn_lag_14', 'fio2_lag_14', 'glu_lag_14', 'hgb_lag_14', 'hr_lag_14', 'inr_pt_lag_14', 'k_lag_14', 'lact_lag_14', 'lymph_lag_14', 'map_lag_14', 'mch_lag_14', 'mchc_lag_14', 'mcv_lag_14', 'methb_lag_14', 'mg_lag_14', 'na_lag_14', 'neut_lag_14', 'o2sat_lag_14', 'pco2_lag_14', 'ph_lag_14', 'phos_lag_14', 'plt_lag_14', 'po2_lag_14', 'ptt_lag_14', 'resp_lag_14', 'sbp_lag_14', 'temp_lag_14', 'tnt_lag_14', 'urine_lag_14', 'wbc_lag_14', 'alb_lag_15', 'alp_lag_15', 'alt_lag_15', 'ast_lag_15', 'be_lag_15', 'bicar_lag_15', 'bili_lag_15', 'bili_dir_lag_15', 'bnd_lag_15', 'bun_lag_15', 'ca_lag_15', 'cai_lag_15', 'ck_lag_15', 'ckmb_lag_15', 'cl_lag_15', 'crea_lag_15', 'crp_lag_15', 'dbp_lag_15', 'fgn_lag_15', 'fio2_lag_15', 'glu_lag_15', 'hgb_lag_15', 'hr_lag_15', 'inr_pt_lag_15', 'k_lag_15', 'lact_lag_15', 'lymph_lag_15', 'map_lag_15', 'mch_lag_15', 'mchc_lag_15', 'mcv_lag_15', 'methb_lag_15', 'mg_lag_15', 'na_lag_15', 'neut_lag_15', 'o2sat_lag_15', 'pco2_lag_15', 'ph_lag_15', 'phos_lag_15', 'plt_lag_15', 'po2_lag_15', 'ptt_lag_15', 'resp_lag_15', 'sbp_lag_15', 'temp_lag_15', 'tnt_lag_15', 'urine_lag_15', 'wbc_lag_15', 'alb_lag_16', 'alp_lag_16', 'alt_lag_16', 'ast_lag_16', 'be_lag_16', 'bicar_lag_16', 'bili_lag_16', 'bili_dir_lag_16', 'bnd_lag_16', 'bun_lag_16', 'ca_lag_16', 'cai_lag_16', 'ck_lag_16', 'ckmb_lag_16', 'cl_lag_16', 'crea_lag_16', 'crp_lag_16', 'dbp_lag_16', 'fgn_lag_16', 'fio2_lag_16', 'glu_lag_16', 'hgb_lag_16', 'hr_lag_16', 'inr_pt_lag_16', 'k_lag_16', 'lact_lag_16', 'lymph_lag_16', 'map_lag_16', 'mch_lag_16', 'mchc_lag_16', 'mcv_lag_16', 'methb_lag_16', 'mg_lag_16', 'na_lag_16', 'neut_lag_16', 'o2sat_lag_16', 'pco2_lag_16', 'ph_lag_16', 'phos_lag_16', 'plt_lag_16', 'po2_lag_16', 'ptt_lag_16', 'resp_lag_16', 'sbp_lag_16', 'temp_lag_16', 'tnt_lag_16', 'urine_lag_16', 'wbc_lag_16', 'alb_lag_17', 'alp_lag_17', 'alt_lag_17', 'ast_lag_17', 'be_lag_17', 'bicar_lag_17', 'bili_lag_17', 'bili_dir_lag_17', 'bnd_lag_17', 'bun_lag_17', 'ca_lag_17', 'cai_lag_17', 'ck_lag_17', 'ckmb_lag_17', 'cl_lag_17', 'crea_lag_17', 'crp_lag_17', 'dbp_lag_17', 'fgn_lag_17', 'fio2_lag_17', 'glu_lag_17', 'hgb_lag_17', 'hr_lag_17', 'inr_pt_lag_17', 'k_lag_17', 'lact_lag_17', 'lymph_lag_17', 'map_lag_17', 'mch_lag_17', 'mchc_lag_17', 'mcv_lag_17', 'methb_lag_17', 'mg_lag_17', 'na_lag_17', 'neut_lag_17', 'o2sat_lag_17', 'pco2_lag_17', 'ph_lag_17', 'phos_lag_17', 'plt_lag_17', 'po2_lag_17', 'ptt_lag_17', 'resp_lag_17', 'sbp_lag_17', 'temp_lag_17', 'tnt_lag_17', 'urine_lag_17', 'wbc_lag_17', 'alb_lag_18', 'alp_lag_18', 'alt_lag_18', 'ast_lag_18', 'be_lag_18', 'bicar_lag_18', 'bili_lag_18', 'bili_dir_lag_18', 'bnd_lag_18', 'bun_lag_18', 'ca_lag_18', 'cai_lag_18', 'ck_lag_18', 'ckmb_lag_18', 'cl_lag_18', 'crea_lag_18', 'crp_lag_18', 'dbp_lag_18', 'fgn_lag_18', 'fio2_lag_18', 'glu_lag_18', 'hgb_lag_18', 'hr_lag_18', 'inr_pt_lag_18', 'k_lag_18', 'lact_lag_18', 'lymph_lag_18', 'map_lag_18', 'mch_lag_18', 'mchc_lag_18', 'mcv_lag_18', 'methb_lag_18', 'mg_lag_18', 'na_lag_18', 'neut_lag_18', 'o2sat_lag_18', 'pco2_lag_18', 'ph_lag_18', 'phos_lag_18', 'plt_lag_18', 'po2_lag_18', 'ptt_lag_18', 'resp_lag_18', 'sbp_lag_18', 'temp_lag_18', 'tnt_lag_18', 'urine_lag_18', 'wbc_lag_18', 'alb_lag_19', 'alp_lag_19', 'alt_lag_19', 'ast_lag_19', 'be_lag_19', 'bicar_lag_19', 'bili_lag_19', 'bili_dir_lag_19', 'bnd_lag_19', 'bun_lag_19', 'ca_lag_19', 'cai_lag_19', 'ck_lag_19', 'ckmb_lag_19', 'cl_lag_19', 'crea_lag_19', 'crp_lag_19', 'dbp_lag_19', 'fgn_lag_19', 'fio2_lag_19', 'glu_lag_19', 'hgb_lag_19', 'hr_lag_19', 'inr_pt_lag_19', 'k_lag_19', 'lact_lag_19', 'lymph_lag_19', 'map_lag_19', 'mch_lag_19', 'mchc_lag_19', 'mcv_lag_19', 'methb_lag_19', 'mg_lag_19', 'na_lag_19', 'neut_lag_19', 'o2sat_lag_19', 'pco2_lag_19', 'ph_lag_19', 'phos_lag_19', 'plt_lag_19', 'po2_lag_19', 'ptt_lag_19', 'resp_lag_19', 'sbp_lag_19', 'temp_lag_19', 'tnt_lag_19', 'urine_lag_19', 'wbc_lag_19', 'alb_lag_20', 'alp_lag_20', 'alt_lag_20', 'ast_lag_20', 'be_lag_20', 'bicar_lag_20', 'bili_lag_20', 'bili_dir_lag_20', 'bnd_lag_20', 'bun_lag_20', 'ca_lag_20', 'cai_lag_20', 'ck_lag_20', 'ckmb_lag_20', 'cl_lag_20', 'crea_lag_20', 'crp_lag_20', 'dbp_lag_20', 'fgn_lag_20', 'fio2_lag_20', 'glu_lag_20', 'hgb_lag_20', 'hr_lag_20', 'inr_pt_lag_20', 'k_lag_20', 'lact_lag_20', 'lymph_lag_20', 'map_lag_20', 'mch_lag_20', 'mchc_lag_20', 'mcv_lag_20', 'methb_lag_20', 'mg_lag_20', 'na_lag_20', 'neut_lag_20', 'o2sat_lag_20', 'pco2_lag_20', 'ph_lag_20', 'phos_lag_20', 'plt_lag_20', 'po2_lag_20', 'ptt_lag_20', 'resp_lag_20', 'sbp_lag_20', 'temp_lag_20', 'tnt_lag_20', 'urine_lag_20', 'wbc_lag_20', 'alb_lag_21', 'alp_lag_21', 'alt_lag_21', 'ast_lag_21', 'be_lag_21', 'bicar_lag_21', 'bili_lag_21', 'bili_dir_lag_21', 'bnd_lag_21', 'bun_lag_21', 'ca_lag_21', 'cai_lag_21', 'ck_lag_21', 'ckmb_lag_21', 'cl_lag_21', 'crea_lag_21', 'crp_lag_21', 'dbp_lag_21', 'fgn_lag_21', 'fio2_lag_21', 'glu_lag_21', 'hgb_lag_21', 'hr_lag_21', 'inr_pt_lag_21', 'k_lag_21', 'lact_lag_21', 'lymph_lag_21', 'map_lag_21', 'mch_lag_21', 'mchc_lag_21', 'mcv_lag_21', 'methb_lag_21', 'mg_lag_21', 'na_lag_21', 'neut_lag_21', 'o2sat_lag_21', 'pco2_lag_21', 'ph_lag_21', 'phos_lag_21', 'plt_lag_21', 'po2_lag_21', 'ptt_lag_21', 'resp_lag_21', 'sbp_lag_21', 'temp_lag_21', 'tnt_lag_21', 'urine_lag_21', 'wbc_lag_21', 'alb_lag_22', 'alp_lag_22', 'alt_lag_22', 'ast_lag_22', 'be_lag_22', 'bicar_lag_22', 'bili_lag_22', 'bili_dir_lag_22', 'bnd_lag_22', 'bun_lag_22', 'ca_lag_22', 'cai_lag_22', 'ck_lag_22', 'ckmb_lag_22', 'cl_lag_22', 'crea_lag_22', 'crp_lag_22', 'dbp_lag_22', 'fgn_lag_22', 'fio2_lag_22', 'glu_lag_22', 'hgb_lag_22', 'hr_lag_22', 'inr_pt_lag_22', 'k_lag_22', 'lact_lag_22', 'lymph_lag_22', 'map_lag_22', 'mch_lag_22', 'mchc_lag_22', 'mcv_lag_22', 'methb_lag_22', 'mg_lag_22', 'na_lag_22', 'neut_lag_22', 'o2sat_lag_22', 'pco2_lag_22', 'ph_lag_22', 'phos_lag_22', 'plt_lag_22', 'po2_lag_22', 'ptt_lag_22', 'resp_lag_22', 'sbp_lag_22', 'temp_lag_22', 'tnt_lag_22', 'urine_lag_22', 'wbc_lag_22', 'alb_lag_23', 'alp_lag_23', 'alt_lag_23', 'ast_lag_23', 'be_lag_23', 'bicar_lag_23', 'bili_lag_23', 'bili_dir_lag_23', 'bnd_lag_23', 'bun_lag_23', 'ca_lag_23', 'cai_lag_23', 'ck_lag_23', 'ckmb_lag_23', 'cl_lag_23', 'crea_lag_23', 'crp_lag_23', 'dbp_lag_23', 'fgn_lag_23', 'fio2_lag_23', 'glu_lag_23', 'hgb_lag_23', 'hr_lag_23', 'inr_pt_lag_23', 'k_lag_23', 'lact_lag_23', 'lymph_lag_23', 'map_lag_23', 'mch_lag_23', 'mchc_lag_23', 'mcv_lag_23', 'methb_lag_23', 'mg_lag_23', 'na_lag_23', 'neut_lag_23', 'o2sat_lag_23', 'pco2_lag_23', 'ph_lag_23', 'phos_lag_23', 'plt_lag_23', 'po2_lag_23', 'ptt_lag_23', 'resp_lag_23', 'sbp_lag_23', 'temp_lag_23', 'tnt_lag_23', 'urine_lag_23', 'wbc_lag_23', 'alb_lag_24', 'alp_lag_24', 'alt_lag_24', 'ast_lag_24', 'be_lag_24', 'bicar_lag_24', 'bili_lag_24', 'bili_dir_lag_24', 'bnd_lag_24', 'bun_lag_24', 'ca_lag_24', 'cai_lag_24', 'ck_lag_24', 'ckmb_lag_24', 'cl_lag_24', 'crea_lag_24', 'crp_lag_24', 'dbp_lag_24', 'fgn_lag_24', 'fio2_lag_24', 'glu_lag_24', 'hgb_lag_24', 'hr_lag_24', 'inr_pt_lag_24', 'k_lag_24', 'lact_lag_24', 'lymph_lag_24', 'map_lag_24', 'mch_lag_24', 'mchc_lag_24', 'mcv_lag_24', 'methb_lag_24', 'mg_lag_24', 'na_lag_24', 'neut_lag_24', 'o2sat_lag_24', 'pco2_lag_24', 'ph_lag_24', 'phos_lag_24', 'plt_lag_24', 'po2_lag_24', 'ptt_lag_24', 'resp_lag_24', 'sbp_lag_24', 'temp_lag_24', 'tnt_lag_24', 'urine_lag_24', 'wbc_lag_24' ] +PredictionDatasetpytorch.time_varying_known_reals=[] PredictionDatasetpytorch.add_relative_time_idx=False -PredictionDatasetpytorch.target="label" - -PredictionDatasetpytorch.time_varying_unknown_reals=[ +PredictionDatasetpytorch.target=[ + "alb", + "alp", + "alt", + "ast", + "be", + "bicar", + "bili", + "bili_dir", + "bnd", + "bun", + "ca", + "cai", + "ck", + "ckmb", + "cl", + "crea", + "crp", + "dbp", + "fgn", + "fio2", + "glu", + "hgb", + "hr", + "inr_pt", + "k", + "lact", + "lymph", + "map", + "mch", + "mchc", + "mcv", + "methb", + "mg", + "na", + "neut", + "o2sat", + "pco2", + "ph", + "phos", + "plt", + "po2", + "ptt", + "resp", + "sbp", + "temp", + "tnt", + "urine", + "wbc", "label", ] -PredictionDatasetpytorch.time_varying_unknown_categoricals=[] -PredictionDatasetpytorch.lagged_variables=[ "alb", + +PredictionDatasetpytorch.time_varying_unknown_reals=[ + "alb", "alp", "alt", "ast", @@ -85,6 +132,11 @@ PredictionDatasetpytorch.lagged_variables=[ "alb", "temp", "tnt", "urine", - "wbc"] + "wbc", + "label", + + ] +PredictionDatasetpytorch.time_varying_unknown_categoricals=[] +PredictionDatasetpytorch.lagged_variables=[] \ No newline at end of file diff --git a/configs/prediction_models/RNNpytorch.gin b/configs/prediction_models/RNNpytorch.gin index 6c0f2e15..8f8132e0 100644 --- a/configs/prediction_models/RNNpytorch.gin +++ b/configs/prediction_models/RNNpytorch.gin @@ -30,17 +30,63 @@ train_common/hyperparameter.gradient_clip_val=100.0 # Dataset params PredictionDatasetpytorch.max_encoder_length = 24 PredictionDatasetpytorch.max_prediction_length = 1 -PredictionDatasetpytorch.time_varying_known_reals=['alb_lag_1', 'alp_lag_1', 'alt_lag_1', 'ast_lag_1', 'be_lag_1', 'bicar_lag_1', 'bili_lag_1', 'bili_dir_lag_1', 'bnd_lag_1', 'bun_lag_1', 'ca_lag_1', 'cai_lag_1', 'ck_lag_1', 'ckmb_lag_1', 'cl_lag_1', 'crea_lag_1', 'crp_lag_1', 'dbp_lag_1', 'fgn_lag_1', 'fio2_lag_1', 'glu_lag_1', 'hgb_lag_1', 'hr_lag_1', 'inr_pt_lag_1', 'k_lag_1', 'lact_lag_1', 'lymph_lag_1', 'map_lag_1', 'mch_lag_1', 'mchc_lag_1', 'mcv_lag_1', 'methb_lag_1', 'mg_lag_1', 'na_lag_1', 'neut_lag_1', 'o2sat_lag_1', 'pco2_lag_1', 'ph_lag_1', 'phos_lag_1', 'plt_lag_1', 'po2_lag_1', 'ptt_lag_1', 'resp_lag_1', 'sbp_lag_1', 'temp_lag_1', 'tnt_lag_1', 'urine_lag_1', 'wbc_lag_1', 'alb_lag_2', 'alp_lag_2', 'alt_lag_2', 'ast_lag_2', 'be_lag_2', 'bicar_lag_2', 'bili_lag_2', 'bili_dir_lag_2', 'bnd_lag_2', 'bun_lag_2', 'ca_lag_2', 'cai_lag_2', 'ck_lag_2', 'ckmb_lag_2', 'cl_lag_2', 'crea_lag_2', 'crp_lag_2', 'dbp_lag_2', 'fgn_lag_2', 'fio2_lag_2', 'glu_lag_2', 'hgb_lag_2', 'hr_lag_2', 'inr_pt_lag_2', 'k_lag_2', 'lact_lag_2', 'lymph_lag_2', 'map_lag_2', 'mch_lag_2', 'mchc_lag_2', 'mcv_lag_2', 'methb_lag_2', 'mg_lag_2', 'na_lag_2', 'neut_lag_2', 'o2sat_lag_2', 'pco2_lag_2', 'ph_lag_2', 'phos_lag_2', 'plt_lag_2', 'po2_lag_2', 'ptt_lag_2', 'resp_lag_2', 'sbp_lag_2', 'temp_lag_2', 'tnt_lag_2', 'urine_lag_2', 'wbc_lag_2', 'alb_lag_3', 'alp_lag_3', 'alt_lag_3', 'ast_lag_3', 'be_lag_3', 'bicar_lag_3', 'bili_lag_3', 'bili_dir_lag_3', 'bnd_lag_3', 'bun_lag_3', 'ca_lag_3', 'cai_lag_3', 'ck_lag_3', 'ckmb_lag_3', 'cl_lag_3', 'crea_lag_3', 'crp_lag_3', 'dbp_lag_3', 'fgn_lag_3', 'fio2_lag_3', 'glu_lag_3', 'hgb_lag_3', 'hr_lag_3', 'inr_pt_lag_3', 'k_lag_3', 'lact_lag_3', 'lymph_lag_3', 'map_lag_3', 'mch_lag_3', 'mchc_lag_3', 'mcv_lag_3', 'methb_lag_3', 'mg_lag_3', 'na_lag_3', 'neut_lag_3', 'o2sat_lag_3', 'pco2_lag_3', 'ph_lag_3', 'phos_lag_3', 'plt_lag_3', 'po2_lag_3', 'ptt_lag_3', 'resp_lag_3', 'sbp_lag_3', 'temp_lag_3', 'tnt_lag_3', 'urine_lag_3', 'wbc_lag_3', 'alb_lag_4', 'alp_lag_4', 'alt_lag_4', 'ast_lag_4', 'be_lag_4', 'bicar_lag_4', 'bili_lag_4', 'bili_dir_lag_4', 'bnd_lag_4', 'bun_lag_4', 'ca_lag_4', 'cai_lag_4', 'ck_lag_4', 'ckmb_lag_4', 'cl_lag_4', 'crea_lag_4', 'crp_lag_4', 'dbp_lag_4', 'fgn_lag_4', 'fio2_lag_4', 'glu_lag_4', 'hgb_lag_4', 'hr_lag_4', 'inr_pt_lag_4', 'k_lag_4', 'lact_lag_4', 'lymph_lag_4', 'map_lag_4', 'mch_lag_4', 'mchc_lag_4', 'mcv_lag_4', 'methb_lag_4', 'mg_lag_4', 'na_lag_4', 'neut_lag_4', 'o2sat_lag_4', 'pco2_lag_4', 'ph_lag_4', 'phos_lag_4', 'plt_lag_4', 'po2_lag_4', 'ptt_lag_4', 'resp_lag_4', 'sbp_lag_4', 'temp_lag_4', 'tnt_lag_4', 'urine_lag_4', 'wbc_lag_4', 'alb_lag_5', 'alp_lag_5', 'alt_lag_5', 'ast_lag_5', 'be_lag_5', 'bicar_lag_5', 'bili_lag_5', 'bili_dir_lag_5', 'bnd_lag_5', 'bun_lag_5', 'ca_lag_5', 'cai_lag_5', 'ck_lag_5', 'ckmb_lag_5', 'cl_lag_5', 'crea_lag_5', 'crp_lag_5', 'dbp_lag_5', 'fgn_lag_5', 'fio2_lag_5', 'glu_lag_5', 'hgb_lag_5', 'hr_lag_5', 'inr_pt_lag_5', 'k_lag_5', 'lact_lag_5', 'lymph_lag_5', 'map_lag_5', 'mch_lag_5', 'mchc_lag_5', 'mcv_lag_5', 'methb_lag_5', 'mg_lag_5', 'na_lag_5', 'neut_lag_5', 'o2sat_lag_5', 'pco2_lag_5', 'ph_lag_5', 'phos_lag_5', 'plt_lag_5', 'po2_lag_5', 'ptt_lag_5', 'resp_lag_5', 'sbp_lag_5', 'temp_lag_5', 'tnt_lag_5', 'urine_lag_5', 'wbc_lag_5', 'alb_lag_6', 'alp_lag_6', 'alt_lag_6', 'ast_lag_6', 'be_lag_6', 'bicar_lag_6', 'bili_lag_6', 'bili_dir_lag_6', 'bnd_lag_6', 'bun_lag_6', 'ca_lag_6', 'cai_lag_6', 'ck_lag_6', 'ckmb_lag_6', 'cl_lag_6', 'crea_lag_6', 'crp_lag_6', 'dbp_lag_6', 'fgn_lag_6', 'fio2_lag_6', 'glu_lag_6', 'hgb_lag_6', 'hr_lag_6', 'inr_pt_lag_6', 'k_lag_6', 'lact_lag_6', 'lymph_lag_6', 'map_lag_6', 'mch_lag_6', 'mchc_lag_6', 'mcv_lag_6', 'methb_lag_6', 'mg_lag_6', 'na_lag_6', 'neut_lag_6', 'o2sat_lag_6', 'pco2_lag_6', 'ph_lag_6', 'phos_lag_6', 'plt_lag_6', 'po2_lag_6', 'ptt_lag_6', 'resp_lag_6', 'sbp_lag_6', 'temp_lag_6', 'tnt_lag_6', 'urine_lag_6', 'wbc_lag_6', 'alb_lag_7', 'alp_lag_7', 'alt_lag_7', 'ast_lag_7', 'be_lag_7', 'bicar_lag_7', 'bili_lag_7', 'bili_dir_lag_7', 'bnd_lag_7', 'bun_lag_7', 'ca_lag_7', 'cai_lag_7', 'ck_lag_7', 'ckmb_lag_7', 'cl_lag_7', 'crea_lag_7', 'crp_lag_7', 'dbp_lag_7', 'fgn_lag_7', 'fio2_lag_7', 'glu_lag_7', 'hgb_lag_7', 'hr_lag_7', 'inr_pt_lag_7', 'k_lag_7', 'lact_lag_7', 'lymph_lag_7', 'map_lag_7', 'mch_lag_7', 'mchc_lag_7', 'mcv_lag_7', 'methb_lag_7', 'mg_lag_7', 'na_lag_7', 'neut_lag_7', 'o2sat_lag_7', 'pco2_lag_7', 'ph_lag_7', 'phos_lag_7', 'plt_lag_7', 'po2_lag_7', 'ptt_lag_7', 'resp_lag_7', 'sbp_lag_7', 'temp_lag_7', 'tnt_lag_7', 'urine_lag_7', 'wbc_lag_7', 'alb_lag_8', 'alp_lag_8', 'alt_lag_8', 'ast_lag_8', 'be_lag_8', 'bicar_lag_8', 'bili_lag_8', 'bili_dir_lag_8', 'bnd_lag_8', 'bun_lag_8', 'ca_lag_8', 'cai_lag_8', 'ck_lag_8', 'ckmb_lag_8', 'cl_lag_8', 'crea_lag_8', 'crp_lag_8', 'dbp_lag_8', 'fgn_lag_8', 'fio2_lag_8', 'glu_lag_8', 'hgb_lag_8', 'hr_lag_8', 'inr_pt_lag_8', 'k_lag_8', 'lact_lag_8', 'lymph_lag_8', 'map_lag_8', 'mch_lag_8', 'mchc_lag_8', 'mcv_lag_8', 'methb_lag_8', 'mg_lag_8', 'na_lag_8', 'neut_lag_8', 'o2sat_lag_8', 'pco2_lag_8', 'ph_lag_8', 'phos_lag_8', 'plt_lag_8', 'po2_lag_8', 'ptt_lag_8', 'resp_lag_8', 'sbp_lag_8', 'temp_lag_8', 'tnt_lag_8', 'urine_lag_8', 'wbc_lag_8', 'alb_lag_9', 'alp_lag_9', 'alt_lag_9', 'ast_lag_9', 'be_lag_9', 'bicar_lag_9', 'bili_lag_9', 'bili_dir_lag_9', 'bnd_lag_9', 'bun_lag_9', 'ca_lag_9', 'cai_lag_9', 'ck_lag_9', 'ckmb_lag_9', 'cl_lag_9', 'crea_lag_9', 'crp_lag_9', 'dbp_lag_9', 'fgn_lag_9', 'fio2_lag_9', 'glu_lag_9', 'hgb_lag_9', 'hr_lag_9', 'inr_pt_lag_9', 'k_lag_9', 'lact_lag_9', 'lymph_lag_9', 'map_lag_9', 'mch_lag_9', 'mchc_lag_9', 'mcv_lag_9', 'methb_lag_9', 'mg_lag_9', 'na_lag_9', 'neut_lag_9', 'o2sat_lag_9', 'pco2_lag_9', 'ph_lag_9', 'phos_lag_9', 'plt_lag_9', 'po2_lag_9', 'ptt_lag_9', 'resp_lag_9', 'sbp_lag_9', 'temp_lag_9', 'tnt_lag_9', 'urine_lag_9', 'wbc_lag_9', 'alb_lag_10', 'alp_lag_10', 'alt_lag_10', 'ast_lag_10', 'be_lag_10', 'bicar_lag_10', 'bili_lag_10', 'bili_dir_lag_10', 'bnd_lag_10', 'bun_lag_10', 'ca_lag_10', 'cai_lag_10', 'ck_lag_10', 'ckmb_lag_10', 'cl_lag_10', 'crea_lag_10', 'crp_lag_10', 'dbp_lag_10', 'fgn_lag_10', 'fio2_lag_10', 'glu_lag_10', 'hgb_lag_10', 'hr_lag_10', 'inr_pt_lag_10', 'k_lag_10', 'lact_lag_10', 'lymph_lag_10', 'map_lag_10', 'mch_lag_10', 'mchc_lag_10', 'mcv_lag_10', 'methb_lag_10', 'mg_lag_10', 'na_lag_10', 'neut_lag_10', 'o2sat_lag_10', 'pco2_lag_10', 'ph_lag_10', 'phos_lag_10', 'plt_lag_10', 'po2_lag_10', 'ptt_lag_10', 'resp_lag_10', 'sbp_lag_10', 'temp_lag_10', 'tnt_lag_10', 'urine_lag_10', 'wbc_lag_10', 'alb_lag_11', 'alp_lag_11', 'alt_lag_11', 'ast_lag_11', 'be_lag_11', 'bicar_lag_11', 'bili_lag_11', 'bili_dir_lag_11', 'bnd_lag_11', 'bun_lag_11', 'ca_lag_11', 'cai_lag_11', 'ck_lag_11', 'ckmb_lag_11', 'cl_lag_11', 'crea_lag_11', 'crp_lag_11', 'dbp_lag_11', 'fgn_lag_11', 'fio2_lag_11', 'glu_lag_11', 'hgb_lag_11', 'hr_lag_11', 'inr_pt_lag_11', 'k_lag_11', 'lact_lag_11', 'lymph_lag_11', 'map_lag_11', 'mch_lag_11', 'mchc_lag_11', 'mcv_lag_11', 'methb_lag_11', 'mg_lag_11', 'na_lag_11', 'neut_lag_11', 'o2sat_lag_11', 'pco2_lag_11', 'ph_lag_11', 'phos_lag_11', 'plt_lag_11', 'po2_lag_11', 'ptt_lag_11', 'resp_lag_11', 'sbp_lag_11', 'temp_lag_11', 'tnt_lag_11', 'urine_lag_11', 'wbc_lag_11', 'alb_lag_12', 'alp_lag_12', 'alt_lag_12', 'ast_lag_12', 'be_lag_12', 'bicar_lag_12', 'bili_lag_12', 'bili_dir_lag_12', 'bnd_lag_12', 'bun_lag_12', 'ca_lag_12', 'cai_lag_12', 'ck_lag_12', 'ckmb_lag_12', 'cl_lag_12', 'crea_lag_12', 'crp_lag_12', 'dbp_lag_12', 'fgn_lag_12', 'fio2_lag_12', 'glu_lag_12', 'hgb_lag_12', 'hr_lag_12', 'inr_pt_lag_12', 'k_lag_12', 'lact_lag_12', 'lymph_lag_12', 'map_lag_12', 'mch_lag_12', 'mchc_lag_12', 'mcv_lag_12', 'methb_lag_12', 'mg_lag_12', 'na_lag_12', 'neut_lag_12', 'o2sat_lag_12', 'pco2_lag_12', 'ph_lag_12', 'phos_lag_12', 'plt_lag_12', 'po2_lag_12', 'ptt_lag_12', 'resp_lag_12', 'sbp_lag_12', 'temp_lag_12', 'tnt_lag_12', 'urine_lag_12', 'wbc_lag_12', 'alb_lag_13', 'alp_lag_13', 'alt_lag_13', 'ast_lag_13', 'be_lag_13', 'bicar_lag_13', 'bili_lag_13', 'bili_dir_lag_13', 'bnd_lag_13', 'bun_lag_13', 'ca_lag_13', 'cai_lag_13', 'ck_lag_13', 'ckmb_lag_13', 'cl_lag_13', 'crea_lag_13', 'crp_lag_13', 'dbp_lag_13', 'fgn_lag_13', 'fio2_lag_13', 'glu_lag_13', 'hgb_lag_13', 'hr_lag_13', 'inr_pt_lag_13', 'k_lag_13', 'lact_lag_13', 'lymph_lag_13', 'map_lag_13', 'mch_lag_13', 'mchc_lag_13', 'mcv_lag_13', 'methb_lag_13', 'mg_lag_13', 'na_lag_13', 'neut_lag_13', 'o2sat_lag_13', 'pco2_lag_13', 'ph_lag_13', 'phos_lag_13', 'plt_lag_13', 'po2_lag_13', 'ptt_lag_13', 'resp_lag_13', 'sbp_lag_13', 'temp_lag_13', 'tnt_lag_13', 'urine_lag_13', 'wbc_lag_13', 'alb_lag_14', 'alp_lag_14', 'alt_lag_14', 'ast_lag_14', 'be_lag_14', 'bicar_lag_14', 'bili_lag_14', 'bili_dir_lag_14', 'bnd_lag_14', 'bun_lag_14', 'ca_lag_14', 'cai_lag_14', 'ck_lag_14', 'ckmb_lag_14', 'cl_lag_14', 'crea_lag_14', 'crp_lag_14', 'dbp_lag_14', 'fgn_lag_14', 'fio2_lag_14', 'glu_lag_14', 'hgb_lag_14', 'hr_lag_14', 'inr_pt_lag_14', 'k_lag_14', 'lact_lag_14', 'lymph_lag_14', 'map_lag_14', 'mch_lag_14', 'mchc_lag_14', 'mcv_lag_14', 'methb_lag_14', 'mg_lag_14', 'na_lag_14', 'neut_lag_14', 'o2sat_lag_14', 'pco2_lag_14', 'ph_lag_14', 'phos_lag_14', 'plt_lag_14', 'po2_lag_14', 'ptt_lag_14', 'resp_lag_14', 'sbp_lag_14', 'temp_lag_14', 'tnt_lag_14', 'urine_lag_14', 'wbc_lag_14', 'alb_lag_15', 'alp_lag_15', 'alt_lag_15', 'ast_lag_15', 'be_lag_15', 'bicar_lag_15', 'bili_lag_15', 'bili_dir_lag_15', 'bnd_lag_15', 'bun_lag_15', 'ca_lag_15', 'cai_lag_15', 'ck_lag_15', 'ckmb_lag_15', 'cl_lag_15', 'crea_lag_15', 'crp_lag_15', 'dbp_lag_15', 'fgn_lag_15', 'fio2_lag_15', 'glu_lag_15', 'hgb_lag_15', 'hr_lag_15', 'inr_pt_lag_15', 'k_lag_15', 'lact_lag_15', 'lymph_lag_15', 'map_lag_15', 'mch_lag_15', 'mchc_lag_15', 'mcv_lag_15', 'methb_lag_15', 'mg_lag_15', 'na_lag_15', 'neut_lag_15', 'o2sat_lag_15', 'pco2_lag_15', 'ph_lag_15', 'phos_lag_15', 'plt_lag_15', 'po2_lag_15', 'ptt_lag_15', 'resp_lag_15', 'sbp_lag_15', 'temp_lag_15', 'tnt_lag_15', 'urine_lag_15', 'wbc_lag_15', 'alb_lag_16', 'alp_lag_16', 'alt_lag_16', 'ast_lag_16', 'be_lag_16', 'bicar_lag_16', 'bili_lag_16', 'bili_dir_lag_16', 'bnd_lag_16', 'bun_lag_16', 'ca_lag_16', 'cai_lag_16', 'ck_lag_16', 'ckmb_lag_16', 'cl_lag_16', 'crea_lag_16', 'crp_lag_16', 'dbp_lag_16', 'fgn_lag_16', 'fio2_lag_16', 'glu_lag_16', 'hgb_lag_16', 'hr_lag_16', 'inr_pt_lag_16', 'k_lag_16', 'lact_lag_16', 'lymph_lag_16', 'map_lag_16', 'mch_lag_16', 'mchc_lag_16', 'mcv_lag_16', 'methb_lag_16', 'mg_lag_16', 'na_lag_16', 'neut_lag_16', 'o2sat_lag_16', 'pco2_lag_16', 'ph_lag_16', 'phos_lag_16', 'plt_lag_16', 'po2_lag_16', 'ptt_lag_16', 'resp_lag_16', 'sbp_lag_16', 'temp_lag_16', 'tnt_lag_16', 'urine_lag_16', 'wbc_lag_16', 'alb_lag_17', 'alp_lag_17', 'alt_lag_17', 'ast_lag_17', 'be_lag_17', 'bicar_lag_17', 'bili_lag_17', 'bili_dir_lag_17', 'bnd_lag_17', 'bun_lag_17', 'ca_lag_17', 'cai_lag_17', 'ck_lag_17', 'ckmb_lag_17', 'cl_lag_17', 'crea_lag_17', 'crp_lag_17', 'dbp_lag_17', 'fgn_lag_17', 'fio2_lag_17', 'glu_lag_17', 'hgb_lag_17', 'hr_lag_17', 'inr_pt_lag_17', 'k_lag_17', 'lact_lag_17', 'lymph_lag_17', 'map_lag_17', 'mch_lag_17', 'mchc_lag_17', 'mcv_lag_17', 'methb_lag_17', 'mg_lag_17', 'na_lag_17', 'neut_lag_17', 'o2sat_lag_17', 'pco2_lag_17', 'ph_lag_17', 'phos_lag_17', 'plt_lag_17', 'po2_lag_17', 'ptt_lag_17', 'resp_lag_17', 'sbp_lag_17', 'temp_lag_17', 'tnt_lag_17', 'urine_lag_17', 'wbc_lag_17', 'alb_lag_18', 'alp_lag_18', 'alt_lag_18', 'ast_lag_18', 'be_lag_18', 'bicar_lag_18', 'bili_lag_18', 'bili_dir_lag_18', 'bnd_lag_18', 'bun_lag_18', 'ca_lag_18', 'cai_lag_18', 'ck_lag_18', 'ckmb_lag_18', 'cl_lag_18', 'crea_lag_18', 'crp_lag_18', 'dbp_lag_18', 'fgn_lag_18', 'fio2_lag_18', 'glu_lag_18', 'hgb_lag_18', 'hr_lag_18', 'inr_pt_lag_18', 'k_lag_18', 'lact_lag_18', 'lymph_lag_18', 'map_lag_18', 'mch_lag_18', 'mchc_lag_18', 'mcv_lag_18', 'methb_lag_18', 'mg_lag_18', 'na_lag_18', 'neut_lag_18', 'o2sat_lag_18', 'pco2_lag_18', 'ph_lag_18', 'phos_lag_18', 'plt_lag_18', 'po2_lag_18', 'ptt_lag_18', 'resp_lag_18', 'sbp_lag_18', 'temp_lag_18', 'tnt_lag_18', 'urine_lag_18', 'wbc_lag_18', 'alb_lag_19', 'alp_lag_19', 'alt_lag_19', 'ast_lag_19', 'be_lag_19', 'bicar_lag_19', 'bili_lag_19', 'bili_dir_lag_19', 'bnd_lag_19', 'bun_lag_19', 'ca_lag_19', 'cai_lag_19', 'ck_lag_19', 'ckmb_lag_19', 'cl_lag_19', 'crea_lag_19', 'crp_lag_19', 'dbp_lag_19', 'fgn_lag_19', 'fio2_lag_19', 'glu_lag_19', 'hgb_lag_19', 'hr_lag_19', 'inr_pt_lag_19', 'k_lag_19', 'lact_lag_19', 'lymph_lag_19', 'map_lag_19', 'mch_lag_19', 'mchc_lag_19', 'mcv_lag_19', 'methb_lag_19', 'mg_lag_19', 'na_lag_19', 'neut_lag_19', 'o2sat_lag_19', 'pco2_lag_19', 'ph_lag_19', 'phos_lag_19', 'plt_lag_19', 'po2_lag_19', 'ptt_lag_19', 'resp_lag_19', 'sbp_lag_19', 'temp_lag_19', 'tnt_lag_19', 'urine_lag_19', 'wbc_lag_19', 'alb_lag_20', 'alp_lag_20', 'alt_lag_20', 'ast_lag_20', 'be_lag_20', 'bicar_lag_20', 'bili_lag_20', 'bili_dir_lag_20', 'bnd_lag_20', 'bun_lag_20', 'ca_lag_20', 'cai_lag_20', 'ck_lag_20', 'ckmb_lag_20', 'cl_lag_20', 'crea_lag_20', 'crp_lag_20', 'dbp_lag_20', 'fgn_lag_20', 'fio2_lag_20', 'glu_lag_20', 'hgb_lag_20', 'hr_lag_20', 'inr_pt_lag_20', 'k_lag_20', 'lact_lag_20', 'lymph_lag_20', 'map_lag_20', 'mch_lag_20', 'mchc_lag_20', 'mcv_lag_20', 'methb_lag_20', 'mg_lag_20', 'na_lag_20', 'neut_lag_20', 'o2sat_lag_20', 'pco2_lag_20', 'ph_lag_20', 'phos_lag_20', 'plt_lag_20', 'po2_lag_20', 'ptt_lag_20', 'resp_lag_20', 'sbp_lag_20', 'temp_lag_20', 'tnt_lag_20', 'urine_lag_20', 'wbc_lag_20', 'alb_lag_21', 'alp_lag_21', 'alt_lag_21', 'ast_lag_21', 'be_lag_21', 'bicar_lag_21', 'bili_lag_21', 'bili_dir_lag_21', 'bnd_lag_21', 'bun_lag_21', 'ca_lag_21', 'cai_lag_21', 'ck_lag_21', 'ckmb_lag_21', 'cl_lag_21', 'crea_lag_21', 'crp_lag_21', 'dbp_lag_21', 'fgn_lag_21', 'fio2_lag_21', 'glu_lag_21', 'hgb_lag_21', 'hr_lag_21', 'inr_pt_lag_21', 'k_lag_21', 'lact_lag_21', 'lymph_lag_21', 'map_lag_21', 'mch_lag_21', 'mchc_lag_21', 'mcv_lag_21', 'methb_lag_21', 'mg_lag_21', 'na_lag_21', 'neut_lag_21', 'o2sat_lag_21', 'pco2_lag_21', 'ph_lag_21', 'phos_lag_21', 'plt_lag_21', 'po2_lag_21', 'ptt_lag_21', 'resp_lag_21', 'sbp_lag_21', 'temp_lag_21', 'tnt_lag_21', 'urine_lag_21', 'wbc_lag_21', 'alb_lag_22', 'alp_lag_22', 'alt_lag_22', 'ast_lag_22', 'be_lag_22', 'bicar_lag_22', 'bili_lag_22', 'bili_dir_lag_22', 'bnd_lag_22', 'bun_lag_22', 'ca_lag_22', 'cai_lag_22', 'ck_lag_22', 'ckmb_lag_22', 'cl_lag_22', 'crea_lag_22', 'crp_lag_22', 'dbp_lag_22', 'fgn_lag_22', 'fio2_lag_22', 'glu_lag_22', 'hgb_lag_22', 'hr_lag_22', 'inr_pt_lag_22', 'k_lag_22', 'lact_lag_22', 'lymph_lag_22', 'map_lag_22', 'mch_lag_22', 'mchc_lag_22', 'mcv_lag_22', 'methb_lag_22', 'mg_lag_22', 'na_lag_22', 'neut_lag_22', 'o2sat_lag_22', 'pco2_lag_22', 'ph_lag_22', 'phos_lag_22', 'plt_lag_22', 'po2_lag_22', 'ptt_lag_22', 'resp_lag_22', 'sbp_lag_22', 'temp_lag_22', 'tnt_lag_22', 'urine_lag_22', 'wbc_lag_22', 'alb_lag_23', 'alp_lag_23', 'alt_lag_23', 'ast_lag_23', 'be_lag_23', 'bicar_lag_23', 'bili_lag_23', 'bili_dir_lag_23', 'bnd_lag_23', 'bun_lag_23', 'ca_lag_23', 'cai_lag_23', 'ck_lag_23', 'ckmb_lag_23', 'cl_lag_23', 'crea_lag_23', 'crp_lag_23', 'dbp_lag_23', 'fgn_lag_23', 'fio2_lag_23', 'glu_lag_23', 'hgb_lag_23', 'hr_lag_23', 'inr_pt_lag_23', 'k_lag_23', 'lact_lag_23', 'lymph_lag_23', 'map_lag_23', 'mch_lag_23', 'mchc_lag_23', 'mcv_lag_23', 'methb_lag_23', 'mg_lag_23', 'na_lag_23', 'neut_lag_23', 'o2sat_lag_23', 'pco2_lag_23', 'ph_lag_23', 'phos_lag_23', 'plt_lag_23', 'po2_lag_23', 'ptt_lag_23', 'resp_lag_23', 'sbp_lag_23', 'temp_lag_23', 'tnt_lag_23', 'urine_lag_23', 'wbc_lag_23', 'alb_lag_24', 'alp_lag_24', 'alt_lag_24', 'ast_lag_24', 'be_lag_24', 'bicar_lag_24', 'bili_lag_24', 'bili_dir_lag_24', 'bnd_lag_24', 'bun_lag_24', 'ca_lag_24', 'cai_lag_24', 'ck_lag_24', 'ckmb_lag_24', 'cl_lag_24', 'crea_lag_24', 'crp_lag_24', 'dbp_lag_24', 'fgn_lag_24', 'fio2_lag_24', 'glu_lag_24', 'hgb_lag_24', 'hr_lag_24', 'inr_pt_lag_24', 'k_lag_24', 'lact_lag_24', 'lymph_lag_24', 'map_lag_24', 'mch_lag_24', 'mchc_lag_24', 'mcv_lag_24', 'methb_lag_24', 'mg_lag_24', 'na_lag_24', 'neut_lag_24', 'o2sat_lag_24', 'pco2_lag_24', 'ph_lag_24', 'phos_lag_24', 'plt_lag_24', 'po2_lag_24', 'ptt_lag_24', 'resp_lag_24', 'sbp_lag_24', 'temp_lag_24', 'tnt_lag_24', 'urine_lag_24', 'wbc_lag_24' ] - +PredictionDatasetpytorch.time_varying_known_reals=[ ] PredictionDatasetpytorch.add_relative_time_idx=False -PredictionDatasetpytorch.target="label" - -PredictionDatasetpytorch.time_varying_unknown_reals=[ +PredictionDatasetpytorch.target=[ + "alb", + "alp", + "alt", + "ast", + "be", + "bicar", + "bili", + "bili_dir", + "bnd", + "bun", + "ca", + "cai", + "ck", + "ckmb", + "cl", + "crea", + "crp", + "dbp", + "fgn", + "fio2", + "glu", + "hgb", + "hr", + "inr_pt", + "k", + "lact", + "lymph", + "map", + "mch", + "mchc", + "mcv", + "methb", + "mg", + "na", + "neut", + "o2sat", + "pco2", + "ph", + "phos", + "plt", + "po2", + "ptt", + "resp", + "sbp", + "temp", + "tnt", + "urine", + "wbc", "label", ] -PredictionDatasetpytorch.time_varying_unknown_categoricals=[] -PredictionDatasetpytorch.lagged_variables=[ "alb", + +PredictionDatasetpytorch.time_varying_unknown_reals=[ + "alb", "alp", "alt", "ast", @@ -87,4 +133,10 @@ PredictionDatasetpytorch.lagged_variables=[ "alb", "temp", "tnt", "urine", - "wbc"] \ No newline at end of file + "wbc", + "label", + + ] +PredictionDatasetpytorch.time_varying_unknown_categoricals=[] +PredictionDatasetpytorch.lagged_variables=[] +PredictionDatasetpytorch.targetnormalizer='multi' \ No newline at end of file diff --git a/configs/prediction_models/TFTpytorch.gin b/configs/prediction_models/TFTpytorch.gin index c7264ed5..37b2dd1a 100644 --- a/configs/prediction_models/TFTpytorch.gin +++ b/configs/prediction_models/TFTpytorch.gin @@ -89,3 +89,4 @@ PredictionDatasetpytorch.time_varying_unknown_reals=["alb", "urine", "wbc",] PredictionDatasetpytorch.lagged_variables=[] +PredictionDatasetpytorch.targetnormalizer='single' \ No newline at end of file diff --git a/environment.yml b/environment.yml index ff548770..2232e1a3 100644 --- a/environment.yml +++ b/environment.yml @@ -33,6 +33,8 @@ dependencies: # Fixed version because of NumPy incompatibility and stale development status. - scikit-optimize-fix==0.9.1 - hydra-submitit-launcher==1.2.0 + - git+https://github.com/youssefmecky96/pytorchforecasting + - git+https://github.com/youssefmecky96/captum # Note: versioning of Pytorch might be dependent on compatible CUDA version. # Please check yourself if your Pytorch installation supports cuda (for gpu acceleration) diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index a0e51dbc..8acc1f88 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -10,7 +10,7 @@ from icu_benchmarks.imputation.amputations import ampute_data from .constants import DataSegment as Segment from .constants import DataSplit as Split -from pytorch_forecasting import TimeSeriesDataSet, GroupNormalizer +from pytorch_forecasting import TimeSeriesDataSet, GroupNormalizer, MultiNormalizer, EncoderNormalizer class CommonDataset(Dataset): @@ -450,6 +450,7 @@ def __init__( time_varying_known_reals: List[str], time_varying_unknown_categoricals: List[str], lagged_variables: List[str], + target_normalizer: str, *args, ram_cache: bool = False, add_relative_time_idx: bool = False, @@ -482,7 +483,13 @@ def __init__( self.ram_cache = ram_cache self.kwargs = kwargs self.column_names = features.columns - + if target_normalizer == 'multi': + target_normalizer = MultiNormalizer([EncoderNormalizer(transformation='relu') for _ in range(len(target)-1)] + [GroupNormalizer(groups=["stay_id"], transformation="relu")] + ) + else: + target_normalizer = GroupNormalizer( + groups=["stay_id"], transformation="relu" + ) super().__init__( data=self.data, time_idx="time_idx", @@ -502,9 +509,7 @@ def __init__( # add_target_scales=True, # add_encoder_length=True, predict_mode=True, - target_normalizer=GroupNormalizer( - groups=["stay_id"], transformation="relu" - ), + target_normalizer=target_normalizer ) def get_balance(self) -> list: diff --git a/icu_benchmarks/models/custom_metrics.py b/icu_benchmarks/models/custom_metrics.py index 4e082d29..baecb3e1 100644 --- a/icu_benchmarks/models/custom_metrics.py +++ b/icu_benchmarks/models/custom_metrics.py @@ -6,7 +6,7 @@ from sklearn.calibration import calibration_curve from scipy.spatial.distance import jensenshannon from torchmetrics.classification import BinaryFairness - +from icu_benchmarks.models.similarity_func import correlation_spearman, distance_euclidean, correlation_pearson, cosine """" This file contains custom metrics that can be added to YAIB. """ diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index b1844d3d..95040aac 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -1072,7 +1072,8 @@ def norm_function(arr): return torch.norm(arr) denominator += (denominator == 0) * eps_min return nominator / denominator - + if not torch.is_tensor(attribution): + attribution = torch.tensor(attribution).to(self.device) if explain_method == "Attention": y_pred = self.model.predict(dataloader) x_original = dataloader.dataset.data["reals"].clone() @@ -1090,6 +1091,7 @@ def norm_function(arr): return torch.norm(arr) RIS = relative_stability_objective( x_original[close_indices, :, :].detach(), x_preturb[close_indices, :, :].detach(), attribution, att_preturb, input=True ) + ROS = relative_stability_objective( y_pred[close_indices], y_pred_preturb[close_indices], attribution, att_preturb, input=False ) @@ -1124,7 +1126,6 @@ def norm_function(arr): return torch.norm(arr) # Find where the difference is less than or equal to a thershold close_indices = torch.nonzero(difference <= thershold).squeeze() - print(close_indices) RIS = relative_stability_objective( x_original[close_indices, :, :].detach(), x["encoder_cont"][close_indices, :, :].detach(), attribution[close_indices, :, :], att_preturb[close_indices, :, :], input=True From 31c81767bd4dc32ef15894db6a557bfa02293f0c Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Thu, 28 Dec 2023 18:48:39 +0100 Subject: [PATCH 128/142] small fixes --- configs/prediction_models/DeepARpytorch.gin | 1 + icu_benchmarks/data/loader.py | 6 ++++-- icu_benchmarks/models/train.py | 6 +++--- icu_benchmarks/models/wrappers.py | 4 ++-- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/configs/prediction_models/DeepARpytorch.gin b/configs/prediction_models/DeepARpytorch.gin index 9407222a..e7dbf54b 100644 --- a/configs/prediction_models/DeepARpytorch.gin +++ b/configs/prediction_models/DeepARpytorch.gin @@ -138,5 +138,6 @@ PredictionDatasetpytorch.time_varying_unknown_reals=[ ] PredictionDatasetpytorch.time_varying_unknown_categoricals=[] PredictionDatasetpytorch.lagged_variables=[] +PredictionDatasetpytorch.targetnormalizer='multi' \ No newline at end of file diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 8acc1f88..499bdc6f 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -450,8 +450,8 @@ def __init__( time_varying_known_reals: List[str], time_varying_unknown_categoricals: List[str], lagged_variables: List[str], - target_normalizer: str, *args, + target_normalizer: str = "", ram_cache: bool = False, add_relative_time_idx: bool = False, name: str = "", @@ -509,7 +509,9 @@ def __init__( # add_target_scales=True, # add_encoder_length=True, predict_mode=True, - target_normalizer=target_normalizer + target_normalizer=GroupNormalizer( + groups=["stay_id"], transformation="relu" + ) ) def get_balance(self) -> list: diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 32358d29..0ced60c0 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -282,9 +282,9 @@ def train_common( # choose which methods to get attributions methods = { - "G": Saliency, + # "G": Saliency, # "L": Lime, - "IG": IntegratedGradients, + # "IG": IntegratedGradients, # "FA": FeatureAblation, "R": "Random", "Att": "Attention" @@ -331,7 +331,7 @@ def create_default_mask(shape): feature_masks = tuple([create_default_mask(shape) if i != 1 else feature_mask_second for i, shape in enumerate(shapes)]) all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score, r_score, st_i_score, st_o_score = model.explantation( - dataloader=test_loader, method=item, log_dir=log_dir, plot=True, return_input_shape=True, XAI_metric=XAI_metric, random_model=random_model + dataloader=test_loader, method=item, log_dir=log_dir, plot=True, feature_mask=feature_masks, return_input_shape=True, XAI_metric=XAI_metric, random_model=random_model ) else: diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 95040aac..ae63ab48 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -1105,8 +1105,9 @@ def norm_function(arr): return torch.norm(arr) x["encoder_cont"] += noise y_pred_preturb = self(self.prep_data(x)).detach() if explain_method == "Random": - att_preturb = np.random.normal(size=[64, 24, 53]) + att_preturb = np.random.normal(size=[64, 24, 53]) + att_preturb = torch.tensor(att_preturb).to(self.device) else: data, baselines = self.prep_data_captum(x) @@ -1126,7 +1127,6 @@ def norm_function(arr): return torch.norm(arr) # Find where the difference is less than or equal to a thershold close_indices = torch.nonzero(difference <= thershold).squeeze() - RIS = relative_stability_objective( x_original[close_indices, :, :].detach(), x["encoder_cont"][close_indices, :, :].detach(), attribution[close_indices, :, :], att_preturb[close_indices, :, :], input=True ) From 07cbc3cf9c413fe2c848ab32746a2c9e62f6cf09 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Thu, 7 Mar 2024 11:35:56 +0100 Subject: [PATCH 129/142] formatted --- icu_benchmarks/data/loader.py | 31 +- icu_benchmarks/data/preprocessor.py | 6 +- icu_benchmarks/models/constants.py | 10 +- icu_benchmarks/models/custom_metrics.py | 194 +++++----- icu_benchmarks/models/dl_models.py | 10 +- icu_benchmarks/models/metrics.py | 9 +- icu_benchmarks/models/similarity_func.py | 14 +- icu_benchmarks/models/train.py | 106 ++++-- icu_benchmarks/models/wrappers.py | 462 +++++++++++++++++------ icu_benchmarks/run.py | 6 +- icu_benchmarks/run_utils.py | 73 ++-- 11 files changed, 616 insertions(+), 305 deletions(-) diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 499bdc6f..1707a2d3 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -3,14 +3,19 @@ import pandas as pd import gin import numpy as np -from torch import Tensor, cat, from_numpy, float32, min, max, randn_like +from torch import Tensor, cat, from_numpy, float32, randn_like from torch.utils.data import Dataset import logging from typing import Dict, Tuple, Union from icu_benchmarks.imputation.amputations import ampute_data from .constants import DataSegment as Segment from .constants import DataSplit as Split -from pytorch_forecasting import TimeSeriesDataSet, GroupNormalizer, MultiNormalizer, EncoderNormalizer +from pytorch_forecasting import ( + TimeSeriesDataSet, + GroupNormalizer, + MultiNormalizer, + EncoderNormalizer, +) class CommonDataset(Dataset): @@ -483,13 +488,13 @@ def __init__( self.ram_cache = ram_cache self.kwargs = kwargs self.column_names = features.columns - if target_normalizer == 'multi': - target_normalizer = MultiNormalizer([EncoderNormalizer(transformation='relu') for _ in range(len(target)-1)] + [GroupNormalizer(groups=["stay_id"], transformation="relu")] - ) - else: - target_normalizer = GroupNormalizer( - groups=["stay_id"], transformation="relu" + if target_normalizer == "multi": + target_normalizer = MultiNormalizer( + [EncoderNormalizer(transformation="relu") for _ in range(len(target) - 1)] + + [GroupNormalizer(groups=["stay_id"], transformation="relu")] ) + else: + target_normalizer = GroupNormalizer(groups=["stay_id"], transformation="relu") super().__init__( data=self.data, time_idx="time_idx", @@ -509,9 +514,7 @@ def __init__( # add_target_scales=True, # add_encoder_length=True, predict_mode=True, - target_normalizer=GroupNormalizer( - groups=["stay_id"], transformation="relu" - ) + target_normalizer=GroupNormalizer(groups=["stay_id"], transformation="relu"), ) def get_balance(self) -> list: @@ -533,12 +536,14 @@ def get_feature_names(self): def randomize_labels(self, num_classes=None, min=None, max=None): if num_classes == 1: random_target = np.random.uniform( - self.data["target"][0].min(), self.data["target"][0].max(), size=len(self.data["target"][0]) + self.data["target"][0].min(), + self.data["target"][0].max(), + size=len(self.data["target"][0]), ) else: random_target = np.random.randint(num_classes, size=len(self.data["target"][0])) self.data["target"][0] = Tensor(random_target) def add_noise(self, num_classes=None, min=None, max=None): - noise = randn_like(self.data["reals"])*0.01 + noise = randn_like(self.data["reals"]) * 0.01 self.data["reals"] += noise diff --git a/icu_benchmarks/data/preprocessor.py b/icu_benchmarks/data/preprocessor.py index 9da25308..486ab0e8 100644 --- a/icu_benchmarks/data/preprocessor.py +++ b/icu_benchmarks/data/preprocessor.py @@ -117,8 +117,7 @@ def _process_static(self, data, vars): sta_rec.add_step(StepScale()) sta_rec.add_step(StepImputeFastZeroFill(sel=all_numeric_predictors())) - sta_rec.add_step(StepSklearn(SimpleImputer(missing_values=None, - strategy="most_frequent"), sel=has_type("object"))) + sta_rec.add_step(StepSklearn(SimpleImputer(missing_values=None, strategy="most_frequent"), sel=has_type("object"))) sta_rec.add_step(StepSklearn(LabelEncoder(), sel=has_type("object"), columnwise=True)) data = apply_recipe_to_splits(sta_rec, data, Segment.static, self.save_cache, self.load_cache) @@ -288,8 +287,7 @@ def _process_dynamic_data(self, data, vars): if self.filter_missing_values: rows_to_remove = data[Segment.dynamic][vars[Segment.dynamic]].isna().sum(axis=1) != 0 ids_to_remove = data[Segment.dynamic].loc[rows_to_remove][vars["GROUP"]].unique() - data = {table_name: table.loc[~table[vars["GROUP"]].isin( - ids_to_remove)] for table_name, table in data.items()} + data = {table_name: table.loc[~table[vars["GROUP"]].isin(ids_to_remove)] for table_name, table in data.items()} logging.info(f"Removed {len(ids_to_remove)} stays with missing values.") return data diff --git a/icu_benchmarks/models/constants.py b/icu_benchmarks/models/constants.py index 093d15bd..224355eb 100644 --- a/icu_benchmarks/models/constants.py +++ b/icu_benchmarks/models/constants.py @@ -1,4 +1,9 @@ -from ignite.contrib.metrics import AveragePrecision, ROC_AUC, RocCurve, PrecisionRecallCurve +from ignite.contrib.metrics import ( + AveragePrecision, + ROC_AUC, + RocCurve, + PrecisionRecallCurve, +) from ignite.metrics import Accuracy, RootMeanSquaredError from sklearn.calibration import calibration_curve from sklearn.metrics import ( @@ -14,8 +19,6 @@ ) from torchmetrics.classification import ( AUROC, - AveragePrecision as TorchMetricsAveragePrecision, - PrecisionRecallCurve as TorchMetricsPrecisionRecallCurve, CalibrationError, F1Score, ) @@ -25,7 +28,6 @@ BalancedAccuracy, MAE, JSD, - BinaryFairnessWrapper, ) diff --git a/icu_benchmarks/models/custom_metrics.py b/icu_benchmarks/models/custom_metrics.py index baecb3e1..84b074cc 100644 --- a/icu_benchmarks/models/custom_metrics.py +++ b/icu_benchmarks/models/custom_metrics.py @@ -6,7 +6,8 @@ from sklearn.calibration import calibration_curve from scipy.spatial.distance import jensenshannon from torchmetrics.classification import BinaryFairness -from icu_benchmarks.models.similarity_func import correlation_spearman, distance_euclidean, correlation_pearson, cosine +from icu_benchmarks.models.similarity_func import correlation_spearman, cosine + """" This file contains custom metrics that can be added to YAIB. """ @@ -32,7 +33,9 @@ def accuracy(output, target, topk=(1,)): class BalancedAccuracy(EpochMetric): def __init__(self, output_transform: Callable = lambda x: x, check_compute_fn: bool = False) -> None: super(BalancedAccuracy, self).__init__( - self.balanced_accuracy_compute, output_transform=output_transform, check_compute_fn=check_compute_fn + self.balanced_accuracy_compute, + output_transform=output_transform, + check_compute_fn=check_compute_fn, ) def balanced_accuracy_compute(y_preds: torch.Tensor, y_targets: torch.Tensor) -> float: @@ -44,7 +47,9 @@ def balanced_accuracy_compute(y_preds: torch.Tensor, y_targets: torch.Tensor) -> class CalibrationCurve(EpochMetric): def __init__(self, output_transform: Callable = lambda x: x, check_compute_fn: bool = False) -> None: super(CalibrationCurve, self).__init__( - self.ece_curve_compute_fn, output_transform=output_transform, check_compute_fn=check_compute_fn + self.ece_curve_compute_fn, + output_transform=output_transform, + check_compute_fn=check_compute_fn, ) def ece_curve_compute_fn(y_preds: torch.Tensor, y_targets: torch.Tensor, n_bins=10) -> float: @@ -67,7 +72,6 @@ def __init__( ) def mae_with_invert_compute_fn(y_preds: torch.Tensor, y_targets: torch.Tensor, invert_fn=Callable) -> float: - y_true = invert_fn(y_targets.numpy().reshape(-1, 1))[:, 0] y_pred = invert_fn(y_preds.numpy().reshape(-1, 1))[:, 0] @@ -138,33 +142,25 @@ def feature_helper(self, trainer, step_prefix): class Faithfulness(EpochMetric): - def __init__( - self, - output_transform: Callable = lambda x: x, - check_compute_fn: bool = False, - *args, **kwargs - ) -> None: - super().__init__(output_transform, - check_compute_fn, *args, **kwargs - ) + def __init__(self, output_transform: Callable = lambda x: x, check_compute_fn: bool = False, *args, **kwargs) -> None: + super().__init__(output_transform, check_compute_fn, *args, **kwargs) def update( - self, - x, - attribution, - model, - similarity_func=None, - nr_runs=100, - pertrub=None, - subset_size=3, - feature=False, - time_step=False, - feature_timestep=False, - device='cuda' - + self, + x, + attribution, + model, + similarity_func=None, + nr_runs=100, + pertrub=None, + subset_size=3, + feature=False, + time_step=False, + feature_timestep=False, + device="cuda", ): """ - Calculates faithfulness scores for captum attributions + Calculates faithfulness scores for captum attributions Args: - x:Batch input @@ -200,22 +196,23 @@ def update( explanations." IJCAI (2020): 3016-3022. 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. """ + def add_noise(x, indices, time_step, feature_timestep): noise = torch.randn_like(x["encoder_cont"]) if time_step: - idx0, idx1 = np.meshgrid(indices[0], indices[1], indexing='ij') + idx0, idx1 = np.meshgrid(indices[0], indices[1], indexing="ij") with torch.no_grad(): x["encoder_cont"][idx0, idx1, :] += noise[idx0, idx1, :] elif feature: - idx0, idx1 = np.meshgrid(indices[0], indices[1], indexing='ij') + idx0, idx1 = np.meshgrid(indices[0], indices[1], indexing="ij") with torch.no_grad(): x["encoder_cont"][idx0, :, idx1] += noise[idx0, :, idx1] elif feature_timestep: - idx0, idx1, idx2 = np.meshgrid(indices[0], indices[1], indices[2], indexing='ij') + idx0, idx1, idx2 = np.meshgrid(indices[0], indices[1], indices[2], indexing="ij") with torch.no_grad(): x["encoder_cont"][idx0, idx1, idx2] += noise[idx0, idx1, idx2] @@ -223,22 +220,28 @@ def add_noise(x, indices, time_step, feature_timestep): def apply_baseline(x, indices, time_step, feature_timestep): mask = torch.ones_like(x["encoder_cont"]) if time_step: - - idx0, idx1, = np.meshgrid(indices[0], indices[1], indexing='ij') + ( + idx0, + idx1, + ) = np.meshgrid(indices[0], indices[1], indexing="ij") mask[idx0, idx1, :] -= mask[idx0, idx1, :] elif feature: - idx0, idx1, = np.meshgrid(indices[0], indices[1], indexing='ij') + ( + idx0, + idx1, + ) = np.meshgrid(indices[0], indices[1], indexing="ij") mask[idx0, :, idx1] -= mask[idx0, :, idx1] elif feature_timestep: - idx0, idx1, idx2 = np.meshgrid(indices[0], indices[1], indices[2], indexing='ij') + idx0, idx1, idx2 = np.meshgrid(indices[0], indices[1], indices[2], indexing="ij") mask[idx0, idx1, idx2] -= mask[idx0, idx1, idx2] with torch.no_grad(): x["encoder_cont"] *= mask + # Assuming 'attribution' is already a GPU tensor if not torch.is_tensor(attribution): attribution = torch.tensor(attribution).to(device) @@ -281,21 +284,17 @@ def apply_baseline(x, indices, time_step, feature_timestep): y_pred_perturb = (model(model.prep_data(x))).detach() # Keep on GPU if time_step: - if attribution.size() == torch.Size([24]): att_sums.append((attribution[timesteps_idx]).sum()) else: att_sums.append((attribution[patient_idx, :][:, timesteps_idx]).sum()) elif feature: - if len(attribution) == 53: att_sums.append((attribution[feature_idx]).sum()) else: - att_sums.append((attribution[patient_idx, :][:, feature_idx]).sum()) elif feature_timestep: - att_sums.append((attribution[patient_idx, :, :] - [:, timesteps_idx, :][:, :, feature_idx]).sum()) + att_sums.append((attribution[patient_idx, :, :][:, timesteps_idx, :][:, :, feature_idx]).sum()) pred_deltas.append((y_pred - y_pred_perturb)[patient_idx].item()) # Convert to CPU for numpy operations @@ -310,40 +309,40 @@ def apply_baseline(x, indices, time_step, feature_timestep): class Stability(EpochMetric): - def __init__( - self, output_transform: Callable = lambda x: x, - check_compute_fn: bool = False, - *args, **kwargs - ) -> None: - super().__init__(output_transform, - check_compute_fn, *args, **kwargs - ) + def __init__(self, output_transform: Callable = lambda x: x, check_compute_fn: bool = False, *args, **kwargs) -> None: + super().__init__(output_transform, check_compute_fn, *args, **kwargs) - def update(self, x, - attribution, model, explain_method, dataloader=None, thershold=0.5, device='cuda', **kwargs - ): + def update(self, x, attribution, model, explain_method, dataloader=None, thershold=0.5, device="cuda", **kwargs): """ - Args: - - x:Batch input - -attribution: attribution - - explain_method:function to generate explantations - - method_name: Name of the explantation - - dataloader:In case of using Attention as the explain method need to pass the dataloader instead of the batch , + Args: + - x:Batch input + -attribution: attribution + - explain_method:function to generate explantations + - method_name: Name of the explantation + - dataloader:In case of using Attention as the explain method need to pass the dataloader instead of the batch , - Returns: - RIS : relative distance between the explantation and the input - ROS: relative distance between the explantation and the output + Returns: + RIS : relative distance between the explantation and the input + ROS: relative distance between the explantation and the output - References: - 1) `https://arxiv.org/pdf/2203.06877.pdf - 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. + References: + 1) `https://arxiv.org/pdf/2203.06877.pdf + 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. """ def relative_stability_objective( - x, xs, e_x, e_xs, close_indices, eps_min=0.0001, input=False, attention=False, device='cuda' + x, + xs, + e_x, + e_xs, + close_indices, + eps_min=0.0001, + input=False, + attention=False, + device="cuda", ) -> torch.Tensor: """ Computes relative input and output stabilities maximization objective @@ -355,15 +354,16 @@ def relative_stability_objective( xs: perturbed tensor. e_x: Explanations for x. e_xs: Explanations for xs. - eps_min:Value to avoid division by zero if needed + eps_min:Value to avoid division by zero if needed input:Boolean to indicate if this is an input or an output - device: the device to keep the tensors on + device: the device to keep the tensors on Returns: ris_obj: Tensor RIS maximization objective. """ + # Function to convert inputs to tensors if they are numpy arrays def to_tensor(input_array): if isinstance(input_array, np.ndarray): @@ -383,11 +383,19 @@ def to_tensor(input_array): num_dim = e_x.ndim if num_dim == 3: - def norm_function(arr): return torch.norm(arr, dim=(-1, -2)) + + def norm_function(arr): + return torch.norm(arr, dim=(-1, -2)) + elif num_dim == 2: - def norm_function(arr): return torch.norm(arr, dim=-1) + + def norm_function(arr): + return torch.norm(arr, dim=-1) + else: - def norm_function(arr): return torch.norm(arr) + + def norm_function(arr): + return torch.norm(arr) nominator = (e_x - e_xs) / (e_x + (e_x == 0) * eps_min) nominator = norm_function(nominator) @@ -420,10 +428,22 @@ def norm_function(arr): return torch.norm(arr) close_indices = torch.nonzero(difference <= thershold).squeeze()[:, 0].to(device) RIS = relative_stability_objective( - x_original.detach(), x_preturb.detach(), attribution, att_preturb, close_indices=close_indices, input=True, attention=True + x_original.detach(), + x_preturb.detach(), + attribution, + att_preturb, + close_indices=close_indices, + input=True, + attention=True, ) ROS = relative_stability_objective( - y_pred, y_pred_preturb, attribution, att_preturb, close_indices=close_indices, input=False, attention=True + y_pred, + y_pred_preturb, + attribution, + att_preturb, + close_indices=close_indices, + input=False, + attention=True, ) else: @@ -431,7 +451,7 @@ def norm_function(arr): return torch.norm(arr) x_original = x["encoder_cont"].detach().clone() with torch.no_grad(): - noise = torch.randn_like(x["encoder_cont"])*0.01 + noise = torch.randn_like(x["encoder_cont"]) * 0.01 x["encoder_cont"] += noise y_pred_preturb = model(model.prep_data(x)).detach() if explain_method == "Random": @@ -450,37 +470,36 @@ def norm_function(arr): return torch.norm(arr) RIS = relative_stability_objective( x_original.detach(), x["encoder_cont"].detach(), - attribution, att_preturb, close_indices=close_indices, input=True + attribution, + att_preturb, + close_indices=close_indices, + input=True, ) ROS = relative_stability_objective( - y_pred, y_pred_preturb, attribution, att_preturb, close_indices=close_indices, input=False + y_pred, + y_pred_preturb, + attribution, + att_preturb, + close_indices=close_indices, + input=False, ) return np.max(RIS.cpu().numpy()).astype(np.float64), np.max(ROS.cpu().numpy()).astype(np.float64) class Randomization(EpochMetric): - def __init__( - self, output_transform: Callable = lambda x: x, - check_compute_fn: bool = False, - *args, **kwargs - ) -> None: - super().__init__(output_transform, - check_compute_fn, *args, **kwargs - ) + def __init__(self, output_transform: Callable = lambda x: x, check_compute_fn: bool = False, *args, **kwargs) -> None: + super().__init__(output_transform, check_compute_fn, *args, **kwargs) - def update( - self, x, - attribution, model, explain_method, random_model, similarity_func=cosine, dataloader=None, **kwargs - ): + def update(self, x, attribution, model, explain_method, random_model, similarity_func=cosine, dataloader=None, **kwargs): """ Args: - x:Batch input - -attribution: attribution + -attribution: attribution - explain_method:function to generate explantations - random_model: Reference to model trained on random labels - - similarity_func: Function to measure similiarity + - similarity_func: Function to measure similiarity - dataloader:In case of using Attention as the explain method need to pass the dataloader instead of the batch , - method_name: Name of the explantation @@ -516,7 +535,6 @@ def update( score = similarity_func(np.random.normal(size=[64, 24, 53]).flatten(), attribution.flatten()) else: data, baselines = model.prep_data_captum(x) - y_pred = model(data).detach() random_attr, features_attrs, timestep_attrs = model.explantation2(x, explain_method) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index ff66c4da..6141079f 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -11,11 +11,13 @@ PositionalEncoding, ) import matplotlib.pyplot as plt -from icu_benchmarks.models.wrappers import DLPredictionWrapper, DLPredictionPytorchForecastingWrapper -from torch import Tensor, FloatTensor, zeros_like, ones_like, randn_like, is_tensor +from icu_benchmarks.models.wrappers import ( + DLPredictionWrapper, + DLPredictionPytorchForecastingWrapper, +) +from torch import Tensor from pytorch_forecasting import TemporalFusionTransformer, RecurrentNetwork, DeepAR -from pytorch_forecasting.metrics import QuantileLoss, MAE -import matplotlib.pyplot as plt +from pytorch_forecasting.metrics import QuantileLoss @gin.configurable diff --git a/icu_benchmarks/models/metrics.py b/icu_benchmarks/models/metrics.py index 7301d8d9..738c5523 100644 --- a/icu_benchmarks/models/metrics.py +++ b/icu_benchmarks/models/metrics.py @@ -1,12 +1,10 @@ import torch from typing import Callable import numpy as np -from ignite.metrics import EpochMetric, Precision, Recall +from ignite.metrics import EpochMetric from sklearn.metrics import balanced_accuracy_score, mean_absolute_error from sklearn.calibration import calibration_curve from scipy.spatial.distance import jensenshannon -import gin -import torch.nn.functional as F """" @@ -45,13 +43,8 @@ def ece_curve_compute_fn(y_preds: torch.Tensor, y_targets: torch.Tensor) -> floa def mae_with_invert_compute_fn(y_preds: torch.Tensor, y_targets: torch.Tensor, invert_fn=Callable) -> float: - print('y_true', y_true) - print('y_pred', y_pred) y_true = invert_fn(y_targets.numpy().reshape(-1, 1))[:, 0] y_pred = invert_fn(y_preds.numpy().reshape(-1, 1))[:, 0] - print('y_true', y_true) - print('y_pred', y_pred) - print('mae', mean_absolute_error(y_true, y_pred)) return mean_absolute_error(y_true, y_pred) diff --git a/icu_benchmarks/models/similarity_func.py b/icu_benchmarks/models/similarity_func.py index 3ff96474..fd931e7e 100644 --- a/icu_benchmarks/models/similarity_func.py +++ b/icu_benchmarks/models/similarity_func.py @@ -1,9 +1,15 @@ -"""This module holds a collection of similarity functions i.e., ways to measure the distance between two inputs (or explanations).""" +"""This module holds a collection of similarity functions i.e., +ways to measure the distance between two inputs (or explanations).""" # This file is part of Quantus. -# Quantus is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. -# Quantus is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. -# You should have received a copy of the GNU Lesser General Public License along with Quantus. If not, see . +# Quantus is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free Software Foundation, +# either version 3 of the License, or (at your option) any later version. +# Quantus is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +# You should have received a copy of the GNU Lesser General Public License along with Quantus. +# If not, see . # Quantus project URL: . # Quantus project URL: https://github.com/understandable-machine-intelligence-lab/Quantus diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index 0ced60c0..e7ed5d68 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -1,14 +1,11 @@ import os import gin import torch -import pickle import logging import json import pandas as pd from joblib import load from torch.optim import Adam -import numpy as np -import quantus from torch.utils.data import DataLoader from pytorch_lightning.loggers import TensorBoardLogger, WandbLogger from pytorch_lightning import Trainer @@ -29,7 +26,6 @@ from icu_benchmarks.models.utils import save_config_file, JSONMetricsLogger from icu_benchmarks.contants import RunMode from icu_benchmarks.data.constants import DataSplit as Split -from collections import OrderedDict from captum.attr import IntegratedGradients, Saliency, FeatureAblation, Lime @@ -78,7 +74,6 @@ def train_common( XAI_metric: bool = False, random_labels: bool = False, random_model_dir: str = None, - ): """Common wrapper to train all benchmarked models. @@ -282,22 +277,39 @@ def train_common( # choose which methods to get attributions methods = { - # "G": Saliency, - # "L": Lime, - # "IG": IntegratedGradients, - # "FA": FeatureAblation, + "G": Saliency, + "L": Lime, + "IG": IntegratedGradients, + "FA": FeatureAblation, "R": "Random", - "Att": "Attention" - + "Att": "Attention", } for key, item in methods.items(): # If conditions needed here as different explantations require different inputs if key == "IG": - all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score, r_score, st_i_score, st_o_score = model.explantation( - dataloader=test_loader, method=item, log_dir=log_dir, plot=True, n_steps=50, XAI_metric=XAI_metric, random_model=random_model + ( + all_attrs, + features_attrs, + timestep_attrs, + ts_v_score, + ts_score, + v_score, + r_score, + st_i_score, + st_o_score, + ) = model.explantation( + dataloader=test_loader, + method=item, + log_dir=log_dir, + plot=True, + n_steps=50, + XAI_metric=XAI_metric, + random_model=random_model, ) elif key == "L" or key == "FA": - # for Lime and feature ablation we need to define what is a feature we define each variable per timestep as a feature + """for Lime and feature ablation we need to define + what is a feature we define each variable + per timestep as a feature""" shapes = [ torch.Size([64, 24, 0]), torch.Size([64, 24, 53]), @@ -309,7 +321,7 @@ def train_common( torch.Size([64]), torch.Size([64, 1]), torch.Size([64, 1]), - torch.Size([64, 2]) + torch.Size([64, 2]), ] # Create a default mask for non-targeted tensors @@ -328,40 +340,72 @@ def create_default_mask(shape): feature_mask_second = feature_mask_second.unsqueeze(0).repeat(shapes[1][0], 1, 1) # Create a tuple of masks - feature_masks = tuple([create_default_mask(shape) if i != - 1 else feature_mask_second for i, shape in enumerate(shapes)]) - all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score, r_score, st_i_score, st_o_score = model.explantation( - dataloader=test_loader, method=item, log_dir=log_dir, plot=True, feature_mask=feature_masks, return_input_shape=True, XAI_metric=XAI_metric, random_model=random_model + feature_masks = tuple( + [create_default_mask(shape) if i != 1 else feature_mask_second for i, shape in enumerate(shapes)] + ) + ( + all_attrs, + features_attrs, + timestep_attrs, + ts_v_score, + ts_score, + v_score, + r_score, + st_i_score, + st_o_score, + ) = model.explantation( + dataloader=test_loader, + method=item, + log_dir=log_dir, + plot=True, + feature_mask=feature_masks, + return_input_shape=True, + XAI_metric=XAI_metric, + random_model=random_model, ) else: - all_attrs, features_attrs, timestep_attrs, ts_v_score, ts_score, v_score, r_score, st_i_score, st_o_score = model.explantation( - dataloader=test_loader, method=item, log_dir=log_dir, plot=True, XAI_metric=XAI_metric, random_model=random_model + ( + all_attrs, + features_attrs, + timestep_attrs, + ts_v_score, + ts_score, + v_score, + r_score, + st_i_score, + st_o_score, + ) = model.explantation( + dataloader=test_loader, + method=item, + log_dir=log_dir, + plot=True, + XAI_metric=XAI_metric, + random_model=random_model, ) if XAI_metric: # logging metric scores print("{} Attributions Faithfulness Timesteps ".format(key), ts_score) XAI_dict["{}_Faith Timesteps".format(key)] = ts_score - print("{}_ROS ".format( - key), st_o_score) + print("{}_ROS ".format(key), st_o_score) XAI_dict["{}_ROS".format(key)] = st_o_score - print("{}_RIS ".format( - key), st_i_score) + print("{}_RIS ".format(key), st_i_score) XAI_dict["{}_RIS".format(key)] = st_i_score if key == "Att": - print("Variable selection weights faithfulness featrues ".format(key), v_score) - XAI_dict["VSN_Faith Features".format(key)] = v_score + print("{} weights faithfulness featrues ".format(key), v_score) + XAI_dict["{}_Faith Features".format(key)] = v_score else: print("{} Attributions faithfulness featrues ".format(key), v_score) XAI_dict["{}_Faith Features".format(key)] = v_score - print("{}_Attributions Faithfulness Variable Per Timestep ".format( - key), ts_v_score) + print( + "{}_Attributions Faithfulness Variable Per Timestep ".format(key), + ts_v_score, + ) XAI_dict["{}_Faith Variable Per Timestep".format(key)] = ts_v_score - print("{}_Data Randomization Distance ".format( - key), r_score) + print("{}_Data Randomization Distance ".format(key), r_score) XAI_dict["{}_Data Randomization Distance".format(key)] = r_score # Getting the interpertations using pytorch forecasting native methods diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index ae63ab48..f22ecf85 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -4,7 +4,7 @@ import torchmetrics from sklearn.metrics import log_loss, mean_squared_error import torch -from torch.nn import MSELoss, CrossEntropyLoss, L1Loss +from torch.nn import MSELoss, CrossEntropyLoss import torch.nn as nn from torch import Tensor, FloatTensor from torch.optim import Optimizer, Adam @@ -19,10 +19,9 @@ from icu_benchmarks.models.constants import MLMetrics, DLMetrics from icu_benchmarks.contants import RunMode import matplotlib.pyplot as plt -from icu_benchmarks.models.similarity_func import correlation_spearman, distance_euclidean, correlation_pearson, cosine +from icu_benchmarks.models.similarity_func import correlation_spearman, cosine import captum from captum._utils.models.linear_model import SkLearnLasso -import os gin.config.external_configurable(nn.functional.nll_loss, module="torch.nn.functional") gin.config.external_configurable(nn.functional.cross_entropy, module="torch.nn.functional") @@ -162,8 +161,7 @@ def finalize_step(self, step_prefix=""): try: for name, metric in self.metrics[step_prefix].items(): try: - value = np.float32(metric.compute()) if isinstance( - metric.compute(), np.float64) else metric.compute() + value = np.float32(metric.compute()) if isinstance(metric.compute(), np.float64) else metric.compute() self.log_dict({f"{step_prefix}/{name}": value}, sync_dist=True) except (NotComputableError, ValueError) as e: @@ -467,7 +465,6 @@ def step_fn(self, element, step_prefix=""): transformed_output = self.output_transform((prediction, target)) for key, value in self.metrics[step_prefix].items(): - if isinstance(value, torchmetrics.Metric): if key == "Binary_Fairness": feature_names = self.metrics[step_prefix][key].feature_helper(self.trainer, step_prefix) @@ -493,7 +490,7 @@ def prep_data_captum(self, x): - x:Batch from dataloader Returns: - data:batch data in a tuple after being prepared - - baselines:Basically zero tensors in the input + - baselines:Basically zero tensors in the input """ # captum requires gradient and float values @@ -553,10 +550,70 @@ def plot_attributions(self, features_attrs, timestep_attrs, method_name, log_dir plt.xlabel("Feature") plt.ylabel("{} Attribution".format(method_name)) plt.title("{} Attribution Values".format(method_name)) - plt.xticks(x_values, ['height', 'weight', 'age', 'sex', 'time_idx', 'alb', 'alp', 'alt', 'ast', 'be', 'bicar', 'bili', 'bili_dir', 'bnd', 'bun', 'ca', 'cai', 'ck', 'ckmb', 'cl', 'crea', 'crp', 'dbp', 'fgn', 'fio2', 'glu', - 'hgb', 'hr', 'inr_pt', 'k', 'lact', 'lymph', 'map', 'mch', 'mchc', 'mcv', 'methb', 'mg', 'na', 'neut', 'o2sat', 'pco2', 'ph', 'phos', 'plt', 'po2', 'ptt', 'resp', 'sbp', 'temp', 'tnt', 'urine', 'wbc'], rotation=90) + plt.xticks( + x_values, + [ + "height", + "weight", + "age", + "sex", + "time_idx", + "alb", + "alp", + "alt", + "ast", + "be", + "bicar", + "bili", + "bili_dir", + "bnd", + "bun", + "ca", + "cai", + "ck", + "ckmb", + "cl", + "crea", + "crp", + "dbp", + "fgn", + "fio2", + "glu", + "hgb", + "hr", + "inr_pt", + "k", + "lact", + "lymph", + "map", + "mch", + "mchc", + "mcv", + "methb", + "mg", + "na", + "neut", + "o2sat", + "pco2", + "ph", + "phos", + "plt", + "po2", + "ptt", + "resp", + "sbp", + "temp", + "tnt", + "urine", + "wbc", + ], + rotation=90, + ) plt.tight_layout() - plt.savefig(log_dir / "{}_attribution_features_plot.png".format(method_name), bbox_inches="tight") + plt.savefig( + log_dir / "{}_attribution_features_plot.png".format(method_name), + bbox_inches="tight", + ) # Plot for timestep attributions x_values = np.arange(1, len(timestep_attrs) + 1) @@ -577,12 +634,22 @@ def plot_attributions(self, features_attrs, timestep_attrs, method_name, log_dir plt.tight_layout() plt.savefig(log_dir / "{}_attribution_plot.png".format(method_name), bbox_inches="tight") - def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=False, random_model=None, test_dataset=None, **kwargs): + def explantation( + self, + dataloader, + method, + log_dir=".", + plot=False, + XAI_metric=False, + random_model=None, + test_dataset=None, + **kwargs, + ): """ Generic method to combine pytorchforecasting data loading , interpertations and captum to generate attributions Args: - - dataloader: pytorchforecasting data loader + - dataloader: pytorchforecasting data loader - method: The explantation method chosen - log_dir= The directory to output the plots - plot= Determines if plots should be done or not @@ -603,19 +670,29 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F st_i_score = [] st_o_score = [] - method_name = method if (method == "Random") or (method == "Attention") else ( - method.__name__) + method_name = method if (method == "Random") or (method == "Attention") else (method.__name__) if (method_name == "Random") or (method_name == "Attention"): if method_name == "Attention": Interpertations = self.interpertations(dataloader=dataloader, log_dir=log_dir, plot=plot) timestep_attrs = Interpertations["attention"] features_attrs = Interpertations["static_variables"].tolist() features_attrs.extend(Interpertations["encoder_variables"].tolist()) - r_score = self.Data_Randomization(x=None, attribution=timestep_attrs, - explain_method=method, random_model=random_model, dataloader=dataloader, method_name=method_name) - st_i_score, st_o_score = self.Relative_Stability(x=None, - attribution=timestep_attrs, explain_method=method, method_name=method_name, dataloader=dataloader, **kwargs - ) + r_score = self.Data_Randomization( + x=None, + attribution=timestep_attrs, + explain_method=method, + random_model=random_model, + dataloader=dataloader, + method_name=method_name, + ) + st_i_score, st_o_score = self.Relative_Stability( + x=None, + attribution=timestep_attrs, + explain_method=method, + method_name=method_name, + dataloader=dataloader, + **kwargs, + ) elif method_name == "Random": # Generate random attributions for baseline comparison all_attrs = np.random.normal(size=[64, 24, 53]) @@ -623,32 +700,82 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F timestep_attrs = all_attrs.mean(axis=(2)) if XAI_metric: for batch in dataloader: - for key, value in batch[0].items(): batch[0][key] = batch[0][key].to(self.device) x = batch[0] if method_name == "Random": - - f_ts_v_score.append(self.Faithfulness_Correlation(x, all_attrs, - pertrub="baseline", feature_timestep=True, subset_size=[4, 9], nr_runs=100)) - f_ts_score.append(self.Faithfulness_Correlation(x, all_attrs, - pertrub="baseline", time_step=True, subset_size=4, nr_runs=100)) - f_v_score.append(self.Faithfulness_Correlation(x, all_attrs, - pertrub="baseline", feature=True, subset_size=9, nr_runs=100)) - - r_score.append(self.Data_Randomization(x, attribution=all_attrs, - explain_method=method, random_model=random_model, method_name=method_name)) - res1, res2 = self.Relative_Stability(x, - all_attrs, explain_method=method, method_name=method_name, dataloader=None, **kwargs - ) + f_ts_v_score.append( + self.Faithfulness_Correlation( + x, + all_attrs, + pertrub="baseline", + feature_timestep=True, + subset_size=[4, 9], + nr_runs=100, + ) + ) + f_ts_score.append( + self.Faithfulness_Correlation( + x, + all_attrs, + pertrub="baseline", + time_step=True, + subset_size=4, + nr_runs=100, + ) + ) + f_v_score.append( + self.Faithfulness_Correlation( + x, + all_attrs, + pertrub="baseline", + feature=True, + subset_size=9, + nr_runs=100, + ) + ) + + r_score.append( + self.Data_Randomization( + x, + attribution=all_attrs, + explain_method=method, + random_model=random_model, + method_name=method_name, + ) + ) + res1, res2 = self.Relative_Stability( + x, + all_attrs, + explain_method=method, + method_name=method_name, + dataloader=None, + **kwargs, + ) st_i_score.append(res1) st_o_score.append(res2) else: - f_ts_score.append(self.Faithfulness_Correlation(x, timestep_attrs, - pertrub="baseline", time_step=True, subset_size=4, nr_runs=100)) - f_v_score.append(self.Faithfulness_Correlation(x, features_attrs, - pertrub="baseline", feature=True, subset_size=9, nr_runs=100)) + f_ts_score.append( + self.Faithfulness_Correlation( + x, + timestep_attrs, + pertrub="baseline", + time_step=True, + subset_size=4, + nr_runs=100, + ) + ) + f_v_score.append( + self.Faithfulness_Correlation( + x, + features_attrs, + pertrub="baseline", + feature=True, + subset_size=9, + nr_runs=100, + ) + ) # Faithfulness score for attribtuons of features per timesteps f_ts_v_score = np.mean(f_ts_v_score) @@ -661,11 +788,20 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F r_score = np.mean(r_score) st_i_score = np.max(st_i_score) st_o_score = np.max(st_o_score) - return all_attrs, features_attrs, timestep_attrs, f_ts_v_score, f_ts_score, f_v_score, r_score, st_i_score, st_o_score + return ( + all_attrs, + features_attrs, + timestep_attrs, + f_ts_v_score, + f_ts_score, + f_v_score, + r_score, + st_i_score, + st_o_score, + ) # Loop through the dataloader to compute attributions for all instances for batch in dataloader: - for key, value in batch[0].items(): batch[0][key] = batch[0][key].to(self.device) x = batch[0] @@ -673,8 +809,11 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F data, baselines = self.prep_data_captum(x) # Initialize the explanation method - explanation = method(self.forward_captum, interpretable_model=SkLearnLasso( - alpha=0.4)) if method_name == 'Lime' else method(self.forward_captum) + explanation = ( + method(self.forward_captum, interpretable_model=SkLearnLasso(alpha=0.4)) + if method_name == "Lime" + else method(self.forward_captum) + ) # Calculate attributions using the selected method if method is not captum.attr.Saliency: @@ -683,23 +822,61 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F attr = explanation.attribute(data, **kwargs) # Process and store the calculated attributions - stacked_attr = attr[1].cpu().detach().numpy() if method_name in [ - 'Lime', 'FeatureAblation'] else torch.stack(attr).cpu().detach().numpy() + stacked_attr = ( + attr[1].cpu().detach().numpy() + if method_name in ["Lime", "FeatureAblation"] + else torch.stack(attr).cpu().detach().numpy() + ) if XAI_metric: + f_ts_v_score.append( + self.Faithfulness_Correlation( + x, + stacked_attr, + pertrub="baseline", + feature_timestep=True, + subset_size=[4, 9], + nr_runs=100, + ) + ) - f_ts_v_score.append(self.Faithfulness_Correlation(x, stacked_attr, - pertrub="baseline", feature_timestep=True, subset_size=[4, 9], nr_runs=100)) - - f_ts_score.append(self.Faithfulness_Correlation(x, stacked_attr, - pertrub="baseline", time_step=True, subset_size=4, nr_runs=100)) - f_v_score.append(self.Faithfulness_Correlation(x, stacked_attr, - pertrub="baseline", feature=True, subset_size=9, nr_runs=100)) - r_score.append(self.Data_Randomization(x, attribution=stacked_attr, - explain_method=method, random_model=random_model, method_name=method_name)) + f_ts_score.append( + self.Faithfulness_Correlation( + x, + stacked_attr, + pertrub="baseline", + time_step=True, + subset_size=4, + nr_runs=100, + ) + ) + f_v_score.append( + self.Faithfulness_Correlation( + x, + stacked_attr, + pertrub="baseline", + feature=True, + subset_size=9, + nr_runs=100, + ) + ) + r_score.append( + self.Data_Randomization( + x, + attribution=stacked_attr, + explain_method=method, + random_model=random_model, + method_name=method_name, + ) + ) - res1, res2 = self.Relative_Stability(x, - stacked_attr, explain_method=method, method_name=method_name, dataloader=None, **kwargs - ) + res1, res2 = self.Relative_Stability( + x, + stacked_attr, + explain_method=method, + method_name=method_name, + dataloader=None, + **kwargs, + ) st_i_score.append(res1) st_o_score.append(res2) @@ -725,7 +902,7 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F st_o_score = np.max(st_o_score) if plot: - log_dir_plots = log_dir / 'plots' + log_dir_plots = log_dir / "plots" if not (log_dir_plots.exists()): log_dir_plots.mkdir(parents=True) # Plot attributions for features and timesteps @@ -733,12 +910,22 @@ def explantation(self, dataloader, method, log_dir=".", plot=False, XAI_metric=F self.plot_attributions(features_attrs, timestep_attrs, method_name, log_dir_plots) # Return computed attributions and metrics - return all_attrs, features_attrs, timestep_attrs, f_ts_v_score, f_ts_score, f_v_score, r_score, st_i_score, st_o_score + return ( + all_attrs, + features_attrs, + timestep_attrs, + f_ts_v_score, + f_ts_score, + f_v_score, + r_score, + st_i_score, + st_o_score, + ) # normalized_means = (means - means.min()) / (means.max() - means.min()) def prep_data(self, x): """ - Prepares data for custom forward method + Prepares data for custom forward method Args: - x:Batch returned from dataloader @@ -771,10 +958,9 @@ def Faithfulness_Correlation( feature=False, time_step=False, feature_timestep=False, - ): """ - Calculates faithfulness scores for captum attributions + Calculates faithfulness scores for captum attributions Args: - x:Batch input @@ -808,24 +994,27 @@ def Faithfulness_Correlation( References: 1) Umang Bhatt et al.: "Evaluating and aggregating feature-based model explanations." IJCAI (2020): 3016-3022. - 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. + 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for + responsible evaluation of neural network explanations and beyond." + Journal of Machine Learning Research 24.34 (2023): 1-11. """ + def add_noise(x, indices, time_step, feature_timestep): noise = torch.randn_like(x["encoder_cont"]) if time_step: - idx0, idx1 = np.meshgrid(indices[0], indices[1], indexing='ij') + idx0, idx1 = np.meshgrid(indices[0], indices[1], indexing="ij") with torch.no_grad(): x["encoder_cont"][idx0, idx1, :] += noise[idx0, idx1, :] elif feature: - idx0, idx1 = np.meshgrid(indices[0], indices[1], indexing='ij') + idx0, idx1 = np.meshgrid(indices[0], indices[1], indexing="ij") with torch.no_grad(): x["encoder_cont"][idx0, :, idx1] += noise[idx0, :, idx1] elif feature_timestep: - idx0, idx1, idx2 = np.meshgrid(indices[0], indices[1], indices[2], indexing='ij') + idx0, idx1, idx2 = np.meshgrid(indices[0], indices[1], indices[2], indexing="ij") with torch.no_grad(): x["encoder_cont"][idx0, idx1, idx2] += noise[idx0, idx1, idx2] @@ -833,22 +1022,28 @@ def add_noise(x, indices, time_step, feature_timestep): def apply_baseline(x, indices, time_step, feature_timestep): mask = torch.ones_like(x["encoder_cont"]) if time_step: - - idx0, idx1, = np.meshgrid(indices[0], indices[1], indexing='ij') + ( + idx0, + idx1, + ) = np.meshgrid(indices[0], indices[1], indexing="ij") mask[idx0, idx1, :] -= mask[idx0, idx1, :] elif feature: - idx0, idx1, = np.meshgrid(indices[0], indices[1], indexing='ij') + ( + idx0, + idx1, + ) = np.meshgrid(indices[0], indices[1], indexing="ij") mask[idx0, :, idx1] -= mask[idx0, :, idx1] elif feature_timestep: - idx0, idx1, idx2 = np.meshgrid(indices[0], indices[1], indices[2], indexing='ij') + idx0, idx1, idx2 = np.meshgrid(indices[0], indices[1], indices[2], indexing="ij") mask[idx0, idx1, idx2] -= mask[idx0, idx1, idx2] with torch.no_grad(): x["encoder_cont"] *= mask + # Assuming 'attribution' is already a GPU tensor if not torch.is_tensor(attribution): attribution = torch.tensor(attribution).to(self.device) @@ -892,22 +1087,17 @@ def apply_baseline(x, indices, time_step, feature_timestep): y_pred_perturb = (self(self.prep_data(x))).detach() # Keep on GPU if time_step: - if attribution.size() == torch.Size([24]): att_sums.append((attribution[timesteps_idx]).sum()) else: att_sums.append((attribution[patient_idx, :, :][:, timesteps_idx, :]).sum()) elif feature: - if len(attribution) == 53: att_sums.append((attribution[feature_idx]).sum()) else: - att_sums.append((attribution[patient_idx, :, :][:, :, feature_idx]).sum()) elif feature_timestep: - - att_sums.append((attribution[patient_idx, :, :] - [:, timesteps_idx, :][:, :, feature_idx]).sum()) + att_sums.append((attribution[patient_idx, :, :][:, timesteps_idx, :][:, :, feature_idx]).sum()) pred_deltas.append((y_pred - y_pred_perturb)[patient_idx].item()) # Convert to CPU for numpy operations @@ -921,17 +1111,24 @@ def apply_baseline(x, indices, time_step, feature_timestep): return score def Data_Randomization( - self, x, - attribution, explain_method, random_model, similarity_func=cosine, dataloader=None, method_name="", **kwargs + self, + x, + attribution, + explain_method, + random_model, + similarity_func=cosine, + dataloader=None, + method_name="", + **kwargs, ): """ Args: - x:Batch input - -attribution: attribution + -attribution: attribution - explain_method:function to generate explantations - random_model: Reference to model trained on random labels - - similarity_func: Function to measure similiarity + - similarity_func: Function to measure similiarity - dataloader:In case of using Attention as the explain method need to pass the dataloader instead of the batch , - method_name: Name of the explantation @@ -947,7 +1144,9 @@ def Data_Randomization( References: 1) Leon Sixt et al.: "When Explanations Lie: Why Many Modified BP Attributions Fail." ICML (2020): 9046-9057. - 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. + 2)Hedström, Anna, et al. "Quantus: An explainable ai + toolkit for responsible evaluation of neural network explanations and beyond." + Journal of Machine Learning Research 24.34 (2023): 1-11. """ @@ -967,7 +1166,6 @@ def Data_Randomization( score = similarity_func(np.random.normal(size=[64, 24, 53]).flatten(), attribution.flatten()) else: data, baselines = self.prep_data_captum(x) - y_pred = self(data).detach() explantation = explain_method(random_model.forward_captum) # Reformat attributions. @@ -977,8 +1175,11 @@ def Data_Randomization( attr = explantation.attribute(data, **kwargs) # Process and store the calculated attributions - random_attr = attr[1].cpu().detach().numpy() if method_name in [ - 'Lime', 'FeatureAblation'] else torch.stack(attr).cpu().detach().numpy() + random_attr = ( + attr[1].cpu().detach().numpy() + if method_name in ["Lime", "FeatureAblation"] + else torch.stack(attr).cpu().detach().numpy() + ) attribution = attribution.flatten() min_val = np.min(attribution) @@ -992,32 +1193,37 @@ def Data_Randomization( score = similarity_func(random_attr, attribution) return score - def Relative_Stability(self, x, - attribution, explain_method, method_name, dataloader=None, thershold=0.5, **kwargs - ): + def Relative_Stability( + self, + x, + attribution, + explain_method, + method_name, + dataloader=None, + thershold=0.5, + **kwargs, + ): """ - Args: - - x:Batch input - -attribution: attribution - - explain_method:function to generate explantations - - method_name: Name of the explantation - - dataloader:In case of using Attention as the explain method need to pass the dataloader instead of the batch , + Args: + - x:Batch input + -attribution: attribution + - explain_method:function to generate explantations + - method_name: Name of the explantation + - dataloader:In case of using Attention as the explain method need to pass the dataloader instead of the batch , - Returns: - RIS : relative distance between the explantation and the input - ROS: relative distance between the explantation and the output + Returns: + RIS : relative distance between the explantation and the input + ROS: relative distance between the explantation and the output - References: - 1) `https://arxiv.org/pdf/2203.06877.pdf - 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. + References: + 1) `https://arxiv.org/pdf/2203.06877.pdf + 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. """ - def relative_stability_objective( - x, xs, e_x, e_xs, eps_min=0.0001, input=False, device='cuda' - ) -> torch.Tensor: + def relative_stability_objective(x, xs, e_x, e_xs, eps_min=0.0001, input=False, device="cuda") -> torch.Tensor: """ Computes relative input and output stabilities maximization objective as defined here :ref:`https://arxiv.org/pdf/2203.06877.pdf` by the authors. @@ -1028,15 +1234,16 @@ def relative_stability_objective( xs: perturbed tensor. e_x: Explanations for x. e_xs: Explanations for xs. - eps_min:Value to avoid division by zero if needed + eps_min:Value to avoid division by zero if needed input:Boolean to indicate if this is an input or an output - device: the device to keep the tensors on + device: the device to keep the tensors on Returns: ris_obj: Tensor RIS maximization objective. """ + # Function to convert inputs to tensors if they are numpy arrays def to_tensor(input_array): if isinstance(input_array, np.ndarray): @@ -1052,11 +1259,19 @@ def to_tensor(input_array): num_dim = e_x.ndim if num_dim == 3: - def norm_function(arr): return torch.norm(arr, dim=(-1, -2)) + + def norm_function(arr): + return torch.norm(arr, dim=(-1, -2)) + elif num_dim == 2: - def norm_function(arr): return torch.norm(arr, dim=-1) + + def norm_function(arr): + return torch.norm(arr, dim=-1) + else: - def norm_function(arr): return torch.norm(arr) + + def norm_function(arr): + return torch.norm(arr) nominator = (e_x - e_xs) / (e_x + (e_x == 0) * eps_min) nominator = norm_function(nominator) @@ -1072,6 +1287,7 @@ def norm_function(arr): return torch.norm(arr) denominator += (denominator == 0) * eps_min return nominator / denominator + if not torch.is_tensor(attribution): attribution = torch.tensor(attribution).to(self.device) if explain_method == "Attention": @@ -1089,11 +1305,19 @@ def norm_function(arr): return torch.norm(arr) # Find where the difference is less than or equal to a thershold close_indices = torch.nonzero(difference <= thershold).squeeze() RIS = relative_stability_objective( - x_original[close_indices, :, :].detach(), x_preturb[close_indices, :, :].detach(), attribution, att_preturb, input=True + x_original[close_indices, :, :].detach(), + x_preturb[close_indices, :, :].detach(), + attribution, + att_preturb, + input=True, ) ROS = relative_stability_objective( - y_pred[close_indices], y_pred_preturb[close_indices], attribution, att_preturb, input=False + y_pred[close_indices], + y_pred_preturb[close_indices], + attribution, + att_preturb, + input=False, ) else: @@ -1101,15 +1325,13 @@ def norm_function(arr): return torch.norm(arr) x_original = x["encoder_cont"].detach().clone() with torch.no_grad(): - noise = torch.randn_like(x["encoder_cont"])*0.01 + noise = torch.randn_like(x["encoder_cont"]) * 0.01 x["encoder_cont"] += noise y_pred_preturb = self(self.prep_data(x)).detach() if explain_method == "Random": - att_preturb = np.random.normal(size=[64, 24, 53]) att_preturb = torch.tensor(att_preturb).to(self.device) else: - data, baselines = self.prep_data_captum(x) explantation = explain_method(self.forward_captum) @@ -1120,19 +1342,29 @@ def norm_function(arr): return torch.norm(arr) att_preturb = explantation.attribute(data, **kwargs) # Process and store the calculated attributions - att_preturb = att_preturb[1].detach() if method_name in [ - 'Lime', 'FeatureAblation'] else torch.stack(att_preturb).detach() + att_preturb = ( + att_preturb[1].detach() + if method_name in ["Lime", "FeatureAblation"] + else torch.stack(att_preturb).detach() + ) # Calculate the absolute difference difference = torch.abs(y_pred_preturb - y_pred) # Find where the difference is less than or equal to a thershold close_indices = torch.nonzero(difference <= thershold).squeeze() RIS = relative_stability_objective( - x_original[close_indices, :, :].detach(), x["encoder_cont"][close_indices, :, :].detach(), attribution[close_indices, :, :], att_preturb[close_indices, :, :], input=True + x_original[close_indices, :, :].detach(), + x["encoder_cont"][close_indices, :, :].detach(), + attribution[close_indices, :, :], + att_preturb[close_indices, :, :], + input=True, ) ROS = relative_stability_objective( - y_pred[close_indices], y_pred_preturb[close_indices], attribution[close_indices, - :, :], att_preturb[close_indices, :, :], input=False + y_pred[close_indices], + y_pred_preturb[close_indices], + attribution[close_indices, :, :], + att_preturb[close_indices, :, :], + input=False, ) return np.max(RIS.cpu().numpy()).astype(np.float64), np.max(ROS.cpu().numpy()).astype(np.float64) diff --git a/icu_benchmarks/run.py b/icu_benchmarks/run.py index bd65c935..5c2d3bac 100644 --- a/icu_benchmarks/run.py +++ b/icu_benchmarks/run.py @@ -118,8 +118,7 @@ def main(my_args=tuple(sys.argv[1:])): name_datasets(args.name, args.name, args.name) run_dir = create_run_dir(log_dir) source_dir = args.source_dir - logging.info( - f"Will load weights from {source_dir} and bind train gin-config. Note: this might override your config.") + logging.info(f"Will load weights from {source_dir} and bind train gin-config. Note: this might override your config.") gin.parse_config_file(source_dir / "train_config.gin") elif args.samples and args.source_dir is not None: # Train model with limited samples and bind existing config logging.info("Binding train gin-config. Note: this might override your config.") @@ -132,8 +131,7 @@ def main(my_args=tuple(sys.argv[1:])): name_datasets(args.name, args.name, args.name) hp_checkpoint = log_dir / args.hp_checkpoint if args.hp_checkpoint else None model_path = ( - Path("configs") / ("imputation_models" if mode == - RunMode.imputation else "prediction_models") / f"{model}.gin" + Path("configs") / ("imputation_models" if mode == RunMode.imputation else "prediction_models") / f"{model}.gin" ) gin_config_files = ( [Path(f"configs/experiments/{args.experiment}.gin")] diff --git a/icu_benchmarks/run_utils.py b/icu_benchmarks/run_utils.py index 969b4f4f..7d57bc2a 100644 --- a/icu_benchmarks/run_utils.py +++ b/icu_benchmarks/run_utils.py @@ -241,8 +241,9 @@ def aggregate_results(log_dir: Path, execution_time: timedelta = None): # Calculate the population standard deviation over aggregated results over folds/iterations # Divide by sqrt(n) to get standard deviation. - std_scores = {metric: (pstdev(list) / sqrt(len(list))) - for metric, list in list_scores.items() if not (np.isnan(list).all())} + std_scores = { + metric: (pstdev(list) / sqrt(len(list))) for metric, list in list_scores.items() if not (np.isnan(list).all()) + } confidence_interval = { metric: (stats.t.interval(0.95, len(list) - 1, loc=mean(list), scale=stats.sem(list))) @@ -255,7 +256,7 @@ def aggregate_results(log_dir: Path, execution_time: timedelta = None): "CI_0.95": confidence_interval, "execution_time": execution_time.total_seconds() if execution_time is not None else 0.0, } - log_dir_plots = log_dir / 'plots' + log_dir_plots = log_dir / "plots" if not (log_dir_plots.exists()): log_dir_plots.mkdir(parents=True) # plot_XAI_Metrics(accumulated_metrics, log_dir_plots=log_dir_plots) @@ -273,41 +274,48 @@ def aggregate_results(log_dir: Path, execution_time: timedelta = None): def plot_XAI_Metrics(accumulated_metrics, log_dir_plots): groups = {} - for key in accumulated_metrics['avg']: - if key in ['loss', 'MAE']: + for key in accumulated_metrics["avg"]: + if key in ["loss", "MAE"]: continue - suffix = key.split('_')[-1] + suffix = key.split("_")[-1] if suffix not in groups: groups[suffix] = [] groups[suffix].append(key) # Define a dictionary for legend labels legend_labels = { - 'IG': 'Integrated Gradient', - 'G': 'Gradient', - 'R': 'Random', - 'FA': 'Feature Ablation', - 'Att': 'Attention', - 'VSN': 'Variable Selection Network', - 'L': 'Lime' + "IG": "Integrated Gradient", + "G": "Gradient", + "R": "Random", + "FA": "Feature Ablation", + "Att": "Attention", + "VSN": "Variable Selection Network", + "L": "Lime", } - colors = ['navy', 'skyblue', 'crimson', 'salmon', 'teal', 'orange', 'darkgreen', 'lightgreen'] + colors = [ + "navy", + "skyblue", + "crimson", + "salmon", + "teal", + "orange", + "darkgreen", + "lightgreen", + ] # Plotting num_groups = len(groups) fig, axs = plt.subplots(num_groups, 1, figsize=(10, num_groups * 5)) # Custom handles for the legend - handles = [plt.Rectangle((0, 0), 1, 1, color='none', label=f'{key}: {value}') - for key, value in legend_labels.items()] + # handles = [plt.Rectangle((0, 0), 1, 1, color="none", label=f"{key}: {value}") for key, value in legend_labels.items()] for i, (suffix, keys) in enumerate(groups.items()): - ax = axs[i] if num_groups > 1 else axs # Extract values and errors - avg_values = [accumulated_metrics['avg'][key] for key in keys] - ci_lower = [accumulated_metrics['CI_0.95'][key][0] for key in keys] - ci_upper = [accumulated_metrics['CI_0.95'][key][1] for key in keys] + avg_values = [accumulated_metrics["avg"][key] for key in keys] + ci_lower = [accumulated_metrics["CI_0.95"][key][0] for key in keys] + ci_upper = [accumulated_metrics["CI_0.95"][key][1] for key in keys] ci_error = [np.abs([a - b, c - a]) for a, b, c in zip(avg_values, ci_lower, ci_upper)] # Sort by absolute values of avg_values @@ -317,21 +325,27 @@ def plot_XAI_Metrics(accumulated_metrics, log_dir_plots): sorted_ci_error = np.array(ci_error)[sorted_indices] # Plot bars - bars = ax.bar(sorted_keys, np.abs(sorted_avg_values), yerr=np.array(sorted_ci_error).T, capsize=5, color=colors) + bars = ax.bar( + sorted_keys, + np.abs(sorted_avg_values), + yerr=np.array(sorted_ci_error).T, + capsize=5, + color=colors, + ) # Set titles and labels - title_suffix = sorted_keys[0].split('_')[1] + title_suffix = sorted_keys[0].split("_")[1] ax.set_title(f'Metric: "{title_suffix}"') - ax.set_ylabel('Values') - ax.axhline(0, color='grey', linewidth=0.8) - ax.grid(axis='y') + ax.set_ylabel("Values") + ax.axhline(0, color="grey", linewidth=0.8) + ax.grid(axis="y") # Set x-ticks ax.set_xticks(sorted_keys) - ax.set_xticklabels([key.split('_')[0] for key in sorted_keys], rotation=45, ha="right") + ax.set_xticklabels([key.split("_")[0] for key in sorted_keys], rotation=45, ha="right") # Create a custom legend for each subplot - custom_labels = [legend_labels[key.split('_')[0]] for key in sorted_keys] - ax.legend(bars, custom_labels, loc='upper right') + custom_labels = [legend_labels[key.split("_")[0]] for key in sorted_keys] + ax.legend(bars, custom_labels, loc="upper right") plt.tight_layout() plt.savefig(log_dir_plots / "metrics_plot.png", bbox_inches="tight") @@ -379,8 +393,7 @@ def load_pretrained_imputation_model(use_pretrained_imputation): pretrained_imputation_model_checkpoint = torch.load(use_pretrained_imputation, map_location=torch.device("cpu")) if isinstance(pretrained_imputation_model_checkpoint, dict): imputation_model_class = pretrained_imputation_model_checkpoint["class"] - pretrained_imputation_model = imputation_model_class( - **pretrained_imputation_model_checkpoint["hyper_parameters"]) + pretrained_imputation_model = imputation_model_class(**pretrained_imputation_model_checkpoint["hyper_parameters"]) pretrained_imputation_model.set_trained_columns(pretrained_imputation_model_checkpoint["trained_columns"]) pretrained_imputation_model.load_state_dict(pretrained_imputation_model_checkpoint["state_dict"]) else: From 041b38b7200bc9ea9791e0885f1d26041f29ec47 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Thu, 7 Mar 2024 14:56:41 +0100 Subject: [PATCH 130/142] formatting --- icu_benchmarks/imputation/diffwave.py | 2 +- icu_benchmarks/imputation/layers/s4layer.py | 12 ++++++------ icu_benchmarks/imputation/sssds4.py | 2 +- icu_benchmarks/imputation/sssdsa.py | 2 +- icu_benchmarks/models/custom_metrics.py | 14 ++++++++++---- icu_benchmarks/models/dl_models.py | 2 +- icu_benchmarks/models/layers.py | 6 +++--- icu_benchmarks/models/similarity_func.py | 2 +- icu_benchmarks/models/wrappers.py | 3 ++- 9 files changed, 26 insertions(+), 19 deletions(-) diff --git a/icu_benchmarks/imputation/diffwave.py b/icu_benchmarks/imputation/diffwave.py index c2f8ecf1..437303ed 100644 --- a/icu_benchmarks/imputation/diffwave.py +++ b/icu_benchmarks/imputation/diffwave.py @@ -330,7 +330,7 @@ def forward(self, input_data): cond = self.cond_conv(cond) h += cond - out = torch.tanh(h[:, : self.res_channels, :]) * torch.sigmoid(h[:, self.res_channels :, :]) + out = torch.tanh(h[:, : self.res_channels, :]) * torch.sigmoid(h[:, self.res_channels:, :]) res = self.res_conv(out) assert x.shape == res.shape diff --git a/icu_benchmarks/imputation/layers/s4layer.py b/icu_benchmarks/imputation/layers/s4layer.py index 1441a834..fd8a53a6 100644 --- a/icu_benchmarks/imputation/layers/s4layer.py +++ b/icu_benchmarks/imputation/layers/s4layer.py @@ -685,9 +685,9 @@ def forward(self, state=None, rate=1.0, L=None): k_f = r[:-1, :-1, :, :] - r[:-1, -1:, :, :] * r[-1:, :-1, :, :] / (1 + r[-1:, -1:, :, :]) elif self.rank == 2: r00 = r[: -self.rank, : -self.rank, :, :] - r01 = r[: -self.rank, -self.rank :, :, :] - r10 = r[-self.rank :, : -self.rank, :, :] - r11 = r[-self.rank :, -self.rank :, :, :] + r01 = r[: -self.rank, -self.rank:, :, :] + r10 = r[-self.rank:, : -self.rank, :, :] + r11 = r[-self.rank:, -self.rank:, :, :] det = (1 + r11[:1, :1, :, :]) * (1 + r11[1:, 1:, :, :]) - r11[:1, 1:, :, :] * r11[1:, :1, :, :] s = ( r01[:, :1, :, :] * (1 + r11[1:, 1:, :, :]) * r10[:1, :, :, :] @@ -699,9 +699,9 @@ def forward(self, state=None, rate=1.0, L=None): k_f = r00 - s else: r00 = r[: -self.rank, : -self.rank, :, :] - r01 = r[: -self.rank, -self.rank :, :, :] - r10 = r[-self.rank :, : -self.rank, :, :] - r11 = r[-self.rank :, -self.rank :, :, :] + r01 = r[: -self.rank, -self.rank:, :, :] + r10 = r[-self.rank:, : -self.rank, :, :] + r11 = r[-self.rank:, -self.rank:, :, :] r11 = rearrange(r11, "a b h n -> h n a b") r11 = torch.linalg.inv(torch.eye(self.rank, device=r.device) + r11) r11 = rearrange(r11, "h n a b -> a b h n") diff --git a/icu_benchmarks/imputation/sssds4.py b/icu_benchmarks/imputation/sssds4.py index 25933b68..205affa4 100644 --- a/icu_benchmarks/imputation/sssds4.py +++ b/icu_benchmarks/imputation/sssds4.py @@ -298,7 +298,7 @@ def forward(self, input_data): h = self.S42(h.permute(2, 0, 1)).permute(1, 2, 0) - out = torch.tanh(h[:, : self.res_channels, :]) * torch.sigmoid(h[:, self.res_channels :, :]) + out = torch.tanh(h[:, : self.res_channels, :]) * torch.sigmoid(h[:, self.res_channels:, :]) res = self.res_conv(out) assert x.shape == res.shape diff --git a/icu_benchmarks/imputation/sssdsa.py b/icu_benchmarks/imputation/sssdsa.py index 55764104..fb384efe 100644 --- a/icu_benchmarks/imputation/sssdsa.py +++ b/icu_benchmarks/imputation/sssdsa.py @@ -389,7 +389,7 @@ def step(self, x, state, **kwargs): if self.unet: for i in range(skipped): next_state.append(state.pop()) - u_layers = list(self.u_layers)[skipped // 3 :] + u_layers = list(self.u_layers)[skipped // 3:] else: for i in range(skipped): for _ in range(len(self.u_layers[i])): diff --git a/icu_benchmarks/models/custom_metrics.py b/icu_benchmarks/models/custom_metrics.py index 84b074cc..536a7e5c 100644 --- a/icu_benchmarks/models/custom_metrics.py +++ b/icu_benchmarks/models/custom_metrics.py @@ -194,7 +194,9 @@ def update( References: 1) Umang Bhatt et al.: "Evaluating and aggregating feature-based model explanations." IJCAI (2020): 3016-3022. - 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. + 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for + responsible evaluation of neural network explanations and beyond." + Journal of Machine Learning Research 24.34 (2023): 1-11. """ def add_noise(x, indices, time_step, feature_timestep): @@ -319,7 +321,7 @@ def update(self, x, attribution, model, explain_method, dataloader=None, thersho -attribution: attribution - explain_method:function to generate explantations - method_name: Name of the explantation - - dataloader:In case of using Attention as the explain method need to pass the dataloader instead of the batch , + - dataloader:In case of using Attention as the explain method need to pass the dataloader instead of the batch. Returns: @@ -329,7 +331,9 @@ def update(self, x, attribution, model, explain_method, dataloader=None, thersho References: 1) `https://arxiv.org/pdf/2203.06877.pdf - 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. + 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for + responsible evaluation of neural network explanations and beyond." + Journal of Machine Learning Research 24.34 (2023): 1-11. """ @@ -515,7 +519,9 @@ def update(self, x, attribution, model, explain_method, random_model, similarity References: 1) Leon Sixt et al.: "When Explanations Lie: Why Many Modified BP Attributions Fail." ICML (2020): 9046-9057. - 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. + 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for + responsible evaluation of neural network explanations and beyond." + Journal of Machine Learning Research 24.34 (2023): 1-11. """ diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 6141079f..98282bd4 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -324,7 +324,7 @@ def forward(self, x): @gin.configurable class TFT(DLPredictionWrapper): """ - Implementation of https://arxiv.org/abs/1912.09363 + Implementation of https://arxiv.org/abs/1912.09363 from https://github.com/NVIDIA/DeepLearningExamples/tree/master/PyTorch/Forecasting/TFT """ diff --git a/icu_benchmarks/models/layers.py b/icu_benchmarks/models/layers.py index c5fd7550..944f4b89 100644 --- a/icu_benchmarks/models/layers.py +++ b/icu_benchmarks/models/layers.py @@ -804,9 +804,9 @@ def forward(self, historical_inputs, cs, ch, cc, ce, future_inputs): x, attn_prob = self.attention(enriched) # Don't compute historical quantiles - x = x[:, self.encoder_length :, :] - temporal_features = temporal_features[:, self.encoder_length :, :] - enriched = enriched[:, self.encoder_length :, :] + x = x[:, self.encoder_length:, :] + temporal_features = temporal_features[:, self.encoder_length:, :] + enriched = enriched[:, self.encoder_length:, :] x = self.attention_gate(x) x = x + enriched diff --git a/icu_benchmarks/models/similarity_func.py b/icu_benchmarks/models/similarity_func.py index fd931e7e..bc49850f 100644 --- a/icu_benchmarks/models/similarity_func.py +++ b/icu_benchmarks/models/similarity_func.py @@ -1,4 +1,4 @@ -"""This module holds a collection of similarity functions i.e., +"""This module holds a collection of similarity functions i.e., ways to measure the distance between two inputs (or explanations).""" # This file is part of Quantus. diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index f22ecf85..f5456a08 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -1219,7 +1219,8 @@ def Relative_Stability( References: 1) `https://arxiv.org/pdf/2203.06877.pdf - 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. + 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation + of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. """ From 2ab11a50b000f74a9ba1a6651c32eee5833cf693 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Thu, 7 Mar 2024 16:04:57 +0100 Subject: [PATCH 131/142] formatting --- icu_benchmarks/models/custom_metrics.py | 99 ++++---- icu_benchmarks/models/train.py | 292 ++++++++++++++---------- icu_benchmarks/models/wrappers.py | 107 +++++---- 3 files changed, 277 insertions(+), 221 deletions(-) diff --git a/icu_benchmarks/models/custom_metrics.py b/icu_benchmarks/models/custom_metrics.py index 536a7e5c..d1526e6d 100644 --- a/icu_benchmarks/models/custom_metrics.py +++ b/icu_benchmarks/models/custom_metrics.py @@ -145,6 +145,53 @@ class Faithfulness(EpochMetric): def __init__(self, output_transform: Callable = lambda x: x, check_compute_fn: bool = False, *args, **kwargs) -> None: super().__init__(output_transform, check_compute_fn, *args, **kwargs) + def add_noise(self, x, indices, time_step, feature, feature_timestep): + noise = torch.randn_like(x["encoder_cont"]) + if time_step: + idx0, idx1 = np.meshgrid(indices[0], indices[1], indexing="ij") + + with torch.no_grad(): + x["encoder_cont"][idx0, idx1, :] += noise[idx0, idx1, :] + + elif feature: + idx0, idx1 = np.meshgrid(indices[0], indices[1], indexing="ij") + + with torch.no_grad(): + x["encoder_cont"][idx0, :, idx1] += noise[idx0, :, idx1] + + elif feature_timestep: + idx0, idx1, idx2 = np.meshgrid(indices[0], indices[1], indices[2], indexing="ij") + + with torch.no_grad(): + x["encoder_cont"][idx0, idx1, idx2] += noise[idx0, idx1, idx2] + return x + + def apply_baseline(self, x, indices, time_step, feature, feature_timestep): + mask = torch.ones_like(x["encoder_cont"]) + if time_step: + ( + idx0, + idx1, + ) = np.meshgrid(indices[0], indices[1], indexing="ij") + + mask[idx0, idx1, :] -= mask[idx0, idx1, :] + elif feature: + ( + idx0, + idx1, + ) = np.meshgrid(indices[0], indices[1], indexing="ij") + + mask[idx0, :, idx1] -= mask[idx0, :, idx1] + + elif feature_timestep: + idx0, idx1, idx2 = np.meshgrid(indices[0], indices[1], indices[2], indexing="ij") + + mask[idx0, idx1, idx2] -= mask[idx0, idx1, idx2] + + with torch.no_grad(): + x["encoder_cont"] *= mask + return x + def update( self, x, @@ -199,54 +246,8 @@ def update( Journal of Machine Learning Research 24.34 (2023): 1-11. """ - def add_noise(x, indices, time_step, feature_timestep): - noise = torch.randn_like(x["encoder_cont"]) - if time_step: - idx0, idx1 = np.meshgrid(indices[0], indices[1], indexing="ij") - - with torch.no_grad(): - x["encoder_cont"][idx0, idx1, :] += noise[idx0, idx1, :] - - elif feature: - idx0, idx1 = np.meshgrid(indices[0], indices[1], indexing="ij") - - with torch.no_grad(): - x["encoder_cont"][idx0, :, idx1] += noise[idx0, :, idx1] - - elif feature_timestep: - idx0, idx1, idx2 = np.meshgrid(indices[0], indices[1], indices[2], indexing="ij") - - with torch.no_grad(): - x["encoder_cont"][idx0, idx1, idx2] += noise[idx0, idx1, idx2] - - def apply_baseline(x, indices, time_step, feature_timestep): - mask = torch.ones_like(x["encoder_cont"]) - if time_step: - ( - idx0, - idx1, - ) = np.meshgrid(indices[0], indices[1], indexing="ij") - - mask[idx0, idx1, :] -= mask[idx0, idx1, :] - elif feature: - ( - idx0, - idx1, - ) = np.meshgrid(indices[0], indices[1], indexing="ij") - - mask[idx0, :, idx1] -= mask[idx0, :, idx1] - - elif feature_timestep: - idx0, idx1, idx2 = np.meshgrid(indices[0], indices[1], indices[2], indexing="ij") - - mask[idx0, idx1, idx2] -= mask[idx0, idx1, idx2] - - with torch.no_grad(): - x["encoder_cont"] *= mask - # Assuming 'attribution' is already a GPU tensor - if not torch.is_tensor(attribution): - attribution = torch.tensor(attribution).to(device) + attribution = torch.tensor(attribution).to(device) # Other initializations if similarity_func is None: similarity_func = correlation_spearman @@ -278,9 +279,9 @@ def apply_baseline(x, indices, time_step, feature_timestep): # Apply perturbation if pertrub == "Noise": - add_noise(x, a_ix, time_step, feature_timestep) + x = self.add_noise(x, a_ix, time_step, feature, feature_timestep) elif pertrub == "baseline": - apply_baseline(x, a_ix, time_step, feature_timestep) + x = self.apply_baseline(x, a_ix, time_step, feature, feature_timestep) # Predict on perturbed input and calculate deltas y_pred_perturb = (model(model.prep_data(x))).detach() # Keep on GPU diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index e7ed5d68..a911a7ac 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -119,6 +119,7 @@ def train_common( batch_size = min(batch_size, len(train_dataset), len(val_dataset)) test_dataset = dataset_class(data, split=test_on, name=dataset_names["test"]) test_dataset = assure_minimum_length(test_dataset) + if not eval_only: logging.info( f"Training on {train_dataset.name} with {len(train_dataset)} samples and validating on {val_dataset.name} with" @@ -126,87 +127,24 @@ def train_common( ) batch_size = int(batch_size) logging.info(f"Using {num_workers} workers for data loading.") - if pytorch_forecasting: - train_loader = train_dataset.to_dataloader( - train=True, - batch_size=batch_size, - num_workers=num_workers, - pin_memory=False, - drop_last=True, - ) - val_loader = val_dataset.to_dataloader( - train=False, - batch_size=batch_size, - num_workers=num_workers, - pin_memory=False, - drop_last=True, - ) - test_loader = test_dataset.to_dataloader( - train=False, - batch_size=batch_size, - num_workers=num_workers, - pin_memory=False, - drop_last=True, - shuffle=False, - ) - if load_weights: - model = load_model( - model, - source_dir, - pl_model=pl_model, - train_dataset=train_dataset, - optimizer=optimizer, - ) - - else: - model = model( - train_dataset, - optimizer=optimizer, - epochs=epochs, - run_mode=mode, - batch_size=batch_size, - ) - if random_labels: - train_dataset.randomize_labels(num_classes=model.num_classes) - val_dataset.randomize_labels(num_classes=model.num_classes) - test_dataset.randomize_labels(num_classes=model.num_classes) - - else: - train_loader = DataLoader( - train_dataset, - batch_size=batch_size, - shuffle=True, - num_workers=num_workers, - pin_memory=True, - drop_last=True, - ) - val_loader = DataLoader( - val_dataset, - batch_size=batch_size, - shuffle=False, - num_workers=num_workers, - pin_memory=True, - drop_last=True, - ) - - test_loader = ( - DataLoader( - test_dataset, - batch_size=min(batch_size * 4, len(test_dataset)), - shuffle=False, - num_workers=num_workers, - pin_memory=True, - drop_last=True, - ) - if model.requires_backprop - else DataLoader([test_dataset.to_tensor()], batch_size=1) - ) - - data_shape = next(iter(train_loader))[0].shape - if load_weights: - model = load_model(model, source_dir, pl_model=pl_model) - else: - model = model(optimizer=optimizer, input_size=data_shape, epochs=epochs, run_mode=mode) + train_loader, val_loader, test_loader, model = prepare_data_loaders( + model=model, + train_dataset=train_dataset, + val_dataset=val_dataset, + test_dataset=test_dataset, + batch_size=batch_size, + num_workers=num_workers, + pin_memory=True, + drop_last=True, + pytorch_forecasting=pytorch_forecasting, + load_weights=load_weights, + source_dir=source_dir, + pl_model=pl_model, + optimizer=optimizer, + epochs=epochs, + mode=mode, + random_labels=random_labels, + ) model.set_weight(weight, train_dataset) model.set_trained_columns(train_dataset.get_feature_names()) @@ -241,7 +179,6 @@ def train_common( logger=loggers, num_sanity_val_steps=-1, log_every_n_steps=5, - # gradient_clip_val=gradient_clip_val ) if not eval_only: if model.requires_backprop: @@ -260,18 +197,14 @@ def train_common( return 0 if explain: - if random_model_dir is None: - random_model = None - else: - path = Path(random_model_dir) - - random_model = load_model( - model, - source_dir=path, - pl_model=pl_model, - train_dataset=train_dataset, - optimizer=optimizer, - ) + path = Path(random_model_dir) + random_model = load_model( + model, + source_dir=path, + pl_model=pl_model, + train_dataset=train_dataset, + optimizer=optimizer, + ) XAI_dict = {} # dictrionary to log attributions metrics @@ -324,21 +257,11 @@ def train_common( torch.Size([64, 2]), ] - # Create a default mask for non-targeted tensors - def create_default_mask(shape): - if len(shape) == 3: - return torch.zeros(shape[0], shape[1], max(1, shape[2]), dtype=torch.int32) - elif len(shape) == 2: - return torch.zeros(shape[0], max(1, shape[1]), dtype=torch.int32) - else: # len(shape) == 1 - return torch.zeros(shape[0], dtype=torch.int32) - # Create a feature mask for the second tensor that includes both features and timesteps num_timesteps = shapes[1][1] num_features = shapes[1][2] feature_mask_second = torch.arange(num_timesteps * num_features).reshape(num_timesteps, num_features) feature_mask_second = feature_mask_second.unsqueeze(0).repeat(shapes[1][0], 1, 1) - # Create a tuple of masks feature_masks = tuple( [create_default_mask(shape) if i != 1 else feature_mask_second for i, shape in enumerate(shapes)] @@ -392,24 +315,18 @@ def create_default_mask(shape): XAI_dict["{}_ROS".format(key)] = st_o_score print("{}_RIS ".format(key), st_i_score) XAI_dict["{}_RIS".format(key)] = st_i_score - if key == "Att": - print("{} weights faithfulness featrues ".format(key), v_score) - XAI_dict["{}_Faith Features".format(key)] = v_score - else: - print("{} Attributions faithfulness featrues ".format(key), v_score) - XAI_dict["{}_Faith Features".format(key)] = v_score - - print( - "{}_Attributions Faithfulness Variable Per Timestep ".format(key), - ts_v_score, - ) - XAI_dict["{}_Faith Variable Per Timestep".format(key)] = ts_v_score + print("{} Attributions faithfulness featrues ".format(key), v_score) + XAI_dict["{}_Faith Features".format(key)] = v_score + + print( + "{}_Attributions Faithfulness Variable Per Timestep ".format(key), + ts_v_score, + ) + XAI_dict["{}_Faith Variable Per Timestep".format(key)] = ts_v_score print("{}_Data Randomization Distance ".format(key), r_score) XAI_dict["{}_Data Randomization Distance".format(key)] = r_score - # Getting the interpertations using pytorch forecasting native methods - # Path to the JSON file in log_dir json_file_path = f"{log_dir}/XAI_metrics.json" @@ -423,7 +340,146 @@ def create_default_mask(shape): return test_loss +def prepare_data_loaders( + model, + train_dataset, + val_dataset, + test_dataset, + batch_size, + num_workers, + pin_memory, + drop_last=True, + shuffle_train=True, + pytorch_forecasting=False, + load_weights=False, + source_dir=None, + pl_model=None, + optimizer=None, + epochs=None, + mode=None, + random_labels=False, +): + """ + Prepares PyTorch data loaders based on the provided datasets and configuration. + + Args: + train_dataset: Training dataset. + val_dataset: Validation dataset. + test_dataset: Test dataset. + batch_size: Batch size for data loaders. + num_workers: Number of worker processes for data loading. + pin_memory: Whether to use pin_memory for faster data transfer to GPU. + drop_last: Whether to drop the last incomplete batch. + shuffle_train: Whether to shuffle the training data loader. + load_weights: Whether to load weights from a pre-trained model. + source_dir: Directory to load weights from. + pl_model: PyTorch Lightning model (used for loading weights). + optimizer: Optimizer for the model. + epochs: Number of training epochs. + mode: Run mode for the model. + random_labels: Whether to randomize labels for the datasets. + + Returns: + tuple: Tuple containing train_loader, val_loader, and test_loader. + """ + if pytorch_forecasting: + train_loader = train_dataset.to_dataloader( + train=True, + batch_size=batch_size, + num_workers=num_workers, + pin_memory=pin_memory, + drop_last=drop_last, + shuffle=shuffle_train, + ) + val_loader = val_dataset.to_dataloader( + train=False, + batch_size=batch_size, + num_workers=num_workers, + pin_memory=pin_memory, + drop_last=drop_last, + ) + test_loader = test_dataset.to_dataloader( + train=False, + batch_size=batch_size, + num_workers=num_workers, + pin_memory=pin_memory, + drop_last=drop_last, + shuffle=False, + ) + if load_weights: + model = load_model( + model, + source_dir, + pl_model=pl_model, + train_dataset=train_dataset, + optimizer=optimizer, + ) + else: + model = model( + train_dataset, + optimizer=optimizer, + epochs=epochs, + run_mode=mode, + batch_size=batch_size, + ) + if random_labels: + train_dataset.randomize_labels(num_classes=model.num_classes) + val_dataset.randomize_labels(num_classes=model.num_classes) + test_dataset.randomize_labels(num_classes=model.num_classes) + + else: + train_loader = DataLoader( + train_dataset, + batch_size=batch_size, + shuffle=shuffle_train, + num_workers=num_workers, + pin_memory=pin_memory, + drop_last=drop_last, + ) + val_loader = DataLoader( + val_dataset, + batch_size=batch_size, + shuffle=False, + num_workers=num_workers, + pin_memory=pin_memory, + drop_last=drop_last, + ) + + test_loader = ( + DataLoader( + test_dataset, + batch_size=min(batch_size * 4, len(test_dataset)), + shuffle=False, + num_workers=num_workers, + pin_memory=pin_memory, + drop_last=drop_last, + ) + if model.requires_backprop + else DataLoader([test_dataset.to_tensor()], batch_size=1) + ) + + data_shape = next(iter(train_loader))[0].shape + if load_weights: + model = load_model(model, source_dir, pl_model=pl_model) + else: + model = model(optimizer=optimizer, input_size=data_shape, epochs=epochs, run_mode=mode) + + return train_loader, val_loader, test_loader, model + + +def create_default_mask(shape): + if len(shape) == 3: + return torch.zeros(shape[0], shape[1], max(1, shape[2]), dtype=torch.int32) + elif len(shape) == 2: + return torch.zeros(shape[0], max(1, shape[1]), dtype=torch.int32) + else: # len(shape) == 1 + return torch.zeros(shape[0], dtype=torch.int32) + + def load_model(model, source_dir, pl_model=True, train_dataset=None, optimizer=None): + if source_dir is None: + return None + if source_dir.exists(): if model.requires_backprop: if (source_dir / "model.ckpt").exists(): diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index f5456a08..5792a5bb 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -901,13 +901,13 @@ def explantation( st_i_score = np.max(st_i_score) st_o_score = np.max(st_o_score) - if plot: + """ if plot: log_dir_plots = log_dir / "plots" if not (log_dir_plots.exists()): log_dir_plots.mkdir(parents=True) # Plot attributions for features and timesteps - self.plot_attributions(features_attrs, timestep_attrs, method_name, log_dir_plots) + self.plot_attributions(features_attrs, timestep_attrs, method_name, log_dir_plots) """ # Return computed attributions and metrics return ( @@ -947,6 +947,53 @@ def prep_data(self, x): ) return data + def add_noise(self, x, indices, time_step, feature, feature_timestep): + noise = torch.randn_like(x["encoder_cont"]) + if time_step: + idx0, idx1 = np.meshgrid(indices[0], indices[1], indexing="ij") + + with torch.no_grad(): + x["encoder_cont"][idx0, idx1, :] += noise[idx0, idx1, :] + + elif feature: + idx0, idx1 = np.meshgrid(indices[0], indices[1], indexing="ij") + + with torch.no_grad(): + x["encoder_cont"][idx0, :, idx1] += noise[idx0, :, idx1] + + elif feature_timestep: + idx0, idx1, idx2 = np.meshgrid(indices[0], indices[1], indices[2], indexing="ij") + + with torch.no_grad(): + x["encoder_cont"][idx0, idx1, idx2] += noise[idx0, idx1, idx2] + return x + + def apply_baseline(self, x, indices, time_step, feature, feature_timestep): + mask = torch.ones_like(x["encoder_cont"]) + if time_step: + ( + idx0, + idx1, + ) = np.meshgrid(indices[0], indices[1], indexing="ij") + + mask[idx0, idx1, :] -= mask[idx0, idx1, :] + elif feature: + ( + idx0, + idx1, + ) = np.meshgrid(indices[0], indices[1], indexing="ij") + + mask[idx0, :, idx1] -= mask[idx0, :, idx1] + + elif feature_timestep: + idx0, idx1, idx2 = np.meshgrid(indices[0], indices[1], indices[2], indexing="ij") + + mask[idx0, idx1, idx2] -= mask[idx0, idx1, idx2] + + with torch.no_grad(): + x["encoder_cont"] *= mask + return x + def Faithfulness_Correlation( self, x, @@ -999,54 +1046,7 @@ def Faithfulness_Correlation( Journal of Machine Learning Research 24.34 (2023): 1-11. """ - def add_noise(x, indices, time_step, feature_timestep): - noise = torch.randn_like(x["encoder_cont"]) - if time_step: - idx0, idx1 = np.meshgrid(indices[0], indices[1], indexing="ij") - - with torch.no_grad(): - x["encoder_cont"][idx0, idx1, :] += noise[idx0, idx1, :] - - elif feature: - idx0, idx1 = np.meshgrid(indices[0], indices[1], indexing="ij") - - with torch.no_grad(): - x["encoder_cont"][idx0, :, idx1] += noise[idx0, :, idx1] - - elif feature_timestep: - idx0, idx1, idx2 = np.meshgrid(indices[0], indices[1], indices[2], indexing="ij") - - with torch.no_grad(): - x["encoder_cont"][idx0, idx1, idx2] += noise[idx0, idx1, idx2] - - def apply_baseline(x, indices, time_step, feature_timestep): - mask = torch.ones_like(x["encoder_cont"]) - if time_step: - ( - idx0, - idx1, - ) = np.meshgrid(indices[0], indices[1], indexing="ij") - - mask[idx0, idx1, :] -= mask[idx0, idx1, :] - elif feature: - ( - idx0, - idx1, - ) = np.meshgrid(indices[0], indices[1], indexing="ij") - - mask[idx0, :, idx1] -= mask[idx0, :, idx1] - - elif feature_timestep: - idx0, idx1, idx2 = np.meshgrid(indices[0], indices[1], indices[2], indexing="ij") - - mask[idx0, idx1, idx2] -= mask[idx0, idx1, idx2] - - with torch.no_grad(): - x["encoder_cont"] *= mask - - # Assuming 'attribution' is already a GPU tensor - if not torch.is_tensor(attribution): - attribution = torch.tensor(attribution).to(self.device) + attribution = torch.tensor(attribution).to(self.device) # Other initializations if similarity_func is None: @@ -1079,9 +1079,9 @@ def apply_baseline(x, indices, time_step, feature_timestep): # Apply perturbation if pertrub == "Noise": - add_noise(x, a_ix, time_step, feature_timestep) + x = self.add_noise(x, a_ix, time_step, feature, feature_timestep) elif pertrub == "baseline": - apply_baseline(x, a_ix, time_step, feature_timestep) + x = self.apply_baseline(x, a_ix, time_step, feature, feature_timestep) # Predict on perturbed input and calculate deltas y_pred_perturb = (self(self.prep_data(x))).detach() # Keep on GPU @@ -1289,8 +1289,7 @@ def norm_function(arr): return nominator / denominator - if not torch.is_tensor(attribution): - attribution = torch.tensor(attribution).to(self.device) + attribution = torch.tensor(attribution).to(self.device) if explain_method == "Attention": y_pred = self.model.predict(dataloader) x_original = dataloader.dataset.data["reals"].clone() From 1f904dccb4419b9d1b11ad7e1c6feaaebc87b678 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Fri, 8 Mar 2024 13:58:33 +0100 Subject: [PATCH 132/142] cleaning up and formatting --- configs/prediction_models/TFT.gin | 25 - configs/prediction_models/common/DLTuning.gin | 2 +- configs/tasks/Regression.gin | 2 +- configs/tasks/RegressionTFT.gin | 33 - configs/tasks/common/CrossValidation.gin | 2 +- environment.yml | 1 + experiments/benchmark_classification.yml | 39 +- experiments/benchmark_regression.yml | 31 +- icu_benchmarks/cross_validation.py | 2 +- icu_benchmarks/data/Checking data-Copy1.ipynb | 582 ------------------ icu_benchmarks/data/constants.py | 4 - icu_benchmarks/imputation/diffwave.py | 2 +- icu_benchmarks/models/constants.py | 9 +- icu_benchmarks/models/custom_metrics.py | 2 +- icu_benchmarks/models/similarity_func.py | 288 --------- icu_benchmarks/models/wrappers.py | 2 +- icu_benchmarks/run_utils.py | 207 +------ saved_dictionary.pkl | Bin 20818976 -> 0 bytes 18 files changed, 63 insertions(+), 1170 deletions(-) delete mode 100644 configs/prediction_models/TFT.gin delete mode 100644 configs/tasks/RegressionTFT.gin delete mode 100644 icu_benchmarks/data/Checking data-Copy1.ipynb delete mode 100644 icu_benchmarks/models/similarity_func.py delete mode 100644 saved_dictionary.pkl diff --git a/configs/prediction_models/TFT.gin b/configs/prediction_models/TFT.gin deleted file mode 100644 index 09fcf537..00000000 --- a/configs/prediction_models/TFT.gin +++ /dev/null @@ -1,25 +0,0 @@ -# Settings for TFT model. - -# Common settings for DL models -include "configs/prediction_models/common/DLCommon.gin" - -# Optimizer params -train_common.model = @TFT - -optimizer/hyperparameter.class_to_tune = @Adam -optimizer/hyperparameter.weight_decay = 1e-6 -optimizer/hyperparameter.lr = (1e-5, 3e-4) - -# Encoder params -model/hyperparameter.class_to_tune = @TFT -model/hyperparameter.encoder_length = 24 -model/hyperparameter.hidden = (32,64,128,256) -model/hyperparameter.num_classes = %NUM_CLASSES -model/hyperparameter.dropout = (0.0, 0.4) -model/hyperparameter.dropout_att = (0.0, 0.4) -model/hyperparameter.n_heads =(1,2,4) -model/hyperparameter.example_length=25 - - - - diff --git a/configs/prediction_models/common/DLTuning.gin b/configs/prediction_models/common/DLTuning.gin index b631dc89..04fb8ff6 100644 --- a/configs/prediction_models/common/DLTuning.gin +++ b/configs/prediction_models/common/DLTuning.gin @@ -1,5 +1,5 @@ # Hyperparameter tuner settings for Deep Learning. tune_hyperparameters.scopes = ["model", "optimizer","train_common"] tune_hyperparameters.n_initial_points = 5 -tune_hyperparameters.n_calls = 15 +tune_hyperparameters.n_calls = 30 tune_hyperparameters.folds_to_tune_on = 2 \ No newline at end of file diff --git a/configs/tasks/Regression.gin b/configs/tasks/Regression.gin index 75041763..6ce4c0f6 100644 --- a/configs/tasks/Regression.gin +++ b/configs/tasks/Regression.gin @@ -15,7 +15,7 @@ train_common.weight = "balanced" train_common.ram_cache = True # LOSS FUNCTION -DLPredictionWrapper.loss = @l1_loss +DLPredictionWrapper.loss = @mse_loss DLPredictionPytorchForecastingWrapper.loss = @l1_loss MLWrapper.loss = @mean_squared_error diff --git a/configs/tasks/RegressionTFT.gin b/configs/tasks/RegressionTFT.gin deleted file mode 100644 index a1ff4d55..00000000 --- a/configs/tasks/RegressionTFT.gin +++ /dev/null @@ -1,33 +0,0 @@ -# COMMON IMPORTS -include "configs/tasks/common/Imports.gin" - -# DATASET CONFIGURATION -include "configs/tasks/common/PredictionTaskVariables.gin" - -# CROSS-VALIDATION -include "configs/tasks/common/CrossValidation.gin" - -# MODE SETTINGS -Run.mode = "Regression" -NUM_CLASSES = 1 -HORIZON = 24 -train_common.weight = "balanced" -train_common.ram_cache = True - -# LOSS FUNCTION -DLPredictionWrapper.loss = @QuantileLoss -QuantileLoss.quantiles=[0.1, 0.5, 0.9] - -# SELECTING PREPROCESSOR -preprocess.preprocessor = @base_regression_preprocessor -preprocess.vars = %vars -preprocess.use_static = True - -# SPECIFYING REGRESSION OUTCOME SCALING -base_regression_preprocessor.outcome_min = 0 -base_regression_preprocessor.outcome_max = 15 - -# SELECTING DATASET -PredictionDataset.vars = %vars -PredictionDataset.ram_cache = True - diff --git a/configs/tasks/common/CrossValidation.gin b/configs/tasks/common/CrossValidation.gin index 519ad3dd..f3efd9ef 100644 --- a/configs/tasks/common/CrossValidation.gin +++ b/configs/tasks/common/CrossValidation.gin @@ -1,3 +1,3 @@ # CROSS-VALIDATION SETTINGS -execute_repeated_cv.cv_repetitions = 2 +execute_repeated_cv.cv_repetitions = 5 execute_repeated_cv.cv_folds = 5 \ No newline at end of file diff --git a/environment.yml b/environment.yml index 2232e1a3..b48befc7 100644 --- a/environment.yml +++ b/environment.yml @@ -28,6 +28,7 @@ dependencies: - pip=23.1 - einops=0.6.1 - hydra-core=1.3 + - quantus=0.5.3 - pip: - recipies==0.1.3 # Fixed version because of NumPy incompatibility and stale development status. diff --git a/experiments/benchmark_classification.yml b/experiments/benchmark_classification.yml index bc1c55c6..61763ffa 100644 --- a/experiments/benchmark_classification.yml +++ b/experiments/benchmark_classification.yml @@ -17,29 +17,26 @@ name: yaib_classification_benchmark parameters: data_dir: values: - # - data/YAIB_Preprint/aki/miiv - # - data/YAIB_Preprint/aki/hirid - # - data/YAIB_Preprint/aki/eicu - # - data/YAIB_Preprint/aki/aumc - # - data/YAIB_Preprint/mortality24/miiv - # - data/YAIB_Preprint/mortality24/hirid - # - data/YAIB_Preprint/mortality24/eicu - # - data/YAIB_Preprint/mortality24/aumc - - data/YAIB_Preprint/sepsis/miiv - - data/YAIB_Preprint/sepsis/hirid - - data/YAIB_Preprint/sepsis/eicu - - data/YAIB_Preprint/sepsis/aumc + - ../data/mortality24/miiv + - ../data/mortality24/hirid + - ../data/mortality24/eicu + - ../data/mortality24/aumc + - ../data/aki/miiv + - ../data/aki/hirid + - ../data/aki/eicu + - ../data/aki/aumc + - ../data/sepsis/miiv + - ../data/sepsis/hirid + - ../data/sepsis/eicu + - ../data/sepsis/aumc model: values: - # - LogisticRegression - # - LGBMClassifier - # - GRU - # - LSTM - # - TCN - # - Transformer - # - TFT - - TFTpytorch - + - LogisticRegression + - LGBMClassifier + - GRU + - LSTM + - TCN + - Transformer seed: values: - 1111 diff --git a/experiments/benchmark_regression.yml b/experiments/benchmark_regression.yml index c13929eb..1f9176f3 100644 --- a/experiments/benchmark_regression.yml +++ b/experiments/benchmark_regression.yml @@ -17,25 +17,22 @@ name: yaib_regression_benchmark parameters: data_dir: values: - - data/YAIB_Preprint/los/miiv - - data/YAIB_Preprint/los/hirid - - data/YAIB_Preprint/los/eicu - - data/YAIB_Preprint/los/aumc - - data/YAIB_Preprint/kf/miiv - - data/YAIB_Preprint/kf/hirid - - data/YAIB_Preprint/kf/eicu - - data/YAIB_Preprint/kf/aumc + - ../data/los/miiv + - ../data/los/hirid + - ../data/los/eicu + - ../data/los/aumc + - ../data/kidney_function/miiv + - ../data/kidney_function/hirid + - ../data/kidney_function/eicu + - ../data/kidney_function/aumc model: values: - # - ElasticNet - # - LGBMRegressor - # - GRU - # - LSTM - # - TCN - # - Transformer - # - TFT - - TFTpytorch - + - ElasticNet + - LGBMRegressor + - GRU + - LSTM + - TCN + - Transformer seed: values: - 1111 diff --git a/icu_benchmarks/cross_validation.py b/icu_benchmarks/cross_validation.py index 535d9619..c19076e5 100644 --- a/icu_benchmarks/cross_validation.py +++ b/icu_benchmarks/cross_validation.py @@ -23,7 +23,7 @@ def execute_repeated_cv( train_size: int = None, load_weights: bool = False, source_dir: Path = None, - cv_repetitions: int = 1, + cv_repetitions: int = 5, cv_repetitions_to_train: int = None, cv_folds: int = 5, cv_folds_to_train: int = None, diff --git a/icu_benchmarks/data/Checking data-Copy1.ipynb b/icu_benchmarks/data/Checking data-Copy1.ipynb deleted file mode 100644 index 9abe4d48..00000000 --- a/icu_benchmarks/data/Checking data-Copy1.ipynb +++ /dev/null @@ -1,582 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 4, - "id": "6b471a18", - "metadata": {}, - "outputs": [ - { - "ename": "ModuleNotFoundError", - "evalue": "No module named 'icu_benchmarks'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)", - "Input \u001b[0;32mIn [4]\u001b[0m, in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01micu_benchmarks\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mdata\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01msplit_process_data\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m preprocess_data\n", - "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'icu_benchmarks'" - ] - } - ], - "source": [ - "from icu_benchmarks.data.split_process_data import preprocess_data" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "9786d96d", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0\n" - ] - } - ], - "source": [ - "print(1-1)" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "b9f28515", - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "dyn=pd.read_parquet('../../demo_data/los/eicu_demo/dyn.parquet', engine='pyarrow')\n", - "outc=pd.read_parquet('../../demo_data/los/eicu_demo/outc.parquet', engine='pyarrow')\n", - "sta=pd.read_parquet('../../demo_data/los/eicu_demo/sta.parquet', engine='pyarrow')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "35342dde", - "metadata": {}, - "outputs": [], - "source": [ - "dyn.columns" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c6f3e4ae", - "metadata": {}, - "outputs": [], - "source": [ - "dyn.columns" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "64c217e9", - "metadata": {}, - "outputs": [], - "source": [ - "outc.head()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "87724d04", - "metadata": {}, - "outputs": [], - "source": [ - "sta.head()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fddac54c", - "metadata": {}, - "outputs": [], - "source": [ - "sta.columns" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "265af37f", - "metadata": {}, - "outputs": [], - "source": [ - "from typing import List\n", - "from pandas import DataFrame\n", - "import numpy as np\n", - "import gin\n", - "from torch import Tensor, cat, from_numpy, float32,empty,stack\n", - "from torch.utils.data import Dataset\n", - "import logging\n", - "from typing import Dict, Tuple\n", - "from constants import DataSegment as Segment\n", - "from constants import DataSplit as Split\n", - "from collections import OrderedDict\n", - "\n", - "FEAT_NAMES = ['s_cat' , 's_cont' , 'k_cat' , 'k_cont' , 'o_cat' , 'o_cont' , 'target', 'id']\n", - "def ampute_data(data, mechanism, p_miss, p_obs=0.3):\n", - " \"\"\"\n", - " Generate missing values for specifics missing-data mechanism and proportion of missing values.\n", - "\n", - " Parameters\n", - " ----------\n", - " data : DataFrame\n", - " Data for which missing values will be simulated.\n", - " mechanism : str,\n", - " Indicates the missing-data mechanism to be used. (\"MCAR\", \"MAR\" or \"MNAR\")\n", - " p_miss : float\n", - " Proportion of missing values to generate for variables which will have missing values.\n", - " p_obs : float\n", - " If mecha = \"MAR\" or \"MNAR\", proportion of variables with *no* missing values\n", - " that will be used for the logistic masking model.\n", - "\n", - " Returns\n", - " ----------\n", - " imputed_data: DataFrame\n", - " The data with the generated missing values.\n", - " \"\"\"\n", - " logging.info(f\"Applying {mechanism} amputation.\")\n", - " X = torch.tensor(data.values.astype(np.float32))\n", - "\n", - " if mechanism == \"MAR\":\n", - " mask = MAR_logistic_mask(X, p_miss, p_obs)\n", - " elif mechanism == \"MNAR\":\n", - " mask = MNAR_logistic_mask(X, p_miss, p_obs)\n", - " elif mechanism == \"MCAR\":\n", - " mask = MCAR_mask(X, p_miss)\n", - " elif mechanism == \"BO\":\n", - " mask = BO_mask(X, p_miss)\n", - " else:\n", - " logging.error(\"Not a valid amputation mechanism. Missing-data mechanisms to be used are MCAR, MAR or MNAR.\")\n", - "\n", - " amputed_data = data.mask(mask)\n", - " return amputed_data, mask\n", - "class CommonDataset(Dataset):\n", - " \"\"\"Common dataset: subclass of Torch Dataset that represents the data to learn on.\n", - "\n", - " Args: data: Dict of the different splits of the data. split: Either 'train','val' or 'test'. vars: Contains the names of\n", - " columns in the data. grouping_segment: str, optional: The segment of the data contains the grouping column with only\n", - " unique values. Defaults to Segment.outcome. Is used to calculate the number of stays in the data.\n", - " \"\"\"\n", - "\n", - " def __init__(\n", - " self,\n", - " data: dict,\n", - " split: str = Split.train,\n", - " vars: Dict[str, str] = gin.REQUIRED,\n", - " grouping_segment: str = Segment.outcome,\n", - " ):\n", - " self.split = split\n", - " self.vars = vars\n", - " self.grouping_df = data[split][grouping_segment].set_index(self.vars[\"GROUP\"])\n", - " self.features_df = (\n", - " #drops time coulmn and sets index to stay_id\n", - " data[split][Segment.features].set_index(self.vars[\"GROUP\"]).drop(labels=self.vars[\"SEQUENCE\"], axis=1)\n", - " )\n", - "\n", - " # calculate basic info for the data\n", - " self.num_stays = self.grouping_df.index.unique().shape[0]\n", - " self.maxlen = self.features_df.groupby([self.vars[\"GROUP\"]]).size().max()\n", - "\n", - " def ram_cache(self, cache: bool = True):\n", - " self._cached_dataset = None\n", - " if cache:\n", - " logging.info(\"Caching dataset in ram.\")\n", - " self._cached_dataset = [self[i] for i in range(len(self))]\n", - "\n", - " def __len__(self) -> int:\n", - " \"\"\"Returns number of stays in the data.\n", - "\n", - " Returns:\n", - " number of stays in the data\n", - " \"\"\"\n", - " return self.num_stays\n", - "\n", - " def get_feature_names(self):\n", - " return self.features_df.columns\n", - "\n", - " def to_tensor(self):\n", - " values = []\n", - " for entry in self:\n", - " for i, value in enumerate(entry):\n", - " if len(values) <= i:\n", - " values.append([])\n", - " values[i].append(value.unsqueeze(0))\n", - " return [cat(value, dim=0) for value in values]\n", - "\n", - "\n", - "@gin.configurable(\"PredictionDataset\")\n", - "class PredictionDataset(CommonDataset):\n", - " \"\"\"Subclass of common dataset for prediction tasks.\n", - "\n", - " Args:\n", - " ram_cache (bool, optional): Whether the complete dataset should be stored in ram. Defaults to True.\n", - " \"\"\"\n", - "\n", - " def __init__(self, *args, ram_cache: bool = True, **kwargs):\n", - " super().__init__(*args, grouping_segment=Segment.outcome, **kwargs)\n", - " self.outcome_df = self.grouping_df\n", - " self.ram_cache(ram_cache)\n", - "\n", - " def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]:\n", - " \"\"\"Function to sample from the data split of choice. Used for deep learning implementations.\n", - "\n", - " Args:\n", - " idx: A specific row index to sample.\n", - "\n", - " Returns:\n", - " A sample from the data, consisting of data, labels and padding mask.\n", - " \"\"\"\n", - " if self._cached_dataset is not None:\n", - " return self._cached_dataset[idx]\n", - "\n", - " pad_value = 0.0\n", - " stay_id = self.outcome_df.index.unique()[idx] # [self.vars[\"GROUP\"]]\n", - "\n", - " # slice to make sure to always return a DF\n", - " window = self.features_df.loc[stay_id:stay_id].to_numpy()\n", - " labels = self.outcome_df.loc[stay_id:stay_id][self.vars[\"LABEL\"]].to_numpy(dtype=float)\n", - "\n", - " if len(labels) == 1:\n", - " # only one label per stay, align with window\n", - " labels = np.concatenate([np.empty(window.shape[0] - 1) * np.nan, labels], axis=0)\n", - "\n", - " length_diff = self.maxlen - window.shape[0]\n", - " \n", - " \n", - " pad_mask = np.ones(window.shape[0])\n", - "\n", - " # Padding the array to fulfill size requirement\n", - " if length_diff > 0:\n", - " # window shorter than the longest window in dataset, pad to same length\n", - " window = np.concatenate([window, np.ones((length_diff, window.shape[1])) * pad_value], axis=0)\n", - " labels = np.concatenate([labels, np.ones(length_diff) * pad_value], axis=0)\n", - " pad_mask = np.concatenate([pad_mask, np.zeros(length_diff)], axis=0)\n", - "\n", - " not_labeled = np.argwhere(np.isnan(labels))\n", - " if len(not_labeled) > 0:\n", - " labels[not_labeled] = -1\n", - " pad_mask[not_labeled] = 0\n", - "\n", - " pad_mask = pad_mask.astype(bool)\n", - " labels = labels.astype(np.float32)\n", - " data = window.astype(np.float32)\n", - "\n", - " return from_numpy(data), from_numpy(labels), from_numpy(pad_mask)\n", - "\n", - " def get_balance(self) -> list:\n", - " \"\"\"Return the weight balance for the split of interest.\n", - "\n", - " Returns:\n", - " Weights for each label.\n", - " \"\"\"\n", - " counts = self.outcome_df[self.vars[\"LABEL\"]].value_counts()\n", - " return list((1 / counts) * np.sum(counts) / counts.shape[0])\n", - "\n", - " def get_data_and_labels(self) -> Tuple[np.array, np.array]:\n", - " \"\"\"Function to return all the data and labels aligned at once.\n", - "\n", - " We use this function for the ML methods which don't require an iterator.\n", - "\n", - " Returns:\n", - " A Tuple containing data points and label for the split.\n", - " \"\"\"\n", - " labels = self.outcome_df[self.vars[\"LABEL\"]].to_numpy().astype(float)\n", - " rep = self.features_df\n", - " if len(labels) == self.num_stays:\n", - " # order of groups could be random, we make sure not to change it\n", - " rep = rep.groupby(level=self.vars[\"GROUP\"], sort=False).last()\n", - " rep = rep.to_numpy().astype(float)\n", - " \n", - " return rep, labels\n", - "\n", - " def to_tensor(self):\n", - " data, labels = self.get_data_and_labels()\n", - " return from_numpy(data).to(float32), from_numpy(labels).to(float32)\n", - "\n", - "@gin.configurable(\"PredictionDatasetTFT\")\n", - "class PredictionDatasetTFT(PredictionDataset):\n", - " \"\"\"Subclass of prediction dataset for TFT as we need to define if variables are cont,static,known or observed.\n", - "\n", - " Args:\n", - " ram_cache (bool, optional): Whether the complete dataset should be stored in ram. Defaults to True.\n", - " \"\"\"\n", - "\n", - " def __init__(self, *args, ram_cache: bool = True, **kwargs):\n", - " super().__init__(*args,ram_cache = True, **kwargs)\n", - " \n", - "\n", - " def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]:\n", - " \"\"\"Function to sample from the data split of choice. Used for TFT.\n", - "\n", - " Args:\n", - " idx: A specific row index to sample.\n", - "\n", - " Returns:\n", - " A sample from the data, consisting of data, labels and padding mask.\n", - " \"\"\"\n", - " if self._cached_dataset is not None:\n", - " return self._cached_dataset[idx]\n", - "\n", - " pad_value = 0.0\n", - " stay_id = self.outcome_df.index.unique()[idx] # [self.vars[\"GROUP\"]]\n", - "\n", - " \n", - " # We need to be sure that tensors are returned in the correct order to be processed correclty by tft\n", - " tensors = [[] for _ in range(8)]\n", - " for var in self.features_df.columns:\n", - " if var == 'sex' :\n", - " tensors[0].append(self.features_df.loc[stay_id:stay_id][var].to_numpy())\n", - " elif var == 'age' or var== 'height' or var== 'weight':\n", - " tensors[1].append(self.features_df.loc[stay_id:stay_id][var].to_numpy())\n", - " else :\n", - " tensors[5].append(self.features_df.loc[stay_id:stay_id][var].to_numpy())\n", - " \n", - " \n", - " tensors[6].extend(self.outcome_df.loc[stay_id:stay_id][self.vars[\"LABEL\"]].to_numpy(dtype=float))\n", - " tensors[7].append(np.asarray([stay_id]))\n", - " \n", - " window_shape0=np.shape(tensors[0])[1]\n", - " # window = self.features_df.loc[stay_id:stay_id].to_numpy()\n", - " # labels = self.outcome_df.loc[stay_id:stay_id][self.vars[\"LABEL\"]].to_numpy(dtype=float)\n", - " if len(tensors[6]) == 1:\n", - " # only one label per stay, align with window\n", - " tensors[6] = np.concatenate([np.empty(window_shape0 - 1) * np.nan, tensors[6]], axis=0)\n", - "\n", - " length_diff = self.maxlen - window_shape0\n", - " pad_mask = np.ones(window_shape0)\n", - "\n", - " # Padding the array to fulfill size requirement\n", - " if length_diff > 0:\n", - " # window shorter than the longest window in dataset, pad to same length\n", - " tensors[0]= np.concatenate([tensors[0], np.ones((length_diff, np.shape(tensors[0])[0])) * pad_value], axis=0)\n", - " tensors[1]= np.concatenate([tensors[1], np.ones((length_diff, np.shape(tensors[1])[0])) * pad_value], axis=0)\n", - " # tensors[2]= np.concatenate([tensors[2], np.ones((length_diff, np.shape(tensors[2])[0])) * pad_value], axis=0)\n", - " # tensors[3]= np.concatenate([tensors[3], np.ones((length_diff, np.shape(tensors[3])[0])) * pad_value], axis=0)\n", - " # tensors[4]= np.concatenate([tensors[4], np.ones((length_diff, np.shape(tensors[4])[0])) * pad_value], axis=0)\n", - " tensors[5]= np.concatenate([tensors[5], np.ones((length_diff, np.shape(tensors[5])[0])) * pad_value], axis=0)\n", - " tensors[7]= np.concatenate([tensors[7], np.ones((length_diff, np.shape(tensors[7])[0])) * stay_id], axis=0)\n", - " # window = np.concatenate([window, np.ones((length_diff, window.shape[1])) * pad_value], axis=0)\n", - " \n", - " tensors[6] = np.concatenate([tensors[6], np.ones(length_diff) * pad_value], axis=0)\n", - " pad_mask = np.concatenate([pad_mask, np.zeros(length_diff)], axis=0)\n", - "\n", - " not_labeled = np.argwhere(np.isnan(tensors[6]))\n", - " if len(not_labeled) > 0:\n", - " tensors[6][not_labeled] = -1\n", - " pad_mask[not_labeled] = 0\n", - " tensors[6]=[tensors[6]]\n", - " pad_mask = pad_mask.astype(bool)\n", - " # data = window.astype(np.float32)\n", - " tensors = (from_numpy(np.array(tensor)).to(float32) for tensor in tensors)\n", - " tensors = [stack((x,), dim=-1) if x.numel() > 0 else empty(0) for x in tensors]\n", - " \n", - " return OrderedDict(zip(FEAT_NAMES, tensors)),from_numpy(pad_mask)\n", - "\n", - " \n", - "\n", - "\n", - "\n", - "@gin.configurable(\"ImputationDataset\")\n", - "class ImputationDataset(CommonDataset):\n", - " \"\"\"Subclass of Common Dataset that contains data for imputation models.\"\"\"\n", - "\n", - " def __init__(\n", - " self,\n", - " data: Dict[str, DataFrame],\n", - " split: str = Split.train,\n", - " vars: Dict[str, str] = gin.REQUIRED,\n", - " mask_proportion=0.3,\n", - " mask_method=\"MCAR\",\n", - " mask_observation_proportion=0.3,\n", - " ram_cache: bool = True,\n", - " ):\n", - " \"\"\"\n", - " Args:\n", - " data (Dict[str, DataFrame]): data to use\n", - " split (str, optional): split to apply. Defaults to Split.train.\n", - " vars (Dict[str, str], optional): contains names of columns in the data. Defaults to gin.REQUIRED.\n", - " mask_proportion (float, optional): proportion to artificially mask for amputation. Defaults to 0.3.\n", - " mask_method (str, optional): masking mechanism. Defaults to \"MCAR\".\n", - " mask_observation_proportion (float, optional): poportion of the observed data to be masked. Defaults to 0.3.\n", - " ram_cache (bool, optional): if the dataset should be completely stored in ram and not generated on the fly during\n", - " training. Defaults to True.\n", - " \"\"\"\n", - " super().__init__(data, split, vars, grouping_segment=Segment.static)\n", - " self.amputated_values, self.amputation_mask = ampute_data(\n", - " self.features_df, mask_method, mask_proportion, mask_observation_proportion\n", - " )\n", - " self.amputation_mask = (self.amputation_mask + self.features_df.isna().values).bool()\n", - " self.amputation_mask = DataFrame(self.amputation_mask, columns=self.vars[Segment.dynamic])\n", - " self.amputation_mask[self.vars[\"GROUP\"]] = self.features_df.index\n", - " self.amputation_mask.set_index(self.vars[\"GROUP\"], inplace=True)\n", - "\n", - " self.target_missingness_mask = self.features_df.isna()\n", - " self.features_df.fillna(0, inplace=True)\n", - " self.ram_cache(ram_cache)\n", - "\n", - " def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]:\n", - " \"\"\"Function to sample from the data split of choice.\n", - "\n", - " Used for deep learning implementations.\n", - "\n", - " Args:\n", - " idx: A specific row index to sample.\n", - "\n", - " Returns:\n", - " A sample from the data, consisting of data, labels and padding mask.\n", - " \"\"\"\n", - " if self._cached_dataset is not None:\n", - " return self._cached_dataset[idx]\n", - " stay_id = self.grouping_df.iloc[idx].name\n", - "\n", - " # slice to make sure to always return a DF\n", - " window = self.features_df.loc[stay_id:stay_id, self.vars[Segment.dynamic]]\n", - " window_missingness_mask = self.target_missingness_mask.loc[stay_id:stay_id, self.vars[Segment.dynamic]]\n", - " amputated_window = self.amputated_values.loc[stay_id:stay_id, self.vars[Segment.dynamic]]\n", - " amputation_mask = self.amputation_mask.loc[stay_id:stay_id, self.vars[Segment.dynamic]]\n", - "\n", - " return (\n", - " from_numpy(amputated_window.values).to(float32),\n", - " from_numpy(amputation_mask.values).to(float32),\n", - " from_numpy(window.values).to(float32),\n", - " from_numpy(window_missingness_mask.values).to(float32),\n", - " )\n", - "\n", - "\n", - "@gin.configurable(\"ImputationPredictionDataset\")\n", - "class ImputationPredictionDataset(Dataset):\n", - " \"\"\"Subclass of torch dataset that represents data with missingness for imputation.\n", - "\n", - " Args:\n", - " data (DataFrame): dict of the different splits of the data\n", - " grouping_column (str, optional): column that is used for grouping. Defaults to \"stay_id\".\n", - " select_columns (List[str], optional): the columns to serve as input for the imputation model. Defaults to None.\n", - " ram_cache (bool, optional): wether the dataset should be stored in ram. Defaults to True.\n", - " \"\"\"\n", - "\n", - " def __init__(\n", - " self,\n", - " data: DataFrame,\n", - " grouping_column: str = \"stay_id\",\n", - " select_columns: List[str] = None,\n", - " ram_cache: bool = True,\n", - " ):\n", - " self.dyn_df = data\n", - "\n", - " if select_columns is not None:\n", - " self.dyn_df = self.dyn_df[list(select_columns) + grouping_column]\n", - "\n", - " if grouping_column is not None:\n", - " self.dyn_df = self.dyn_df.set_index(grouping_column)\n", - " else:\n", - " self.dyn_df = data\n", - "\n", - " # calculate basic info for the data\n", - " self.group_indices = self.dyn_df.index.unique()\n", - " self.maxlen = self.dyn_df.groupby(grouping_column).size().max()\n", - "\n", - " self._cached_dataset = None\n", - " if ram_cache:\n", - " logging.info(\"Caching dataset in ram.\")\n", - " self._cached_dataset = [self[i] for i in range(len(self))]\n", - "\n", - " def __len__(self) -> int:\n", - " \"\"\"Returns number of stays in the data.\n", - "\n", - " Returns:\n", - " number of stays in the data\n", - " \"\"\"\n", - " return self.group_indices.shape[0]\n", - "\n", - " def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]:\n", - " \"\"\"Function to sample from the data split of choice.\n", - "\n", - " Used for deep learning implementations.\n", - "\n", - " Args:\n", - " idx: A specific row index to sample.\n", - "\n", - " Returns:\n", - " A sample from the data, consisting of data, labels and padding mask.\n", - " \"\"\"\n", - " if self._cached_dataset is not None:\n", - " return self._cached_dataset[idx]\n", - " stay_id = self.group_indices[idx]\n", - "\n", - " # slice to make sure to always return a DF\n", - " window = self.dyn_df.loc[stay_id:stay_id, :]\n", - "\n", - " return from_numpy(window.values).to(float32)\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "504fd6c4", - "metadata": {}, - "outputs": [ - { - "ename": "KeyError", - "evalue": "'train'\n In call to configurable 'PredictionDataset' ()", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", - "Input \u001b[0;32mIn [9]\u001b[0m, in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0m train_dataset \u001b[38;5;241m=\u001b[39m \u001b[43mPredictionDataset\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdyn\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msplit\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mSplit\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtrain\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/opt/anaconda3/lib/python3.9/site-packages/gin/config.py:1605\u001b[0m, in \u001b[0;36m_make_gin_wrapper..gin_wrapper\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 1603\u001b[0m scope_info \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m in scope \u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;241m.\u001b[39mformat(scope_str) \u001b[38;5;28;01mif\u001b[39;00m scope_str \u001b[38;5;28;01melse\u001b[39;00m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m'\u001b[39m\n\u001b[1;32m 1604\u001b[0m err_str \u001b[38;5;241m=\u001b[39m err_str\u001b[38;5;241m.\u001b[39mformat(name, fn_or_cls, scope_info)\n\u001b[0;32m-> 1605\u001b[0m \u001b[43mutils\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43maugment_exception_message_and_reraise\u001b[49m\u001b[43m(\u001b[49m\u001b[43me\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43merr_str\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/opt/anaconda3/lib/python3.9/site-packages/gin/utils.py:41\u001b[0m, in \u001b[0;36maugment_exception_message_and_reraise\u001b[0;34m(exception, message)\u001b[0m\n\u001b[1;32m 39\u001b[0m proxy \u001b[38;5;241m=\u001b[39m ExceptionProxy()\n\u001b[1;32m 40\u001b[0m ExceptionProxy\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__qualname__\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mtype\u001b[39m(exception)\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__qualname__\u001b[39m\n\u001b[0;32m---> 41\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m proxy\u001b[38;5;241m.\u001b[39mwith_traceback(exception\u001b[38;5;241m.\u001b[39m__traceback__) \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;28mNone\u001b[39m\n", - "File \u001b[0;32m~/opt/anaconda3/lib/python3.9/site-packages/gin/config.py:1582\u001b[0m, in \u001b[0;36m_make_gin_wrapper..gin_wrapper\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 1579\u001b[0m new_kwargs\u001b[38;5;241m.\u001b[39mupdate(kwargs)\n\u001b[1;32m 1581\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m-> 1582\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mnew_args\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mnew_kwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1583\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e: \u001b[38;5;66;03m# pylint: disable=broad-except\u001b[39;00m\n\u001b[1;32m 1584\u001b[0m err_str \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m'\u001b[39m\n", - "Input \u001b[0;32mIn [3]\u001b[0m, in \u001b[0;36mPredictionDataset.__init__\u001b[0;34m(self, ram_cache, *args, **kwargs)\u001b[0m\n\u001b[1;32m 113\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__init__\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;241m*\u001b[39margs, ram_cache: \u001b[38;5;28mbool\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mTrue\u001b[39;00m, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[0;32m--> 114\u001b[0m \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[38;5;21;43m__init__\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mgrouping_segment\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mSegment\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43moutcome\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 115\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39moutcome_df \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mgrouping_df\n\u001b[1;32m 116\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mram_cache(ram_cache)\n", - "Input \u001b[0;32mIn [3]\u001b[0m, in \u001b[0;36mCommonDataset.__init__\u001b[0;34m(self, data, split, vars, grouping_segment)\u001b[0m\n\u001b[1;32m 66\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39msplit \u001b[38;5;241m=\u001b[39m split\n\u001b[1;32m 67\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mvars \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mvars\u001b[39m\n\u001b[0;32m---> 68\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mgrouping_df \u001b[38;5;241m=\u001b[39m \u001b[43mdata\u001b[49m\u001b[43m[\u001b[49m\u001b[43msplit\u001b[49m\u001b[43m]\u001b[49m[grouping_segment]\u001b[38;5;241m.\u001b[39mset_index(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mvars[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mGROUP\u001b[39m\u001b[38;5;124m\"\u001b[39m])\n\u001b[1;32m 69\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfeatures_df \u001b[38;5;241m=\u001b[39m (\n\u001b[1;32m 70\u001b[0m \u001b[38;5;66;03m#drops time coulmn and sets index to stay_id\u001b[39;00m\n\u001b[1;32m 71\u001b[0m data[split][Segment\u001b[38;5;241m.\u001b[39mfeatures]\u001b[38;5;241m.\u001b[39mset_index(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mvars[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mGROUP\u001b[39m\u001b[38;5;124m\"\u001b[39m])\u001b[38;5;241m.\u001b[39mdrop(labels\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mvars[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mSEQUENCE\u001b[39m\u001b[38;5;124m\"\u001b[39m], axis\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m1\u001b[39m)\n\u001b[1;32m 72\u001b[0m )\n\u001b[1;32m 74\u001b[0m \u001b[38;5;66;03m# calculate basic info for the data\u001b[39;00m\n", - "File \u001b[0;32m~/opt/anaconda3/lib/python3.9/site-packages/pandas/core/frame.py:3505\u001b[0m, in \u001b[0;36mDataFrame.__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 3503\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mcolumns\u001b[38;5;241m.\u001b[39mnlevels \u001b[38;5;241m>\u001b[39m \u001b[38;5;241m1\u001b[39m:\n\u001b[1;32m 3504\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_getitem_multilevel(key)\n\u001b[0;32m-> 3505\u001b[0m indexer \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcolumns\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget_loc\u001b[49m\u001b[43m(\u001b[49m\u001b[43mkey\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 3506\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m is_integer(indexer):\n\u001b[1;32m 3507\u001b[0m indexer \u001b[38;5;241m=\u001b[39m [indexer]\n", - "File \u001b[0;32m~/opt/anaconda3/lib/python3.9/site-packages/pandas/core/indexes/base.py:3631\u001b[0m, in \u001b[0;36mIndex.get_loc\u001b[0;34m(self, key, method, tolerance)\u001b[0m\n\u001b[1;32m 3629\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_engine\u001b[38;5;241m.\u001b[39mget_loc(casted_key)\n\u001b[1;32m 3630\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m err:\n\u001b[0;32m-> 3631\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m(key) \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01merr\u001b[39;00m\n\u001b[1;32m 3632\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m:\n\u001b[1;32m 3633\u001b[0m \u001b[38;5;66;03m# If we have a listlike key, _check_indexing_error will raise\u001b[39;00m\n\u001b[1;32m 3634\u001b[0m \u001b[38;5;66;03m# InvalidIndexError. Otherwise we fall through and re-raise\u001b[39;00m\n\u001b[1;32m 3635\u001b[0m \u001b[38;5;66;03m# the TypeError.\u001b[39;00m\n\u001b[1;32m 3636\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_check_indexing_error(key)\n", - "\u001b[0;31mKeyError\u001b[0m: 'train'\n In call to configurable 'PredictionDataset' ()" - ] - } - ], - "source": [ - "train_dataset = PredictionDataset(dyn, split=Split.train)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c7f48f70", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "yaib", - "language": "python", - "name": "yaib" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.13" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/icu_benchmarks/data/constants.py b/icu_benchmarks/data/constants.py index 4e4a9c34..031f35ca 100644 --- a/icu_benchmarks/data/constants.py +++ b/icu_benchmarks/data/constants.py @@ -15,7 +15,3 @@ class VarType: group = "GROUP" sequence = "SEQUENCE" label = "LABEL" - - -class FeatType: - FEAT_NAMES = ["s_cat", "s_cont", "k_cat", "k_cont", "o_cat", "o_cont", "target", "id"] diff --git a/icu_benchmarks/imputation/diffwave.py b/icu_benchmarks/imputation/diffwave.py index 437303ed..458c2c5e 100644 --- a/icu_benchmarks/imputation/diffwave.py +++ b/icu_benchmarks/imputation/diffwave.py @@ -330,7 +330,7 @@ def forward(self, input_data): cond = self.cond_conv(cond) h += cond - out = torch.tanh(h[:, : self.res_channels, :]) * torch.sigmoid(h[:, self.res_channels:, :]) + out = torch.tanh(h[:, :self.res_channels, :]) * torch.sigmoid(h[:, self.res_channels:, :]) res = self.res_conv(out) assert x.shape == res.shape diff --git a/icu_benchmarks/models/constants.py b/icu_benchmarks/models/constants.py index 224355eb..cd03bcd0 100644 --- a/icu_benchmarks/models/constants.py +++ b/icu_benchmarks/models/constants.py @@ -19,6 +19,8 @@ ) from torchmetrics.classification import ( AUROC, + AveragePrecision as TorchMetricsAveragePrecision, + PrecisionRecallCurve as TorchMetricsPrecisionRecallCurve, CalibrationError, F1Score, ) @@ -28,6 +30,7 @@ BalancedAccuracy, MAE, JSD, + BinaryFairnessWrapper, ) @@ -66,11 +69,11 @@ class DLMetrics: BINARY_CLASSIFICATION_TORCHMETRICS = { "AUC": AUROC(task="binary"), - # "PR": TorchMetricsAveragePrecision(task="binary"), - # "PrecisionRecallCurve": TorchMetricsPrecisionRecallCurve(task="binary"), + "PR": TorchMetricsAveragePrecision(task="binary"), + "PrecisionRecallCurve": TorchMetricsPrecisionRecallCurve(task="binary"), "Calibration_Error": CalibrationError(task="binary", n_bins=10), "F1": F1Score(task="binary", num_classes=2), - # "Binary_Fairness": BinaryFairnessWrapper(num_groups=2, task="demographic_parity", group_name="sex"), + "Binary_Fairness": BinaryFairnessWrapper(num_groups=2, task="demographic_parity", group_name="sex"), } MULTICLASS_CLASSIFICATION = { diff --git a/icu_benchmarks/models/custom_metrics.py b/icu_benchmarks/models/custom_metrics.py index d1526e6d..fd88699f 100644 --- a/icu_benchmarks/models/custom_metrics.py +++ b/icu_benchmarks/models/custom_metrics.py @@ -6,7 +6,7 @@ from sklearn.calibration import calibration_curve from scipy.spatial.distance import jensenshannon from torchmetrics.classification import BinaryFairness -from icu_benchmarks.models.similarity_func import correlation_spearman, cosine +from quantus.functions.similarity_func import correlation_spearman, cosine """" This file contains custom metrics that can be added to YAIB. diff --git a/icu_benchmarks/models/similarity_func.py b/icu_benchmarks/models/similarity_func.py deleted file mode 100644 index bc49850f..00000000 --- a/icu_benchmarks/models/similarity_func.py +++ /dev/null @@ -1,288 +0,0 @@ -"""This module holds a collection of similarity functions i.e., -ways to measure the distance between two inputs (or explanations).""" - -# This file is part of Quantus. -# Quantus is free software: you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free Software Foundation, -# either version 3 of the License, or (at your option) any later version. -# Quantus is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. -# You should have received a copy of the GNU Lesser General Public License along with Quantus. -# If not, see . -# Quantus project URL: . -# Quantus project URL: https://github.com/understandable-machine-intelligence-lab/Quantus - -from typing import Union - -import numpy as np -import scipy -import skimage - - -def correlation_spearman(a: np.array, b: np.array, **kwargs) -> float: - """ - Calculate Spearman rank of two images (or explanations). - - Parameters - ---------- - a: np.ndarray - The first array to use for similarity scoring. - b: np.ndarray - The second array to use for similarity scoring. - kwargs: optional - Keyword arguments. - - Returns - ------- - float - The similarity score. - """ - return scipy.stats.spearmanr(a, b)[0] - - -def correlation_pearson(a: np.array, b: np.array, **kwargs) -> float: - """ - Calculate Pearson correlation of two images (or explanations). - - Parameters - ---------- - a: np.ndarray - The first array to use for similarity scoring. - b: np.ndarray - The second array to use for similarity scoring. - kwargs: optional - Keyword arguments. - - Returns - ------- - float - The similarity score. - """ - return scipy.stats.pearsonr(a, b)[0] - - -def correlation_kendall_tau(a: np.array, b: np.array, **kwargs) -> float: - """ - Calculate Kendall Tau correlation of two images (or explanations). - - Parameters - ---------- - a: np.ndarray - The first array to use for similarity scoring. - b: np.ndarray - The second array to use for similarity scoring. - kwargs: optional - Keyword arguments. - - Returns - ------- - float - The similarity score. - """ - return scipy.stats.kendalltau(a, b)[0] - - -def distance_euclidean(a: np.array, b: np.array, **kwargs) -> float: - """ - Calculate Euclidean distance of two images (or explanations). - - Parameters - ---------- - a: np.ndarray - The first array to use for similarity scoring. - b: np.ndarray - The second array to use for similarity scoring. - kwargs: optional - Keyword arguments. - - Returns - ------- - float - The similarity score. - """ - return scipy.spatial.distance.euclidean(u=a, v=b) - - -def distance_manhattan(a: np.array, b: np.array, **kwargs) -> float: - """ - Calculate Manhattan distance of two images (or explanations). - - Parameters - ---------- - a: np.ndarray - The first array to use for similarity scoring. - b: np.ndarray - The second array to use for similarity scoring. - kwargs: optional - Keyword arguments. - - Returns - ------- - float - The similarity score. - """ - return scipy.spatial.distance.cityblock(u=a, v=b) - - -def distance_chebyshev(a: np.array, b: np.array, **kwargs) -> float: - """ - Calculate Chebyshev distance of two images (or explanations). - - Parameters - ---------- - a: np.ndarray - The first array to use for similarity scoring. - b: np.ndarray - The second array to use for similarity scoring. - kwargs: optional - Keyword arguments. - - Returns - ------- - float - The similarity score. - """ - return scipy.spatial.distance.chebyshev(u=a, v=b) - - -def lipschitz_constant(a: np.array, b: np.array, c: Union[np.array, None], d: Union[np.array, None], **kwargs) -> float: - """ - Calculate non-negative local Lipschitz abs(||a-b||/||c-d||), where a,b can be f(x) or a(x) and c,d is x. - - For numerical stability, a small value is added to division. - - Parameters - ---------- - a: np.ndarray - The first array to use for similarity scoring. - b: np.ndarray - The second array to use for similarity scoring. - c: np.ndarray - The third array to use for similarity scoring. - d: np.ndarray - The fourth array to use for similarity scoring. - kwargs: optional - Keyword arguments. - - Returns - ------- - float - The similarity score. - """ - eps = 1e-10 - - d1 = kwargs.get("norm_numerator", distance_manhattan) - d2 = kwargs.get("norm_denominator", distance_euclidean) - - if np.shape(a) == (): - return float(abs(a - b) / (d2(c, d) + eps)) - else: - return float(d1(a, b) / (d2(a=c, b=d) + eps)) - - -def abs_difference(a: np.array, b: np.array, **kwargs) -> float: - """ - Calculate the mean of the absolute differences between two images (or explanations). - - Parameters - ---------- - a: np.ndarray - The first array to use for similarity scoring. - b: np.ndarray - The second array to use for similarity scoring. - kwargs: optional - Keyword arguments. - - Returns - ------- - float - The similarity score. - """ - return np.mean(abs(a - b)) - - -def squared_difference(a: np.array, b: np.array, **kwargs) -> float: - """ - Calculate the sqaured differences between two images (or explanations). - - Parameters - ---------- - a: np.ndarray - The first array to use for similarity scoring. - b: np.ndarray - The second array to use for similarity scoring. - kwargs: optional - Keyword arguments. - - Returns - ------- - float - The similarity score. - """ - return np.sum((a - b) ** 2) - - -def cosine(a: np.array, b: np.array, **kwargs) -> float: - """ - Calculate Cosine of two images (or explanations). - - Parameters - ---------- - a: np.ndarray - The first array to use for similarity scoring. - b: np.ndarray - The second array to use for similarity scoring. - kwargs: optional - Keyword arguments. - - Returns - ------- - float - The similarity score. - """ - return scipy.spatial.distance.cosine(u=a, v=b) - - -def ssim(a: np.array, b: np.array, **kwargs) -> float: - """ - Calculate Structural Similarity Index Measure of two images (or explanations). - - Parameters - ---------- - a: np.ndarray - The first array to use for similarity scoring. - b: np.ndarray - The second array to use for similarity scoring. - kwargs: optional - Keyword arguments. - - Returns - ------- - float - The similarity score. - """ - max_point, min_point = np.max(np.concatenate([a, b])), np.min(np.concatenate([a, b])) - data_range = float(np.abs(max_point - min_point)) - return skimage.metrics.structural_similarity(im1=a, im2=b, win_size=kwargs.get("win_size", None), data_range=data_range) - - -def difference(a: np.array, b: np.array, **kwargs) -> np.array: - """ - Calculate the difference between two images (or explanations). - - Parameters - ---------- - a: np.ndarray - The first array to use for similarity scoring. - b: np.ndarray - The second array to use for similarity scoring. - kwargs: optional - Keyword arguments. - - Returns - ------- - np.array - The difference in each element. - """ - return a - b diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 5792a5bb..79f13ef4 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -19,7 +19,7 @@ from icu_benchmarks.models.constants import MLMetrics, DLMetrics from icu_benchmarks.contants import RunMode import matplotlib.pyplot as plt -from icu_benchmarks.models.similarity_func import correlation_spearman, cosine +from quantus.functions.similarity_func import correlation_spearman, cosine import captum from captum._utils.models.linear_model import SkLearnLasso diff --git a/icu_benchmarks/run_utils.py b/icu_benchmarks/run_utils.py index 7d57bc2a..dac9730d 100644 --- a/icu_benchmarks/run_utils.py +++ b/icu_benchmarks/run_utils.py @@ -16,7 +16,6 @@ from icu_benchmarks.models.utils import JsonResultLoggingEncoder from icu_benchmarks.wandb_utils import wandb_log import numpy as np -import matplotlib.pyplot as plt def build_parser() -> ArgumentParser: @@ -27,132 +26,39 @@ def build_parser() -> ArgumentParser: """ parser = ArgumentParser(description="Framework for benchmarking ML/DL models on ICU data") - parser.add_argument( - "-d", - "--data-dir", - required=True, - type=Path, - help="Path to the parquet data directory.", - ) - parser.add_argument( - "-t", - "--task", - default="BinaryClassification", - required=True, - help="Name of the task gin.", - ) + parser.add_argument("-d", "--data-dir", required=True, type=Path, help="Path to the parquet data directory.") + parser.add_argument("-t", "--task", default="BinaryClassification", required=True, help="Name of the task gin.") parser.add_argument("-n", "--name", help="Name of the (target) dataset.") parser.add_argument("-tn", "--task-name", help="Name of the task, used for naming experiments.") parser.add_argument("-m", "--model", default="LGBMClassifier", help="Name of the model gin.") parser.add_argument("-e", "--experiment", help="Name of the experiment gin.") - parser.add_argument( - "-l", - "--log-dir", - default=Path("../yaib_logs/"), - type=Path, - help="Log directory for model weights.", - ) - parser.add_argument( - "-s", - "--seed", - default=1234, - type=int, - help="Random seed for processing, tuning and training.", - ) - parser.add_argument( - "-v", - "--verbose", - default=False, - action=BOA, - help="Set to log verbosly. Disable for clean logs.", - ) + parser.add_argument("-l", "--log-dir", default=Path("../yaib_logs/"), type=Path, help="Log directory for model weights.") + parser.add_argument("-s", "--seed", default=1234, type=int, help="Random seed for processing, tuning and training.") + parser.add_argument("-v", "--verbose", default=False, action=BOA, help="Set to log verbosly. Disable for clean logs.") parser.add_argument("--cpu", default=False, action=BOA, help="Set to use CPU.") parser.add_argument("-db", "--debug", default=False, action=BOA, help="Set to load less data.") parser.add_argument("--reproducible", default=True, action=BOA, help="Make torch reproducible.") - parser.add_argument( - "-lc", - "--load_cache", - default=False, - action=BOA, - help="Set to load generated data cache.", - ) - parser.add_argument( - "-gc", - "--generate_cache", - default=False, - action=BOA, - help="Set to generate data cache.", - ) + parser.add_argument("-lc", "--load_cache", default=False, action=BOA, help="Set to load generated data cache.") + parser.add_argument("-gc", "--generate_cache", default=False, action=BOA, help="Set to generate data cache.") parser.add_argument("-p", "--preprocessor", type=Path, help="Load custom preprocessor from file.") parser.add_argument("-pl", "--plot", action=BOA, help="Generate common plots.") - parser.add_argument( - "-wd", - "--wandb-sweep", - action="store_true", - help="Activates wandb hyper parameter sweep.", - ) - parser.add_argument( - "-imp", - "--pretrained-imputation", - type=str, - help="Path to pretrained imputation model.", - ) + parser.add_argument("-wd", "--wandb-sweep", action="store_true", help="Activates wandb hyper parameter sweep.") + parser.add_argument("-imp", "--pretrained-imputation", type=str, help="Path to pretrained imputation model.") parser.add_argument("-hp", "--hyperparams", nargs="+", help="Hyperparameters for model.") parser.add_argument("--tune", default=False, action=BOA, help="Find best hyperparameters.") parser.add_argument("--hp-checkpoint", type=Path, help="Use previous hyperparameter checkpoint.") parser.add_argument("--eval", default=False, action=BOA, help="Only evaluate model, skip training.") - parser.add_argument( - "--complete-train", - default=False, - action=BOA, - help="Use all data to train model, skip testing.", - ) - parser.add_argument( - "-ft", - "--fine-tune", - default=None, - type=int, - help="Finetune model with amount of train data.", - ) + parser.add_argument("--complete-train", default=False, action=BOA, help="Use all data to train model, skip testing.") + parser.add_argument("-ft", "--fine-tune", default=None, type=int, help="Finetune model with amount of train data.") parser.add_argument("-sn", "--source-name", type=Path, help="Name of the source dataset.") parser.add_argument("--source-dir", type=Path, help="Directory containing gin and model weights.") + parser.add_argument("-sa", "--samples", type=int, default=None, help="Number of samples to use for evaluation.") + parser.add_argument("--explain", default=False, action=BOA, help="Provide explaintations for predictions.") + parser.add_argument("--pytorch-forecasting", default=False, action=BOA, help="use pytorch forecasting library ") + parser.add_argument("--XAI_metric", default=False, action=BOA, help="Compare explantations ") + parser.add_argument("--random_labels", default=False, action=BOA, help="randmize target labels") parser.add_argument( - "-sa", - "--samples", - type=int, - default=None, - help="Number of samples to use for evaluation.", - ) - parser.add_argument( - "--explain", - default=False, - action=BOA, - help="Provide explaintations for predictions.", - ) - parser.add_argument( - "--pytorch-forecasting", - default=False, - action=BOA, - help="use pytorch forecasting library ", - ) - parser.add_argument( - "--XAI_metric", - default=False, - action=BOA, - help="Compare explantations ", - ) - parser.add_argument( - "--random_labels", - default=False, - action=BOA, - help="randmize target labels", - ) - - parser.add_argument( - "--random_model", - default=Path("."), - type=Path, - help="Log directory for model weights that is trained on random labels", + "--random_model", default=Path("."), type=Path, help="path for model weights that is trained on random labels" ) return parser @@ -272,85 +178,6 @@ def aggregate_results(log_dir: Path, execution_time: timedelta = None): wandb_log(json.loads(json.dumps(accumulated_metrics, cls=JsonResultLoggingEncoder))) -def plot_XAI_Metrics(accumulated_metrics, log_dir_plots): - groups = {} - for key in accumulated_metrics["avg"]: - if key in ["loss", "MAE"]: - continue - suffix = key.split("_")[-1] - if suffix not in groups: - groups[suffix] = [] - groups[suffix].append(key) - - # Define a dictionary for legend labels - legend_labels = { - "IG": "Integrated Gradient", - "G": "Gradient", - "R": "Random", - "FA": "Feature Ablation", - "Att": "Attention", - "VSN": "Variable Selection Network", - "L": "Lime", - } - colors = [ - "navy", - "skyblue", - "crimson", - "salmon", - "teal", - "orange", - "darkgreen", - "lightgreen", - ] - - # Plotting - num_groups = len(groups) - fig, axs = plt.subplots(num_groups, 1, figsize=(10, num_groups * 5)) - - # Custom handles for the legend - # handles = [plt.Rectangle((0, 0), 1, 1, color="none", label=f"{key}: {value}") for key, value in legend_labels.items()] - - for i, (suffix, keys) in enumerate(groups.items()): - ax = axs[i] if num_groups > 1 else axs - # Extract values and errors - avg_values = [accumulated_metrics["avg"][key] for key in keys] - ci_lower = [accumulated_metrics["CI_0.95"][key][0] for key in keys] - ci_upper = [accumulated_metrics["CI_0.95"][key][1] for key in keys] - ci_error = [np.abs([a - b, c - a]) for a, b, c in zip(avg_values, ci_lower, ci_upper)] - - # Sort by absolute values of avg_values - sorted_indices = np.argsort([np.abs(val) for val in avg_values])[::-1] # Indices to sort in descending order - sorted_keys = np.array(keys)[sorted_indices] - sorted_avg_values = np.array(avg_values)[sorted_indices] - sorted_ci_error = np.array(ci_error)[sorted_indices] - - # Plot bars - bars = ax.bar( - sorted_keys, - np.abs(sorted_avg_values), - yerr=np.array(sorted_ci_error).T, - capsize=5, - color=colors, - ) - - # Set titles and labels - title_suffix = sorted_keys[0].split("_")[1] - ax.set_title(f'Metric: "{title_suffix}"') - ax.set_ylabel("Values") - ax.axhline(0, color="grey", linewidth=0.8) - ax.grid(axis="y") - - # Set x-ticks - ax.set_xticks(sorted_keys) - ax.set_xticklabels([key.split("_")[0] for key in sorted_keys], rotation=45, ha="right") - # Create a custom legend for each subplot - custom_labels = [legend_labels[key.split("_")[0]] for key in sorted_keys] - ax.legend(bars, custom_labels, loc="upper right") - - plt.tight_layout() - plt.savefig(log_dir_plots / "metrics_plot.png", bbox_inches="tight") - - def name_datasets(train="default", val="default", test="default"): """Names the datasets for logging (optional).""" gin.bind_parameter("train_common.dataset_names", {"train": train, "val": val, "test": test}) diff --git a/saved_dictionary.pkl b/saved_dictionary.pkl deleted file mode 100644 index 8a15fe6a723a8fdaa797ab67c82361959fa6c0a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20818976 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 z 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ΦTgGlbb)SHPIu3fdhCf0|5{K0T2KI z5C8$U5|HO*Ku3KVGIXyb3J16PEAkBjsS%LZ2|>RYoUrLH2^_$Ig&(9=*YKTv1bCT; z!RlN(R`J+ZB1jnoKmY`sNr3n9AJ{i_;_Df|mB7JW9L#QQ%-+`n@r${?4$2MXCgQ$O zf&A9&b`pVug@`H3i00cn5Z3M9XTiyC^ zr*U$dpCiBe2wV>R{loxrTe4d9S*v`NQ}%7rdoHvxA$vlII!-Cu%}YPGQ9I(M<`2(W z;V?0(_IMFebqfKb{%QC1c9oOS_Q9;{S)H$=#MP|vTcvdII|#5Ku;KOm1E2pk*b4=R zNBIE@U;B3)tlDS9(3bHW?gUAIV5J;t2JJ;&%e@Yk7(g}uUBrdPc*-Qy&%8?0khV> z@sKyzz%K!_{LZd;`6cZR%UGWiWLA7y-|u+~KkmEFqi@~4OrVeE!0Nrq9kmy^1OX5L z0pkP$?c>Did*c=&1`se#K=g4C*7nOp%^shW(@|1Z?Tx7M`13LAeaflv`t!qQ5coF% zz5N3JjsyP(0jCf!YnV4klBYP8jMn3bMhe+V7G`>FMnUP`Zw_U+@~A(f#Rp%@^zf z0T2KI5C8!Xu%1A=Efe~!$#k07t=8upDS`k9fB*=9fN=s=^-~8*$9N79gP8=3j?0*7 z8{!555C8!Xu%5uA=1uoqoN-aIO0CZ!QUn1I009sH0T2KI5coHNeu>v6ZmD)pvRnUc zla>Du)SqSb_g3avPg+Lj|M*|8#|B&@9)WaHnKxS=2=O{ijrOOqx}H~$uxtMeq?@%@ z)A?s4vpMMXJFEJc0;QwYZmajlVV$1Ryk)h&i!u)#c8>ZqWawTA9KbGA%%+WVl|aI$}j^1fr%x#uzmy{h#5syAMx>)djFot#f4 z9!7sRtDRL_A4Lym7C)=wHR?zH*ITtus$0@w{r>5e`279SZJ%GbRXN|FZg&Z5bsIGt%^ey*4O9hy1;v**#I zo~r%d8r?q=*M~h_CoW0S%0cP=%=&k|o)&04*68tZ2cwTtG4r2r^hyH$I z0J$x(Qe)OAU#)kZrE<*rLYV#hq&NO9MHg2nTkCI06czP-PPUu%ebYZaecl-q>$P$=8lKhj zpd61;d;G6A>%GQ5UjKCcKac;mYV88!E=#uqm&yH37evn<_ktJ!`1qsk_=-*~TwO7_YA4Yg)HmRX!r^G!Rx6Q$n^c8b+ciSj~u>FHNj zy8qerJ1_lJM(q&wIn}Ilj1h0hu!)6ZBqa`JJqICPb%XoO-c*S@tKUVRZzDg?v*ICoJfhAcgxzA#1Jzo7X4f-T{k=+d zvPuW>WNTWb_n(qh4s!c%wJQ0A-M{l_=M19e1)jywUSfaQ(rKD$w!ud82=I2Uo!sARm6}J^nwSI4 zGqUS@YjW-URMf7|a!WYpm$XV?eU+N9G7GcqVG2$+b`$GEETbBhRq-V0w4ea zAOHd&00QO_uxow=(hYmMPF#{CItRD+3&`hALLYs;JL`Rx&1(_OiP@>+$)e9U&^jK) zTX?h14>YG11di1y^L~&IvG*Sh?4kI@2m#T~DKe5*Tnhpq00JOjKLOGESG2Alv!6KM z7Rcl7tktadFstGfy&uo6^<#W{JFMe{0I&19S=$w;U1i<>%Q`+@{6woAqRmJ3w^!?Z zM=Kw*!&|*Sn_c%gtJ5{R+*rjg%KA9iIicU0Os5Gr7~`O~-UNCsQ2JhLdZNGYn{B^V zo(K;DAOHd&00JNY0-_SoJ3mv@{K9S!;E@2UeMJ1@G5fs<@jGL^g4yM0b)C~YA6c!P ztjC8CUymK*>tB<2;h@$Jw1x|`zYxBE^!`g)`DiT{w}&@tpAE$OV1E|3`xo*L0?7pI zy1)C+uVDSzs?yauFT_8*RdymB5C8%71X$183lyJvix3h7{zt&-_3Zyk8UF(T?F8(4 z&Sagg-uGwi#N2TOZ|9-;+hcbrSUsQFuJo*)pYER?FZSAJv%7s^oqmgR zgQN0g&FB?fSwA<}6`y~9lh)6y{X>-aSgmIkCBLu}1V8`;KmY`sM8N8PQeM+DyG&V+ z-y*({VG|3-NLo3VUCzA4@86H(_RqYP5A1O!0bcq`mF(b+zS2Gd_`lFQm6~BW32!H?xfB*=vBOvOyfZ6-WnH9g@^_IL{Cy%fBdm=A( z`rBpqXRY(z5dOh@LA%Qh>4N|W@JE2xdFgJk%kA@$k^c!354^ma80Fyh_3~=-&tD77 zmqi1^ADi`9n;@^>NLQhMgM z)JOpYKmY_l00fi?7+nW~>!(h9J>$2M+c_BRKg0EG`XRwFb&kp{p}G*`zWY4-*4@hl z3JxOpfjX~7C=dVv5YSJ+>iKd?`wPjxtG!Qa?eEpP?no(q{p7{I^7})prN^HS_Cd9A zQ0iwE{dcjw8rFjV2!Mcl2`D}9@cO%ZOUzi#)$YHe6i)5)x>C4>`EqqBf9V#1x|4%3 zZ^qRi00JNY0&XK<*LiTfr#qoSiYG+%j%puKEG#4}G%R>zRFkN%kmP?OvPNW&$Qc$i zJSrl0^5+s^!C}D-Zg(CL6)}Bu%>z=X+x$p(ewRJBr00Plal~mlW@ekb^T+QZW~*-S zj4o6vH%7@I8V~>h5a5nLwk;XTjrs7hWS7c7I``=O^^a+9Nm}*LdnC`%xNDNAdM{}5 zCr!>INp`DT4p2S_3}01!Y}x4(h||<4-7#egzU{8jT z3K4(oi2gp2@TRhTP!IqC5C8!X009sH0T9qbfVbycz4p}364Cdqug?%s^-hP2BTkg~ zgNUxbpMQE#;@mxwsM@Yw=)SyjPDxg&o;r(bKmY_DO~B}V;L))w|N7YZ(3L;84e`2N zl}U*a!l*ytdUKx7az7ih=JAj-#4HuVJ7FaVfB*=900@8p2!Mdw2>c#2V|(U&TO?66 zq*0Dab2luOjH=tp9r*$~y0GxsmByIj8-d_1Z^ol^v=jRW7U5v&6N5a5YG!(aN9ZM!T% z(yBb=0k(hu2s}*Swf$XZ&fXtSpx|JoA3W@Z_~dp1qOWgroBdY%tJddE-oo#0-?~ME z_Y);iHLBOSmi;SSkpijJ{$Mn{3Lm$BdfStyB(-WZJzUQhfuWxj`zfsZHpwil*&}aa z^V;(yqskXG=UcNgQ?~1+VlNV?GdMWkW5vp0wN>929+dwiVWqt0K9BTT<`+M>AVJ0E zz^i}3uG9&*?LFLW`Axml@hu3jA+YiZ-``)fJ5J2j&eLigj$3q+7*#g8c5f_&_U-SJ z#giaG!NJXbP`l8D4I|TEl%QgA;H3pO+TY3R_3MWe^3BY+N|Kcq-mn7%K!862Ge#HR zobQVmVz%0q^ZrwwI<>IgQAw-XWiQeNfn)-2#vHzw^u$%kZsol+JOjsOKR@6+;YE4p z1^Yk%1jHv$t$f0rGsk`>qNvm6?Xw!svda@E*FUwk^N0gPR@QcywV$=s@y?vOAZOj6 z!^A1d%5Uw8NAz}tbv;9TAfSi9`z;#8eUNa4uu?rXhN z@eK%o00@8p2!Md-1oZC5XxDYT@E$g<^v>})W)i!U7Y?uk1V8`;SQ9XNeGKBaW*ri* zoLT#;Qhf5fZdR(49)5qAK!u1uc0_-lNZhLFhgZ+tI7=eoRY}L;4cGQE4uG(_Q`a{v8S^u~xLA7x(dUt3SHgMGCO2>#*YSdm_Zw>*o z_6bz7)~@yJNVmw9+OIDqR&h}B1N;sGAOHe(6X30%#VQ{1x(VpT5hqIgLDX`vs(e`O zJBxVO?&Axd*7DT4j)2$jYu^}~eQV4WNi@y2B}2I}A6}MNt5xkNFX`x=Pigmcm=|x& z`G0}t*W0!49dGI0FL1N+pfjIKR;%6h&uID^7C!&6H|jUSi`vy5e|2n%r`=>9;YFvM zE?cR1xl58-Rl5$ss&H!U<~7{Zjf)nxSa@BsO8dY0bJGr`FH2^rS?gxaig(Q2nzsvj zZb*97u66|JI-7vnejw4qrI}Iovq|N*6Rm1i>5H=ekXiXhydc06fnym?-p}~X8ObW; zDeo+8F}g3W)#rK2=>=l&Lf&~_e!4nV5?$5Kg9yZd_4i|Dw#hqx{4N4z!$I%5L%r9r z_6NiV0$K?4s~tCV%*+eKZ5oKtoePT=_J8{l5k*V2`Y7w;buJT8Rqeiq2&ZkINSRk4 zKM(YM`!4xz7wf!2;#GMWkI-s|+UG{r;R2Pj*6-D>&u~jPc|L|#jtCC|AOHd&00KM` zV0HeLUGeb9REtfAmU|XFd+vlJnpSUlcH&E$4okeL7WN1W0w5qJ0i*l>6xz4HPZm#t zB#Qp^@!!XqZrCM>uFo!QcX{ycy^?4;W_zdQ_j|`nPF17zDbV#|wl1(51lSOmIdwtK zxnLxn-e(=9~ptn`G zYQ4JNboAy6*MR^CfB*=900@A9TM3ld)wt8v>z4>rEDmnUG0=0(CYP9*S{X|qvKfHSG##s`HsCw>| zV(HfOI89ioQkjoFpS8~uBXWC1)!_D_MaExw!V9&BL!TBqcjOq5$!nD=`7?e80T2KI z5C8!;5D@*`RoLI*&k_~)5pVzp_Pm@3vz^bk#e~W)@eY@wN*aIO=kxV?SWR}Y5DCy>v^}l!5w6Bxd# z`q;A5ClIZwKSf*#0{jxF^`0km+WB=vR9!!$kZ)$jRgzU|RY|_Kzw6A|`{RkI8fbhA z->1Hg&MLn`Iv@Z7AdqH8+0Q1G-%bLl|N381KhK=HAZOj6!$efA*&}aa^V;(y(R9PY z=RfvF{YI?PKs8#l@zy~0-sy00#EBAr5Um=B3%&;d5O5xWp`R7|DXjZ8Nw4z$e&)Oq zLykZI1b8E`;PU%jiuC`JI7PKSH|h<~TRFlW5U`T~Z}S*fvk26GB-;0S+dHeTGizTD z)_%cjeC@mByIrjF3Q4c>nlIP|0w4eaAOHd&U_AlSad?Aw51t}+tMxfYiXZ?2AOHd& zV4Q&7_i|R(8P5S?Fq6QsuG7ZUU$vX)RWofv+#mn~AOHf^6KMELzp`zYB}i7O^*Ka} zAOHd&00JNY0w4ea|0YmkSL04wuU{f|>%VQX^8dV?TWe*W^`w>i*_btthnyj-RF4g~ zMmz$Zfn&3uA8?*HP5af38#-p@1!A{~=Mk{j%>?XTPwp1>ddGSEuXEeEYsjxV&)Y+} z+gt8#X_t{ta|zge&Y_&U&eLigj$3q+xK(8idezr2)8udJpO*ru-guR+vufWRUeZzG zVf6Q37R5J9x}49;TCG0MrgotV8%CzTC|RwdoRh0$r%^xhzdq1;3HZKx?#5XXiB=Bn z!Um4ITBW_i_ z3c~T$}KE)mbhJ6 z?eAe%JXX*DW}P1I=g2DAr}bPfvz`|N#k(_8w(F&0FA}Ia4uLqT{rK{E*8nVw(mf2HdUNmOmuE_7esIj1DvREayQ--mcsMK0)( zL`2mod*+_Lv*xU2S6BgYp=<@QH3bxObV z$|rn(f6?wZv04N9125m-xv*$q|FMf{B~lMipp2*Ik0A}_jo2J|@2?`GI1Agj(sO>21PMyH{6FvHWlKW4| zDox!Adl$UcT&9+0deFAeW3R$~qZUk|P_Bu5= z{TKZ^FT!T-l~4C|FvxR9|-)HK&@f38f|`SKM`Gr9BO&t?Zyd2R2BWcQoZHb zi7#zBEQzjB1xsf!*j>U7j)G zzQkI!miU;OGe7TI{j?NFwZ@0=ARri*t=Cz3C#C-n8t}}-fP9jcItIk0&E1h$zJN#Lq;yxm(n$>?~ zPAv!=>+?tQ&V8)wP6D-ugH`)q@SaX!55+G=2&5lgJ$K_Qi9{4Nl2=>{0w4eaAYeZM zQPQpf5CwDaqT)ylWG z!#Z9Fyx*ch+y@C)h|{%HtB%|vT1zIJrb|Vi=SwoXrf#7b>jXFWBQu(>i>1uYlv5J3Pt2{*;6<;So!NCoFpts&AozEWVcdhAJ zb)L7V>1gGN@E`yJAOHd&00JN&DuJ3k@+LN~Jx|iAqUINNg8+{N0=?%M{l4?GT8HBn zog`3ju+tB$u5-gzRUcb+`UIj?)!G?o{yn~*Kd)8779u(awSJ&A+_NR8ZaF{cm;^PK zgVu6!dw9>lvDwcLI8UJFaB#bSA^#wdOrUm_h`w)qeTFzqtt#FA#STASYw+)qUbV_j zqyqvVpq_wL{WIzLd?JfnY{r}me?Jf`Ay;rhY@7KALZED(Ml3q1>e(`&0KmTO^I zNup`(LKilSOn*@lRr^nQ>eRw|ME{ZPX5<? z1OX5L0T2KIy#z|^YTRk-^-BZ_4m|jQUO&clAOHd&00JNY0@?}eD^hya_MstO(e%X~ zGka#Z8sZgA(>?#mre$01N}}s;r*cMBIhMie?{U2J6Z+d__h+Ny25!CH?s7x=AOHd& z00IvaC^Dtw@IniS1O*2peh|_qN2R$N7E91ab9lJ+;gdgsLgC*|nbBgm1U1G%@4k;} zuk$ZAr+;4ggzxV!+8rlu)diQ|?^2}ypF}jBIdwtKx|4iCR@U zM7#2NkV2j1 zB_`&k_#2Chzgc=aaheXg-hRcU_*i1LnpiZdqQ0DGL1e28J4hf^6$t`?00@A9fCQQr z&D3*7Xe_ZyWn_BqpILT4b$-~w?L<_)-f+mGC1qoY-71#@ln(;;=Jh?arTcHhX=;?N z-g;s*-9Xoy{d}wxzigLX?YC08vVBkx009sH0T2KI5C8!X&_kg6obTrK8M2*-u77Mh z+dJW>SR$%EQ}ED@duii{=vwvrUKMr?izTA!H~nrUt!)`gtWrI77T16P2t1mA-u`d- zx<|*Z{Hy5qQ&#pXlM*9@QokX7w`P8WKWDR_m&EW+SP23k00JNY0w4eaAmBCvGY|hb z@}t%}iQCm}<&OM<00@8p2!H?xfB*Aiy7i)+hTmN$k3gn5}l@{QV<+<74M+ zC0f-kdyy^(BopA}J&*U&$dm6_muypZ5?+*dUa$`YKtOx~_n#b?W^1J%BvJHA_S+X@ zAOBf$iq^hXqRr6a-w~r~mnT-w{k>=WZ$3#@)^@ZTy(UfTv^$CD%FA;YD?9R^NwazO zpF4=CDtdn%>w1RxKtK-x*7aYH4I*E|>Ue?zl+-uiFM_yz<(00ck)1VBJ^0%o6M$2xvqc-y`1 ziWd&B0|Y<-1XvSb_5O-@oH<|GXt}cXSEcyI*DSKGONAYTl`5r&-ybG0{MjleUkTn$ z+^W2dSNPlWO}|@7Yg@*WK>BcLDu3E@cJJm~GdB~xs+Q0Z(3BH?J9$2k|vO6BL`V(eneth`UEPqI>wMCtN2ljemB++&K2R{w!6FFGYtEu@U zzO#-%yl279ceidLqN>(?Xaa@TO5E=DPAPq}+C#1JYUPUX?k3Q*Xr`VsLSuu`bDY^yuOLJMFW+p)^QWN!~fR#rO#>?-9of#p#0-|XA=;8zbjVu zt39t&lyGXtDG*MK^GZ=pe3p~ZyMxw#7^C5h+KcPWArPqF-01g8)*6i;*X!G_-)e43 zet_RW00cn5ZURQvH?zLpDjs(4yC-V;R+W!i*H0tg?p{CdU;csCsf+f#+I2y^h0{7e zNNsql+EHH8Y1Dt)mGT8P5z%x=A(>7+_3p%HylVBes~dWiC|%HN zmUex*;gDwUJmpoZe;=Ng;e|~(y`t*P9mD!n%#z7#m9BVe+mHKCg?i1>{5Ph(J>hb& zR}`%p`*pXsYG0T1s$J~}(secgrF9ri`CV`Sn^~o=_Px>WaAxHn@qz$P1gx%SJmsCG zEk^gRx2r!e5CiXldL2s6-$q1N|9w#deGbHd_4kuQ$IqJc?FIs6!{OZuRob>$zn)m7 zto;G;fq)hQHxEA5aZ=9>#BCagQRy)w-~6e`Rw9Zf2Ctp@;=XM}R26MJRy2D9`FWu4 zdH4Gr%kF=(=98^NG!3hLecizuF~n*ORL)wzA9TI_ic9ga1p0u3{C=gCBf^6K2!H?x zfB=sKL|?zjBU3FlU2iyK(UP*UL^SQxYg_-P+r$yxR115A1pyEclYr5Er&;aKfq2%W zX`OZ_aXJU1{>JsG}B6=Ruhsk zzBfH)4RMQ#^8WnCwzItxeu^bfaDX3JUk}*a)e(yl0p8zt?6yydAAWZx0nDdx$Gj3z z_Tz<}S579?*-5~`8V-6}b-VA^O6lv(7p?;V5C8!X009sH0k;xZJ2l7ZDPb`LDi#N~ z`YZAc0;v(uyI&5jTM*eQ!wv!t;Ej4}i+tp$Givk>0uJB+KlpDwFuNT^{2-u)z?t$VYo-ZV zDv7GI%J#px?eoz@RPFmk+0h>aFD0y0smxz!{z8qWZ)}r9)$DIe*=m0=M}peJVN~N5 zN7FsEnpmYu{*2#200ck)1VF$I1h9@A>&UT=-1>FosCUk)cW$^>Adesb0w4ea>F4B1Y=;QQh$y$ z>c?hXZ`Qd^X4$JX-rdr~*JiC*OUzQO_J|g~!KDgP*dK8OYA*-T{08=d01pJzj#q09 z$3xy=1HS~$4{Y{b-WvTRrzpRq-C^1BuP3$|wj@sCU8^i@xie(?Cz7Z-JJY7u%CGsA zh^D0y>K&@lV?D7-XB?m1xYz70#HhNX_9B-c00JOjoPb&Vb#lDMEkq0;V4Oh6(n@u^ zmfK074>-s~y$9-bC^>%{F-zril$4i!*zK}Ii4T4U0T9qnATfCD%oq1E00cnb!s!M>E4{IV1k!0G z7e{wqvYd#nHxEA5aZ=9>L{yy+yQ)v4-kXSMdZ9;;G-nEJCRS;n8a@2kDkonF-cCf< z+rwHUWO`#8(W-&C;Cm1N0p}6eP_Jo~Eal^fUe!8Z(dzKdD>38<1VDf{0;ela+%xXZ z7UC3b(K=z;?ULJw*~(iv!X6N?lfb8MXU!E|d@JEaS+jWmNZ zCA{m@A9p^JH*yoxyG-Hk-A@|rK$1p*)d0w4eaAYeU#K1p3m1i!zN z*sa#*94UeT2!H?xfPirVR;@D+l#cNnAOb00@A9^#m^GSrFMO z!wzDVTAxFt2m&Ag0w4eaAOHd&@NWXV%-i|5O;-M&mwnW&%(I@fW*+`=~3m z23#W^fw0=w*B!hOL!72&oqMS@UhzBv7Q2~1#qLu=KWe*!*rje^pK(g9-%5{+BSy7T zuWkLGZWBkK-~c~BJus^sa7(+4e40xjPrhSavQ61Z>{2;*tk)Td7N5*Px6a!1YK>kS zrJz8nJK{%QnEyD0=N8;I3v*LxD{ zbd7t`!!d}yFBNb0xBYTOT+>Edh^VS{e=kwOX7%Vs#ITD z|E||-305fH6@b)WOyygoy=6Rm2DQsApux0kHq`?!9Q zsspcYBBE%#XTi;Pw{9Y$s@dmW1ops3|Dtt%AaCK#8V|B6UeTTp{q5EJe9Zc~M|(N@ zU#;G6pLKd#&s}CuF!dO%S~qM~I;{O#ZG5TI#J6@6_&w&z$)q|v2`_3l52WvG0<7;- zX2+-XKFE7`tGvtXazXt4XC>y{Ndj@uTF%}B^*WTCzm15hrN@kX^QR_TiD=5pc@ui= zV9i^r-R>v1;o@Ai!&3ax86fZCRdZO47h@;x?wT|l> z4X@TNgfo{wmfcUCAGUBi5k;F8&D3*7Xe{Af^_H4h<7fWy2C~n89NMhUfzrqKAmBy< zPrbA$tm3WB#O=ze?5lp?tHQ2fv4pjGbAyBB~mFKR4@oCGM<#=Y2d= z$v*kLhZPUe<7vL=yF#DV-aw$>z_TBiUC%nijJ-Gi!x&;zmFyI?Uv|*-_A4&M#}d(X z@$E0mL><^ptX3tzu={uUzUZ%3+0}0Jnl!D`?j%rqI0TwEB+B>j8)JTBRXw*V9jp2q zl=8!B{hiu)l@L6jwq?<&mXYg8PAa@M`xEKuk9r4(nsLOMokwNy1J2A zrTVPIRUiNYAOHd&00M3z@cxm$@v(EZ61y~PW1c^=dd^5r*N@IFT2r|94M`LYJ<;Ts z1vRfoPSNNg%V)IQeM%Bl%hVpccHNh!BvCbYj``>Qu6|51Tiw)lBd^*C{8g=aXz@RH zNusOtN>G6nJy%Pj>Xr6`iZ8qTIT2NNJeQC=^P*)$bj{!KMBQso{7OXAdXrYvjmW;1 zSf$!q8eHKd0%on-&>OGS>l;NcOWxLFiO!Q3`&X2YcO5ZA zZl}y&kn>}gn&Vx{vF6BdIo}_@ZzZCtsQWfqwa>R%>6pF$iP`b1-QQSmIOUp$5M~e% zWuCcSI|J=M!`t_)-e*JWZd(89Z$6?{owEM0R6@N&HF~TktW|5BQ<_JuH9mv~0TBr- z{;=1AqnTC`r|aZtPhE(7e+}VHXZL(5ZOc%cv1M3G`tO=G20~v7W9xanHCr zTL{!14m+0J|7Oi6TZ!8>u!rIoBLpgTpAz~}+Z{v{HIi3c3j!bj0w7>N0sr?<(LO89 z*Yf|}e&T#v5M3o=Z_wIxgtaamQSxa2r#28#bwcc_K8<>BBBE;L8x>Zzo4$!yt$ces ztmB1%UHdm#oo=A@R#u;ztTjDe{6woAiNR}UzPN82af@o@V|I8}>mt;~{%8vOWd*z&T%P&iT??Z|3CvWboGQF+{6sP0#J~F_iMJl_$c300@8p2!H?xfPkn3 zs*PxQsKDUwC9NuIeqlEV@JPVyeSi>uk#$`v>>%J^4+pF3oZfl$YVCB(xPn{rsn!p) zhI86F0bcW?wOrgDe)zLhPQDVnoj}du;CBB){y`v_K+~d`dd>)qC01+qIp59eGh{n4 zORXy1K=X*yes7hXNCyN!Ks^Dg`X$s`gpeTcKLYzFC_t5q6~XjC^(cCXzU-}ON7n`6>>Mbp;hYc>5l?r%v{9rR-xPiWqAlGSQ=`@%Z? zj8kg;R(fO{5nc73r^(xOM&A!u`-dp;{n)7K;zCz95~C_geqkpFfB*=900=mVK|z2=GLpW`~4a+0L&cN?M~#rc*07 zZXnQyIq+1LumuD_00ck)1e6H$N$Of6`2C#(3JyH@fs()CcMt#p5C8!X00DLcN+r}g zRHMgwBAPy5{iSqo%wIx8(?Rdms#xRkpNZ(&v1X4?{+_d#_}c&)+ml{maeipGDs{!l?aP&jaQqyw&^1BE8i2?XCKKuL`?{#S&3e z@3|1XUB}z`#!kOaAb%hL0w4eaVi1V={p|T`?{6efa4_Nrci z;%}DTPN3i*f*-K*d&C0*AOHe^2v~g{VxV>D{@<(Z|I^xUxn*F+25Qh#^pI9E|QC&inOF_j}|I1V8`;oJv5{{t>5^huQgJwf??t zy5d_#jo3+y>hCdEPA1jaNuciJV79*_eh>fw5C8#Z5ttCWs!yZdn+Vj8Lxq%bj_MuN zKB8Dyny|EC!6T!ZM1_SU{~M7tB6~#6u%O{l5xJ8;mk0|E3vO_`^N6U3>7#2NkV2j1 zB_`&kS?sj?Nd-0&r|G<0Rofg{vYD8zCKipVsPDgjzRc-}%_NYjiUff`00cllKms?y zW)FV)V?yjw8QH5nnz!yYcRA6j2T#}hz1#d>iKu#`=bmmYe5;AwDwhM44+85)-Z=T{ zP#yyDF0sBgALj`X5};GKN4o<6v?>tr*@3 zD?tDRKmY_l00ck)1l&fz?0Lmz$DjE9J>UPackXdtjQRiXd=OFyIfO{$u&tG_%xBYv zZig-8v{1Nv=zzOc96n#tM0qgCY@8HBS}dIl2Sh&c7C?`HqBf! z*EMrp^LhSrzwVj2uJ`r6p4a=j=9+8fGt*<*WD!^Mt=*|F1Rwwb2tWV=5P-n$32cA& z+;Z2fpD1K?_f|Oy0SG_<0uV5pK%-`RwXV2FiHNGTZ>rLxNApsF!Xe2xIQUNs2Q;rV zS)g(p%pQ&K4gwG`i9pL^YM$3)$^;>+Ce;C7AOL}#37mN6=@mYj^o~H`kYpU}9EEJ< zJAwTUF8ZeXK|-K*a7a2{+rKenX@|w7!d6|j;OxT}H!Kyl>c#8-^VRV;lnOshIv(Pd z=l32;zx{E`BfnV$bdMv;N{@fFxcbk(+m5MbQOmwJ2VcCtdSU%Z0?h`8>_@C!H5`6= zVY7oamkL#CYVC*DZ!vT0oJj(;n}ezGgCRqqd0 zQ|tV-!u-tc`5En}ru}5!^G^C`@5k50&6E*5?(eis&F>v&OyP|m5P*Pr1Zv-2=f}bS zED^djt(upvJZ4JnFO!9=rsXf?Lf{tyy5Cn5*1qYjQ965{OX{9DMVO+d^TIy}K)`+i z=YKZv-`$^nPuQZievi}7?}tasd;R!j6GU81D-s`h{;+LZzIe=WRka@_Ra=edT2v~m zSIfW4;{Be?sYia^ZE8uWuvPoq)&IgdT}y?ns=A+H4+2pL?6`LBn5oUC2vr)z2b=Tw zJ%4Ze{-oPP)$;{u+wtCa(Y>8?J5in-fesIK+T3WsWRbSYdHsJ=tBT|QYneR|fB*y_ z009WtPC(c5V!G^_!aM5sJyqp1g#&&-00IzDO(5)ZE~GpCuZ#aZS-?S#gX(w<5Ft9xw7$mu~B@$3dMZ3RCss@++=s-lIhL>CUYgye0b{S#SE_ z5=fCx1mZp~l$HEp+x*~@CLDdjil5(`pOA+2i*$An$hY~d>8%p>c@xv>u}{gotG++; zX_1zi#sh!yi$LEtcYZSZib*1@<`*4mn=b^GuD@*sb*&vB;B z+tlv^nC@5Hv%^oHbzbxIDHB9e8rNU)>ml&M==JYhcIIfIT7$f`^}Ff#E%)8~pg7<- z1Rwx`vpm2=X`^l}ORsJ}Uf8NjntrtSk?qC8R^9$Y?E~w+UMlikWxr3TD!uK$ zkJmMRN41;Qczkbn=Yvb0`phv!-#hx6`llR!bS|tuQho4QM>jYkr%TJ+)%bxa!>i}Q z>Vq}r*4+E}iaA?#{zXHwl&`V#j& z^vg!>-)P9%Ng`?WW6-{MY^#1{N`>tj_P&ZA2i5y)_iog#_>qYMRl}j(idDI(O(%+^ zRCPRH4+1g-Cbsx@ul0va5c#Hlj85HWa^=^DOcJ)Js_zJVg#(&b`uTfSd7`ClN8EqW(x0!#E!Uv)tf|N2zGv3-{JQ#4c1Iv;27zD#ryluv zx2Ywi0)<1IIC#Hw*6^akw>e?e8T-2~PtDlj>I# z<%?P`@<9Ls5P$##AOL}UCD8WxKkuKLUMx_%IOJ=*Qf~JB;b(W z9Cp>$*q0uGq~9~+SDvolWkj``UW-sN1Rwx`Y!dJr|Jk3;d*9#=_g~&OsC%@r<-JV! zY1ezSXfN8!r1!q*9!IecfiMK#U4QfPe++v|*s8XEf6Db+_WdP6>4Vz5-MCd}pStQL zVXJN~c5t2u$V;xQNMQ@H=iz+QNPD-TE5}ucOTd2pi&{Ly12%* z+y0FqOFJz7`MayO>T`YmdC7Z|gzE2cCvC6q6*t`Y%&C(_T#XwKVCV~F-`Y=v*whp-+bc4)gX3lYo9pi$X7oUsx_!wjzd5Nf#IhYHalqZ z&+|SM4!em16|wfeHi$Q~`W^3`%#Zg@($49&^Z$;4Vg6rMdi<-!)hCI3(=cpQq$yc) z>F8Cb4i{lHZke_{XHatRE%l!nT_S8%+juACZ`?T6m0#ETq%MDD?QU*2r_JTX?+aZj z>yK^exo$Ayy1}-%!CwfNKp^gYY`^>_)D0iZC2-y?t*&Y_e3ZzFnoBxAJUit2*5|hG zP~@1dC$`$N_T%@zD{R$vD^}&EHk~MJ)1=oKTeNF?=++j+BCh5~@1>RyfB*#I6KK5s zt;hP^J6Y(`_#R>b0`UnX_5D0s%5jPI>G$%4^#eL4lZ32BFOi%OFqeR?`F>J%OIIE7myH*; z>h@s``{rhp2;213BR}spwWL%erG7oSWkK%b;=RTT+x6j+wMRCu@bf%GQvJBF9|91_ z9)Y;;FYtRV(Ukq!YcXmB0SK5zVCl9FdmPkxqR5I~Tzq?fB*y_5TC%XsfVwtP^DO;rSWTk1)T)qzBf##Z`g(a z1Rwx`>ucQN1vv%$BDSw;h|2O8x5E&P&g0=^aEYvK)#I2)Kf13RoACc?YOmT)NHTT7569+ z`Km65s5)f%8y8&o-VDc2quLG17q#7O%@@QY?(x2D?)+r*6_bQ&P5S#P_fMGnx3O1^ z5lO49apmH2P*tX)%h;72?HQRfYQ`QmvKoV(qunbTf7vP8&gu;pke4to8@ zE4I!WFHF<6RSsxSvG;gki?%$b=6OA)Oc1uJ?)Hxson^}CdXHgJc9)eN|7vmdNy1jO z{r*AH_s#suiyHq?^QHZMskr5P@23TAN981&FK)fk&hPh}sP6r;oJpSyfuMes^zmeB zO)2?Gz?9$r<*Oe_#hvYZb)S!Q+m~(Um)pPoVu!0I3S0H3&pNMp`jiR6I-Pdy?Opes zD1Ne3Yxi!{uK1COLY2zKt*-Q!d_JmU^*>D#C>)3b`a#;`z~FM#j&8W>17VuBFCN>f zUzt*2yDl5Kf1@F5e_kg}NDuzg!U4@IO%`$0_IY;F>l>7}^LtfvUx(!H;9u{Yl^F3+or@^b**%?|TL7pDh-)Xj1RLjayFCRvTWw#mud9 zCJEi@=b!F!UHg9JvmXNaNZ{rBCf4rPeS*k$Rn_*V?lZaa>q909)vC(BAUmfFy63H@ zM-~fPwd8~c2991ZQJAJd+*KWq`yH3G^KXBm_JMU@FBPhI*ly>%TUuS!X80(9!olP? z(B02eJtrRI=fM}RuU=SxlCVwF9=BDM@Ao{g?)^b=k@j)F`y5I6mG*PE`CdNvL3X|m zl5aec>gP$7lhpY&s6MCu;rsPpepw=t(jb3KJ??k@@$Ogs9>28hdEe*7_UGNEmXr!O zz#+{zFm;^7uV@73ciGzYviC-aq%@kBw8=w5#gvcG=)88aK*_KA~q zZ8qL7su#Ynvi#WG8;-4d!NX1d)Afvd z9Nn5vV>fk`C$RF^lLl4)?tRC09oOUYqsA^AC2ZA+E&knW{UH;Ct-9p!L7OffJ6_nX zy5IMw%YM?|ZIK^o$dM%iroEq9_wO0K_HA#=+Ba3{(W7~(FhyAndVcS7tfp?uzCXt={iT0xa^hE~y&=Ntf=j0NKcvejVY}XY z(%b zWKFl9#4TUf`^oh50)MNfuQz7hzpSof%ht<3LOBwLfNksaaq3E5 z2tWV=5J;au-*e7i*LD9X!d8`CS4p3^*&ehk$emog*La~?A09F9_2ZXK5VoqS?{cSQ zr_aAcme>$>n`}q&{6J9m__eiG1iL4)e`n^11 z{lF4otIB>4T$YdS^tyj{ zwmkR#Fk0B`pv|Si%R!bW=^+3C2tWV=5P*QK1jY^DtNM3`jTEwKYklz>0wxmB^&F7t z?}x&_8*Y5&)X5_29Fp#HQJ+f<>!+^wbz#?x@4bhO1KD)4&#jO?+V7}j+a=%A|8DiY zS1lX;fk5?e$oKf7{t)yvN?T{re()#E9THjRZjstR0_0faYmp?jQGd^uwR~A>Q|bZ%mw>JB z^VQ|I%LRpiP68LN|Ib&)-%u({SDg%^*nZ^4L#w>e@pXa1fjFQaxZ^;Sv1C_ymJQxs z++*5gk#*fw-J|UDzIPJB%-RHUG_sD4FV5s{r!D||4}T0 zH0$4Mk6OPp+Ke(G009U<00L17==y#gc2EE7;(t#TaFF8=HIB�SG_<0uX?JJb~?Z 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 z0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI z5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X z009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH z0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI z5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X z009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH z0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI z5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X z009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH z0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI z5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X z009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH z0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI z5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X z009sH0T2KI5C8!XP!Q<1^?w4IdNp-#+@N9Eh7Jw&mo^>L)X?Jlzl|LmS7=(1;xxvVxhvhCT+-M0MHQpoD7Ig|(l zKmY_l00cmwRtapm{ww#-KjgzQHF?9<4J{_On^Ky}OLpnIaNox!mqMmeua|GK$;hAg z8dpNb<&a8yfE^$J0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4ea zAOHd&00JP8Gl88Sy?T26oEZhT++pSo`r( zSLWRIV0-=q<_u`Mw9hT$O5d{URZmPDyz=NWRhfTH`YA!vgO|1F*m`)8tNi-HGmcvB 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%0D&5Lse`|^n9$$E%(Tsga|J^ISE zmsXyJ^a84{AM~VQ9G7}Yh(7((5B*Xe(R?)x=@t7u6Q z@?Z9+_O1L56wmNO;z0gLT*u#t|Fjo}@~iBEb9?#xCI8RP^8fsta&aTO>>j1(WBj9d zf!|jjU3z0Y-F@^fXY<3Zt8bi8{2`4_tnb{^`AVdVU;AA9fAq=K{6qR}zb{Ur zx7yR!m=?!Fdai$ZKrZM?(`#s6p!QI`yl6cdukqjm-}*Hl^2LYzm~#*EpjYD3y!`cA z-~8fNJer2$3iB7`!7iCk@hv-O{Opi)JU1jydTo6`eCtnpdd?oPKh~rEj9*%N{X%j^ zSDYxVUDVSk7y76_e3O$nK-?hi@%+p4N9$UB`#gxQ{>&5oh4>>+`S{abenj=OCl~UP zubuiZtzP60J^AQB{TMGfK;wb(BbrC`p!+YL`+MGGp2TmS>zEJczw9u(f^YrfpFTOy zl`qZS@H70c{V#n=L(iR{_yjsHvk#?jN#jFDJ$4g4&yCQNPd?&gsGjFp>dR+OjGsNV zU*@0WtH%zrOX}m#IP_yY^39|E^`kvLjbFS+p7JB&BcgsFf1$nS5YomcjbC)}3(+Mf z^yEY3(&$0@PR{htdStKZebi_6**cH*fxcUh{43;_p!qV7#zj8(Bv0`uzM*>j6}dw5 zN>Ayt=eOpceDI|nBu8{1yUGvRr}1mzaB;Eyi~UyI2Z?jli|2RFN5sFvo;2fHL{Ky}9FX(7~?N96*vyNN)SU)L0 z)=Nv9cuX@tz%a=~V*e=#%2iYO^%0AV;iv1F=Sl`gPkH~+B10nmN9Li6tN8har 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≧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

A@4=6dJ^n=PnQSwF2gR*CopQCU9Rp&*; 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

E(RuQ$H*Jr@YgLF7NC9U#xki#V#M~_|Lp2 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 z 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 z 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⁡ip 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 z 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%0AClIq(=1jMU#X=(&2sIq6-L4kMLIxiu3i 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%0D_sFEw3)}BZ*Q?g; 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 z zCF zgIA3le6B%mh^_3u6z%KDXGv3K!M zTxCD@`QF!5-#Hk$^z$C7af|EnLiL?5Id9Uh=PKfT7_W_6K5IR(zM6mX=q*olA9~&- zuQv|)EV+1B&v}ac);xJ{_FXR=z1EU{n0Fw=(0%08Pee)-7 z@K56uNBH}KANsneA>xcgM5Z|EogY|>&=U4~WMW`S7_)yP!VgG8J;*s}u_^r5Op1gl$ey!`y2Sem1 z`WY%{T4;}AnR z%Hp8-qKtR>CqCJw@u_bf#7q7IOMaYriKS6Bqay|1po^g#4JF(gz>Vc#N0+#>L)TSKs`LpZF0+yDy zAI=wkAiENWw1dv!W^RG+kNpE-||Lf_Cim3gyUDwI3Ydt2l1GN#$z7wV7}cq zuljM{c-5nSXz#|se#q0#{)K(JZr+XCd@CD2`PL6{UK!%ue#^Yz3+k7ANH2(o@V_^0nvJA)0@3+$YC6Ek0N;!~V1jQGIBil7;q{)+_NqT(@q8 z`R&Ln_nSCvsksJRSC1cCC$mtz7iYD%zKNf%E89=gS9@{IIh%g1(_uYw#BKeuBhT^p z2Y(Vb?dQpHo&8(ytl#cuwKE=Z*gPonV{-VvcA*~p!hDD;+Cy<3zvSYRzV2)1x-vQX zF>mrH?X5%Jn-V{u{>6FmT>j?0bn`1e^FBM?{eDAPJKv!gvG18DjX(UgRQ`6{)5p#I z;B6xYl;wf!)b}Cyh2P)O>#|E{OxR|*5d-vMcjWoqv-<3jeJIlxf6D9=;uHTNs%QQ~ z`%tEjvUxR+`k{yUqc8dFg*hlBgmFY{qyoJ8nhjRIg`_@5mz_`pm zdHRFmLc7S1v(Wz7xai9s@P&8xt$W5{T=EF@@u4igbZ(xt-dfkh6+A$G3&lZb-k|e6 za-nrpKk_f_+-INSHNA~jd+VUzGsr8*wa$B=Ond7syVK4(U>#S_d7^cleCsYA=q2Cy z_U4~o_wL^<(ihs1``(xK9YFaMJ&m7y`+N84OP+HO`WY9bzkI~JWTEF-@{~tSqxIbRuee1nJNMj$ ze$Eg1FTK2HtG@Sdtyk75>(C#*yU;zWoHlyE^EKaz{l*@BtIYSwdHX!Ka*pKO={rXq zbl#Ekyf*mvyJkP~-0ZKsZ}#B#uARGw_53b^A2_%1dokxauKT?TJ9gdqJbj$wL;85H zm|Xq%9f30W>O--iv=M{gA55L-rBgPHA|Au$#CV$ee`sDF*aZA7a z8~@^hIA#9G;lFs+pYhO7o+^&0FV7{`;H~PJi_F-lDunzTsTVzDYc^u7!3_fA1;kU*61q@IW5D&8zV^=V8yrD_+X$#9L@R zw%?Y|7#|+#fp77~ef@{|20QmV4)f%FNOogg!!teDC%gB&njZRhZfo6A&-~$ueDlPQ zJcqPCkz;-H+}nCX4xW_Bu@1?f>~oaq>$y9B4CT0Q9b^Z_%}&@8Ir?!vflp|h^jF_H z%0A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�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

jyeZ99o+w{i( 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 WW(c2cG%T$6|HXo!8GbWn^!zWIX7f1! From 843c7e8adc6c7936ecce699aba55610991e9e73c Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Fri, 8 Mar 2024 14:02:41 +0100 Subject: [PATCH 133/142] cleaning up and formatting --- environment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/environment.yml b/environment.yml index b48befc7..d081b46d 100644 --- a/environment.yml +++ b/environment.yml @@ -28,8 +28,8 @@ dependencies: - pip=23.1 - einops=0.6.1 - hydra-core=1.3 - - quantus=0.5.3 - pip: + - quantus==0.5.3 - recipies==0.1.3 # Fixed version because of NumPy incompatibility and stale development status. - scikit-optimize-fix==0.9.1 From fe62ee281f5860fe106d9dd9d9818553270119c4 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Fri, 8 Mar 2024 14:38:13 +0100 Subject: [PATCH 134/142] formatting --- icu_benchmarks/imputation/diffwave.py | 2 +- icu_benchmarks/models/custom_metrics.py | 30 +- icu_benchmarks/models/utils.py | 379 ++++++++++++++++- icu_benchmarks/models/wrappers.py | 540 ++---------------------- scripts/plotting/plotting.py | 192 +++++++++ 5 files changed, 615 insertions(+), 528 deletions(-) diff --git a/icu_benchmarks/imputation/diffwave.py b/icu_benchmarks/imputation/diffwave.py index 458c2c5e..437303ed 100644 --- a/icu_benchmarks/imputation/diffwave.py +++ b/icu_benchmarks/imputation/diffwave.py @@ -330,7 +330,7 @@ def forward(self, input_data): cond = self.cond_conv(cond) h += cond - out = torch.tanh(h[:, :self.res_channels, :]) * torch.sigmoid(h[:, self.res_channels:, :]) + out = torch.tanh(h[:, : self.res_channels, :]) * torch.sigmoid(h[:, self.res_channels:, :]) res = self.res_conv(out) assert x.shape == res.shape diff --git a/icu_benchmarks/models/custom_metrics.py b/icu_benchmarks/models/custom_metrics.py index fd88699f..0cff08c0 100644 --- a/icu_benchmarks/models/custom_metrics.py +++ b/icu_benchmarks/models/custom_metrics.py @@ -422,30 +422,30 @@ def norm_function(arr): x_original = dataloader.dataset.data["reals"].clone() dataloader.dataset.add_noise() - x_preturb = dataloader.dataset.data["reals"].clone() - y_pred_preturb = model.model.predict(dataloader) + x_perturb = dataloader.dataset.data["reals"].clone() + y_pred_perturb = model.model.predict(dataloader) Attention_weights = model.interpertations(dataloader) - att_preturb = Attention_weights["attention"] + att_perturb = Attention_weights["attention"] # Calculate the absolute difference - difference = torch.abs(y_pred_preturb - y_pred) + difference = torch.abs(y_pred_perturb - y_pred) # Find where the difference is less than or equal to a thershold close_indices = torch.nonzero(difference <= thershold).squeeze()[:, 0].to(device) RIS = relative_stability_objective( x_original.detach(), - x_preturb.detach(), + x_perturb.detach(), attribution, - att_preturb, + att_perturb, close_indices=close_indices, input=True, attention=True, ) ROS = relative_stability_objective( y_pred, - y_pred_preturb, + y_pred_perturb, attribution, - att_preturb, + att_perturb, close_indices=close_indices, input=False, attention=True, @@ -458,16 +458,16 @@ def norm_function(arr): with torch.no_grad(): noise = torch.randn_like(x["encoder_cont"]) * 0.01 x["encoder_cont"] += noise - y_pred_preturb = model(model.prep_data(x)).detach() + y_pred_perturb = model(model.prep_data(x)).detach() if explain_method == "Random": - att_preturb = np.random.normal(size=[64, 24, 53]) + att_perturb = np.random.normal(size=[64, 24, 53]) else: - att_preturb, features_attrs, timestep_attrs = model.explantation2(x, explain_method) + att_perturb, features_attrs, timestep_attrs = model.explantation2(x, explain_method) # # Calculate the absolute difference - difference = torch.abs(y_pred_preturb - y_pred) + difference = torch.abs(y_pred_perturb - y_pred) # Find where the difference is less than or equal to a thershold close_indices = torch.nonzero(difference <= thershold).squeeze()[:, 0].to(device) @@ -476,15 +476,15 @@ def norm_function(arr): x_original.detach(), x["encoder_cont"].detach(), attribution, - att_preturb, + att_perturb, close_indices=close_indices, input=True, ) ROS = relative_stability_objective( y_pred, - y_pred_preturb, + y_pred_perturb, attribution, - att_preturb, + att_perturb, close_indices=close_indices, input=False, ) diff --git a/icu_benchmarks/models/utils.py b/icu_benchmarks/models/utils.py index 6c944ae7..34a5de0b 100644 --- a/icu_benchmarks/models/utils.py +++ b/icu_benchmarks/models/utils.py @@ -8,13 +8,14 @@ import logging import numpy as np import torch - +from quantus.functions.similarity_func import correlation_spearman, cosine from pytorch_lightning.loggers.logger import Logger from pytorch_lightning.utilities import rank_zero_only from torch.nn import Module from torch.optim import Optimizer, Adam, SGD, RAdam from typing import Optional, Union from torch.optim.lr_scheduler import _LRScheduler, CosineAnnealingLR, MultiStepLR, ExponentialLR +import captum def save_config_file(log_dir): @@ -188,3 +189,379 @@ def version(self): @rank_zero_only def log_hyperparams(self, params): pass + + +def Faithfulness_Correlation( + model, + x, + attribution, + similarity_func=None, + nr_runs=100, + pertrub=None, + subset_size=3, + feature=False, + time_step=False, + feature_timestep=False, +): + """ + Calculates faithfulness scores for captum attributions + + Args: + - x:Batch input + -attribution: attribution generated by captum, + - similarity_func:function to determine similarity between sum of attributions and difference in prediction + - nr_runs: How many times to repeat the experiment, + - pertrub: What change to do to the input, + - subset_size: The size of the subset of featrues to alter , + - feature: Determines if to calcualte faithfulness of feature attributions, + - time_step: Determines if to calcualte faithfulness of timesteps attributions, + - feature_timestep: Determines if to calcualte faithfulness of featrues per timesteps attributions, + Returns: + score: similarity score between sum of attributions and difference in prediction averaged over nr_runs + + Implementation of faithfulness correlation by Bhatt et al., 2020. + + The Faithfulness Correlation metric intend to capture an explanation's relative faithfulness + (or 'fidelity') with respect to the model behaviour. + + Faithfulness correlation scores shows to what extent the predicted logits of each modified test point and + the average explanation attribution for only the subset of features are (linearly) correlated, taking the + average over multiple runs and test samples. The metric returns one float per input-attribution pair that + ranges between -1 and 1, where higher scores are better. + + For each test sample, |S| features are randomly selected and replace them with baseline values (zero baseline + or average of set). Thereafter, Pearson’s correlation coefficient between the predicted logits of each modified + test point and the average explanation attribution for only the subset of features is calculated. Results is + average over multiple runs and several test samples. + This code is adapted from the quantus libray to suit our use case + + References: + 1) Umang Bhatt et al.: "Evaluating and aggregating feature-based model + explanations." IJCAI (2020): 3016-3022. + 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for + responsible evaluation of neural network explanations and beyond." + Journal of Machine Learning Research 24.34 (2023): 1-11. + """ + + attribution = torch.tensor(attribution).to(model.device) + + # Other initializations + if similarity_func is None: + similarity_func = correlation_spearman + if pertrub is None: + pertrub = "baseline" + similarities = [] + + # Assuming this is a method to prepare your data + + y_pred = model(model.prep_data(x)).detach() # Keep on GPU + pred_deltas = [] + att_sums = [] + + for i_ix in range(nr_runs): + if time_step: + timesteps_idx = np.random.choice(24, subset_size, replace=False) + patient_idx = np.random.choice(64, 1, replace=False) + a_ix = [patient_idx, timesteps_idx] + + elif feature: + feature_idx = np.random.choice(53, subset_size, replace=False) + patient_idx = np.random.choice(64, 1, replace=False) + a_ix = [patient_idx, feature_idx] + elif feature_timestep: + timesteps_idx = np.random.choice(24, subset_size[0], replace=False) + feature_idx = np.random.choice(53, subset_size[1], replace=False) + patient_idx = np.random.choice(64, 1, replace=False) + a_ix = [patient_idx, timesteps_idx, feature_idx] + + # Apply perturbation + if pertrub == "Noise": + x = model.add_noise(x, a_ix, time_step, feature, feature_timestep) + elif pertrub == "baseline": + x = model.apply_baseline(x, a_ix, time_step, feature, feature_timestep) + + # Predict on perturbed input and calculate deltas + y_pred_perturb = (model(model.prep_data(x))).detach() # Keep on GPU + + if time_step: + if attribution.size() == torch.Size([24]): + att_sums.append((attribution[timesteps_idx]).sum()) + else: + att_sums.append((attribution[patient_idx, :, :][:, timesteps_idx, :]).sum()) + elif feature: + if len(attribution) == 53: + att_sums.append((attribution[feature_idx]).sum()) + else: + att_sums.append((attribution[patient_idx, :, :][:, :, feature_idx]).sum()) + elif feature_timestep: + att_sums.append((attribution[patient_idx, :, :][:, timesteps_idx, :][:, :, feature_idx]).sum()) + + pred_deltas.append((y_pred - y_pred_perturb)[patient_idx].item()) + # Convert to CPU for numpy operations + + pred_deltas_cpu = torch.tensor(pred_deltas).cpu().numpy() + att_sums_cpu = torch.tensor(att_sums).cpu().numpy() + + similarities.append(similarity_func(pred_deltas_cpu, att_sums_cpu)) + + score = np.nanmean(similarities) + return score + + +def Data_Randomization( + model, + x, + attribution, + explain_method, + random_model, + similarity_func=cosine, + dataloader=None, + method_name="", + **kwargs, +): + """ + + Args: + - x:Batch input + -attribution: attribution + - explain_method:function to generate explantations + - random_model: Reference to model trained on random labels + - similarity_func: Function to measure similiarity + - dataloader:In case of using Attention as the explain method need to pass the dataloader instead of the batch , + - method_name: Name of the explantation + + Returns: + score: similarity score between attributions of model trained on random data and model trained on real data + + Implementation of the Random Logit Metric by Sixt et al., 2020. + + The Random Logit Metric computes the distance between the original explanation and a reference explanation of + a randomly chosen non-target class. + This code is adapted from the quantus libray to suit our use case + + References: + 1) Leon Sixt et al.: "When Explanations Lie: Why Many Modified BP + Attributions Fail." ICML (2020): 9046-9057. + 2)Hedström, Anna, et al. "Quantus: An explainable ai + toolkit for responsible evaluation of neural network explanations and beyond." + Journal of Machine Learning Research 24.34 (2023): 1-11. + + """ + + if explain_method == "Attention": + Attention_weights = random_model.interpertations(dataloader) + attribution = attribution.cpu().numpy() + min_val = np.min(attribution) + max_val = np.max(attribution) + + attribution = (attribution - min_val) / (max_val - min_val) + random_attr = Attention_weights["attention"].cpu().numpy() + min_val = np.min(random_attr) + max_val = np.max(random_attr) + random_attr = (random_attr - min_val) / (max_val - min_val) + score = similarity_func(random_attr, attribution) + elif explain_method == "Random": + score = similarity_func(np.random.normal(size=[64, 24, 53]).flatten(), attribution.flatten()) + else: + data, baselines = model.prep_data_captum(x) + + explantation = explain_method(random_model.forward_captum) + # Reformat attributions. + if explain_method is not captum.attr.Saliency: + attr = explantation.attribute(data, baselines=baselines, **kwargs) + else: + attr = explantation.attribute(data, **kwargs) + + # Process and store the calculated attributions + random_attr = ( + attr[1].cpu().detach().numpy() + if method_name in ["Lime", "FeatureAblation"] + else torch.stack(attr).cpu().detach().numpy() + ) + + attribution = attribution.flatten() + min_val = np.min(attribution) + max_val = np.max(attribution) + attribution = (attribution - min_val) / (max_val - min_val) + random_attr = random_attr.flatten() + min_val = np.min(random_attr) + max_val = np.max(random_attr) + random_attr = (random_attr - min_val) / (max_val - min_val) + + score = similarity_func(random_attr, attribution) + return score + + +def Relative_Stability( + model, + x, + attribution, + explain_method, + method_name, + dataloader=None, + threshold=0.5, + **kwargs, +): + """ + Args: + - x:Batch input + -attribution: attribution + - explain_method:function to generate explantations + - method_name: Name of the explantation + - dataloader:In case of using Attention as the explain method need to pass the dataloader instead of the batch , + + + Returns: + RIS : relative distance between the explantation and the input + ROS: relative distance between the explantation and the output + + + References: + 1) `https://arxiv.org/pdf/2203.06877.pdf + 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation + of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. + + """ + + def relative_stability_objective(x, xs, e_x, e_xs, eps_min=0.0001, input=False, device="cuda") -> torch.Tensor: + """ + Computes relative input and output stabilities maximization objective + as defined here :ref:`https://arxiv.org/pdf/2203.06877.pdf` by the authors. + + Args: + + x: Input tensor + xs: perturbed tensor. + e_x: Explanations for x. + e_xs: Explanations for xs. + eps_min:Value to avoid division by zero if needed + input:Boolean to indicate if this is an input or an output + device: the device to keep the tensors on + + Returns: + + ris_obj: Tensor + RIS maximization objective. + """ + + # Function to convert inputs to tensors if they are numpy arrays + def to_tensor(input_array): + if isinstance(input_array, np.ndarray): + return torch.tensor(input_array).to(device) + return input_array.to(device) + + # Convert all inputs to tensors and move to GPU + x, xs, e_x, e_xs = map(to_tensor, [x, xs, e_x, e_xs]) + + if input: + num_dim = x.ndim + else: + num_dim = e_x.ndim + + if num_dim == 3: + + def norm_function(arr): + return torch.norm(arr, dim=(-1, -2)) + + elif num_dim == 2: + + def norm_function(arr): + return torch.norm(arr, dim=-1) + + else: + + def norm_function(arr): + return torch.norm(arr) + + nominator = (e_x - e_xs) / (e_x + (e_x == 0) * eps_min) + nominator = norm_function(nominator) + + if input: + denominator = x - xs + denominator /= x + (x == 0) * eps_min + denominator = norm_function(denominator) + denominator += (denominator == 0) * eps_min + else: + denominator = torch.squeeze(x) - torch.squeeze(xs) + denominator = torch.norm(denominator, dim=-1) + denominator += (denominator == 0) * eps_min + + return nominator / denominator + + attribution = torch.tensor(attribution).to(model.device) + if explain_method == "Attention": + y_pred = model.model.predict(dataloader) + x_original = dataloader.dataset.data["reals"].clone() + + dataloader.dataset.add_noise() + x_perturb = dataloader.dataset.data["reals"].clone() + y_pred_perturb = model.model.predict(dataloader) + Attention_weights = model.interpertations(dataloader) + att_perturb = Attention_weights["attention"] + # Calculate the absolute difference + difference = torch.abs(y_pred_perturb - y_pred) + + # Find where the difference is less than or equal to a threshold + close_indices = torch.nonzero(difference <= threshold).squeeze() + RIS = relative_stability_objective( + x_original[close_indices, :, :].detach(), + x_perturb[close_indices, :, :].detach(), + attribution, + att_perturb, + input=True, + ) + + ROS = relative_stability_objective( + y_pred[close_indices], + y_pred_perturb[close_indices], + attribution, + att_perturb, + input=False, + ) + + else: + y_pred = model(model.prep_data(x)).detach() + x_original = x["encoder_cont"].detach().clone() + + with torch.no_grad(): + noise = torch.randn_like(x["encoder_cont"]) * 0.01 + x["encoder_cont"] += noise + y_pred_perturb = model(model.prep_data(x)).detach() + if explain_method == "Random": + att_perturb = np.random.normal(size=[64, 24, 53]) + att_perturb = torch.tensor(att_perturb).to(model.device) + else: + data, baselines = model.prep_data_captum(x) + + explantation = explain_method(model.forward_captum) + # Reformat attributions. + if explain_method is not captum.attr.Saliency: + att_perturb = explantation.attribute(data, baselines=baselines, **kwargs) + else: + att_perturb = explantation.attribute(data, **kwargs) + + # Process and store the calculated attributions + att_perturb = ( + att_perturb[1].detach() if method_name in ["Lime", "FeatureAblation"] else torch.stack(att_perturb).detach() + ) + # Calculate the absolute difference + difference = torch.abs(y_pred_perturb - y_pred) + + # Find where the difference is less than or equal to a threshold + close_indices = torch.nonzero(difference <= threshold).squeeze() + RIS = relative_stability_objective( + x_original[close_indices, :, :].detach(), + x["encoder_cont"][close_indices, :, :].detach(), + attribution[close_indices, :, :], + att_perturb[close_indices, :, :], + input=True, + ) + ROS = relative_stability_objective( + y_pred[close_indices], + y_pred_perturb[close_indices], + attribution[close_indices, :, :], + att_perturb[close_indices, :, :], + input=False, + ) + + return np.max(RIS.cpu().numpy()).astype(np.float64), np.max(ROS.cpu().numpy()).astype(np.float64) diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index 79f13ef4..ae70406a 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -18,8 +18,7 @@ from pytorch_lightning import LightningModule from icu_benchmarks.models.constants import MLMetrics, DLMetrics from icu_benchmarks.contants import RunMode -import matplotlib.pyplot as plt -from quantus.functions.similarity_func import correlation_spearman, cosine +from icu_benchmarks.models.utils import Faithfulness_Correlation, Data_Randomization, Relative_Stability import captum from captum._utils.models.linear_model import SkLearnLasso @@ -522,118 +521,6 @@ def prep_data_captum(self, x): ) return data, baselines - def plot_attributions(self, features_attrs, timestep_attrs, method_name, log_dir): - """ - Plots the attribution values for features and timesteps. - - Args: - - features_attrs: Array of feature attribution values. - - timestep_attrs: Array of timestep attribution values. - - method_name: Name of the attribution method. - - log_dir: Directory to save the plots. - Returns: - Nothing - """ - - # Plot for feature attributions - x_values = np.arange(1, len(features_attrs) + 1) - plt.figure(figsize=(8, 6)) - plt.plot( - x_values, - features_attrs, - marker="o", - color="skyblue", - linestyle="-", - linewidth=2, - markersize=8, - ) - plt.xlabel("Feature") - plt.ylabel("{} Attribution".format(method_name)) - plt.title("{} Attribution Values".format(method_name)) - plt.xticks( - x_values, - [ - "height", - "weight", - "age", - "sex", - "time_idx", - "alb", - "alp", - "alt", - "ast", - "be", - "bicar", - "bili", - "bili_dir", - "bnd", - "bun", - "ca", - "cai", - "ck", - "ckmb", - "cl", - "crea", - "crp", - "dbp", - "fgn", - "fio2", - "glu", - "hgb", - "hr", - "inr_pt", - "k", - "lact", - "lymph", - "map", - "mch", - "mchc", - "mcv", - "methb", - "mg", - "na", - "neut", - "o2sat", - "pco2", - "ph", - "phos", - "plt", - "po2", - "ptt", - "resp", - "sbp", - "temp", - "tnt", - "urine", - "wbc", - ], - rotation=90, - ) - plt.tight_layout() - plt.savefig( - log_dir / "{}_attribution_features_plot.png".format(method_name), - bbox_inches="tight", - ) - - # Plot for timestep attributions - x_values = np.arange(1, len(timestep_attrs) + 1) - plt.figure(figsize=(8, 6)) - plt.plot( - x_values, - timestep_attrs, - marker="o", - color="skyblue", - linestyle="-", - linewidth=2, - markersize=8, - ) - plt.xlabel("Time Step") - plt.ylabel("{} Attribution".format(method_name)) - plt.title("{} Attribution Values".format(method_name)) - plt.xticks(x_values) - plt.tight_layout() - plt.savefig(log_dir / "{}_attribution_plot.png".format(method_name), bbox_inches="tight") - def explantation( self, dataloader, @@ -677,7 +564,8 @@ def explantation( timestep_attrs = Interpertations["attention"] features_attrs = Interpertations["static_variables"].tolist() features_attrs.extend(Interpertations["encoder_variables"].tolist()) - r_score = self.Data_Randomization( + r_score = Data_Randomization( + self, x=None, attribution=timestep_attrs, explain_method=method, @@ -685,7 +573,8 @@ def explantation( dataloader=dataloader, method_name=method_name, ) - st_i_score, st_o_score = self.Relative_Stability( + st_i_score, st_o_score = Relative_Stability( + self, x=None, attribution=timestep_attrs, explain_method=method, @@ -706,7 +595,8 @@ def explantation( if method_name == "Random": f_ts_v_score.append( - self.Faithfulness_Correlation( + Faithfulness_Correlation( + self, x, all_attrs, pertrub="baseline", @@ -716,7 +606,8 @@ def explantation( ) ) f_ts_score.append( - self.Faithfulness_Correlation( + Faithfulness_Correlation( + self, x, all_attrs, pertrub="baseline", @@ -726,7 +617,8 @@ def explantation( ) ) f_v_score.append( - self.Faithfulness_Correlation( + Faithfulness_Correlation( + self, x, all_attrs, pertrub="baseline", @@ -737,7 +629,8 @@ def explantation( ) r_score.append( - self.Data_Randomization( + Data_Randomization( + self, x, attribution=all_attrs, explain_method=method, @@ -745,7 +638,8 @@ def explantation( method_name=method_name, ) ) - res1, res2 = self.Relative_Stability( + res1, res2 = Relative_Stability( + self, x, all_attrs, explain_method=method, @@ -757,7 +651,8 @@ def explantation( st_o_score.append(res2) else: f_ts_score.append( - self.Faithfulness_Correlation( + Faithfulness_Correlation( + self, x, timestep_attrs, pertrub="baseline", @@ -767,7 +662,8 @@ def explantation( ) ) f_v_score.append( - self.Faithfulness_Correlation( + Faithfulness_Correlation( + self, x, features_attrs, pertrub="baseline", @@ -829,7 +725,8 @@ def explantation( ) if XAI_metric: f_ts_v_score.append( - self.Faithfulness_Correlation( + Faithfulness_Correlation( + self, x, stacked_attr, pertrub="baseline", @@ -840,7 +737,8 @@ def explantation( ) f_ts_score.append( - self.Faithfulness_Correlation( + Faithfulness_Correlation( + self, x, stacked_attr, pertrub="baseline", @@ -850,7 +748,8 @@ def explantation( ) ) f_v_score.append( - self.Faithfulness_Correlation( + Faithfulness_Correlation( + self, x, stacked_attr, pertrub="baseline", @@ -860,7 +759,8 @@ def explantation( ) ) r_score.append( - self.Data_Randomization( + Data_Randomization( + self, x, attribution=stacked_attr, explain_method=method, @@ -869,7 +769,8 @@ def explantation( ) ) - res1, res2 = self.Relative_Stability( + res1, res2 = Relative_Stability( + self, x, stacked_attr, explain_method=method, @@ -901,14 +802,6 @@ def explantation( st_i_score = np.max(st_i_score) st_o_score = np.max(st_o_score) - """ if plot: - log_dir_plots = log_dir / "plots" - if not (log_dir_plots.exists()): - log_dir_plots.mkdir(parents=True) - # Plot attributions for features and timesteps - - self.plot_attributions(features_attrs, timestep_attrs, method_name, log_dir_plots) """ - # Return computed attributions and metrics return ( all_attrs, @@ -994,381 +887,6 @@ def apply_baseline(self, x, indices, time_step, feature, feature_timestep): x["encoder_cont"] *= mask return x - def Faithfulness_Correlation( - self, - x, - attribution, - similarity_func=None, - nr_runs=100, - pertrub=None, - subset_size=3, - feature=False, - time_step=False, - feature_timestep=False, - ): - """ - Calculates faithfulness scores for captum attributions - - Args: - - x:Batch input - -attribution: attribution generated by captum, - - similarity_func:function to determine similarity between sum of attributions and difference in prediction - - nr_runs: How many times to repeat the experiment, - - pertrub: What change to do to the input, - - subset_size: The size of the subset of featrues to alter , - - feature: Determines if to calcualte faithfulness of feature attributions, - - time_step: Determines if to calcualte faithfulness of timesteps attributions, - - feature_timestep: Determines if to calcualte faithfulness of featrues per timesteps attributions, - Returns: - score: similarity score between sum of attributions and difference in prediction averaged over nr_runs - - Implementation of faithfulness correlation by Bhatt et al., 2020. - - The Faithfulness Correlation metric intend to capture an explanation's relative faithfulness - (or 'fidelity') with respect to the model behaviour. - - Faithfulness correlation scores shows to what extent the predicted logits of each modified test point and - the average explanation attribution for only the subset of features are (linearly) correlated, taking the - average over multiple runs and test samples. The metric returns one float per input-attribution pair that - ranges between -1 and 1, where higher scores are better. - - For each test sample, |S| features are randomly selected and replace them with baseline values (zero baseline - or average of set). Thereafter, Pearson’s correlation coefficient between the predicted logits of each modified - test point and the average explanation attribution for only the subset of features is calculated. Results is - average over multiple runs and several test samples. - This code is adapted from the quantus libray to suit our use case - - References: - 1) Umang Bhatt et al.: "Evaluating and aggregating feature-based model - explanations." IJCAI (2020): 3016-3022. - 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for - responsible evaluation of neural network explanations and beyond." - Journal of Machine Learning Research 24.34 (2023): 1-11. - """ - - attribution = torch.tensor(attribution).to(self.device) - - # Other initializations - if similarity_func is None: - similarity_func = correlation_spearman - if pertrub is None: - pertrub = "baseline" - similarities = [] - - # Assuming this is a method to prepare your data - - y_pred = self(self.prep_data(x)).detach() # Keep on GPU - pred_deltas = [] - att_sums = [] - - for i_ix in range(nr_runs): - if time_step: - timesteps_idx = np.random.choice(24, subset_size, replace=False) - patient_idx = np.random.choice(64, 1, replace=False) - a_ix = [patient_idx, timesteps_idx] - - elif feature: - feature_idx = np.random.choice(53, subset_size, replace=False) - patient_idx = np.random.choice(64, 1, replace=False) - a_ix = [patient_idx, feature_idx] - elif feature_timestep: - timesteps_idx = np.random.choice(24, subset_size[0], replace=False) - feature_idx = np.random.choice(53, subset_size[1], replace=False) - patient_idx = np.random.choice(64, 1, replace=False) - a_ix = [patient_idx, timesteps_idx, feature_idx] - - # Apply perturbation - if pertrub == "Noise": - x = self.add_noise(x, a_ix, time_step, feature, feature_timestep) - elif pertrub == "baseline": - x = self.apply_baseline(x, a_ix, time_step, feature, feature_timestep) - - # Predict on perturbed input and calculate deltas - y_pred_perturb = (self(self.prep_data(x))).detach() # Keep on GPU - - if time_step: - if attribution.size() == torch.Size([24]): - att_sums.append((attribution[timesteps_idx]).sum()) - else: - att_sums.append((attribution[patient_idx, :, :][:, timesteps_idx, :]).sum()) - elif feature: - if len(attribution) == 53: - att_sums.append((attribution[feature_idx]).sum()) - else: - att_sums.append((attribution[patient_idx, :, :][:, :, feature_idx]).sum()) - elif feature_timestep: - att_sums.append((attribution[patient_idx, :, :][:, timesteps_idx, :][:, :, feature_idx]).sum()) - - pred_deltas.append((y_pred - y_pred_perturb)[patient_idx].item()) - # Convert to CPU for numpy operations - - pred_deltas_cpu = torch.tensor(pred_deltas).cpu().numpy() - att_sums_cpu = torch.tensor(att_sums).cpu().numpy() - - similarities.append(similarity_func(pred_deltas_cpu, att_sums_cpu)) - - score = np.nanmean(similarities) - return score - - def Data_Randomization( - self, - x, - attribution, - explain_method, - random_model, - similarity_func=cosine, - dataloader=None, - method_name="", - **kwargs, - ): - """ - - Args: - - x:Batch input - -attribution: attribution - - explain_method:function to generate explantations - - random_model: Reference to model trained on random labels - - similarity_func: Function to measure similiarity - - dataloader:In case of using Attention as the explain method need to pass the dataloader instead of the batch , - - method_name: Name of the explantation - - Returns: - score: similarity score between attributions of model trained on random data and model trained on real data - - Implementation of the Random Logit Metric by Sixt et al., 2020. - - The Random Logit Metric computes the distance between the original explanation and a reference explanation of - a randomly chosen non-target class. - This code is adapted from the quantus libray to suit our use case - - References: - 1) Leon Sixt et al.: "When Explanations Lie: Why Many Modified BP - Attributions Fail." ICML (2020): 9046-9057. - 2)Hedström, Anna, et al. "Quantus: An explainable ai - toolkit for responsible evaluation of neural network explanations and beyond." - Journal of Machine Learning Research 24.34 (2023): 1-11. - - """ - - if explain_method == "Attention": - Attention_weights = random_model.interpertations(dataloader) - attribution = attribution.cpu().numpy() - min_val = np.min(attribution) - max_val = np.max(attribution) - - attribution = (attribution - min_val) / (max_val - min_val) - random_attr = Attention_weights["attention"].cpu().numpy() - min_val = np.min(random_attr) - max_val = np.max(random_attr) - random_attr = (random_attr - min_val) / (max_val - min_val) - score = similarity_func(random_attr, attribution) - elif explain_method == "Random": - score = similarity_func(np.random.normal(size=[64, 24, 53]).flatten(), attribution.flatten()) - else: - data, baselines = self.prep_data_captum(x) - - explantation = explain_method(random_model.forward_captum) - # Reformat attributions. - if explain_method is not captum.attr.Saliency: - attr = explantation.attribute(data, baselines=baselines, **kwargs) - else: - attr = explantation.attribute(data, **kwargs) - - # Process and store the calculated attributions - random_attr = ( - attr[1].cpu().detach().numpy() - if method_name in ["Lime", "FeatureAblation"] - else torch.stack(attr).cpu().detach().numpy() - ) - - attribution = attribution.flatten() - min_val = np.min(attribution) - max_val = np.max(attribution) - attribution = (attribution - min_val) / (max_val - min_val) - random_attr = random_attr.flatten() - min_val = np.min(random_attr) - max_val = np.max(random_attr) - random_attr = (random_attr - min_val) / (max_val - min_val) - - score = similarity_func(random_attr, attribution) - return score - - def Relative_Stability( - self, - x, - attribution, - explain_method, - method_name, - dataloader=None, - thershold=0.5, - **kwargs, - ): - """ - Args: - - x:Batch input - -attribution: attribution - - explain_method:function to generate explantations - - method_name: Name of the explantation - - dataloader:In case of using Attention as the explain method need to pass the dataloader instead of the batch , - - - Returns: - RIS : relative distance between the explantation and the input - ROS: relative distance between the explantation and the output - - - References: - 1) `https://arxiv.org/pdf/2203.06877.pdf - 2)Hedström, Anna, et al. "Quantus: An explainable ai toolkit for responsible evaluation - of neural network explanations and beyond." Journal of Machine Learning Research 24.34 (2023): 1-11. - - """ - - def relative_stability_objective(x, xs, e_x, e_xs, eps_min=0.0001, input=False, device="cuda") -> torch.Tensor: - """ - Computes relative input and output stabilities maximization objective - as defined here :ref:`https://arxiv.org/pdf/2203.06877.pdf` by the authors. - - Args: - - x: Input tensor - xs: perturbed tensor. - e_x: Explanations for x. - e_xs: Explanations for xs. - eps_min:Value to avoid division by zero if needed - input:Boolean to indicate if this is an input or an output - device: the device to keep the tensors on - - Returns: - - ris_obj: Tensor - RIS maximization objective. - """ - - # Function to convert inputs to tensors if they are numpy arrays - def to_tensor(input_array): - if isinstance(input_array, np.ndarray): - return torch.tensor(input_array).to(device) - return input_array.to(device) - - # Convert all inputs to tensors and move to GPU - x, xs, e_x, e_xs = map(to_tensor, [x, xs, e_x, e_xs]) - - if input: - num_dim = x.ndim - else: - num_dim = e_x.ndim - - if num_dim == 3: - - def norm_function(arr): - return torch.norm(arr, dim=(-1, -2)) - - elif num_dim == 2: - - def norm_function(arr): - return torch.norm(arr, dim=-1) - - else: - - def norm_function(arr): - return torch.norm(arr) - - nominator = (e_x - e_xs) / (e_x + (e_x == 0) * eps_min) - nominator = norm_function(nominator) - - if input: - denominator = x - xs - denominator /= x + (x == 0) * eps_min - denominator = norm_function(denominator) - denominator += (denominator == 0) * eps_min - else: - denominator = torch.squeeze(x) - torch.squeeze(xs) - denominator = torch.norm(denominator, dim=-1) - denominator += (denominator == 0) * eps_min - - return nominator / denominator - - attribution = torch.tensor(attribution).to(self.device) - if explain_method == "Attention": - y_pred = self.model.predict(dataloader) - x_original = dataloader.dataset.data["reals"].clone() - - dataloader.dataset.add_noise() - x_preturb = dataloader.dataset.data["reals"].clone() - y_pred_preturb = self.model.predict(dataloader) - Attention_weights = self.interpertations(dataloader) - att_preturb = Attention_weights["attention"] - # Calculate the absolute difference - difference = torch.abs(y_pred_preturb - y_pred) - - # Find where the difference is less than or equal to a thershold - close_indices = torch.nonzero(difference <= thershold).squeeze() - RIS = relative_stability_objective( - x_original[close_indices, :, :].detach(), - x_preturb[close_indices, :, :].detach(), - attribution, - att_preturb, - input=True, - ) - - ROS = relative_stability_objective( - y_pred[close_indices], - y_pred_preturb[close_indices], - attribution, - att_preturb, - input=False, - ) - - else: - y_pred = self(self.prep_data(x)).detach() - x_original = x["encoder_cont"].detach().clone() - - with torch.no_grad(): - noise = torch.randn_like(x["encoder_cont"]) * 0.01 - x["encoder_cont"] += noise - y_pred_preturb = self(self.prep_data(x)).detach() - if explain_method == "Random": - att_preturb = np.random.normal(size=[64, 24, 53]) - att_preturb = torch.tensor(att_preturb).to(self.device) - else: - data, baselines = self.prep_data_captum(x) - - explantation = explain_method(self.forward_captum) - # Reformat attributions. - if explain_method is not captum.attr.Saliency: - att_preturb = explantation.attribute(data, baselines=baselines, **kwargs) - else: - att_preturb = explantation.attribute(data, **kwargs) - - # Process and store the calculated attributions - att_preturb = ( - att_preturb[1].detach() - if method_name in ["Lime", "FeatureAblation"] - else torch.stack(att_preturb).detach() - ) - # Calculate the absolute difference - difference = torch.abs(y_pred_preturb - y_pred) - - # Find where the difference is less than or equal to a thershold - close_indices = torch.nonzero(difference <= thershold).squeeze() - RIS = relative_stability_objective( - x_original[close_indices, :, :].detach(), - x["encoder_cont"][close_indices, :, :].detach(), - attribution[close_indices, :, :], - att_preturb[close_indices, :, :], - input=True, - ) - ROS = relative_stability_objective( - y_pred[close_indices], - y_pred_preturb[close_indices], - attribution[close_indices, :, :], - att_preturb[close_indices, :, :], - input=False, - ) - - return np.max(RIS.cpu().numpy()).astype(np.float64), np.max(ROS.cpu().numpy()).astype(np.float64) - @gin.configurable("MLWrapper") class MLWrapper(BaseModule, ABC): diff --git a/scripts/plotting/plotting.py b/scripts/plotting/plotting.py index 779eea26..881d4e1b 100644 --- a/scripts/plotting/plotting.py +++ b/scripts/plotting/plotting.py @@ -1,4 +1,5 @@ import matplotlib.pyplot as plt +import numpy as np class Plotter: @@ -49,3 +50,194 @@ def calibration_curve(self): plt.legend(loc="lower right") plt.savefig(self.save_dir / f"call_curve {self.specifier}.png") plt.clf() + + def plot_XAI_Metrics(accumulated_metrics, log_dir_plots): + groups = {} + for key in accumulated_metrics["avg"]: + if key in ["loss", "MAE"]: + continue + suffix = key.split("_")[-1] + if suffix not in groups: + groups[suffix] = [] + groups[suffix].append(key) + + # Define a dictionary for legend labels + legend_labels = { + "IG": "Integrated Gradient", + "G": "Gradient", + "R": "Random", + "FA": "Feature Ablation", + "Att": "Attention", + "VSN": "Variable Selection Network", + "L": "Lime", + } + colors = [ + "navy", + "skyblue", + "crimson", + "salmon", + "teal", + "orange", + "darkgreen", + "lightgreen", + ] + + # Plotting + num_groups = len(groups) + fig, axs = plt.subplots(num_groups, 1, figsize=(10, num_groups * 5)) + + # Custom handles for the legend + # handles = [plt.Rectangle((0, 0), 1, 1, color="none", + # label=f"{key}: {value}") for key, value in legend_labels.items()] + + for i, (suffix, keys) in enumerate(groups.items()): + ax = axs[i] if num_groups > 1 else axs + # Extract values and errors + avg_values = [accumulated_metrics["avg"][key] for key in keys] + ci_lower = [accumulated_metrics["CI_0.95"][key][0] for key in keys] + ci_upper = [accumulated_metrics["CI_0.95"][key][1] for key in keys] + ci_error = [np.abs([a - b, c - a]) for a, b, c in zip(avg_values, ci_lower, ci_upper)] + + # Sort by absolute values of avg_values + sorted_indices = np.argsort([np.abs(val) for val in avg_values])[::-1] # Indices to sort in descending order + sorted_keys = np.array(keys)[sorted_indices] + sorted_avg_values = np.array(avg_values)[sorted_indices] + sorted_ci_error = np.array(ci_error)[sorted_indices] + + # Plot bars + bars = ax.bar( + sorted_keys, + np.abs(sorted_avg_values), + yerr=np.array(sorted_ci_error).T, + capsize=5, + color=colors, + ) + + # Set titles and labels + title_suffix = sorted_keys[0].split("_")[1] + ax.set_title(f'Metric: "{title_suffix}"') + ax.set_ylabel("Values") + ax.axhline(0, color="grey", linewidth=0.8) + ax.grid(axis="y") + + # Set x-ticks + ax.set_xticks(sorted_keys) + ax.set_xticklabels([key.split("_")[0] for key in sorted_keys], rotation=45, ha="right") + # Create a custom legend for each subplot + custom_labels = [legend_labels[key.split("_")[0]] for key in sorted_keys] + ax.legend(bars, custom_labels, loc="upper right") + + plt.tight_layout() + plt.savefig(log_dir_plots / "metrics_plot.png", bbox_inches="tight") + + def plot_attributions(self, features_attrs, timestep_attrs, method_name, log_dir): + """ + Plots the attribution values for features and timesteps. + + Args: + - features_attrs: Array of feature attribution values. + - timestep_attrs: Array of timestep attribution values. + - method_name: Name of the attribution method. + - log_dir: Directory to save the plots. + Returns: + Nothing + """ + + # Plot for feature attributions + x_values = np.arange(1, len(features_attrs) + 1) + plt.figure(figsize=(8, 6)) + plt.plot( + x_values, + features_attrs, + marker="o", + color="skyblue", + linestyle="-", + linewidth=2, + markersize=8, + ) + plt.xlabel("Feature") + plt.ylabel("{} Attribution".format(method_name)) + plt.title("{} Attribution Values".format(method_name)) + plt.xticks( + x_values, + [ + "height", + "weight", + "age", + "sex", + "time_idx", + "alb", + "alp", + "alt", + "ast", + "be", + "bicar", + "bili", + "bili_dir", + "bnd", + "bun", + "ca", + "cai", + "ck", + "ckmb", + "cl", + "crea", + "crp", + "dbp", + "fgn", + "fio2", + "glu", + "hgb", + "hr", + "inr_pt", + "k", + "lact", + "lymph", + "map", + "mch", + "mchc", + "mcv", + "methb", + "mg", + "na", + "neut", + "o2sat", + "pco2", + "ph", + "phos", + "plt", + "po2", + "ptt", + "resp", + "sbp", + "temp", + "tnt", + "urine", + "wbc", + ], + rotation=90, + ) + plt.tight_layout() + plt.savefig( + log_dir / "{}_attribution_features_plot.png".format(method_name), + bbox_inches="tight", + ) + + # Plot for timestep attributions + x_values = np.arange(1, len(timestep_attrs) + 1) + plt.figure(figsize=(8, 6)) + plt.plot( + x_values, + timestep_attrs, + marker="o", + color="skyblue", + linestyle="-", + linewidth=2, + markersize=8, + ) + plt.xlabel("Time Step") + plt.ylabel("{} Attribution".format(method_name)) + plt.title("{} Attribution Values".format(method_name)) + plt.xticks(x_values) + plt.tight_layout() + plt.savefig(log_dir / "{}_attribution_plot.png".format(method_name), bbox_inches="tight") From 6782d4fefc92be69b598656a0ab178b67d84437b Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Mon, 29 Apr 2024 13:59:27 +0200 Subject: [PATCH 135/142] restored tft gin file --- icu_benchmarks/data/loader.py | 4 ++-- icu_benchmarks/models/dl_models.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 1707a2d3..f5ed350f 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -169,7 +169,7 @@ def to_tensor(self): return from_numpy(data), from_numpy(labels) -""" + @gin.configurable("PredictionDatasetTFT") class PredictionDatasetTFT(PredictionDataset): Subclass of prediction dataset for TFT as we need to define if variables are cont,static,known or observed. @@ -298,7 +298,7 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: tensors = (from_numpy(np.array(tensor)).to(float32) for tensor in tensors) tensors = [stack((x,), dim=-1) if x.numel() > 0 else empty(0) for x in tensors] return OrderedDict(zip(Features.FEAT_NAMES, tensors)), from_numpy(pad_mask) -""" + @gin.configurable("ImputationDataset") diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 98282bd4..59384af8 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -320,7 +320,7 @@ def forward(self, x): return pred -''' + @gin.configurable class TFT(DLPredictionWrapper): """ @@ -454,7 +454,7 @@ def forward(self, x: Dict[str, Tensor]) -> Tensor: ) pred = self.logit(o) return pred -''' + @gin.configurable From 3670bd6451e69e8b60db984ce0ecd25f51698fe1 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Mon, 13 May 2024 15:34:54 +0200 Subject: [PATCH 136/142] changes to get tft to work with yaib dataloader --- icu_benchmarks/data/loader.py | 9 +++++-- icu_benchmarks/models/dl_models.py | 40 ++++++++++++++++-------------- icu_benchmarks/models/train.py | 4 +-- 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index f5ed350f..809d3226 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -172,16 +172,19 @@ def to_tensor(self): @gin.configurable("PredictionDatasetTFT") class PredictionDatasetTFT(PredictionDataset): + """ Subclass of prediction dataset for TFT as we need to define if variables are cont,static,known or observed. We also need to feed the model the variables in a specific order Args: ram_cache (bool, optional): Whether the complete dataset should be stored in ram. Defaults to True. - + """ def __init__(self, *args, ram_cache: bool = True, **kwargs): super().__init__(*args, ram_cache=True, **kwargs) def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: + + """ Function to sample from the data split of choice. Used for TFT. The data needs to be given to the model in the following order [static categorical,static contious,known catergorical,known continous, @@ -189,7 +192,8 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: Args: idx: A specific row index to sample. Returns: - A sample from the data, consisting of data, labels and padding mask. + A sample from the data, consisting of data, labels and padding mask. + """ if self._cached_dataset is not None: return self._cached_dataset[idx] @@ -199,6 +203,7 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: # We need to be sure that tensors are returned in the correct order to be processed correclty by tft tensors = [[] for _ in range(8)] + print(self.features_df.columns) for var in self.features_df.columns: if var == "sex": tensors[0].append(self.features_df.loc[stay_id:stay_id][var].to_numpy()) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 59384af8..9bff14f8 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -9,6 +9,15 @@ LocalBlock, TemporalBlock, PositionalEncoding, + MaybeLayerNorm, + GLU, + GRN, + TFTEmbedding, + LazyEmbedding, + VariableSelectionNetwork, + StaticCovariateEncoder, + InterpretableMultiHeadAttention, + TFTBack ) import matplotlib.pyplot as plt from icu_benchmarks.models.wrappers import ( @@ -340,16 +349,14 @@ def __init__( dropout_att, example_length, # determines interval to predict *args, - quantiles=[0.1, 0.5, 0.9], # quantiles to produce - static_categorical_inp_size=[2], # number of catergories - temporal_known_categorical_inp_size=[], - temporal_observed_categorical_inp_size=[ - 48 - ], # number of categorical observed variables - static_continuous_inp_size=3, # number of static coutinous variables - temporal_known_continuous_inp_size=0, - temporal_observed_continuous_inp_size=48, - temporal_target_size=1, # number of target variables + quantiles :list = gin.REQUIRED, # quantiles to produce + static_categorical_inp_size:list = gin.REQUIRED , # number of catergories + temporal_known_categorical_inp_size:list = gin.REQUIRED, + temporal_observed_categorical_inp_size:list = gin.REQUIRED, # number of categorical observed variables + static_continuous_inp_size: int=gin.REQUIRED, # number of static coutinous variables + temporal_known_continuous_inp_size:int=gin.REQUIRED, + temporal_observed_continuous_inp_size:int=gin.REQUIRED, + temporal_target_size:int=gin.REQUIRED, # number of target variables **kwargs, ): # derived variables @@ -379,13 +386,6 @@ def __init__( num_future_vars=num_future_vars, num_historic_vars=num_historic_vars, *args, - static_categorical_inp_size=1, - temporal_known_categorical_inp_size=0, - temporal_observed_categorical_inp_size=48, - static_continuous_inp_size=3, - temporal_known_continuous_inp_size=0, - temporal_observed_continuous_inp_size=48, - temporal_target_size=1, **kwargs, ) @@ -420,7 +420,11 @@ def __init__( len(quantiles), num_classes ) # Linear layer on top to output to the number of classes and allow modification by predictionwrapper - def forward(self, x: Dict[str, Tensor]) -> Tensor: + def forward(self, x) -> Tensor: + #Prep data to be in format model expects + + + s_inp, t_known_inp, t_observed_inp, t_observed_tgt = self.embedding(x) # Static context cs, ce, ch, cc = self.static_encoder(s_inp) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index a911a7ac..ea400cf2 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -107,8 +107,8 @@ def train_common( dataset_class = ( ImputationDataset if mode == RunMode.imputation - else (PredictionDatasetpytorch if (pytorch_forecasting) else PredictionDataset) - ) + else PredictionDataset) + logging.info(f"Logging to directory: {log_dir}.") save_config_file(log_dir) # We save the operative config before and also after training From 8e6db7150dbe2a78a76d6b1917d552010ad54391 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Wed, 15 May 2024 13:13:29 +0200 Subject: [PATCH 137/142] infered datatypes from dict supplied through gin --- icu_benchmarks/models/constants.py | 21 ++++++++++++++++- icu_benchmarks/models/dl_models.py | 37 ++++++++++++++++++++++++------ 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/icu_benchmarks/models/constants.py b/icu_benchmarks/models/constants.py index cd03bcd0..752fd8ba 100644 --- a/icu_benchmarks/models/constants.py +++ b/icu_benchmarks/models/constants.py @@ -24,7 +24,7 @@ CalibrationError, F1Score, ) -from enum import Enum +from enum import Enum,IntEnum from icu_benchmarks.models.custom_metrics import ( CalibrationCurve, BalancedAccuracy, @@ -32,6 +32,7 @@ JSD, BinaryFairnessWrapper, ) +import gin class MLMetrics: @@ -100,3 +101,21 @@ class ImputationInit(str, Enum): XAVIER = "xavier" KAIMING = "kaiming" ORTHOGONAL = "orthogonal" + + +@gin.constants_from_enum +class DataTypes(Enum): + """Defines numerical types of each column.""" + CONTINUOUS = 0 + CATEGORICAL = 1 + DATE = 2 + STR = 3 +@gin.constants_from_enum +class InputTypes(IntEnum): + """Defines input types of each column.""" + TARGET = 0 + OBSERVED = 1 + KNOWN = 2 + STATIC = 3 + ID = 4 # Single column used as an entity identifier + TIME = 5 # Single column exclusively used as a time index \ No newline at end of file diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 9bff14f8..8b388b2c 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -27,6 +27,7 @@ from torch import Tensor from pytorch_forecasting import TemporalFusionTransformer, RecurrentNetwork, DeepAR from pytorch_forecasting.metrics import QuantileLoss +from constants import InputTypes,DataTypes @gin.configurable @@ -350,15 +351,37 @@ def __init__( example_length, # determines interval to predict *args, quantiles :list = gin.REQUIRED, # quantiles to produce - static_categorical_inp_size:list = gin.REQUIRED , # number of catergories - temporal_known_categorical_inp_size:list = gin.REQUIRED, - temporal_observed_categorical_inp_size:list = gin.REQUIRED, # number of categorical observed variables - static_continuous_inp_size: int=gin.REQUIRED, # number of static coutinous variables - temporal_known_continuous_inp_size:int=gin.REQUIRED, - temporal_observed_continuous_inp_size:int=gin.REQUIRED, - temporal_target_size:int=gin.REQUIRED, # number of target variables + # number of target variables + vars_type:dict =gin.REQUIRED, + temporal_target_size:int=gin.REQUIRED, **kwargs, ): + + static_categorical_inp_size=0 # number of catergories + temporal_known_categorical_inp_size=0 + temporal_observed_categorical_inp_size=0 # number of categorical observed variables + static_continuous_inp_size=0 # number of static coutinous variables + temporal_known_continuous_inp_size=0 + temporal_observed_continuous_inp_size=0 + for value in vars_type.values(): + if value==[DataTypes.CONTINUOUS, InputTypes.OBSERVED]: + temporal_observed_continuous_inp_size+=1 + elif value==[DataTypes.CATEGORICAL, InputTypes.OBSERVED]: + temporal_observed_categorical_inp_size+=1 + elif value==[DataTypes.CONTINUOUS, InputTypes.STATIC]: + static_continuous_inp_size+=1 + elif value==[DataTypes.CATEGORICAL, InputTypes.STATIC]: + static_categorical_inp_size+=1 + elif value==[DataTypes.CONTINUOUS, InputTypes.KNOWN]: + temporal_known_continuous_inp_size+=1 + elif value==[DataTypes.CATEGORICAL, InputTypes.KNOWN]: + temporal_known_categorical_inp_size+=1 + else: + print('incorrect datatype') + + + + # derived variables num_static_vars = len(static_categorical_inp_size) + static_continuous_inp_size num_future_vars = ( From 97d41f7b784f0545d8d895723249f4e20cc0ca3f Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Fri, 17 May 2024 15:33:51 +0200 Subject: [PATCH 138/142] adjusted forward method in tft to handle input as model expects --- icu_benchmarks/models/dl_models.py | 38 +++++++++++++++++++++++++----- icu_benchmarks/models/layers.py | 4 ++-- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 8b388b2c..868a2161 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -24,7 +24,7 @@ DLPredictionWrapper, DLPredictionPytorchForecastingWrapper, ) -from torch import Tensor +from torch import Tensor,cat,stack,empty from pytorch_forecasting import TemporalFusionTransformer, RecurrentNetwork, DeepAR from pytorch_forecasting.metrics import QuantileLoss from constants import InputTypes,DataTypes @@ -353,17 +353,19 @@ def __init__( quantiles :list = gin.REQUIRED, # quantiles to produce # number of target variables vars_type:dict =gin.REQUIRED, + vars: Dict[str, str] = gin.REQUIRED, temporal_target_size:int=gin.REQUIRED, **kwargs, ): - + self.vars_type=vars_type + self.vars=vars static_categorical_inp_size=0 # number of catergories temporal_known_categorical_inp_size=0 temporal_observed_categorical_inp_size=0 # number of categorical observed variables static_continuous_inp_size=0 # number of static coutinous variables temporal_known_continuous_inp_size=0 temporal_observed_continuous_inp_size=0 - for value in vars_type.values(): + for value in self.vars_type.values(): if value==[DataTypes.CONTINUOUS, InputTypes.OBSERVED]: temporal_observed_continuous_inp_size+=1 elif value==[DataTypes.CATEGORICAL, InputTypes.OBSERVED]: @@ -445,10 +447,34 @@ def __init__( def forward(self, x) -> Tensor: #Prep data to be in format model expects + tensors = [[] for _ in range(8)] + i=0 + nan_array = np.full_like(x[:, 0], np.nan) + for var in vars: + + if self.vars_type[var] == [DataTypes.CATEGORICAL, InputTypes.STATIC]: + tensors[0].append(x[:, i].to_numpy()) + elif self.vars_type[var] == [DataTypes.CONTINUOUS, InputTypes.STATIC]: + tensors[1].append(x[:, i].to_numpy()) + elif self.vars_type[var] == [DataTypes.CATEGORICAL, InputTypes.KNOWN]: + tensors[2].append(x[:, i].to_numpy()) + elif self.vars_type[var] == [DataTypes.CONTINUOUS, InputTypes.KNOWN]: + tensors[3].append(x[:, i].to_numpy()) + elif self.vars_type[var] == [DataTypes.CATEGORICAL, InputTypes.OBSERVED]: + tensors[4].append(x[:, i].to_numpy()) + elif self.vars_type[var] == [DataTypes.CONTINUOUS, InputTypes.OBSERVED]: + tensors[5].append(x[:, i].to_numpy()) + + i+=1 + tensors[6].append( + nan_array#target needs to be there + ) + + - - - s_inp, t_known_inp, t_observed_inp, t_observed_tgt = self.embedding(x) + tensors = [stack(x, dim=-1) if x else empty(0) for x in tensors] + FEAT_NAMES = ['s_cat' , 's_cont' , 'k_cat' , 'k_cont' , 'o_cat' , 'o_cont' , 'target'] + s_inp, t_known_inp, t_observed_inp, t_observed_tgt = self.embedding(OrderedDict(zip(FEAT_NAMES, tensors))) # Static context cs, ce, ch, cc = self.static_encoder(s_inp) ch, cc = ch.unsqueeze(0), cc.unsqueeze(0) # lstm initial states diff --git a/icu_benchmarks/models/layers.py b/icu_benchmarks/models/layers.py index 944f4b89..e276efb2 100644 --- a/icu_benchmarks/models/layers.py +++ b/icu_benchmarks/models/layers.py @@ -8,7 +8,7 @@ from torch.nn.parameter import UninitializedParameter from typing import Dict, Tuple, Optional from torch.nn import LayerNorm - +from collections import OrderedDict @gin.configurable("masking") def parallel_recomb(q_t, kv_t, att_type="all", local_context=3, bin_size=None): @@ -546,7 +546,7 @@ def forward(self, x: Dict[str, Tensor]): t_cont_k_inp = x.get("k_cont", None) t_cat_o_inp = x.get("o_cat", None) t_cont_o_inp = x.get("o_cont", None) - t_tgt_obs = x["target"] # Has to be present + t_tgt_obs = x.get("target") # Has to be present # Static inputs are expected to be equal for all timesteps # For memory efficiency there is no assert statement From afab7a44cdce9f4c98370ea950330ef59d41cfc1 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Thu, 23 May 2024 13:18:28 +0200 Subject: [PATCH 139/142] changes to get tft to work --- configs/prediction_models/common/DLCommon.gin | 2 +- icu_benchmarks/data/loader.py | 3 - icu_benchmarks/models/dl_models.py | 56 +++++++++++-------- icu_benchmarks/models/layers.py | 8 +-- 4 files changed, 36 insertions(+), 33 deletions(-) diff --git a/configs/prediction_models/common/DLCommon.gin b/configs/prediction_models/common/DLCommon.gin index bfc48ef5..0d44a936 100644 --- a/configs/prediction_models/common/DLCommon.gin +++ b/configs/prediction_models/common/DLCommon.gin @@ -15,7 +15,7 @@ base_regression_preprocessor.generate_features = False # Train params train_common.optimizer = @Adam train_common.epochs = 1000 -#train_common.batch_size = 64 +#train_common.batch_size = 2 train_common.patience = 20 train_common.min_delta = 1e-4 diff --git a/icu_benchmarks/data/loader.py b/icu_benchmarks/data/loader.py index 809d3226..a0eafc38 100644 --- a/icu_benchmarks/data/loader.py +++ b/icu_benchmarks/data/loader.py @@ -102,10 +102,8 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: """ if self._cached_dataset is not None: return self._cached_dataset[idx] - pad_value = 0.0 stay_id = self.outcome_df.index.unique()[idx] - # slice to make sure to always return a DF window = self.features_df.loc[stay_id:stay_id].to_numpy() labels = self.outcome_df.loc[stay_id:stay_id][self.vars["LABEL"]].to_numpy(dtype=float) @@ -203,7 +201,6 @@ def __getitem__(self, idx: int) -> Tuple[Tensor, Tensor, Tensor]: # We need to be sure that tensors are returned in the correct order to be processed correclty by tft tensors = [[] for _ in range(8)] - print(self.features_df.columns) for var in self.features_df.columns: if var == "sex": tensors[0].append(self.features_df.loc[stay_id:stay_id][var].to_numpy()) diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 868a2161..9e19a42c 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -4,6 +4,7 @@ import torch.nn as nn from typing import Dict from icu_benchmarks.contants import RunMode +from icu_benchmarks.models.constants import InputTypes,DataTypes from icu_benchmarks.models.layers import ( TransformerBlock, LocalBlock, @@ -24,10 +25,10 @@ DLPredictionWrapper, DLPredictionPytorchForecastingWrapper, ) -from torch import Tensor,cat,stack,empty +from torch import Tensor,cat,stack,empty,from_numpy from pytorch_forecasting import TemporalFusionTransformer, RecurrentNetwork, DeepAR from pytorch_forecasting.metrics import QuantileLoss -from constants import InputTypes,DataTypes +from collections import OrderedDict @gin.configurable @@ -359,25 +360,26 @@ def __init__( ): self.vars_type=vars_type self.vars=vars - static_categorical_inp_size=0 # number of catergories - temporal_known_categorical_inp_size=0 - temporal_observed_categorical_inp_size=0 # number of categorical observed variables + static_categorical_inp_size=[] # number of catergories + temporal_known_categorical_inp_size=[] + temporal_observed_categorical_inp_size=[] # number of categories in each category of observed variables static_continuous_inp_size=0 # number of static coutinous variables temporal_known_continuous_inp_size=0 temporal_observed_continuous_inp_size=0 + #Infering # of varaibles in each category based on the gin input for value in self.vars_type.values(): if value==[DataTypes.CONTINUOUS, InputTypes.OBSERVED]: temporal_observed_continuous_inp_size+=1 - elif value==[DataTypes.CATEGORICAL, InputTypes.OBSERVED]: - temporal_observed_categorical_inp_size+=1 + elif value[0:2]==[DataTypes.CATEGORICAL, InputTypes.OBSERVED]:#categoral variables need to define also # of categories + temporal_observed_categorical_inp_size.append(value[2]) elif value==[DataTypes.CONTINUOUS, InputTypes.STATIC]: static_continuous_inp_size+=1 - elif value==[DataTypes.CATEGORICAL, InputTypes.STATIC]: - static_categorical_inp_size+=1 + elif value[0:2]==[DataTypes.CATEGORICAL, InputTypes.STATIC]: + static_categorical_inp_size.append(value[2]) elif value==[DataTypes.CONTINUOUS, InputTypes.KNOWN]: temporal_known_continuous_inp_size+=1 - elif value==[DataTypes.CATEGORICAL, InputTypes.KNOWN]: - temporal_known_categorical_inp_size+=1 + elif value[0:2]==[DataTypes.CATEGORICAL, InputTypes.KNOWN]: + temporal_known_categorical_inp_size.append(value[2]) else: print('incorrect datatype') @@ -449,29 +451,35 @@ def forward(self, x) -> Tensor: #Prep data to be in format model expects tensors = [[] for _ in range(8)] i=0 - nan_array = np.full_like(x[:, 0], np.nan) - for var in vars: - - if self.vars_type[var] == [DataTypes.CATEGORICAL, InputTypes.STATIC]: - tensors[0].append(x[:, i].to_numpy()) + nan_array = from_numpy(np.full_like(x[:, 0], np.nan))#target is nan in the input + print(x.size()) + for var in self.vars: + print(var) + print(i) + print(self.vars_type[var]) + print(x[:, i].size()) + + + if self.vars_type[var][0:2] == [DataTypes.CATEGORICAL, InputTypes.STATIC]: + tensors[0].append(x[:, i]) elif self.vars_type[var] == [DataTypes.CONTINUOUS, InputTypes.STATIC]: - tensors[1].append(x[:, i].to_numpy()) - elif self.vars_type[var] == [DataTypes.CATEGORICAL, InputTypes.KNOWN]: - tensors[2].append(x[:, i].to_numpy()) + tensors[1].append(x[:, i]) + elif self.vars_type[var][0:2] == [DataTypes.CATEGORICAL, InputTypes.KNOWN]: + tensors[2].append(x[:, i]) elif self.vars_type[var] == [DataTypes.CONTINUOUS, InputTypes.KNOWN]: - tensors[3].append(x[:, i].to_numpy()) - elif self.vars_type[var] == [DataTypes.CATEGORICAL, InputTypes.OBSERVED]: - tensors[4].append(x[:, i].to_numpy()) + tensors[3].append(x[:, i]) + elif self.vars_type[var][0:2] == [DataTypes.CATEGORICAL, InputTypes.OBSERVED]: + tensors[4].append(x[:, i]) elif self.vars_type[var] == [DataTypes.CONTINUOUS, InputTypes.OBSERVED]: - tensors[5].append(x[:, i].to_numpy()) + tensors[5].append(x[:, i]) i+=1 + tensors[6].append( nan_array#target needs to be there ) - tensors = [stack(x, dim=-1) if x else empty(0) for x in tensors] FEAT_NAMES = ['s_cat' , 's_cont' , 'k_cat' , 'k_cont' , 'o_cat' , 'o_cont' , 'target'] s_inp, t_known_inp, t_observed_inp, t_observed_tgt = self.embedding(OrderedDict(zip(FEAT_NAMES, tensors))) diff --git a/icu_benchmarks/models/layers.py b/icu_benchmarks/models/layers.py index e276efb2..860a8339 100644 --- a/icu_benchmarks/models/layers.py +++ b/icu_benchmarks/models/layers.py @@ -445,7 +445,6 @@ def __init__( # 5. Temporal observed categorical # 6. Temporal observed continuous # 7. Temporal observed targets (time series obseved so far) - self.s_cat_embed = ( nn.ModuleList([nn.Embedding(n, self.hidden) for n in self.s_cat_inp_size]) if self.s_cat_inp_size else None ) @@ -552,7 +551,6 @@ def forward(self, x: Dict[str, Tensor]): s_cat_inp = s_cat_inp[:, 0, :] if s_cat_inp is not None else None s_cont_inp = s_cont_inp[:, 0, :] if s_cont_inp is not None else None - s_inp = self._apply_embedding( s_cat_inp, s_cont_inp, self.s_cat_embed, self.s_cont_embedding_vectors, self.s_cont_embedding_bias ) @@ -627,15 +625,15 @@ def initialize_parameters(self, x): t_cont_k_inp = x.get("k_cont", None) t_cont_o_inp = x.get("o_cont", None) t_tgt_obs = x["target"] # Has to be present - if (s_cont_inp is not None) and (s_cont_inp.size()[1] > 0): + if (s_cont_inp is not None) and (s_cont_inp.size()[0] > 0): self.s_cont_embedding_vectors.materialize((s_cont_inp.shape[-1], self.hidden)) self.s_cont_embedding_bias.materialize((s_cont_inp.shape[-1], self.hidden)) - if (t_cont_k_inp is not None) and (t_cont_k_inp.size()[1] > 0): + if (t_cont_k_inp is not None) and (t_cont_k_inp.size()[0] > 0): self.t_cont_k_embedding_vectors.materialize((t_cont_k_inp.shape[-1], self.hidden)) self.t_cont_k_embedding_bias.materialize((t_cont_k_inp.shape[-1], self.hidden)) - if (t_cont_o_inp) is not None and (t_cont_o_inp.size()[1] > 0): + if (t_cont_o_inp) is not None and (t_cont_o_inp.size()[0] > 0): self.t_cont_o_embedding_vectors.materialize((t_cont_o_inp.shape[-1], self.hidden)) self.t_cont_o_embedding_bias.materialize((t_cont_o_inp.shape[-1], self.hidden)) From f6390533122157aae4662215c7a01a1ad7f6158b Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Thu, 23 May 2024 13:30:40 +0200 Subject: [PATCH 140/142] changed size check and sliced along the correct dimension --- configs/prediction_models/common/DLCommon.gin | 2 +- icu_benchmarks/models/dl_models.py | 20 +++++++++---------- icu_benchmarks/models/layers.py | 5 +++-- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/configs/prediction_models/common/DLCommon.gin b/configs/prediction_models/common/DLCommon.gin index 0d44a936..1544d07b 100644 --- a/configs/prediction_models/common/DLCommon.gin +++ b/configs/prediction_models/common/DLCommon.gin @@ -15,7 +15,7 @@ base_regression_preprocessor.generate_features = False # Train params train_common.optimizer = @Adam train_common.epochs = 1000 -#train_common.batch_size = 2 +train_common.batch_size = 10 train_common.patience = 20 train_common.min_delta = 1e-4 diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 9e19a42c..8c7a29df 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -449,29 +449,27 @@ def __init__( def forward(self, x) -> Tensor: #Prep data to be in format model expects + tensors = [[] for _ in range(8)] i=0 nan_array = from_numpy(np.full_like(x[:, 0], np.nan))#target is nan in the input - print(x.size()) + for var in self.vars: - print(var) - print(i) - print(self.vars_type[var]) - print(x[:, i].size()) + if self.vars_type[var][0:2] == [DataTypes.CATEGORICAL, InputTypes.STATIC]: - tensors[0].append(x[:, i]) + tensors[0].append(x[:, :, i]) elif self.vars_type[var] == [DataTypes.CONTINUOUS, InputTypes.STATIC]: - tensors[1].append(x[:, i]) + tensors[1].append(x[:, :, i]) elif self.vars_type[var][0:2] == [DataTypes.CATEGORICAL, InputTypes.KNOWN]: - tensors[2].append(x[:, i]) + tensors[2].append(x[:, :, i]) elif self.vars_type[var] == [DataTypes.CONTINUOUS, InputTypes.KNOWN]: - tensors[3].append(x[:, i]) + tensors[3].append(x[:, :, i]) elif self.vars_type[var][0:2] == [DataTypes.CATEGORICAL, InputTypes.OBSERVED]: - tensors[4].append(x[:, i]) + tensors[4].append(x[:, :, i]) elif self.vars_type[var] == [DataTypes.CONTINUOUS, InputTypes.OBSERVED]: - tensors[5].append(x[:, i]) + tensors[5].append(x[:, :, i]) i+=1 diff --git a/icu_benchmarks/models/layers.py b/icu_benchmarks/models/layers.py index 860a8339..b88d26f5 100644 --- a/icu_benchmarks/models/layers.py +++ b/icu_benchmarks/models/layers.py @@ -513,12 +513,13 @@ def _apply_embedding( cont_emb: Tensor, cont_bias: Tensor, ) -> Tuple[Optional[Tensor], Optional[Tensor]]: + e_cat = ( torch.stack([embed(cat[..., i].int()) for i, embed in enumerate(cat_emb)], dim=-2) - if (cat is not None) and (cat.size()[1] > 0) + if (cat is not None) and (cat.size()[0] > 0) else None ) - if (cont is not None) and (cont.size()[1] > 0): + if (cont is not None) and (cont.size()[0] > 0): # the line below is equivalent to following einsums # e_cont = torch.einsum('btf,fh->bthf', cont, cont_emb) # e_cont = torch.einsum('bf,fh->bhf', cont, cont_emb) From 039377af74cb77850dcda7a63e19cd2ccaba3ec8 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Mon, 3 Jun 2024 15:20:24 +0200 Subject: [PATCH 141/142] added changes to get pytorch forecasting to wrok and gradient clipping --- configs/prediction_models/common/DLCommon.gin | 2 +- configs/tasks/Regression.gin | 2 +- icu_benchmarks/cross_validation.py | 1 - icu_benchmarks/models/dl_models.py | 7 ++-- icu_benchmarks/models/layers.py | 38 ++++++++++++++----- icu_benchmarks/models/train.py | 7 ++-- icu_benchmarks/models/wrappers.py | 5 ++- 7 files changed, 40 insertions(+), 22 deletions(-) diff --git a/configs/prediction_models/common/DLCommon.gin b/configs/prediction_models/common/DLCommon.gin index 1544d07b..6b76c60d 100644 --- a/configs/prediction_models/common/DLCommon.gin +++ b/configs/prediction_models/common/DLCommon.gin @@ -15,7 +15,7 @@ base_regression_preprocessor.generate_features = False # Train params train_common.optimizer = @Adam train_common.epochs = 1000 -train_common.batch_size = 10 +#train_common.batch_size = 10 train_common.patience = 20 train_common.min_delta = 1e-4 diff --git a/configs/tasks/Regression.gin b/configs/tasks/Regression.gin index 6ce4c0f6..1234f707 100644 --- a/configs/tasks/Regression.gin +++ b/configs/tasks/Regression.gin @@ -16,7 +16,7 @@ train_common.ram_cache = True # LOSS FUNCTION DLPredictionWrapper.loss = @mse_loss -DLPredictionPytorchForecastingWrapper.loss = @l1_loss +DLPredictionPytorchForecastingWrapper.loss = @mse_loss MLWrapper.loss = @mean_squared_error # SELECTING PREPROCESSOR diff --git a/icu_benchmarks/cross_validation.py b/icu_benchmarks/cross_validation.py index c19076e5..ffa11b2a 100644 --- a/icu_benchmarks/cross_validation.py +++ b/icu_benchmarks/cross_validation.py @@ -132,7 +132,6 @@ def execute_repeated_cv( ) train_time = datetime.now() - start_time - log_full_line( f"FINISHED FOLD {fold_index}| PREPROCESSING DURATION {preprocess_time}| PROCEDURE DURATION {train_time}", level=logging.INFO, diff --git a/icu_benchmarks/models/dl_models.py b/icu_benchmarks/models/dl_models.py index 8c7a29df..68b6d2ae 100644 --- a/icu_benchmarks/models/dl_models.py +++ b/icu_benchmarks/models/dl_models.py @@ -25,7 +25,7 @@ DLPredictionWrapper, DLPredictionPytorchForecastingWrapper, ) -from torch import Tensor,cat,stack,empty,from_numpy +from torch import Tensor,cat,stack,from_numpy,zeros from pytorch_forecasting import TemporalFusionTransformer, RecurrentNetwork, DeepAR from pytorch_forecasting.metrics import QuantileLoss from collections import OrderedDict @@ -452,7 +452,7 @@ def forward(self, x) -> Tensor: tensors = [[] for _ in range(8)] i=0 - nan_array = from_numpy(np.full_like(x[:, 0], np.nan))#target is nan in the input + nan_array = from_numpy(np.full_like(x[:,:, 0].cpu(), -1)).to(x[:,:, 0].device)#target is nan in the input for var in self.vars: @@ -478,11 +478,12 @@ def forward(self, x) -> Tensor: ) - tensors = [stack(x, dim=-1) if x else empty(0) for x in tensors] + tensors = [stack(x, dim=-1) if x else zeros(0) for x in tensors] FEAT_NAMES = ['s_cat' , 's_cont' , 'k_cat' , 'k_cont' , 'o_cat' , 'o_cont' , 'target'] s_inp, t_known_inp, t_observed_inp, t_observed_tgt = self.embedding(OrderedDict(zip(FEAT_NAMES, tensors))) # Static context cs, ce, ch, cc = self.static_encoder(s_inp) + ch, cc = ch.unsqueeze(0), cc.unsqueeze(0) # lstm initial states # Temporal input diff --git a/icu_benchmarks/models/layers.py b/icu_benchmarks/models/layers.py index b88d26f5..0ea1bb02 100644 --- a/icu_benchmarks/models/layers.py +++ b/icu_benchmarks/models/layers.py @@ -421,7 +421,7 @@ def __init__( temporal_observed_continuous_inp_size, temporal_target_size, hidden, - initialize_cont_params=False, + initialize_cont_params=True, ): # initialize_cont_params=False prevents form initializing parameters inside this class # so they can be lazily initialized in LazyEmbedding module @@ -513,22 +513,36 @@ def _apply_embedding( cont_emb: Tensor, cont_bias: Tensor, ) -> Tuple[Optional[Tensor], Optional[Tensor]]: - + """ print("Input cat:") + print(cat) + print("Shape:", cat.shape) + print("Contains NaNs:", torch.isnan(cat).any()) + if cat is not None and cat.size(0) > 0: + e_cat = [] + for i, embed in enumerate(cat_emb): + indices = cat[..., i].int() + embedded_values = embed(indices) + print(f"Output of embedding layer {i}:") + print(embedded_values) + print("Contains NaNs:", torch.isnan(embedded_values).any()) + e_cat.append(embedded_values) + e_cat = torch.stack(e_cat, dim=-2) + print(1,e_cat) """ e_cat = ( torch.stack([embed(cat[..., i].int()) for i, embed in enumerate(cat_emb)], dim=-2) if (cat is not None) and (cat.size()[0] > 0) else None - ) + ) + #print(2,e_cat) + if (cont is not None) and (cont.size()[0] > 0): - # the line below is equivalent to following einsums - # e_cont = torch.einsum('btf,fh->bthf', cont, cont_emb) - # e_cont = torch.einsum('bf,fh->bhf', cont, cont_emb) + + e_cont = torch.mul(cont.unsqueeze(-1), cont_emb) e_cont = e_cont + cont_bias - # e_cont = fused_pointwise_linear_v1(cont, cont_emb, cont_bias) + else: e_cont = None - if e_cat is not None and e_cont is not None: return torch.cat([e_cat, e_cont], dim=-2) elif e_cat is not None: @@ -549,25 +563,28 @@ def forward(self, x: Dict[str, Tensor]): t_tgt_obs = x.get("target") # Has to be present # Static inputs are expected to be equal for all timesteps # For memory efficiency there is no assert statement + s_cat_inp = s_cat_inp[:, 0, :] if s_cat_inp is not None else None s_cont_inp = s_cont_inp[:, 0, :] if s_cont_inp is not None else None s_inp = self._apply_embedding( s_cat_inp, s_cont_inp, self.s_cat_embed, self.s_cont_embedding_vectors, self.s_cont_embedding_bias ) + t_known_inp = self._apply_embedding( t_cat_k_inp, t_cont_k_inp, self.t_cat_k_embed, self.t_cont_k_embedding_vectors, self.t_cont_k_embedding_bias ) - + t_observed_inp = self._apply_embedding( t_cat_o_inp, t_cont_o_inp, self.t_cat_o_embed, self.t_cont_o_embedding_vectors, self.t_cont_o_embedding_bias ) - + # Temporal observed targets t_observed_tgt = torch.matmul(t_tgt_obs.unsqueeze(3).unsqueeze(4), self.t_tgt_embedding_vectors.unsqueeze(1)).squeeze( 3 ) + t_observed_tgt = t_observed_tgt + self.t_tgt_embedding_bias return s_inp, t_known_inp, t_observed_inp, t_observed_tgt @@ -621,6 +638,7 @@ def __init__( self.t_tgt_embedding_bias = UninitializedParameter() def initialize_parameters(self, x): + if self.has_uninitialized_params(): s_cont_inp = x.get("s_cont", None) t_cont_k_inp = x.get("k_cont", None) diff --git a/icu_benchmarks/models/train.py b/icu_benchmarks/models/train.py index ea400cf2..86677269 100644 --- a/icu_benchmarks/models/train.py +++ b/icu_benchmarks/models/train.py @@ -104,10 +104,8 @@ def train_common( logging.info(f"Training model: {model.__name__}.") # choose dataset_class based on the model - dataset_class = ( - ImputationDataset - if mode == RunMode.imputation - else PredictionDataset) + dataset_class = ImputationDataset if mode == RunMode.imputation else (PredictionDatasetpytorch if pytorch_forecasting else PredictionDataset) + logging.info(f"Logging to directory: {log_dir}.") @@ -179,6 +177,7 @@ def train_common( logger=loggers, num_sanity_val_steps=-1, log_every_n_steps=5, + gradient_clip_val=gradient_clip_val ) if not eval_only: if model.requires_backprop: diff --git a/icu_benchmarks/models/wrappers.py b/icu_benchmarks/models/wrappers.py index ae70406a..3ca1322a 100644 --- a/icu_benchmarks/models/wrappers.py +++ b/icu_benchmarks/models/wrappers.py @@ -319,7 +319,7 @@ def step_fn(self, element, step_prefix=""): element (object): step_prefix (str): Step type, by default: test, train, val. """ - + if len(element) == 2: data, labels = element[0], (element[1]).to(self.device) if isinstance(data, list): @@ -342,8 +342,9 @@ def step_fn(self, element, step_prefix=""): data = data.float().to(self.device) else: raise Exception("Loader should return either (data, label) or (data, label, mask)") + out = self(data) - + # If aux_loss is present, it is returned as a tuple if len(out) == 2 and isinstance(out, tuple): out, aux_loss = out From 2b509d2b59aec8880400aa572cdcd0d8b5f06346 Mon Sep 17 00:00:00 2001 From: youssefmecky96 Date: Mon, 3 Jun 2024 15:32:50 +0200 Subject: [PATCH 142/142] config file for tft --- configs/prediction_models/TFT.gin | 166 ++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 configs/prediction_models/TFT.gin diff --git a/configs/prediction_models/TFT.gin b/configs/prediction_models/TFT.gin new file mode 100644 index 00000000..4a0a4e82 --- /dev/null +++ b/configs/prediction_models/TFT.gin @@ -0,0 +1,166 @@ +# Settings for TFT model. + +# Common settings for DL models +include "configs/prediction_models/common/DLCommon.gin" + +# Optimizer params +train_common.model = @TFT +train_common/hyperparameter.class_to_tune = @train_common + + +optimizer/hyperparameter.class_to_tune = @Adam +optimizer/hyperparameter.weight_decay = 1e-6 +optimizer/hyperparameter.lr = (0.0001, 0.001,0.01) +#(0.0001, 0.001,0.01) +# Encoder params +model/hyperparameter.class_to_tune = @TFT +model/hyperparameter.encoder_length = 24 +model/hyperparameter.hidden = 32 +#(10,20,40,80,160,240,320) +model/hyperparameter.num_classes = %NUM_CLASSES +model/hyperparameter.dropout = (0.37, 0.38) +model/hyperparameter.dropout_att = 0.0589 +model/hyperparameter.n_heads =2 +#(1,2,4) +model/hyperparameter.example_length=25 + +#TFT parameters + +TFT.temporal_target_size=1 +TFT.quantiles=[0.1,0.5,0.9] + +train_common/hyperparameter.batch_size=64 +#(32,64,128,256,512) + +train_common/hyperparameter.gradient_clip_val=0.01 +#(0,0.01, 1.0, 100.0) + + +#Vars types + +TFT.vars_type = { + "alb": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "alp": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "alt": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "ast": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "be": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "bicar": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "bili": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "bili_dir": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "bnd": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "bun": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "ca": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "cai": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "ck": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "ckmb": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "cl": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "crea": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "crp": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "dbp": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "fgn": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "fio2": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "glu": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "hgb": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "hr": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "inr_pt": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "k": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "lact": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "lymph": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "map": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "mch": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "mchc": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "mcv": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "methb": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "mg": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "na": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "neut": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "o2sat": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "pco2": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "ph": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "phos": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "plt": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "po2": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "ptt": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "resp": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "sbp": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "temp": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "tnt": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "urine": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "wbc": [%DataTypes.CONTINUOUS, %InputTypes.OBSERVED], + "MissingIndicator_1": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_2": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_3": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_4": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_5": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_6": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_7": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_8": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_9": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_10": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_11": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_12": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_13": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_14": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_15": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_16": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_17": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_18": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_19": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_20": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_21": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_22": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_23": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_24": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_25": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_26": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_27": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_28": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_29": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_30": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_31": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_32": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_33": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_34": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_35": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_36": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_37": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_38": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_39": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_40": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_41": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_42": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_43": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_44": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_45": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_46": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_47": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "MissingIndicator_48": [%DataTypes.CATEGORICAL, %InputTypes.OBSERVED, 2], + "age": [%DataTypes.CONTINUOUS, %InputTypes.STATIC], + "sex": [%DataTypes.CATEGORICAL, %InputTypes.STATIC,2], + "height": [%DataTypes.CONTINUOUS, %InputTypes.STATIC], + "weight": [%DataTypes.CONTINUOUS, %InputTypes.STATIC] + } + + +TFT.vars= ['alb', 'alp', 'alt', 'ast', 'be', 'bicar', 'bili', 'bili_dir', 'bnd', + 'bun', 'ca', 'cai', 'ck', 'ckmb', 'cl', 'crea', 'crp', 'dbp', 'fgn', + 'fio2', 'glu', 'hgb', 'hr', 'inr_pt', 'k', 'lact', 'lymph', 'map', + 'mch', 'mchc', 'mcv', 'methb', 'mg', 'na', 'neut', 'o2sat', 'pco2', + 'ph', 'phos', 'plt', 'po2', 'ptt', 'resp', 'sbp', 'temp', 'tnt', + 'urine', 'wbc', 'MissingIndicator_1', 'MissingIndicator_2', + 'MissingIndicator_3', 'MissingIndicator_4', 'MissingIndicator_5', + 'MissingIndicator_6', 'MissingIndicator_7', 'MissingIndicator_8', + 'MissingIndicator_9', 'MissingIndicator_10', 'MissingIndicator_11', + 'MissingIndicator_12', 'MissingIndicator_13', 'MissingIndicator_14', + 'MissingIndicator_15', 'MissingIndicator_16', 'MissingIndicator_17', + 'MissingIndicator_18', 'MissingIndicator_19', 'MissingIndicator_20', + 'MissingIndicator_21', 'MissingIndicator_22', 'MissingIndicator_23', + 'MissingIndicator_24', 'MissingIndicator_25', 'MissingIndicator_26', + 'MissingIndicator_27', 'MissingIndicator_28', 'MissingIndicator_29', + 'MissingIndicator_30', 'MissingIndicator_31', 'MissingIndicator_32', + 'MissingIndicator_33', 'MissingIndicator_34', 'MissingIndicator_35', + 'MissingIndicator_36', 'MissingIndicator_37', 'MissingIndicator_38', + 'MissingIndicator_39', 'MissingIndicator_40', 'MissingIndicator_41', + 'MissingIndicator_42', 'MissingIndicator_43', 'MissingIndicator_44', + 'MissingIndicator_45', 'MissingIndicator_46', 'MissingIndicator_47', + 'MissingIndicator_48', 'age', 'sex', 'height', 'weight']