From f30b53a68ad711126730998a0f0cef906f83b062 Mon Sep 17 00:00:00 2001 From: fazelehh <78154933+fazelehh@users.noreply.github.com> Date: Fri, 10 Jan 2025 12:43:36 +0100 Subject: [PATCH] GRU model for LOS (#170) * ReadMe file for creating dataset from MIMICIII, and training LOS>3 classifier * adding links to readMe file * fixing the LR model * Mimic_Hnadler, data_processeing, model and the main file * all attacks are running * add relevant files from MIMIC_Extract and mimic_code repos * fix some bugs regarding nivduration * revert conflicting tabular_mia files * adding subset to the mimicDataset * fixing loading the database and indices * adding GRU * adding GRU-D structure * updating grud class * adding early stopping * the model is running * the model is running * working on meta data * mimic hanlder gru is added * LiRA in place * running lira * LiRA and RMIA for GRU, temp implmemntation * adding flag for changing the classifier * LR and read me * up to adding report handler module * renaming files, adding report handler module * renaming files, adding report handler module * adding the license, renaming some files and classes * fixing the accuracy in model handler * fixing pandas warning and adding a check to saved shadow models model type * fixing a bug in shadow_model_handler * ruff fix --- examples/mia/LOS/ReadMe.md | 5 + examples/mia/LOS/audit.yaml | 27 +- examples/mia/LOS/mimic_GRUD_main.ipynb | 846 ++++++++++++++++++ .../{mimic_handler.py => mimic_LR_handler.py} | 13 +- examples/mia/LOS/mimic_LR_main.ipynb | 318 +++++++ examples/mia/LOS/mimic_gru_handler.py | 82 ++ examples/mia/LOS/mimic_main.ipynb | 278 ------ .../mimiciii_prepration/MIMIC_Extract/run.sh | 2 +- examples/mia/LOS/utils/data_processing.py | 366 +++++--- .../mia/LOS/utils/{model.py => model_LR.py} | 79 +- examples/mia/LOS/utils/model_grud.py | 447 +++++++++ examples/mia/cifar/audit.yaml | 22 +- leakpro/attacks/utils/shadow_model_handler.py | 61 +- .../input_handler/abstract_input_handler.py | 5 + 14 files changed, 2063 insertions(+), 488 deletions(-) create mode 100644 examples/mia/LOS/ReadMe.md create mode 100644 examples/mia/LOS/mimic_GRUD_main.ipynb rename examples/mia/LOS/{mimic_handler.py => mimic_LR_handler.py} (92%) create mode 100644 examples/mia/LOS/mimic_LR_main.ipynb create mode 100644 examples/mia/LOS/mimic_gru_handler.py delete mode 100644 examples/mia/LOS/mimic_main.ipynb rename examples/mia/LOS/utils/{model.py => model_LR.py} (76%) create mode 100644 examples/mia/LOS/utils/model_grud.py diff --git a/examples/mia/LOS/ReadMe.md b/examples/mia/LOS/ReadMe.md new file mode 100644 index 00000000..973e906d --- /dev/null +++ b/examples/mia/LOS/ReadMe.md @@ -0,0 +1,5 @@ +# Lenght-of-Stay Usecase +In this use case, we focus on attacking length-of-stay classifier models. As part of the example, we train a Logistic Regression model and a Gated Recurrent Unit with Decay (GRU-D). + +## MIMIC-III Data Preprocessing +To prepare the data, refer to instructions in ```mimic_prepration/ReadMe.md ``` diff --git a/examples/mia/LOS/audit.yaml b/examples/mia/LOS/audit.yaml index 62ef45e2..f6cb89d9 100644 --- a/examples/mia/LOS/audit.yaml +++ b/examples/mia/LOS/audit.yaml @@ -1,15 +1,15 @@ audit: # Configurations for auditing random_seed: 1234 # Integer specifying the random seed attack_list: - # rmia: - # training_data_fraction: 0.5 # Fraction of the auxilary dataset to use for this attack (in each shadow model training) - # attack_data_fraction: 0.5 # Fraction of auxiliary dataset to sample from during attack - # num_shadow_models: 8 # Number of shadow models to train - # online: True # perform online or offline attack - # temperature: 2 - # gamma: 1.0 - # offline_a: 0.33 # parameter from which we compute p(x) from p_OUT(x) such that p_IN(x) = a p_OUT(x) + b. - # offline_b: 0.66 + rmia: + training_data_fraction: 0.5 # Fraction of the auxilary dataset to use for this attack (in each shadow model training) + attack_data_fraction: 0.5 # Fraction of auxiliary dataset to sample from during attack + num_shadow_models: 8 # Number of shadow models to train + online: True # perform online or offline attack + temperature: 2 + gamma: 1.0 + offline_a: 0.33 # parameter from which we compute p(x) from p_OUT(x) such that p_IN(x) = a p_OUT(x) + b. + offline_b: 0.66 # qmia: # training_data_fraction: 1.0 # Fraction of the auxilary dataset (data without train and test indices) to use for training the quantile regressor # epochs: 5 # Number of training epochs for quantile regression @@ -32,12 +32,13 @@ audit: # Configurations for auditing target: # Target model path - module_path: "utils/model.py" - model_class: "MimicLR" + module_path: "utils/model_LR.py" # either model_grud.py or model_LR.py for logestic regression + model_class: "LR" # LR/GRUD # Data paths - target_folder: "./target" - data_path: "./data/dataset.pkl" + target_folder: "./target_LR" # either target_GRUD or target_LR + data_path: "./data/flattened/dataset.pkl" #unflattened dataset for GRUD and flattened dataset for LR shadow_model: + model_class: # LR/GRUD distillation_model: diff --git a/examples/mia/LOS/mimic_GRUD_main.ipynb b/examples/mia/LOS/mimic_GRUD_main.ipynb new file mode 100644 index 00000000..8676ba82 --- /dev/null +++ b/examples/mia/LOS/mimic_GRUD_main.ipynb @@ -0,0 +1,846 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# MIA attacks on Length-of-Stay predictor, Gated Recurrent Unit with Decay (GRU-D)\n", + "## Installation of Packages in Conda\n", + "\n", + "To install the required packages in your conda environment, you can use the following commands:\n", + "\n", + "```bash\n", + "conda install h5py\n", + "conda install pytables" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "%reload_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import sys\n", + "\n", + "from torch import zeros\n", + "\n", + "project_root = os.path.abspath(os.path.join(os.getcwd(), \"../../..\"))\n", + "sys.path.append(project_root)\n", + "\n", + "from utils.data_processing import get_mimic_dataloaders, get_mimic_dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `batch_size` is one of the parameters which is assigned based on hyperparameter tuning as detailed in [this notebook](https://github.com/MLforHealth/MIMIC_Extract/blob/4daf3c89be7de05d26f47819d68d5532de6f753a/notebooks/Baselines%20for%20Mortality%20and%20LOS%20prediction%20-%20GRU-D.ipynb)." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loading dataset...\n", + "Loaded dataset from /home/fazeleh/LeakPro/examples/mia/LOS/data/unflattened/dataset.pkl\n" + ] + } + ], + "source": [ + "# Generate the dataset and dataloaders\n", + "path = os.path.join(os.getcwd(), \"data/\")\n", + "\n", + "train_frac = 0.4\n", + "valid_frac = 0.0\n", + "test_frac = 0.0\n", + "early_stop_frac = 0.4\n", + "batch_size = 59\n", + "use_LR = False # True if you want to use the LR model, False if you want to use the GRUD model\n", + "\n", + "dataset, train_indices, validation_indices, test_indices, early_stop_indices= get_mimic_dataset(path,\n", + " train_frac ,\n", + " valid_frac,\n", + " test_frac,\n", + " early_stop_frac,\n", + " use_LR)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "train_loader, validation_loader, test_loader, early_stop_loader = get_mimic_dataloaders(dataset,\n", + " train_indices,\n", + " validation_indices,\n", + " test_indices,\n", + " early_stop_indices,\n", + " batch_size)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `optimized_hyperparams` is assigned based on hyperparameter tuning as detailed in [this notebook](https://github.com/MLforHealth/MIMIC_Extract/blob/4daf3c89be7de05d26f47819d68d5532de6f753a/notebooks/Baselines%20for%20Mortality%20and%20LOS%20prediction%20-%20GRU-D.ipynb)." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "optimized_hyperparams ={\n", + " \"cell_size\": 58,\n", + " \"hidden_size\": 78,\n", + " \"learning_rate\": 0.0004738759319792616,\n", + " \"num_epochs\":37,\n", + " \"patience_early_stopping\": 20,\n", + " \"patience_lr_scheduler\": 5,\n", + " \"batch_size\": 59,\n", + " \"seed\": 4410,\n", + " \"min_delta\": 0.00001,\n", + " }\n", + "n_features = int(dataset.x.shape[1]/3)\n", + "X_mean = zeros(1,dataset.x.shape[2],n_features)\n", + "\n", + "model_params = {k: optimized_hyperparams[k] for k in [\"cell_size\", \"hidden_size\", \"batch_size\"]}\n", + "\n", + "# Add other required parameters to model_params\n", + "model_params.update({\n", + " \"input_size\": n_features,\n", + " \"X_mean\": X_mean,\n", + " \"output_last\": False\n", + "})" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model Structure: GRUD(\n", + " (zl): Linear(in_features=286, out_features=78, bias=True)\n", + " (rl): Linear(in_features=286, out_features=78, bias=True)\n", + " (hl): Linear(in_features=286, out_features=78, bias=True)\n", + " (gamma_x_l): FilterLinear(in_features=104, out_features=104, bias=True)\n", + " (gamma_h_l): Linear(in_features=104, out_features=78, bias=True)\n", + " (fc): Linear(in_features=78, out_features=2, bias=True)\n", + " (bn): BatchNorm1d(2, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (drop): Dropout(p=0.7, inplace=False)\n", + ")\n", + "Start Training ... \n", + "Output type dermined by the model\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training Batches: 100%|██████████| 163/163 [00:07<00:00, 22.28it/s]\n", + "Training Progress: 3%|▎ | 1/37 [00:07<04:24, 7.34s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 0: Validation loss improved to 0.5127\n", + "Learning Rate: 0.000474\n", + "Epoch: 0, train_loss: 0.90600625, valid_loss: 0.51268703, time: 7.34\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training Batches: 100%|██████████| 163/163 [00:06<00:00, 24.12it/s]\n", + "Training Progress: 5%|▌ | 2/37 [00:14<04:05, 7.01s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 1: Validation loss improved to 0.5078\n", + "Learning Rate: 0.000474\n", + "Epoch: 1, train_loss: 0.80034931, valid_loss: 0.50783253, time: 6.78\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training Batches: 100%|██████████| 163/163 [00:06<00:00, 23.95it/s]\n", + "Training Progress: 8%|▊ | 3/37 [00:20<03:55, 6.93s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 2: No improvement. Patience counter: 1/20\n", + "Learning Rate: 0.000474\n", + "Epoch: 2, train_loss: 0.76733459, valid_loss: 0.55360639, time: 6.83\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training Batches: 100%|██████████| 163/163 [00:06<00:00, 24.15it/s]\n", + "Training Progress: 11%|█ | 4/37 [00:27<03:46, 6.87s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 3: No improvement. Patience counter: 2/20\n", + "Learning Rate: 0.000474\n", + "Epoch: 3, train_loss: 0.74066611, valid_loss: 0.53837186, time: 6.77\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training Batches: 100%|██████████| 163/163 [00:06<00:00, 23.87it/s]\n", + "Training Progress: 14%|█▎ | 5/37 [00:34<03:39, 6.86s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 4: No improvement. Patience counter: 3/20\n", + "Learning Rate: 0.000474\n", + "Epoch: 4, train_loss: 0.72348623, valid_loss: 0.52800447, time: 6.85\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training Batches: 100%|██████████| 163/163 [00:06<00:00, 23.92it/s]\n", + "Training Progress: 16%|█▌ | 6/37 [00:41<03:32, 6.86s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 5: No improvement. Patience counter: 4/20\n", + "Learning Rate: 0.000474\n", + "Epoch: 5, train_loss: 0.70422922, valid_loss: 0.5873853, time: 6.84\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training Batches: 100%|██████████| 163/163 [00:06<00:00, 23.78it/s]\n", + "Training Progress: 19%|█▉ | 7/37 [00:48<03:25, 6.86s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 6: No improvement. Patience counter: 5/20\n", + "Learning Rate: 0.000474\n", + "Epoch: 6, train_loss: 0.68721295, valid_loss: 0.54968578, time: 6.88\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training Batches: 100%|██████████| 163/163 [00:06<00:00, 23.98it/s]\n", + "Training Progress: 22%|██▏ | 8/37 [00:55<03:18, 6.85s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 7: No improvement. Patience counter: 6/20\n", + "Epoch 00008: reducing learning rate of group 0 to 2.3694e-04.\n", + "Learning Rate: 0.000237\n", + "Epoch: 7, train_loss: 0.65936494, valid_loss: 0.56551975, time: 6.82\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training Batches: 100%|██████████| 163/163 [00:06<00:00, 23.80it/s]\n", + "Training Progress: 24%|██▍ | 9/37 [01:02<03:12, 6.86s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 8: No improvement. Patience counter: 7/20\n", + "Learning Rate: 0.000237\n", + "Epoch: 8, train_loss: 0.61763578, valid_loss: 0.57842529, time: 6.88\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training Batches: 100%|██████████| 163/163 [00:06<00:00, 23.89it/s]\n", + "Training Progress: 27%|██▋ | 10/37 [01:08<03:05, 6.86s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 9: No improvement. Patience counter: 8/20\n", + "Learning Rate: 0.000237\n", + "Epoch: 9, train_loss: 0.62895368, valid_loss: 0.56870216, time: 6.85\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training Batches: 100%|██████████| 163/163 [00:06<00:00, 23.81it/s]\n", + "Training Progress: 30%|██▉ | 11/37 [01:15<02:58, 6.86s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 10: No improvement. Patience counter: 9/20\n", + "Learning Rate: 0.000237\n", + "Epoch: 10, train_loss: 0.6022617, valid_loss: 0.5680961, time: 6.87\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training Batches: 100%|██████████| 163/163 [00:06<00:00, 23.66it/s]\n", + "Training Progress: 32%|███▏ | 12/37 [01:22<02:51, 6.88s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 11: No improvement. Patience counter: 10/20\n", + "Learning Rate: 0.000237\n", + "Epoch: 11, train_loss: 0.59018976, valid_loss: 0.55305964, time: 6.92\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training Batches: 100%|██████████| 163/163 [00:06<00:00, 23.78it/s]\n", + "Training Progress: 35%|███▌ | 13/37 [01:29<02:45, 6.88s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 12: No improvement. Patience counter: 11/20\n", + "Learning Rate: 0.000237\n", + "Epoch: 12, train_loss: 0.58661764, valid_loss: 0.57494694, time: 6.88\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training Batches: 100%|██████████| 163/163 [00:06<00:00, 23.83it/s]\n", + "Training Progress: 38%|███▊ | 14/37 [01:36<02:38, 6.87s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 13: No improvement. Patience counter: 12/20\n", + "Epoch 00014: reducing learning rate of group 0 to 1.1847e-04.\n", + "Learning Rate: 0.000118\n", + "Epoch: 13, train_loss: 0.58373045, valid_loss: 0.56426746, time: 6.87\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training Batches: 100%|██████████| 163/163 [00:06<00:00, 23.66it/s]\n", + "Training Progress: 41%|████ | 15/37 [01:43<02:31, 6.89s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 14: No improvement. Patience counter: 13/20\n", + "Learning Rate: 0.000118\n", + "Epoch: 14, train_loss: 0.5602767, valid_loss: 0.56786698, time: 6.91\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training Batches: 100%|██████████| 163/163 [00:06<00:00, 23.72it/s]\n", + "Training Progress: 43%|████▎ | 16/37 [01:50<02:24, 6.89s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 15: No improvement. Patience counter: 14/20\n", + "Learning Rate: 0.000118\n", + "Epoch: 15, train_loss: 0.55402973, valid_loss: 0.56124943, time: 6.9\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training Batches: 100%|██████████| 163/163 [00:06<00:00, 23.68it/s]\n", + "Training Progress: 46%|████▌ | 17/37 [01:57<02:17, 6.90s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 16: No improvement. Patience counter: 15/20\n", + "Learning Rate: 0.000118\n", + "Epoch: 16, train_loss: 0.55244331, valid_loss: 0.5543347, time: 6.91\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training Batches: 100%|██████████| 163/163 [00:06<00:00, 23.61it/s]\n", + "Training Progress: 49%|████▊ | 18/37 [02:04<02:11, 6.91s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 17: No improvement. Patience counter: 16/20\n", + "Learning Rate: 0.000118\n", + "Epoch: 17, train_loss: 0.54556851, valid_loss: 0.56179345, time: 6.93\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training Batches: 100%|██████████| 163/163 [00:06<00:00, 23.75it/s]\n", + "Training Progress: 51%|█████▏ | 19/37 [02:10<02:04, 6.90s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 18: No improvement. Patience counter: 17/20\n", + "Learning Rate: 0.000118\n", + "Epoch: 18, train_loss: 0.53646851, valid_loss: 0.57852155, time: 6.89\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training Batches: 100%|██████████| 163/163 [00:06<00:00, 26.42it/s]\n", + "Training Progress: 54%|█████▍ | 20/37 [02:17<01:53, 6.69s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 19: No improvement. Patience counter: 18/20\n", + "Epoch 00020: reducing learning rate of group 0 to 5.9234e-05.\n", + "Learning Rate: 0.000059\n", + "Epoch: 19, train_loss: 0.53324886, valid_loss: 0.57487661, time: 6.19\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training Batches: 100%|██████████| 163/163 [00:05<00:00, 27.72it/s]\n", + "Training Progress: 57%|█████▋ | 21/37 [02:23<01:43, 6.45s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 20: No improvement. Patience counter: 19/20\n", + "Learning Rate: 0.000059\n", + "Epoch: 20, train_loss: 0.52443605, valid_loss: 0.57012284, time: 5.9\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training Batches: 100%|██████████| 163/163 [00:05<00:00, 27.85it/s]\n", + "Training Progress: 57%|█████▋ | 21/37 [02:28<01:53, 7.09s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 21: No improvement. Patience counter: 20/20\n", + "Early stopping at epoch 21. Best validation loss: 0.5078\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "from utils.model_grud import *\n", + "\n", + "# Initialize the model with filtered parameters\n", + "model = GRUD(**model_params)\n", + "\n", + "# Train the model with Train_Model function\n", + "train_losses, test_losses , train_acc, test_acc = gru_trained_model_and_metadata(model,\n", + " train_loader,\n", + " early_stop_loader,\n", + " epochs = optimized_hyperparams[\"num_epochs\"],\n", + " patience_early_stopping = optimized_hyperparams[\"patience_early_stopping\"],\n", + " patience_lr= optimized_hyperparams[\"patience_lr_scheduler\"],\n", + " min_delta = optimized_hyperparams[\"min_delta\"],\n", + " learning_rate = optimized_hyperparams[\"learning_rate\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "# Convert losses to numpy-compatible lists directly\n", + "train_losses_cpu = [float(loss) for loss in train_losses]\n", + "test_losses_cpu = [float(loss) for loss in test_losses]\n", + "\n", + "# Plot training and test accuracy\n", + "plt.figure(figsize=(5, 4))\n", + "\n", + "plt.subplot(1, 2, 1)\n", + "plt.plot(train_acc, label=\"Train Accuracy\")\n", + "plt.plot(test_acc, label=\"Test Accuracy\")\n", + "plt.xlabel(\"Epoch\")\n", + "plt.ylabel(\"Accuracy\")\n", + "plt.title(\"Accuracy over Epochs\")\n", + "plt.legend()\n", + "\n", + "# Plot training and test loss\n", + "plt.subplot(1, 2, 2)\n", + "plt.plot(train_losses, label=\"Train Loss\")\n", + "plt.plot(test_losses, label=\"Test Loss\")\n", + "plt.xlabel(\"Epoch\")\n", + "plt.ylabel(\"Loss\")\n", + "plt.title(\"Loss over Epochs\")\n", + "plt.legend()\n", + "\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Attacking the GRUD model\n", + "Modify ```audit.yaml ``` file to attack GRUD model: \n", + " \n", + " ```\n", + " module_path: \"utils/model_grud.py\" \n", + " model_class: \"GRUD\"\n", + " target_folder: \"./target_GRUD\"\n", + " data_path: \"./data/unflattened/dataset.pkl\"\n", + " ```" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2025-01-04 09:41:35,162 INFO Target model blueprint created from GRUD in utils/model_grud.py.\n", + "2025-01-04 09:41:35,170 INFO Loaded target model metadata from ./target_GRUD/model_metadata.pkl\n", + "2025-01-04 09:41:35,174 INFO Loaded target model from ./target_GRUD\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2025-01-04 09:41:36,126 INFO Loaded population dataset from ./data/unflattened/dataset.pkl\n", + "2025-01-04 09:41:36,127 INFO Loaded population dataset from ./data/unflattened/dataset.pkl\n", + "2025-01-04 09:41:36,128 INFO Creating shadow model handler singleton\n", + "2025-01-04 09:41:36,132 INFO Creating distillation model handler singleton\n", + "2025-01-04 09:41:36,138 INFO Configuring RMIA attack\n", + "2025-01-04 09:41:36,138 INFO Added attack: rmia\n", + "2025-01-04 09:41:36,138 INFO Added attack: lira\n", + "2025-01-04 09:41:36,139 INFO Preparing attack: rmia\n", + "2025-01-04 09:41:36,139 INFO Preparing shadow models for RMIA attack\n", + "2025-01-04 09:41:36,139 INFO Preparing attack data for training the RMIA attack\n", + "2025-01-04 09:41:36,141 INFO Check for 8 shadow models (dataset: 23944 points)\n", + "2025-01-04 09:41:36,143 INFO Number of existing models exceeds or equals the number of models to create\n", + "2025-01-04 09:41:36,144 INFO Loading shadow model 0\n", + "2025-01-04 09:41:36,150 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_0.pkl\n", + "2025-01-04 09:41:36,150 INFO Loading shadow model 4\n", + "2025-01-04 09:41:36,154 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_4.pkl\n", + "2025-01-04 09:41:36,154 INFO Loading shadow model 2\n", + "2025-01-04 09:41:36,158 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_2.pkl\n", + "2025-01-04 09:41:36,158 INFO Loading shadow model 3\n", + "2025-01-04 09:41:36,162 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_3.pkl\n", + "2025-01-04 09:41:36,163 INFO Loading shadow model 5\n", + "2025-01-04 09:41:36,166 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_5.pkl\n", + "2025-01-04 09:41:36,167 INFO Loading shadow model 7\n", + "2025-01-04 09:41:36,171 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_7.pkl\n", + "2025-01-04 09:41:36,171 INFO Loading shadow model 1\n", + "2025-01-04 09:41:36,175 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_1.pkl\n", + "2025-01-04 09:41:36,175 INFO Loading shadow model 6\n", + "2025-01-04 09:41:36,179 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_6.pkl\n", + "2025-01-04 09:41:36,179 INFO Running attack: rmia\n", + "2025-01-04 09:41:36,180 INFO Running RMIA online attack\n", + "2025-01-04 09:41:36,180 INFO Loading metadata 0\n", + "2025-01-04 09:41:36,181 INFO Loading metadata 4\n", + "2025-01-04 09:41:36,181 INFO Loading metadata 2\n", + "2025-01-04 09:41:36,182 INFO Loading metadata 3\n", + "2025-01-04 09:41:36,183 INFO Loading metadata 5\n", + "2025-01-04 09:41:36,183 INFO Loading metadata 7\n", + "2025-01-04 09:41:36,184 INFO Loading metadata 1\n", + "2025-01-04 09:41:36,184 INFO Loading metadata 6\n", + "2025-01-04 09:41:36,952 INFO Number of points in the audit dataset that are used for online attack: 18985\n", + "2025-01-04 09:43:01,678 INFO Subsampling attack data from 4790 points \n", + "2025-01-04 09:43:01,679 INFO Number of attack data points after subsampling: 2395\n", + "2025-01-04 09:43:13,335 INFO Finished attack: rmia \n", + "2025-01-04 09:43:13,336 INFO Preparing attack: lira\n", + "2025-01-04 09:43:13,339 INFO Number of existing models exceeds or equals the number of models to create\n", + "2025-01-04 09:43:13,340 INFO Loading shadow model 0\n", + "2025-01-04 09:43:13,343 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_0.pkl\n", + "2025-01-04 09:43:13,344 INFO Loading shadow model 4\n", + "2025-01-04 09:43:13,348 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_4.pkl\n", + "2025-01-04 09:43:13,348 INFO Loading shadow model 2\n", + "2025-01-04 09:43:13,352 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_2.pkl\n", + "2025-01-04 09:43:13,352 INFO Loading shadow model 3\n", + "2025-01-04 09:43:13,356 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_3.pkl\n", + "2025-01-04 09:43:13,356 INFO Loading shadow model 5\n", + "2025-01-04 09:43:13,360 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_5.pkl\n", + "2025-01-04 09:43:13,361 INFO Loading shadow model 7\n", + "2025-01-04 09:43:13,364 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_7.pkl\n", + "2025-01-04 09:43:13,365 INFO Loading shadow model 1\n", + "2025-01-04 09:43:13,369 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_1.pkl\n", + "2025-01-04 09:43:13,369 INFO Loading shadow model 6\n", + "2025-01-04 09:43:13,373 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_6.pkl\n", + "2025-01-04 09:43:13,374 INFO Create masks for all IN and OUT samples\n", + "2025-01-04 09:43:13,374 INFO Loading metadata 0\n", + "2025-01-04 09:43:13,375 INFO Loading metadata 4\n", + "2025-01-04 09:43:13,376 INFO Loading metadata 2\n", + "2025-01-04 09:43:13,376 INFO Loading metadata 3\n", + "2025-01-04 09:43:13,377 INFO Loading metadata 5\n", + "2025-01-04 09:43:13,377 INFO Loading metadata 7\n", + "2025-01-04 09:43:13,378 INFO Loading metadata 1\n", + "2025-01-04 09:43:13,379 INFO Loading metadata 6\n", + "2025-01-04 09:43:13,388 INFO Calculating the logits for all 8 shadow models\n", + "2025-01-04 09:44:30,637 INFO Calculating the logits for the target model \n", + "2025-01-04 09:44:40,363 INFO Running attack: lira \n", + "Processing audit samples: 100%|██████████| 18985/18985 [00:03<00:00, 5962.65it/s]\n", + "2025-01-04 09:44:43,585 INFO Finished attack: lira\n", + "2025-01-04 09:44:43,586 INFO Preparing results for attack: rmia\n", + "2025-01-04 09:44:43,586 INFO Preparing results for attack: lira\n", + "2025-01-04 09:44:43,586 INFO Auditing completed\n" + ] + } + ], + "source": [ + "from mimic_gru_handler import MimicInputHandlerGRU\n", + "\n", + "from leakpro import LeakPro\n", + "\n", + "# Read the config file\n", + "config_path = \"audit.yaml\"\n", + "\n", + "# Prepare leakpro object\n", + "leakpro = LeakPro(MimicInputHandlerGRU, config_path)\n", + "\n", + "# Run the audit\n", + "mia_results = leakpro.run_audit(return_results=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Report Genrating" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2025-01-04 09:45:16,653 INFO Initializing report handler...\n", + "2025-01-04 09:45:16,654 INFO report_dir set to: ./leakpro_output/results\n", + "2025-01-04 09:45:16,654 INFO Saving results for rmia\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2025-01-04 09:45:20,466 INFO Saving results for lira\n" + ] + }, + { + "data": { + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "\n", + "# Import and initialize ReportHandler\n", + "from leakpro.reporting.report_handler import ReportHandler\n", + "\n", + "# report_handler = ReportHandler()\n", + "report_handler = ReportHandler(report_dir=\"./leakpro_output/results\")\n", + "\n", + "# Save MIA resuls using report handler\n", + "for res in mia_results:\n", + " report_handler.save_results(attack_name=res.attack_name, result_data=res, config=res.configs)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2025-01-04 09:45:33,289 INFO No results of type GIAResults found.\n", + "2025-01-04 09:45:33,289 INFO No results of type SinglingOutResults found.\n", + "2025-01-04 09:45:33,290 INFO No results of type InferenceResults found.\n", + "2025-01-04 09:45:33,290 INFO No results of type LinkabilityResults found.\n", + "2025-01-04 09:45:40,303 INFO PDF compiled\n" + ] + }, + { + "data": { + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "\n", + "# # Create the report by compiling the latex text\n", + "report_handler.create_report()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "leakpro_test", + "language": "python", + "name": "python3" + }, + "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.12.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/mia/LOS/mimic_handler.py b/examples/mia/LOS/mimic_LR_handler.py similarity index 92% rename from examples/mia/LOS/mimic_handler.py rename to examples/mia/LOS/mimic_LR_handler.py index eafa12b4..9f8ade1d 100644 --- a/examples/mia/LOS/mimic_handler.py +++ b/examples/mia/LOS/mimic_LR_handler.py @@ -7,15 +7,16 @@ from leakpro import AbstractInputHandler + class MimicInputHandler(AbstractInputHandler): - """Class to handle the user input for the CIFAR10 dataset.""" + """Class to handle the user input for the MIMICIII dataset.""" def __init__(self, configs: dict) -> None: super().__init__(configs = configs) def get_criterion(self)->None: - """Set the CrossEntropyLoss for the model.""" + """Set the Binary Cross Entropy Loss for the model.""" return BCELoss() def get_optimizer(self, model:torch.nn.Module) -> None: @@ -40,11 +41,11 @@ def train( criterion = self.get_criterion() optimizer = self.get_optimizer(model) - + for e in tqdm(range(epochs), desc="Training Progress"): model.train() train_acc, train_loss = 0.0, 0.0 - + for data, target in dataloader: target = target.float().unsqueeze(1) data, target = data.to(compute_device, non_blocking=True), target.to(compute_device, non_blocking=True) @@ -54,11 +55,11 @@ def train( loss = criterion(output, target) pred = sigmoid(output) >= 0.5 train_acc += pred.eq(target).sum().item() - + loss.backward() optimizer.step() train_loss += loss.item() - + train_acc = train_acc/len(dataloader.dataset) train_loss = train_loss/len(dataloader) diff --git a/examples/mia/LOS/mimic_LR_main.ipynb b/examples/mia/LOS/mimic_LR_main.ipynb new file mode 100644 index 00000000..f0ca43cc --- /dev/null +++ b/examples/mia/LOS/mimic_LR_main.ipynb @@ -0,0 +1,318 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# MIA attacks on Length-of-Stay predictor, Logistic Regression\n", + "## Installation of Packages in Conda\n", + "\n", + "To install the required packages in your conda environment, you can use the following commands:\n", + "\n", + "```bash\n", + "conda install h5py\n", + "conda install pytables\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%reload_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import sys\n", + "\n", + "project_root = os.path.abspath(os.path.join(os.getcwd(), \"../../..\"))\n", + "sys.path.append(project_root)\n", + "\n", + "from examples.mia.LOS.utils.data_processing import get_mimic_dataloaders, get_mimic_dataset\n", + "from examples.mia.LOS.utils.model_LR import LR, create_trained_model_and_metadata\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Train the classifier\n", + "For the LR, the data should be flatten. So set the value to True for the LR model anb False for the GRU-D" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Generate the dataset and dataloaders\n", + "path = os.path.join(os.getcwd(), \"data/\")\n", + "use_LR = True # If True, use a logistic regression model. If False, use a GRUD model.\n", + "dataset, train_indices, validation_indices, test_indices, early_stop_indices = get_mimic_dataset(path, train_frac = 0.5,\n", + " test_frac = 0.2,\n", + " validation_frac = 0,\n", + " early_stop_frac = 0,\n", + " use_LR = use_LR)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "train_loader, validation_loader, test_loader, early_stop_loader = get_mimic_dataloaders(dataset,\n", + " train_indices,\n", + " validation_indices,\n", + " test_indices,\n", + " early_stop_indices,\n", + " batch_size=128)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of features: 7488\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training Progress: 100%|██████████| 20/20 [00:27<00:00, 1.37s/it]\n" + ] + } + ], + "source": [ + "n_features = dataset.x.shape[1]\n", + "print(f\"Number of features: {n_features}\")\n", + "\n", + "model = LR(n_features)\n", + "train_acc, train_loss, test_acc, test_loss = create_trained_model_and_metadata(model,\n", + " train_loader,\n", + " test_loader,\n", + " lr = 0.0001,\n", + " weight_decay = 5.392,\n", + " epochs=20)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "# Plot training and test accuracy\n", + "plt.figure(figsize=(5, 4))\n", + "\n", + "plt.subplot(1, 2, 1)\n", + "plt.plot(train_acc, label=\"Train Accuracy\")\n", + "plt.plot(test_acc, label=\"Test Accuracy\")\n", + "plt.xlabel(\"Epoch\")\n", + "plt.ylabel(\"Accuracy\")\n", + "plt.title(\"Accuracy over Epochs\")\n", + "plt.legend()\n", + "\n", + "# Plot training and test loss\n", + "plt.subplot(1, 2, 2)\n", + "plt.plot(train_loss, label=\"Train Loss\")\n", + "plt.plot(test_loss, label=\"Test Loss\")\n", + "plt.xlabel(\"Epoch\")\n", + "plt.ylabel(\"Loss\")\n", + "plt.title(\"Loss over Epochs\")\n", + "plt.legend()\n", + "\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Attack the LR model\n", + "Modify ```audit.yaml ``` file to attack LR model: \n", + " \n", + " ```\n", + " module_path: \"utils/model_LR.py\" \n", + " model_class: \"LR\"\n", + " target_folder: \"./target_LR\"\n", + " data_path: \"./data/flattened/dataset.pkl\"\n", + " ```\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2025-01-10 09:53:42,304 INFO Target model blueprint created from LR in utils/model_LR.py.\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2025-01-10 09:53:42,358 INFO Loaded target model metadata from ./target_LR/model_metadata.pkl\n", + "2025-01-10 09:53:42,360 INFO Loaded target model from ./target_LR\n", + "2025-01-10 09:53:43,098 INFO Loaded population dataset from ./data/flattened/dataset.pkl\n", + "2025-01-10 09:53:43,100 INFO Loaded population dataset from ./data/flattened/dataset.pkl\n", + "2025-01-10 09:53:43,101 INFO Configuring RMIA attack\n", + "2025-01-10 09:53:43,101 INFO Added attack: rmia\n", + "2025-01-10 09:53:43,102 INFO Added attack: lira\n", + "2025-01-10 09:53:43,103 INFO Preparing attack: rmia\n", + "2025-01-10 09:53:43,103 INFO Preparing shadow models for RMIA attack\n", + "2025-01-10 09:53:43,104 INFO Preparing attack data for training the RMIA attack\n", + "2025-01-10 09:53:43,105 INFO Check for 8 shadow models (dataset: 23944 points)\n", + "2025-01-10 09:53:43,106 WARNING Using the same model class for shadow models as the target model.\n", + "2025-01-10 09:53:43,121 WARNING Mismatched model types found in saved shadow models: [(0, 'Unknown'), (4, 'Unknown'), (2, 'Unknown'), (15, 'LogisticRegression'), (14, 'LogisticRegression'), (11, 'LogisticRegression'), (3, 'Unknown'), (9, 'LogisticRegression'), (5, 'Unknown'), (8, 'LogisticRegression'), (7, 'Unknown'), (16, 'LogisticRegression'), (13, 'LogisticRegression'), (1, 'Unknown'), (12, 'LogisticRegression'), (10, 'LogisticRegression'), (17, 'LogisticRegression'), (6, 'Unknown')]. Expected model type: LR.\n", + "2025-01-10 09:53:43,122 INFO Number of existing models exceeds or equals the number of models to create\n", + "2025-01-10 09:53:43,123 INFO Loading shadow model 23\n", + "2025-01-10 09:53:43,125 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_23.pkl\n", + "2025-01-10 09:53:43,126 INFO Loading shadow model 20\n", + "2025-01-10 09:53:43,128 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_20.pkl\n", + "2025-01-10 09:53:43,129 INFO Loading shadow model 21\n", + "2025-01-10 09:53:43,131 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_21.pkl\n", + "2025-01-10 09:53:43,131 INFO Loading shadow model 25\n", + "2025-01-10 09:53:43,133 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_25.pkl\n", + "2025-01-10 09:53:43,134 INFO Loading shadow model 18\n", + "2025-01-10 09:53:43,135 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_18.pkl\n", + "2025-01-10 09:53:43,136 INFO Loading shadow model 24\n", + "2025-01-10 09:53:43,138 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_24.pkl\n", + "2025-01-10 09:53:43,138 INFO Loading shadow model 22\n", + "2025-01-10 09:53:43,140 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_22.pkl\n", + "2025-01-10 09:53:43,141 INFO Loading shadow model 19\n", + "2025-01-10 09:53:43,143 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_19.pkl\n", + "2025-01-10 09:53:43,143 INFO Running attack: rmia\n", + "2025-01-10 09:53:43,144 INFO Running RMIA online attack\n", + "2025-01-10 09:53:43,144 INFO Loading metadata 23\n", + "2025-01-10 09:53:43,146 INFO Loading metadata 20\n", + "2025-01-10 09:53:43,147 INFO Loading metadata 21\n", + "2025-01-10 09:53:43,148 INFO Loading metadata 25\n", + "2025-01-10 09:53:43,149 INFO Loading metadata 18\n", + "2025-01-10 09:53:43,150 INFO Loading metadata 24\n", + "2025-01-10 09:53:43,151 INFO Loading metadata 22\n", + "2025-01-10 09:53:43,152 INFO Loading metadata 19\n", + "2025-01-10 09:53:43,705 INFO Number of points in the audit dataset that are used for online attack: 16632\n", + "2025-01-10 09:53:49,467 INFO Subsampling attack data from 7184 points \n", + "2025-01-10 09:53:49,468 INFO Number of attack data points after subsampling: 3592\n", + "2025-01-10 09:53:51,950 INFO Finished attack: rmia \n", + "2025-01-10 09:53:51,951 INFO Preparing attack: lira\n", + "2025-01-10 09:53:51,953 WARNING Using the same model class for shadow models as the target model.\n", + "2025-01-10 09:53:51,968 WARNING Mismatched model types found in saved shadow models: [(0, 'Unknown'), (4, 'Unknown'), (2, 'Unknown'), (15, 'LogisticRegression'), (14, 'LogisticRegression'), (11, 'LogisticRegression'), (3, 'Unknown'), (9, 'LogisticRegression'), (5, 'Unknown'), (8, 'LogisticRegression'), (7, 'Unknown'), (16, 'LogisticRegression'), (13, 'LogisticRegression'), (1, 'Unknown'), (12, 'LogisticRegression'), (10, 'LogisticRegression'), (17, 'LogisticRegression'), (6, 'Unknown')]. Expected model type: LR.\n", + "2025-01-10 09:53:51,969 INFO Number of existing models exceeds or equals the number of models to create\n", + "2025-01-10 09:53:51,969 INFO Loading shadow model 23\n", + "2025-01-10 09:53:51,971 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_23.pkl\n", + "2025-01-10 09:53:51,972 INFO Loading shadow model 20\n", + "2025-01-10 09:53:51,974 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_20.pkl\n", + "2025-01-10 09:53:51,974 INFO Loading shadow model 21\n", + "2025-01-10 09:53:51,976 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_21.pkl\n", + "2025-01-10 09:53:51,977 INFO Loading shadow model 25\n", + "2025-01-10 09:53:51,978 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_25.pkl\n", + "2025-01-10 09:53:51,979 INFO Loading shadow model 18\n", + "2025-01-10 09:53:51,980 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_18.pkl\n", + "2025-01-10 09:53:51,981 INFO Loading shadow model 24\n", + "2025-01-10 09:53:51,983 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_24.pkl\n", + "2025-01-10 09:53:51,984 INFO Loading shadow model 22\n", + "2025-01-10 09:53:51,985 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_22.pkl\n", + "2025-01-10 09:53:51,986 INFO Loading shadow model 19\n", + "2025-01-10 09:53:51,987 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_19.pkl\n", + "2025-01-10 09:53:51,988 INFO Create masks for all IN and OUT samples\n", + "2025-01-10 09:53:51,989 INFO Loading metadata 23\n", + "2025-01-10 09:53:51,990 INFO Loading metadata 20\n", + "2025-01-10 09:53:51,991 INFO Loading metadata 21\n", + "2025-01-10 09:53:51,992 INFO Loading metadata 25\n", + "2025-01-10 09:53:51,993 INFO Loading metadata 18\n", + "2025-01-10 09:53:51,994 INFO Loading metadata 24\n", + "2025-01-10 09:53:51,995 INFO Loading metadata 22\n", + "2025-01-10 09:53:51,996 INFO Loading metadata 19\n", + "2025-01-10 09:53:52,003 INFO Calculating the logits for all 8 shadow models\n", + "2025-01-10 09:53:58,105 INFO Calculating the logits for the target model \n", + "2025-01-10 09:53:58,985 INFO Running attack: lira \n", + "Processing audit samples: 100%|██████████| 16632/16632 [00:05<00:00, 3226.14it/s]\n", + "2025-01-10 09:54:04,171 INFO Finished attack: lira\n", + "2025-01-10 09:54:04,172 INFO Preparing results for attack: rmia\n", + "2025-01-10 09:54:10,179 INFO Preparing results for attack: lira\n", + "2025-01-10 09:54:15,544 INFO Auditing completed\n" + ] + }, + { + "data": { + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from mimic_LR_handler import MimicInputHandler\n", + "\n", + "from leakpro import LeakPro\n", + "\n", + "# Read the config file\n", + "config_path = \"audit.yaml\"\n", + "\n", + "# Prepare leakpro object\n", + "leakpro = LeakPro(MimicInputHandler, config_path)\n", + "\n", + "# Run the audit\n", + "leakpro.run_audit()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "leakpro_test", + "language": "python", + "name": "python3" + }, + "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.12.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/mia/LOS/mimic_gru_handler.py b/examples/mia/LOS/mimic_gru_handler.py new file mode 100644 index 00000000..be51e6b8 --- /dev/null +++ b/examples/mia/LOS/mimic_gru_handler.py @@ -0,0 +1,82 @@ + +from torch import cuda, device, nn, optim, squeeze +from torch.nn import CrossEntropyLoss +from torch.utils.data import DataLoader +from tqdm import tqdm +from sklearn.metrics import accuracy_score +from leakpro import AbstractInputHandler + + +class MimicInputHandlerGRU(AbstractInputHandler): + """Class to handle the user input for the MIMICIII dataset.""" + + def __init__(self, configs: dict) -> None: + super().__init__(configs = configs) + + def get_criterion(self)->CrossEntropyLoss: + """Set the CrossEntropyLoss for the model.""" + return CrossEntropyLoss() + + def get_optimizer(self, model:nn.Module) -> optim.Optimizer: + """Set the optimizer for the model.""" + learning_rate = 0.01 + return optim.Adam(model.parameters(), lr=learning_rate) + + def convert_to_device(self, x): + device_name = device("cuda" if cuda.is_available() else "cpu") + return x.to(device_name) + + def to_numpy(self, tensor) : + return tensor.detach().cpu().numpy() if tensor.is_cuda else tensor.detach().numpy() + + def train( + self, + dataloader: DataLoader, + model: nn.Module = None, + criterion: nn.Module = None, + optimizer: optim.Optimizer = None, + epochs: int = None, + ) -> dict: + + """Model training procedure.""" + device_name = device("cuda" if cuda.is_available() else "cpu") + model.to(device_name) + model.train() + + criterion = self.get_criterion() + optimizer = self.get_optimizer(model) + + for e in tqdm(range(epochs), desc="Training Progress"): + model.train() + train_acc, train_loss = 0.0, 0.0 + + for _, (x, labels) in enumerate(tqdm(dataloader, desc="Training Batches")): + x = self.convert_to_device(x) + labels = self.convert_to_device(labels) + labels = labels.long() + + optimizer.zero_grad() + output = model(x) + + loss = criterion(squeeze(output), squeeze(labels).long()) + loss.backward() + optimizer.step() + train_loss += loss.item() + + train_loss = train_loss/len(dataloader) + binary_predictions = self.to_numpy(output).argmax(axis=1) + + # Ensure labels are integer and 1D + binary_labels = self.to_numpy(labels).astype(int) + # Compute accuracy + train_acc = accuracy_score(binary_labels, binary_predictions) + + return {"model": model, "metrics": {"accuracy": train_acc, "loss": train_loss}} + + + + + + + + diff --git a/examples/mia/LOS/mimic_main.ipynb b/examples/mia/LOS/mimic_main.ipynb deleted file mode 100644 index 47578870..00000000 --- a/examples/mia/LOS/mimic_main.ipynb +++ /dev/null @@ -1,278 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Installation of Packages in Conda\n", - "\n", - "After prepraring the data according to ```mimiciii_prepration/ReadMe.md```, to install the required packages in your conda environment, you can use the following commands:\n", - "\n", - "```bash\n", - "conda install h5py\n", - "conda install pytables\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%reload_ext autoreload\n", - "%autoreload 2" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "import sys\n", - "\n", - "project_root = os.path.abspath(os.path.join(os.getcwd(), \"../../..\"))\n", - "sys.path.append(project_root)\n", - "\n", - "from examples.mia.LOS.utils.data_processing import get_mimic_dataset, get_mimic_dataloaders\n", - "from examples.mia.LOS.utils.model import MimicLR, create_trained_model_and_metadata\n", - "\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "# Generate the dataset and dataloaders\n", - "path = os.path.join(os.getcwd(), \"data/\")\n", - "\n", - "dataset, train_indices, test_indices= get_mimic_dataset(path, train_frac = 0.5, test_frac= 0.2)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "train_loader, test_loader= get_mimic_dataloaders(dataset, train_indices, test_indices, batch_size=128)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Number of features: 7488\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Training Progress: 100%|██████████| 50/50 [01:06<00:00, 1.33s/it]\n" - ] - } - ], - "source": [ - "n_features = dataset.x.shape[1]\n", - "print(f\"Number of features: {n_features}\")\n", - "\n", - "# Train the model\n", - "if not os.path.exists(\"target\"):\n", - " os.makedirs(\"target\")\n", - "model = MimicLR(n_features)\n", - "train_acc, train_loss, test_acc, test_loss = create_trained_model_and_metadata(model, \n", - " train_loader, \n", - " test_loader, \n", - " lr = 0.0001,\n", - " weight_decay = 0.5392, # chosing 5.392 decrease the train test gap \n", - " epochs=50)\n", - "\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import matplotlib.pyplot as plt\n", - "\n", - "# Plot training and test accuracy\n", - "plt.figure(figsize=(5, 4))\n", - "\n", - "plt.subplot(1, 2, 1)\n", - "plt.plot(train_acc, label='Train Accuracy')\n", - "plt.plot(test_acc, label='Test Accuracy')\n", - "plt.xlabel('Epoch')\n", - "plt.ylabel('Accuracy')\n", - "plt.title('Accuracy over Epochs')\n", - "plt.legend()\n", - "\n", - "# Plot training and test loss\n", - "plt.subplot(1, 2, 2)\n", - "plt.plot(train_loss, label='Train Loss')\n", - "plt.plot(test_loss, label='Test Loss')\n", - "plt.xlabel('Epoch')\n", - "plt.ylabel('Loss')\n", - "plt.title('Loss over Epochs')\n", - "plt.legend()\n", - "\n", - "plt.tight_layout()\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-11-22 09:25:45,391 INFO Target model blueprint created from MimicLR in utils/model.py.\n", - "2024-11-22 09:25:45,398 INFO Loaded target model metadata from ./target/model_metadata.pkl\n", - "2024-11-22 09:25:45,400 INFO Loaded target model from ./target\n", - "2024-11-22 09:25:46,568 INFO Loaded population dataset from ./data/dataset.pkl\n", - "2024-11-22 09:25:46,569 INFO Loaded population dataset from ./data/dataset.pkl\n", - "2024-11-22 09:25:46,570 INFO Added attack: lira\n", - "2024-11-22 09:25:46,571 INFO Preparing attack: lira\n", - "2024-11-22 09:25:46,574 INFO Number of existing models exceeds or equals the number of models to create\n", - "2024-11-22 09:25:46,575 INFO Loading shadow model 0\n", - "2024-11-22 09:25:46,578 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_0.pkl\n", - "2024-11-22 09:25:46,579 INFO Loading shadow model 4\n", - "2024-11-22 09:25:46,580 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_4.pkl\n", - "2024-11-22 09:25:46,581 INFO Loading shadow model 2\n", - "2024-11-22 09:25:46,582 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_2.pkl\n", - "2024-11-22 09:25:46,582 INFO Loading shadow model 3\n", - "2024-11-22 09:25:46,584 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_3.pkl\n", - "2024-11-22 09:25:46,584 INFO Loading shadow model 5\n", - "2024-11-22 09:25:46,585 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_5.pkl\n", - "2024-11-22 09:25:46,586 INFO Loading shadow model 7\n", - "2024-11-22 09:25:46,587 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_7.pkl\n", - "2024-11-22 09:25:46,587 INFO Loading shadow model 1\n", - "2024-11-22 09:25:46,589 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_1.pkl\n", - "2024-11-22 09:25:46,589 INFO Loading shadow model 6\n", - "2024-11-22 09:25:46,594 INFO Loaded model from ./leakpro_output/attack_objects/shadow_model/shadow_model_6.pkl\n", - "2024-11-22 09:25:46,594 INFO Create masks for all IN and OUT samples\n", - "2024-11-22 09:25:46,595 INFO Loading metadata 0\n", - "2024-11-22 09:25:46,596 INFO Loading metadata 4\n", - "2024-11-22 09:25:46,596 INFO Loading metadata 2\n", - "2024-11-22 09:25:46,597 INFO Loading metadata 3\n", - "2024-11-22 09:25:46,598 INFO Loading metadata 5\n", - "2024-11-22 09:25:46,598 INFO Loading metadata 7\n", - "2024-11-22 09:25:46,599 INFO Loading metadata 1\n", - "2024-11-22 09:25:46,599 INFO Loading metadata 6\n", - "2024-11-22 09:25:46,610 INFO Calculating the logits for all 8 shadow models\n", - "2024-11-22 09:25:52,584 INFO Calculating the logits for the target model \n", - "2024-11-22 09:25:53,550 INFO Running attack: lira \n", - "Processing audit samples: 100%|██████████| 16634/16634 [00:02<00:00, 6073.71it/s]\n", - "2024-11-22 09:25:56,390 INFO Finished attack: lira\n", - "2024-11-22 09:25:56,390 INFO Preparing results for attack: lira\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "No existing file named './leakpro_output/results/lira/results.txt'. A new file will be created.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-11-22 09:25:59,687 INFO Auditing completed\n" - ] - }, - { - "data": { - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "from mimic_handler import MimicInputHandler\n", - "\n", - "from leakpro import LeakPro\n", - "\n", - "# Read the config file\n", - "config_path = \"audit.yaml\"\n", - "\n", - "# Prepare leakpro object\n", - "leakpro = LeakPro(MimicInputHandler, config_path)\n", - "\n", - "# Run the audit \n", - "leakpro.run_audit()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "leakpro_test", - "language": "python", - "name": "python3" - }, - "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.12.2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/mia/LOS/mimiciii_prepration/MIMIC_Extract/run.sh b/examples/mia/LOS/mimiciii_prepration/MIMIC_Extract/run.sh index 277c1c25..5fcf13ac 100644 --- a/examples/mia/LOS/mimiciii_prepration/MIMIC_Extract/run.sh +++ b/examples/mia/LOS/mimiciii_prepration/MIMIC_Extract/run.sh @@ -48,4 +48,4 @@ python3 mimic_direct_extract.py --resource_path resources/ --out_path output/ -- # # Part 3: Copy the output to the data directory # echo 'Copying file to target directory' -cp ./output/all_hourly_data.h5 ../../data/ \ No newline at end of file +cp ./output/all_hourly_data.h5 ../../data/ diff --git a/examples/mia/LOS/utils/data_processing.py b/examples/mia/LOS/utils/data_processing.py index b4a25098..b3b2a26c 100644 --- a/examples/mia/LOS/utils/data_processing.py +++ b/examples/mia/LOS/utils/data_processing.py @@ -1,26 +1,19 @@ """ +This file is inspired by https://github.com/MLforHealth/MIMIC_Extract MIT License - Copyright (c) 2019 MIT Laboratory for Computational Physiology - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. """ #TODO: Do I need to include the license for this file.? import os +import pickle + import numpy as np import pandas as pd -import pickle -from torch import from_numpy, Tensor -from torch.utils.data import Dataset, Subset, DataLoader from sklearn.preprocessing import StandardScaler +from torch import Tensor, from_numpy +from torch.utils.data import DataLoader, Dataset, Subset +from utils.model_grud import to_3D_tensor +from tqdm import tqdm class MimicDataset(Dataset): @@ -30,7 +23,7 @@ def __init__(self, x, y): self.x = from_numpy(x).float() # Convert features to torch tensors if needed else: self.x = x.float() # Ensure it is of type float - + # Check if y is already a tensor if not isinstance(y, Tensor): self.y = from_numpy(y).float() # Convert labels to torch tensors if needed @@ -42,104 +35,148 @@ def __len__(self): def __getitem__(self, idx): return self.x[idx], self.y[idx].squeeze(0) - + def subset(self, indices): return MimicDataset(self.x[indices], self.y[indices]) - -def get_mimic_dataset(path, train_frac, test_frac): + +def get_mimic_dataset(data_path, + train_frac, + validation_frac, + test_frac, + early_stop_frac, + use_LR = True): """Get the dataset, download it if necessary, and store it.""" - + + # Assert that the sum of all fractions is between 0 and 1 + total_frac = train_frac + validation_frac + test_frac + early_stop_frac + assert 0 < total_frac <= 1, "The sum of dataset fractions must be between 0 and 1." + + if use_LR: + path = data_path + "flattened/" + else: + path = data_path + "unflattened/" dataset_path = os.path.join(path, "dataset.pkl") indices_path = os.path.join(path, "indices.pkl") - + if os.path.exists(dataset_path) and os.path.exists(indices_path): + print("Loading dataset...") with open(dataset_path, "rb") as f: dataset = pickle.load(f) # Load the dataset with open(indices_path, "rb") as f: indices_dict = pickle.load(f) # Load the dictionary containing indices - train_indices = indices_dict['train_indices'] # Get the actual train indices - test_indices = indices_dict['test_indices'] # Get the actual test indices - return dataset, train_indices, test_indices - - else: - data_file_path = os.path.join(path, "all_hourly_data.h5") - if os.path.exists(data_file_path): - data= pd.read_hdf(data_file_path, 'vitals_labs') - statics = pd.read_hdf(data_file_path, 'patients') - - ID_COLS = ['subject_id', 'hadm_id', 'icustay_id'] - - train_data, holdout_data, y_train, y_holdout_data = data_splitter(statics, - data, - train_frac) - - train_data , holdout_data = data_normalization(train_data, holdout_data) - - train_data, holdout_data = [simple_imputer(df, ID_COLS) for df in (train_data, holdout_data)] - flat_train, flat_holdout = [df.pivot_table(index=['subject_id', 'hadm_id', 'icustay_id'], - columns=['hours_in']) for df in (train_data, holdout_data) ] - - # Reset the index to flatten the multi-index structure - flat_train, flat_holdout, Ys_train, Ys_test = [flatten_multi_index(df) - for df in (flat_train, flat_holdout, y_train, y_holdout_data)] - - # Check for missing values in all relevant DataFrames - assert_no_missing_values(train_data, holdout_data, flat_train, flat_holdout) - - train_df, test_df = standard_scaler(flat_train, flat_holdout) + train_indices = indices_dict["train_indices"] # Get the actual train indices + validation_indices = indices_dict["validation_indices"] # Get the actual validation indices + test_indices = indices_dict["test_indices"] # Get the actual test indices + early_stop_indices = indices_dict["early_stop_indices"] # Get the actual early stop indices + print(f"Loaded dataset from {dataset_path}") + return dataset, train_indices, validation_indices ,test_indices, early_stop_indices + + data_file_path = os.path.join(data_path, "all_hourly_data.h5") + if os.path.exists(data_file_path): + print("Loading data...") + data = pd.read_hdf(data_file_path, "vitals_labs") + statics = pd.read_hdf(data_file_path, "patients") + + ID_COLS = ["subject_id", "hadm_id", "icustay_id"] + + print("Splitting data...") + train_data, holdout_data, y_train, y_holdout_data = data_splitter(statics, + data, + train_frac) - # Creating the dataset - data_x = pd.concat((train_df, test_df), axis=0) - data_y = pd.concat((Ys_train, Ys_test), axis=0) + print("Normalizing data...") + train_data , holdout_data = data_normalization(train_data, holdout_data) + + print("Imputing missing values...") + train_data, holdout_data = [ + simple_imputer(df, ID_COLS) for df in tqdm((train_data, holdout_data), desc="Imputation")] + + if use_LR: + # Apply pivot_table to flatten the data + print("Flattening data for LR...") + flat_train, flat_holdout = [ + df.pivot_table(index=ID_COLS, columns=["hours_in"]) + for df in tqdm((train_data, holdout_data), desc="Flattening") + ] + print("Flattening data...") + train, holdout, label_train, label_holdout = [ + flatten_multi_index(df) + for df in tqdm((flat_train, flat_holdout, y_train, y_holdout_data), desc="Flattening Index") + ] + else: + # Skip pivot_table if flatten is False + train, holdout, label_train, label_holdout = train_data, holdout_data, y_train, y_holdout_data + + assert_no_missing_values(train_data, holdout_data, train, holdout) + + train_df, holdout_df = standard_scaler(train, holdout) + + # Creating the dataset + data_x = pd.concat((train_df, holdout_df), axis=0) + data_y = pd.concat((label_train, label_holdout), axis=0) - assert np.issubdtype(data_x.values.dtype, np.number), "Non-numeric data found in features." - assert np.issubdtype(data_y.values.dtype, np.number), "Non-numeric data found in labels." + assert np.issubdtype(data_x.values.dtype, np.number), "Non-numeric data found in features." + assert np.issubdtype(data_y.values.dtype, np.number), "Non-numeric data found in labels." + print("Creating dataset...") + if use_LR: dataset = MimicDataset(data_x.values, data_y.values) - - # Generate indices for training and testing - train_indices, test_indices = data_indices(data_x, train_frac, test_frac) - - # Save the dataset to dataset.pkl - with open(dataset_path, "wb") as file: - pickle.dump(dataset, file) - print(f"Saved dataset to {dataset_path}") - - # Save train and test indices to indices.pkl - indices_to_save = { - "train_indices": train_indices, - "test_indices": test_indices - } - with open(indices_path, "wb") as file: - pickle.dump(indices_to_save, file) - print(f"Saved train and test indices to {indices_path}") - - else: - msg = "Please download the MIMIC-III dataset from https://physionet.org/content/mimiciii/1.4/ and save it in the specified path." - raise FileNotFoundError(msg) - return dataset, train_indices, test_indices - - -def data_splitter(statics, data, train_frac): + else: + data_x = to_3D_tensor(data_x) + dataset = MimicDataset(data_x, data_y.values) + + # Generate indices for training, validation, test, and early stopping + train_indices, validation_indices, test_indices, early_stop_indices = data_indices(data_x, + train_frac, + validation_frac, + test_frac, + early_stop_frac) + + os.makedirs(os.path.dirname(dataset_path), exist_ok=True) + # Save the dataset to dataset.pkl + print("Saving dataset and indices...") + with open(dataset_path, "wb") as file: + pickle.dump(dataset, file) + print(f"Saved dataset to {dataset_path}") + + # Save train and test indices to indices.pkl + indices_to_save = { + "train_indices": train_indices, + "validation_indices": validation_indices, + "test_indices": test_indices, + "early_stop_indices": early_stop_indices, + } + with open(indices_path, "wb") as file: + pickle.dump(indices_to_save, file) + print(f"Saved train and test indices to {indices_path}") + else: + msg = "Please download the MIMIC-III dataset from https://physionet.org/content/mimiciii/1.4/ and save it in the specified path." + raise FileNotFoundError(msg) + return dataset, train_indices, validation_indices, test_indices, early_stop_indices + + +def data_splitter(statics, + data, + train_frac): GAP_TIME = 6 # In hours WINDOW_SIZE = 24 # In hours SEED = 1 - Ys = statics[statics.max_hours > WINDOW_SIZE + GAP_TIME][['los_icu']] - Ys['los_3'] = Ys['los_icu'] > 3 - Ys.drop(columns=['los_icu'], inplace=True) - Ys['los_3'] = Ys['los_3'].astype(float) + Ys = statics[statics.max_hours > WINDOW_SIZE + GAP_TIME][["los_icu"]] + Ys["los_3"] = Ys["los_icu"] > 3 + Ys.drop(columns=["los_icu"], inplace=True) + Ys["los_3"] = Ys["los_3"].astype(float) lvl2 = data[ - (data.index.get_level_values('icustay_id').isin(set(Ys.index.get_level_values('icustay_id')))) & - (data.index.get_level_values('hours_in') < WINDOW_SIZE) + (data.index.get_level_values("icustay_id").isin(set(Ys.index.get_level_values("icustay_id")))) & + (data.index.get_level_values("hours_in") < WINDOW_SIZE) ] - - data_subj_idx, y_subj_idx = [df.index.get_level_values('subject_id') for df in (lvl2, Ys)] + + data_subj_idx, y_subj_idx = [df.index.get_level_values("subject_id") for df in (lvl2, Ys)] data_subjects = set(data_subj_idx) assert data_subjects == set(y_subj_idx), "Subject ID pools differ!" - + # Randomly shuffle subjects and compute the sizes of the splits np.random.seed(SEED) subjects = np.random.permutation(list(data_subjects)) @@ -152,75 +189,154 @@ def data_splitter(statics, data, train_frac): # Split the data according to the subjects (train_data, holdout_data), (y_train, y_holdout) = [ - [df[df.index.get_level_values('subject_id').isin(s)] for s in (train_subj, test_subj)] + [df[df.index.get_level_values("subject_id").isin(s)] for s in (train_subj, test_subj)] for df in (lvl2, Ys) ] return train_data, holdout_data, y_train, y_holdout -def simple_imputer(df, ID_COLS): +# def simple_imputer(dataframe, +# ID_COLS): +# idx = pd.IndexSlice +# df = dataframe.copy() +# if len(df.columns.names) > 2: df.columns = df.columns.droplevel(("label", "LEVEL1", "LEVEL2")) + +# df_out = df.loc[:, idx[:, ["mean", "count"]]] +# icustay_means = df_out.loc[:, idx[:, "mean"]].groupby(ID_COLS).mean() + +# df_out.loc[:, idx[:, "mean"]] = ( +# df_out.loc[:, idx[:, "mean"]] +# .groupby(ID_COLS) +# .ffill() # Replace forward fill method +# .groupby(ID_COLS) +# .fillna(icustay_means) # Fill remaining NaNs with icustay_means +# .fillna(0) # Fill any remaining NaNs with 0 +# ) + +# # df_out.loc[:,idx[:,"mean"]] = df_out.loc[:,idx[:,"mean"]].groupby(ID_COLS).fillna( +# # method="ffill" +# # ).groupby(ID_COLS).fillna(icustay_means).fillna(0) + +# df_out.loc[:, idx[:, "count"]] = (df.loc[:, idx[:, "count"]] > 0).astype(float) +# df_out.rename(columns={"count": "mask"}, level="Aggregation Function", inplace=True) + +# is_absent = (1 - df_out.loc[:, idx[:, "mask"]]) +# hours_of_absence = is_absent.cumsum() +# time_since_measured = hours_of_absence - hours_of_absence[is_absent==0].fillna(method="ffill") +# time_since_measured.rename(columns={"mask": "time_since_measured"}, level="Aggregation Function", inplace=True) + +# df_out = pd.concat((df_out, time_since_measured), axis=1) +# df_out.loc[:, idx[:, "time_since_measured"]] = df_out.loc[:, idx[:, "time_since_measured"]].fillna(100) + +# df_out.sort_index(axis=1, inplace=True) +# return df_out + +def simple_imputer(dataframe, ID_COLS): idx = pd.IndexSlice - df = df.copy() - if len(df.columns.names) > 2: df.columns = df.columns.droplevel(('label', 'LEVEL1', 'LEVEL2')) - - df_out = df.loc[:, idx[:, ['mean', 'count']]] - icustay_means = df_out.loc[:, idx[:, 'mean']].groupby(ID_COLS).mean() - - df_out.loc[:,idx[:,'mean']] = df_out.loc[:,idx[:,'mean']].groupby(ID_COLS).fillna( - method='ffill' - ).groupby(ID_COLS).fillna(icustay_means).fillna(0) - - df_out.loc[:, idx[:, 'count']] = (df.loc[:, idx[:, 'count']] > 0).astype(float) - df_out.rename(columns={'count': 'mask'}, level='Aggregation Function', inplace=True) - - is_absent = (1 - df_out.loc[:, idx[:, 'mask']]) + df = dataframe.copy() + + # Adjust column levels if necessary + if len(df.columns.names) > 2: + df.columns = df.columns.droplevel(("label", "LEVEL1", "LEVEL2")) + + # Select mean and count columns + df_out = df.loc[:, idx[:, ["mean", "count"]]].copy() # Explicit deep copy + + # Compute group-level means + icustay_means = df_out.loc[:, idx[:, "mean"]].groupby(ID_COLS).transform("mean") + + # Forward fill and fill NaNs with icustay_means + df_out.loc[:, idx[:, "mean"]] = ( + df_out.loc[:, idx[:, "mean"]] + .groupby(ID_COLS) + .ffill() # Forward fill within groups + ) + df_out.loc[:, idx[:, "mean"]] = df_out.loc[:, idx[:, "mean"]].fillna(icustay_means) + + # Fill remaining NaNs with 0 + df_out.loc[:, idx[:, "mean"]] = df_out.loc[:, idx[:, "mean"]].fillna(0) + + # Binary mask for count columns + df_out.loc[:, idx[:, "count"]] = (df.loc[:, idx[:, "count"]] > 0).astype(float) + df_out = df_out.rename(columns={"count": "mask"}, level="Aggregation Function") # Avoid inplace=True + + # Calculate time since last measurement + is_absent = (1 - df_out.loc[:, idx[:, "mask"]]) hours_of_absence = is_absent.cumsum() - time_since_measured = hours_of_absence - hours_of_absence[is_absent==0].fillna(method='ffill') - time_since_measured.rename(columns={'mask': 'time_since_measured'}, level='Aggregation Function', inplace=True) + time_since_measured = hours_of_absence - hours_of_absence[is_absent == 0].ffill() + time_since_measured.rename(columns={"mask": "time_since_measured"}, level="Aggregation Function", inplace=True) + # Add time_since_measured to the output df_out = pd.concat((df_out, time_since_measured), axis=1) - df_out.loc[:, idx[:, 'time_since_measured']] = df_out.loc[:, idx[:, 'time_since_measured']].fillna(100) - + df_out.loc[:, idx[:, "time_since_measured"]] = df_out.loc[:, idx[:, "time_since_measured"]].fillna(100) + + # Sort columns by index df_out.sort_index(axis=1, inplace=True) + return df_out -def data_indices(dataset, train_frac, test_frac): + + + +def data_indices(dataset, + train_frac, + valid_frac, + test_frac, + early_stop_frac): N = len(dataset) N_train = int(train_frac * N) + N_validation = int(valid_frac * N) N_test = int(test_frac * N) - - # Generate sequential indices for training and testing - train_indices = list(range(N_train)) # Indices from 0 to N_train-1 - test_indices = list(range(N_train, N_train + N_test)) # Indices from N_train to N_train + N_test-1 - - return train_indices, test_indices - + N_early_stop = int(early_stop_frac * N) -def get_mimic_dataloaders(dataset, train_indices, test_indices, batch_size=128): + # Generate sequential indices for training and testing + # Indices from 0 to N_train-1 + train_indices = list(range(N_train)) + # Indices from N_train to N_train + N_validation-1 + validation_indices = list(range(N_train, N_train + N_validation)) + # Indices for test set + test_indices = list(range(N_train + N_validation, N_train + N_validation + N_test)) + # Indices for early stopping + early_stop_indices = list(range(N_train + N_validation + N_test, N_train + N_validation + N_test + N_early_stop)) + return train_indices, validation_indices, test_indices, early_stop_indices + + +def get_mimic_dataloaders(dataset, + train_indices, + validation_indices, + test_indices, + early_stop_indices, + batch_size=128): train_subset = Subset(dataset, train_indices) test_subset = Subset(dataset, test_indices) + validation_subset = Subset(dataset, validation_indices) + early_stop_subset = Subset(dataset, early_stop_indices) train_loader = DataLoader(train_subset, batch_size, shuffle=False) test_loader = DataLoader(test_subset, batch_size, shuffle=False) + validation_loader = DataLoader(validation_subset, batch_size, shuffle=False) + early_stop_loader = DataLoader(early_stop_subset, batch_size, shuffle=False) - return train_loader, test_loader + return train_loader, validation_loader, test_loader, early_stop_loader -def data_normalization(lvl2_train, lvl2_test): +def data_normalization(lvl2_train, + lvl2_test): idx = pd.IndexSlice - lvl2_means, lvl2_stds = lvl2_train.loc[:, idx[:,'mean']].mean(axis=0), lvl2_train.loc[:, idx[:,'mean']].std(axis=0) + lvl2_means, lvl2_stds = lvl2_train.loc[:, idx[:,"mean"]].mean(axis=0), lvl2_train.loc[:, idx[:,"mean"]].std(axis=0) - lvl2_train.loc[:, idx[:,'mean']] = (lvl2_train.loc[:, idx[:,'mean']] - lvl2_means)/lvl2_stds - lvl2_test.loc[:, idx[:,'mean']] = (lvl2_test.loc[:, idx[:,'mean']] - lvl2_means)/lvl2_stds + lvl2_train.loc[:, idx[:,"mean"]] = (lvl2_train.loc[:, idx[:,"mean"]] - lvl2_means)/lvl2_stds + lvl2_test.loc[:, idx[:,"mean"]] = (lvl2_test.loc[:, idx[:,"mean"]] - lvl2_means)/lvl2_stds return lvl2_train, lvl2_test -def standard_scaler(flat_train, flat_test): +def standard_scaler(flat_train, + flat_test): # Initialize the scaler scaler = StandardScaler() # Identify continuous columns (float64 and int64 types) - continuous_columns = flat_train.select_dtypes(include=['float64', 'int64']).columns + continuous_columns = flat_train.select_dtypes(include=["float64", "int64"]).columns # Fit the scaler on training data and transform both training and test sets train_flat_continuous = scaler.fit_transform(flat_train[continuous_columns]) diff --git a/examples/mia/LOS/utils/model.py b/examples/mia/LOS/utils/model_LR.py similarity index 76% rename from examples/mia/LOS/utils/model.py rename to examples/mia/LOS/utils/model_LR.py index a525085b..a99c61ad 100644 --- a/examples/mia/LOS/utils/model.py +++ b/examples/mia/LOS/utils/model_LR.py @@ -1,19 +1,20 @@ +import os +import pickle import tqdm as tqdm -import pickle -import torch.nn as nn -from torch import device, optim, no_grad, save, sigmoid, cuda +from torch import cuda, device, nn, no_grad, optim, save, sigmoid -class MimicLR(nn.Module): +class LR(nn.Module): def __init__(self, input_dim: int): """Initialize the logistic regression model with a single linear layer. Args: ---- input_dim (int): The size of the input feature vector. + """ - super(MimicLR, self).__init__() + super(LR, self).__init__() self.linear = nn.Linear(input_dim, 1) # Binary classification (1 output) # Metadata initialization self.init_params = {"input_dim": input_dim} @@ -24,26 +25,24 @@ def forward(self, x): def evaluate(model, loader, criterion, device): model.eval() - loss, correct = 0, 0 - for data, target in loader: - data, target = data.to(device), target.to(device) - target = target.float().unsqueeze(1) - - with no_grad(): + loss, acc = 0, 0 + with no_grad(): + for data, target in loader: + data, target = data.to(device), target.to(device) + target = target.float().unsqueeze(1) output = model(data) loss += criterion(output, target).item() - pred = output >= 0.5 - correct += (pred == target).float().sum() - acc = float(correct)/ len(loader.dataset) - loss /= len(loader) - + pred = (output) >= 0.5 + acc += pred.eq(target.data.view_as(pred)).sum() + loss /= len(loader) + acc = float(acc) / len(loader.dataset) return loss, acc def create_trained_model_and_metadata(model, train_loader, test_loader, - epochs , + epochs , lr , weight_decay , metadata = None): @@ -52,17 +51,17 @@ def create_trained_model_and_metadata(model, model.to(device_name) model.train() - criterion = nn.BCELoss(reduction="mean") + criterion = nn.BCELoss() optimizer = optim.SGD(model.parameters(), lr = lr, weight_decay = weight_decay) train_losses, train_accuracies = [], [] test_losses, test_accuracies = [], [] - + for e in tqdm.tqdm(range(epochs), desc="Training Progress"): model.train() train_acc, train_loss = 0.0, 0.0 - + for data, target in train_loader: target = target.float().unsqueeze(1) data, target = data.to(device_name, non_blocking=True), target.to(device_name, non_blocking=True) @@ -70,27 +69,30 @@ def create_trained_model_and_metadata(model, output = model(data) loss = criterion(output, target) - pred =output >= 0.5 + pred = (output) >= 0.5 train_acc += pred.eq(target).sum().item() - + loss.backward() optimizer.step() train_loss += loss.item() - - epoch_train_loss = train_loss / len(train_loader) - epoch_train_acc = train_acc / len(train_loader.dataset) - - train_losses.append(epoch_train_loss) - train_accuracies.append(epoch_train_acc) - + + train_loss /= len(train_loader) + train_acc /= len(train_loader.dataset) + + train_losses.append(train_loss) + train_accuracies.append(train_acc) + test_loss, test_acc = evaluate(model, test_loader, criterion, device_name) - # _ , train_loss = evaluate(model, train_loader, criterion, device_name) test_losses.append(test_loss) test_accuracies.append(test_acc) - + # Move the model back to the CPU model.to("cpu") - with open("target/target_model.pkl", "wb") as f: + + + if not os.path.exists("target_LR"): + os.makedirs("target_LR") + with open("target_LR/target_model.pkl", "wb") as f: save(model.state_dict(), f) # Create metadata and store it @@ -98,12 +100,12 @@ def create_trained_model_and_metadata(model, meta_data["train_indices"] = train_loader.dataset.indices meta_data["test_indices"] = test_loader.dataset.indices meta_data["num_train"] = len(meta_data["train_indices"]) - + # Write init params meta_data["init_params"] = {} for key, value in model.init_params.items(): meta_data["init_params"][key] = value - + # read out optimizer parameters meta_data["optimizer"] = {} meta_data["optimizer"]["name"] = optimizer.__class__.__name__.lower() @@ -124,8 +126,9 @@ def create_trained_model_and_metadata(model, meta_data["train_loss"] = train_loss meta_data["test_loss"] = test_loss meta_data["dataset"] = "mimiciii" - - with open("target/model_metadata.pkl", "wb") as f: + + + with open("target_LR/model_metadata.pkl", "wb") as f: pickle.dump(meta_data, f) - - return train_accuracies, train_losses, test_accuracies, test_losses \ No newline at end of file + + return train_accuracies, train_losses, test_accuracies, test_losses diff --git a/examples/mia/LOS/utils/model_grud.py b/examples/mia/LOS/utils/model_grud.py new file mode 100644 index 00000000..fed35043 --- /dev/null +++ b/examples/mia/LOS/utils/model_grud.py @@ -0,0 +1,447 @@ +""" +This file is inspired by https://github.com/MLforHealth/MIMIC_Extract +MIT License +Copyright (c) 2019 MIT Laboratory for Computational Physiology +""" +import math +import os +import pickle +import time +import warnings + +import numpy as np +import pandas as pd +import torch.nn.functional as F +import torch.utils.data as utils +from sklearn.metrics import accuracy_score +from torch import Tensor, cat, cuda, device, exp, eye, from_numpy, isnan, max, nn, optim, save, sigmoid, squeeze, tanh, zeros +from torch.autograd import Variable +from torch.nn.parameter import Parameter +from torch.optim.lr_scheduler import ReduceLROnPlateau +from tqdm import tqdm + + +def to_3D_tensor(df): + idx = pd.IndexSlice + np_3D = np.dstack([df.loc[idx[:, :, :, i], :].values for i in sorted(set(df.index.get_level_values("hours_in")))]) + return from_numpy(np_3D) + +def prepare_dataloader(df, Ys, batch_size, shuffle=True): + """Dfs = (df_train, df_dev, df_test). + df_* = (subject, hadm, icustay, hours_in) X (level2, agg fn \ni {mask, mean, time}) + Ys_series = (subject, hadm, icustay) => label. + """ + X = from_numpy(to_3D_tensor(df).astype(np.float32)) + label = from_numpy(Ys.values.astype(np.int64)) + dataset = utils.TensorDataset(X, label) + return utils.DataLoader(dataset, batch_size =int(batch_size) , shuffle=shuffle, drop_last = True) + +class FilterLinear(nn.Module): + def __init__(self, in_features, out_features, filter_square_matrix, device, bias=True): + """filter_square_matrix : filter square matrix, whose each elements is 0 or 1. + """ + super(FilterLinear, self).__init__() + self.in_features = in_features + self.out_features = out_features + + assert in_features > 1 and out_features > 1, "Passing in nonsense sizes" + + self.filter_square_matrix = None + self.filter_square_matrix = Variable(filter_square_matrix.to(device), requires_grad=False) + + self.weight = Parameter(Tensor(out_features, in_features)).to(device) + + if bias: + self.bias = Parameter(Tensor(out_features)).to(device) + else: + self.register_parameter("bias", None) + self.reset_parameters() + + def reset_parameters(self): + stdv = 1. / math.sqrt(self.weight.size(1)) + self.weight.data.uniform_(-stdv, stdv) + if self.bias is not None: + self.bias.data.uniform_(-stdv, stdv) + + def forward(self, x): + return F.linear( + x, + self.filter_square_matrix.mul(self.weight), + self.bias + ) + + def __repr__(self): + return self.__class__.__name__ + "(" \ + + "in_features=" + str(self.in_features) \ + + ", out_features=" + str(self.out_features) \ + + ", bias=" + str(self.bias is not None) + ")" + +class GRUD(nn.Module): + def __init__(self, input_size, cell_size, hidden_size, X_mean, batch_size = 0, output_last = False): + """With minor modifications from https://github.com/zhiyongc/GRU-D/ + + Recurrent Neural Networks for Multivariate Times Series with Missing Values + GRU-D: GRU exploit two representations of informative missingness patterns, i.e., masking and time interval. + cell_size is the size of cell_state. + + Implemented based on the paper: + @article{che2018recurrent, + title={Recurrent neural networks for multivariate time series with missing values}, + author={Che, Zhengping and Purushotham, Sanjay and Cho, Kyunghyun and Sontag, David and Liu, Yan}, + journal={Scientific reports}, + volume={8}, + number={1}, + pages={6085}, + year={2018}, + publisher={Nature Publishing Group} + } + + GRU-D: + input_size: variable dimension of each time + hidden_size: dimension of hidden_state + mask_size: dimension of masking vector + X_mean: the mean of the historical input data + """ + + super(GRUD, self).__init__() + + # Save init params to a dictionary + self.init_params = { + "input_size": input_size, + "cell_size": cell_size, + "hidden_size": hidden_size, + "X_mean": X_mean, + "batch_size": batch_size, + "output_last": output_last + } + + self.hidden_size = hidden_size + self.delta_size = input_size + self.mask_size = input_size + + self.device = device("cuda" if cuda.is_available() else "cpu") + self.identity = eye(input_size).to(self.device) + self.X_mean = Variable(Tensor(X_mean).to(self.device)) + + # Wz, Uz are part of the same network. the bias is bz + self.zl = nn.Linear(input_size + hidden_size + self.mask_size, hidden_size).to(self.device) + + # Wr, Ur are part of the same network. the bias is br + self.rl = nn.Linear(input_size + hidden_size + self.mask_size, hidden_size).to(self.device) + + # W, U are part of the same network. the bias is b + self.hl = nn.Linear(input_size + hidden_size + self.mask_size, hidden_size).to(self.device) + + self.gamma_x_l = FilterLinear(self.delta_size, self.delta_size, self.identity, self.device) + self.gamma_h_l = nn.Linear(self.delta_size, self.hidden_size).to(self.device) + self.output_last = output_last + + self.fc = nn.Linear(self.hidden_size, 2).to(self.device) + self.bn= nn.BatchNorm1d(2, eps=1e-05, momentum=0.1, affine=True).to(self.device) + self.drop=nn.Dropout(p=0.7, inplace=False) + + + def step(self, x, x_last_obsv, x_mean, h, mask, delta): + """Inputs: + x: input tensor + x_last_obsv: input tensor with forward fill applied + x_mean: the mean of each feature + h: the hidden state of the network + mask: the mask of whether or not the current value is observed + delta: the tensor indicating the number of steps since the last time a feature was observed. + + Returns: + h: the updated hidden state of the network + + """ + + # Assert to check for NaNs in x_mean + assert not isnan(x_mean).any(), "NaN values found in x_mean" + + batch_size = x.size()[0] + feature_size = x.size()[1] + zero_x = zeros(batch_size, feature_size).to(self.device) + zero_h = zeros(batch_size, self.hidden_size).to(self.device) + + + gamma_x_l_delta = self.gamma_x_l(delta) + delta_x = exp(-max(zero_x, gamma_x_l_delta)) + + gamma_h_l_delta = self.gamma_h_l(delta) + delta_h = exp(-max(zero_h, gamma_h_l_delta)) + + x_mean = x_mean.repeat(batch_size, 1) + + x = mask * x + (1 - mask) * (delta_x * x_last_obsv + (1 - delta_x) * x_mean) + h = delta_h * h + + combined = cat((x, h, mask), 1) + # Assert to check for NaNs in combined + assert not isnan(combined).any(), "NaN values found in combined" + + z = sigmoid(self.zl(combined)) #sigmoid(W_z*x_t + U_z*h_{t-1} + V_z*m_t + bz) + r = sigmoid(self.rl(combined)) #sigmoid(W_r*x_t + U_r*h_{t-1} + V_r*m_t + br) + combined_new = cat((x, r*h, mask), 1) + h_tilde = tanh(self.hl(combined_new)) #tanh(W*x_t +U(r_t*h_{t-1}) + V*m_t) + b + h = (1 - z) * h + z * h_tilde + + return h + + + def forward(self, X): + """X: Input tensor of shape (batch_size, time_steps * 3, features) + The tensor includes Mask, Measurement, and Delta sequentially for each time step. + """ + + # Step 1: Split the input tensor into Mask, Measurement, and Delta + batch_size = X.size(0) + time_steps = X.size(1) // 3 # Since every 3 consecutive steps represent Mask, Measurement, and Delta + + # Reshape X into 3 separate tensors for Mask, Measurement, and Delta + Mask = X[:, np.arange(0, X.size(1), 3), :] # Extract Mask + Measurement = X[:, np.arange(1, X.size(1), 3), :] # Extract Measurement + Delta = X[:, np.arange(2, X.size(1), 3), :] # Extract Delta + + # Transpose tensors to match (batch_size, time_steps, features) + Mask = Mask.transpose(1, 2) + Measurement = Measurement.transpose(1, 2) + Delta = Delta.transpose(1, 2) + + # X_last_obsv is initialized to Measurement at the starting point + X_last_obsv = Measurement + + # Step 2: Initialize hidden state + step_size = Measurement.size(1) # Number of time points + Hidden_State = self.initHidden(batch_size) + + # Step 3: Iterate through time steps and update the GRU hidden state + outputs = None + for i in range(step_size): + Hidden_State = self.step( + squeeze(Measurement[:, i, :], 1), + squeeze(X_last_obsv[:, i, :], 1), + squeeze(self.X_mean[:, i, :], 1), + Hidden_State, + squeeze(Mask[:, i, :], 1), + squeeze(Delta[:, i, :], 1), + ) + # Collect hidden states + if outputs is None: + outputs = Hidden_State.unsqueeze(1) + else: + outputs = cat((Hidden_State.unsqueeze(1), outputs), 1) + + # Step 4: Predict a binary outcome using FC, BatchNorm, and Dropout layers + return self.drop(self.bn(self.fc(Hidden_State))) + + def initHidden(self, batch_size): + Hidden_State = Variable(zeros(batch_size, self.hidden_size)).to(self.device) + return Hidden_State + +def to_numpy(tensor): + return tensor.detach().cpu().numpy() if tensor.is_cuda else tensor.detach().numpy() + +def gru_trained_model_and_metadata(model, + train_dataloader, + test_dataloader, + epochs, + patience_early_stopping, + patience_lr, + min_delta, + learning_rate): + + print("Model Structure: ", model) + print("Start Training ... ") + + # Check if the input tensor is 3D + # This check is nessary because the GRU-D model expects a 3D tensor, meaning the input data should not be flattened + # The input tensor should have the shape (num_datapoints, num_features, num_timepoints) + if train_dataloader.dataset.dataset.x.ndimension() != 3: + warnings.warn("Input tensor is not 3D. There might be a mismatch between .", UserWarning) + + # Early Stopping + min_loss_epoch_valid = float("inf") # Initialize to infinity for comparison + patient_epoch = 0 # Initialize patient counter + + device_name = device("cuda" if cuda.is_available() else "cpu") + + if isinstance(model, nn.Sequential): + output_last = model[-1].output_last + print("Output type dermined by the last layer") + else: + output_last = model.output_last + print("Output type dermined by the model") + + criterion_CEL = nn.CrossEntropyLoss() + criterion_MSE = nn.MSELoss() + optimizer = optim.Adam(model.parameters(), lr=learning_rate) + + # Reduce learning rate when a metric has stopped improving + scheduler = ReduceLROnPlateau(optimizer, mode="min", factor=0.5, patience = patience_lr) + + + train_losses = [] + test_losses = [] + test_acces = [] + train_acces = [] + + + cur_time = time.time() + pre_time = time.time() + + + model.to(device_name) + + for epoch in tqdm(range(epochs), desc="Training Progress"): + + model.train() + train_loss = 0.0 + + test_dataloader_iter = iter(test_dataloader) + + for _, (X, labels) in enumerate(tqdm(train_dataloader, desc="Training Batches")): + + X = X.to(device_name) + labels = labels.to(device_name) + labels = labels.long() + prediction = model(X) + + output_last = True + if output_last: + loss = criterion_CEL(squeeze(prediction), squeeze(labels)) + else: + full_labels = cat((X[:,1:,:], labels), dim = 1) + loss = criterion_MSE(prediction, full_labels) + + + optimizer.zero_grad() + loss.backward() + optimizer.step() + train_loss += loss.item() + + train_loss /= len(train_dataloader) + train_losses.append(train_loss) + + # Convert predictions to class indices + binary_predictions = to_numpy(prediction).argmax(axis=1) + + # Ensure labels are integer and 1D + binary_labels = to_numpy(labels).astype(int) + # Compute accuracy + train_acc = accuracy_score(binary_labels, binary_predictions) + train_acces.append(train_acc) + + # test + model.eval() + try: + X_test, labels_test = next(test_dataloader_iter) + except StopIteration: + valid_dataloader_iter = iter(test_dataloader) + X_test, labels_test = next(valid_dataloader_iter) + + + model.zero_grad() + X_test = X_test.to(device_name) + labels_test = labels_test.to(device_name) + labels_test = labels_test.long() + + prediction_test = model(X_test) + + + if output_last: + test_loss = criterion_CEL(squeeze(prediction_test), squeeze(labels_test)) + else: + full_labels_val = cat((X_test[:,1:,:], labels_test), dim = 1) + test_loss = criterion_MSE(prediction_test, full_labels_val) + + test_loss = test_loss.cpu().item() + test_losses.append(test_loss) + + # Convert predictions to class indices + binary_predictions_test = to_numpy(prediction_test).argmax(axis=1) + + # Ensure labels are integer and 1D + binary_labels_test = to_numpy(labels_test).astype(int) + # Compute accuracy + test_acc = accuracy_score(binary_labels_test, binary_predictions_test) + test_acces.append(test_acc) + + # Early stopping + # Assume test_loss is computed for validation set + if test_loss < min_loss_epoch_valid - min_delta: # Improvement condition + min_loss_epoch_valid = test_loss + patient_epoch = 0 + print(f"Epoch {epoch}: Validation loss improved to {test_loss:.4f}") + else: + patient_epoch += 1 + print(f"Epoch {epoch}: No improvement. Patience counter: {patient_epoch}/{patience_early_stopping}") + + if patient_epoch >= patience_early_stopping: + print(f"Early stopping at epoch {epoch}. Best validation loss: {min_loss_epoch_valid:.4f}") + break + + # Step the scheduler + scheduler.step(test_loss) + + # Check the learning rate + current_lr = optimizer.param_groups[0]["lr"] + print(f"Learning Rate: {current_lr:.6f}") + + # Stop if learning rate becomes too small + if current_lr < 1e-6: + print("Learning rate too small, stopping training.") + break + + + # Print training parameters + cur_time = time.time() + print("Epoch: {}, train_loss: {}, valid_loss: {}, time: {}".format( \ + epoch, \ + np.around(train_loss, decimals=8),\ + np.around(test_loss, decimals=8),\ + np.around(cur_time - pre_time, decimals=2))) + pre_time = cur_time + # Move the model back to the CPU + # Ensure the target directory exists + os.makedirs("target_GRUD", exist_ok=True) + model.to("cpu") + with open("target_GRUD/target_model.pkl", "wb") as f: + save(model.state_dict(), f) + + # Create metadata and store it + meta_data = {} + meta_data["train_indices"] = train_dataloader.dataset.indices + meta_data["test_indices"] = test_dataloader.dataset.indices + meta_data["num_train"] = len(meta_data["train_indices"]) + + # Write init params + meta_data["init_params"] = {} + for key, value in model.init_params.items(): + meta_data["init_params"][key] = value + + # read out optimizer parameters + meta_data["optimizer"] = {} + meta_data["optimizer"]["name"] = optimizer.__class__.__name__.lower() + meta_data["optimizer"]["lr"] = optimizer.param_groups[0].get("lr", 0) + meta_data["optimizer"]["weight_decay"] = optimizer.param_groups[0].get("weight_decay", 0) + meta_data["optimizer"]["momentum"] = optimizer.param_groups[0].get("momentum", 0) + meta_data["optimizer"]["dampening"] = optimizer.param_groups[0].get("dampening", 0) + meta_data["optimizer"]["nesterov"] = optimizer.param_groups[0].get("nesterov", False) + + # read out criterion parameters + meta_data["loss"] = {} + meta_data["loss"]["name"] = criterion_CEL.__class__.__name__.lower() + + meta_data["batch_size"] = train_dataloader.batch_size + meta_data["epochs"] = epochs + meta_data["train_acc"] = train_acc + meta_data["test_acc"] = test_acc + meta_data["train_loss"] = train_loss + meta_data["test_loss"] = test_loss + meta_data["dataset"] = "mimiciii" + with open("target_GRUD/model_metadata.pkl", "wb") as f: + pickle.dump(meta_data, f) + return train_losses, test_losses, train_acces, test_acces + + + diff --git a/examples/mia/cifar/audit.yaml b/examples/mia/cifar/audit.yaml index 8440826b..dc38dd37 100644 --- a/examples/mia/cifar/audit.yaml +++ b/examples/mia/cifar/audit.yaml @@ -10,11 +10,11 @@ audit: # Configurations for auditing gamma: 2.0 offline_a: 0.33 # parameter from which we compute p(x) from p_OUT(x) such that p_IN(x) = a p_OUT(x) + b. offline_b: 0.66 - qmia: - training_data_fraction: 1.0 # Fraction of the auxilary dataset (data without train and test indices) to use for training the quantile regressor - epochs: 5 # Number of training epochs for quantile regression - population: - attack_data_fraction: 1.0 # Fraction of the auxilary dataset to use for this attack + # qmia: + # training_data_fraction: 1.0 # Fraction of the auxilary dataset (data without train and test indices) to use for training the quantile regressor + # epochs: 5 # Number of training epochs for quantile regression + # population: + # attack_data_fraction: 1.0 # Fraction of the auxilary dataset to use for this attack lira: training_data_fraction: 0.5 # Fraction of the auxilary dataset to use for this attack (in each shadow model training) num_shadow_models: 8 # Number of shadow models to train @@ -38,12 +38,12 @@ audit: # Configurations for auditing batch_size: 50 verbose: True epsilon_threshold: 1e-6 - yoqo: - training_data_fraction: 0.5 # Fraction of the auxilary dataset to use for this attack (in each shadow model training) - num_shadow_models: 8 # Number of shadow models to train - online: True # perform online or offline attack - lr_xprime_optimization: .01 - max_iterations: 35 + # yoqo: + # training_data_fraction: 0.5 # Fraction of the auxilary dataset to use for this attack (in each shadow model training) + # num_shadow_models: 8 # Number of shadow models to train + # online: True # perform online or offline attack + # lr_xprime_optimization: .01 + # max_iterations: 35 output_dir: "./leakpro_output" attack_type: "mia" #mia, gia diff --git a/leakpro/attacks/utils/shadow_model_handler.py b/leakpro/attacks/utils/shadow_model_handler.py index b06612ef..de8d25b9 100755 --- a/leakpro/attacks/utils/shadow_model_handler.py +++ b/leakpro/attacks/utils/shadow_model_handler.py @@ -59,25 +59,44 @@ def __init__(self:Self, handler: AbstractInputHandler) -> None: # noqa: PLR0912 """ caller = "shadow_model" super().__init__(handler, caller) - self.configs = handler.configs.get("shadow_model", None) + + shadow_model = self.handler.configs.get("shadow_model", None) + self.shadow_model_type = shadow_model.get("model_class", None) if isinstance(shadow_model, dict) else None + + self.target_model_type = self.handler.configs.get("target", {}).get("model_class", None) + if self.target_model_type is None: + raise ValueError("Target model type is not specified") # Set up the names of the shadow model self.model_storage_name = "shadow_model" self.metadata_storage_name = "metadata" - def _filter(self:Self, data_size:int, online:bool)->list[int]: + def _filter(self:Self, data_size:int, online:bool, model_type: str)->list[int]: # Get the metadata for the shadow models entries = os.listdir(self.storage_path) pattern = re.compile(rf"^{self.metadata_storage_name}_\d+\.pkl$") files = [f for f in entries if pattern.match(f)] # Extract the index of the metadata all_indices = [int(re.search(r"\d+", f).group()) for f in files] + # Filter out indices to only keep the ones with the same data size filtered_indices = [] + mismatched_model_types = [] + for i in all_indices: metadata = self._load_shadow_metadata(i) if metadata["num_train"] == data_size and metadata["online"] == online: - filtered_indices.append(i) + if metadata.get("model_type") == model_type: + filtered_indices.append(i) + else: + mismatched_model_types.append((i, metadata.get("model_type", "Unknown"))) + + # Warn about mismatched model types + if mismatched_model_types: + logger.warning( + f"Mismatched model types found in saved shadow models: {mismatched_model_types}. " + f"Expected model type: {model_type}." + ) return all_indices, filtered_indices def create_shadow_models( @@ -104,9 +123,18 @@ def create_shadow_models( if num_models < 0: raise ValueError("Number of models cannot be negative") + # Get shadow model class + if self.shadow_model_type is None: + logger.warning( + "Using the same model class for shadow models as the target model." + ) + shadow_model_type = self.target_model_type + else: + shadow_model_type = self.shadow_model_type + # Get the size of the dataset data_size = int(len(shadow_population)*training_fraction) - all_indices, filtered_indices = self._filter(data_size, online) + all_indices, filtered_indices = self._filter(data_size, online, shadow_model_type) # Create a list of indices to use for the new shadow models n_existing_models = len(filtered_indices) @@ -143,18 +171,19 @@ def create_shadow_models( logger.info(f"Saved shadow model {i} to {self.storage_path}") logger.info(f"Storing metadata for shadow model {i}") - meta_data = {} - meta_data["init_params"] = self.init_params - meta_data["train_indices"] = data_indices - meta_data["num_train"] = len(data_indices) - meta_data["optimizer"] = optimizer.__class__.__name__ - meta_data["criterion"] = criterion.__class__.__name__ - meta_data["batch_size"] = self.batch_size - meta_data["epochs"] = self.epochs - meta_data["train_acc"] = train_acc - meta_data["train_loss"] = train_loss - meta_data["online"] = online - + meta_data = { + "init_params": self.init_params, + "train_indices": data_indices, + "num_train": len(data_indices), + "optimizer": optimizer.__class__.__name__, + "criterion": criterion.__class__.__name__, + "batch_size": self.batch_size, + "epochs": self.epochs, + "train_acc": train_acc, + "train_loss": train_loss, + "online": online, + "model_type": shadow_model_type, + } with open(f"{self.storage_path}/{self.metadata_storage_name}_{i}.pkl", "wb") as f: pickle.dump(meta_data, f) diff --git a/leakpro/input_handler/abstract_input_handler.py b/leakpro/input_handler/abstract_input_handler.py index 1fa65a8a..c546fc5e 100755 --- a/leakpro/input_handler/abstract_input_handler.py +++ b/leakpro/input_handler/abstract_input_handler.py @@ -25,6 +25,11 @@ def get_optimizer(self:Self, model:torch.nn.Module) -> torch.optim.Optimizer: """Get the optimizer used for the target model to be used in model training.""" pass + # @abstractmethod + # def get_shadow_model_type(self:Self) -> str: + # """Get the type of shadow model to be used in the attack.""" + # pass + @abstractmethod def train( self: Self,