Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TF implementation of Inception Time - optimized for each task using Grid Search #23

Open
wants to merge 52 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
d9b6c3a
push NBs
Bsingstad Sep 19, 2022
eb421a2
updated notebook
Bsingstad Sep 19, 2022
d6d0aaa
Do changes in order to be able to run on Google Colab
Bsingstad Sep 19, 2022
f95b819
Update PTB_XL_experiments.ipynb
Bsingstad Sep 19, 2022
c6b98e8
set output folder and change NB
Bsingstad Sep 28, 2022
4869a96
override hardcoded folders
Bsingstad Sep 28, 2022
2a9b41c
Make it possible to store results in google drive
Bsingstad Sep 28, 2022
0f8537d
Update reproduce_results.py
Bsingstad Sep 28, 2022
df0a33d
main take folder as arg and add correct args to evaluate in order to …
Bsingstad Nov 1, 2022
2feb174
Add support for using arguments when running file
Bsingstad Nov 1, 2022
ee3e3a1
Update reproduce_results.py
Bsingstad Nov 1, 2022
aaf865a
Update scp_experiment.py
Bsingstad Nov 17, 2022
8ec778c
Add my model
Bsingstad Dec 6, 2022
9f58464
small changes to noise induction
Bsingstad Dec 7, 2022
680444c
fix
Bsingstad Dec 7, 2022
0942c90
Update your_configs.py
Bsingstad Dec 7, 2022
ec99295
change config
Bsingstad Dec 7, 2022
5c6bdc8
FIX: found a change that had to be done in order to run my_model
Bsingstad Jan 2, 2023
1e17bf1
Update reproduce_results.py
Bsingstad Jan 2, 2023
cd5b90c
Add IF-statement to control if noise should be added or not
Bsingstad Jan 2, 2023
09c1be5
change predict statment and set noise as False by default
Bsingstad Jan 2, 2023
096ccc0
Update to 30 epochs
Bsingstad Jan 2, 2023
cb84116
Add my finetuning NB
Bsingstad Jan 4, 2023
cd4596f
Update your_model.py
Bsingstad Jan 6, 2023
0199427
fix errors
Bsingstad Jan 21, 2023
e2de13e
Add grid search files
Bsingstad Feb 7, 2023
f35b22f
Update My_Finetuning.ipynb
Bsingstad Feb 8, 2023
9284812
Create My_Finetuning2.ipynb
Bsingstad Feb 8, 2023
3b771f5
Update My_Finetuning2.ipynb
Bsingstad Feb 9, 2023
1125219
Add files via upload
Bsingstad Feb 17, 2023
d5e0e47
Update My_Finetuning170223.ipynb
Bsingstad Feb 19, 2023
0223e14
Add files via upload
Bsingstad Mar 28, 2023
a7af6c0
Update model params after grid search
Bsingstad Apr 16, 2023
5c36a0b
add tf addons
Bsingstad Apr 16, 2023
8018e8b
add results from optimized Inception model
Bsingstad Apr 16, 2023
cbb43b2
update models to run
Bsingstad Apr 28, 2023
54587b5
Delete make_gridsearch_file.ipynb
Bsingstad Apr 28, 2023
070d066
Delete gridsearch_params.csv
Bsingstad Apr 28, 2023
3cd9cdb
Delete final_results.txt
Bsingstad Apr 28, 2023
b605049
Delete PTB_XL_experiments.ipynb
Bsingstad Apr 28, 2023
401c39d
Delete My_Finetuning170223.ipynb
Bsingstad Apr 28, 2023
99c98c8
Delete My_Finetuning2.ipynb
Bsingstad Apr 28, 2023
8333ec3
Delete Gridsearch280323.ipynb
Bsingstad Apr 28, 2023
9475602
Delete My_Finetuning.ipynb
Bsingstad Apr 28, 2023
6232cb9
delete GS folder
Bsingstad Apr 28, 2023
65a7b86
Merge branch 'repo_for_PR' of https://github.com/Bsingstad/ecg_ptbxl_…
Bsingstad Apr 28, 2023
7a06985
PR Update
Bsingstad Jul 31, 2024
f06fc9f
add template to scp_experiment.py
Bsingstad Jul 31, 2024
222a8d7
Update scp_experiment.py
helme Aug 1, 2024
7994737
Update utils.py
helme Aug 1, 2024
b9708e3
Requested changes
Bsingstad Aug 4, 2024
16b8cb7
change file path names
Bsingstad Aug 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions code/configs/tf_inception_time_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
conf_tf_inception = {'modelname':'tf_inception', 'modeltype':'inception_time_model',
'parameters':dict()}


conf_tf_inception_all = {'modelname':'tf_inception_all', 'modeltype':'inception_time_model',
'parameters':dict(epoch=15, batch_size=16, lr_init=0.001, lr_red="no", model_depth=9 , loss="bce" , kernel_size=60)}

conf_tf_inception_diagnostic = {'modelname':'tf_inception_diagnostic', 'modeltype':'inception_time_model',
'parameters':dict(epoch=25, batch_size=32, lr_init=0.001 ,lr_red="no", model_depth=6 , loss="bce" , kernel_size=60)}

conf_tf_inception_form = {'modelname':'tf_inception_form', 'modeltype':'inception_time_model',
'parameters':dict(epoch=25, batch_size=64, lr_init=0.001 ,lr_red="no", model_depth=6 , loss="bce" , kernel_size=20)}

conf_tf_inception_rhythm = {'modelname':'tf_inception_rhythm', 'modeltype':'inception_time_model',
'parameters':dict(epoch=25, batch_size=16, lr_init=0.001, lr_red="no", model_depth=9 , loss="wbce" , kernel_size=40)}

conf_tf_inception_subdiagnostic = {'modelname':'tf_inception_subdiagnostic', 'modeltype':'inception_time_model',
'parameters':dict(epoch=15, batch_size=64, lr_init=0.001 , lr_red="no", model_depth=6 , loss="wbce" , kernel_size=20)}

conf_tf_inception_superdiagnostic = {'modelname':'tf_inception_superdiagnostic', 'modeltype':'inception_time_model',
'parameters':dict(epoch=25, batch_size=64, lr_init=0.001 ,lr_red="yes", model_depth=12 , loss="bce" , kernel_size=40)}

12 changes: 11 additions & 1 deletion code/experiments/scp_experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ def __init__(self, experiment_name, task, datafolder, outputfolder, models, samp
self.outputfolder = outputfolder
self.datafolder = datafolder
self.sampling_frequency = sampling_frequency
self.noise_mean = 0
self.noise_std_scale = 0.1
Bsingstad marked this conversation as resolved.
Show resolved Hide resolved

# create folder structure if needed
if not os.path.exists(self.outputfolder+self.experiment_name):
Expand All @@ -34,7 +36,7 @@ def __init__(self, experiment_name, task, datafolder, outputfolder, models, samp
if not os.path.exists(outputfolder+self.experiment_name+'/data/'):
os.makedirs(self.outputfolder+self.experiment_name+'/data/')

def prepare(self):
def prepare(self, add_noise=False):
# Load PTB-XL data
self.data, self.raw_labels = utils.load_dataset(self.datafolder, self.sampling_frequency)

Expand All @@ -59,6 +61,11 @@ def prepare(self):
self.X_train, self.X_val, self.X_test = utils.preprocess_signals(self.X_train, self.X_val, self.X_test, self.outputfolder+self.experiment_name+'/data/')
self.n_classes = self.y_train.shape[1]

# Add noise to test data
if add_noise == True:
noise = np.random.normal(self.noise_mean,self.X_test.std() * self.noise_std_scale, size = self.X_test.shape)
self.X_test = self.X_test + noise

# save train and test labels
self.y_train.dump(self.outputfolder + self.experiment_name+ '/data/y_train.npy')
self.y_val.dump(self.outputfolder + self.experiment_name+ '/data/y_val.npy')
Expand Down Expand Up @@ -100,6 +107,9 @@ def perform(self):
elif modeltype == "fastai_model":
from models.fastai_model import fastai_model
model = fastai_model(modelname, n_classes, self.sampling_frequency, mpath, self.input_shape, **modelparams)
elif modeltype == "inception_time_model":
from code.models.inception_time import inception_time_model
model = inception_time_model(modelname, n_classes, self.sampling_frequency, mpath, self.input_shape, **modelparams)
elif modeltype == "YOUR_MODEL_TYPE":
# YOUR MODEL GOES HERE!
from models.your_model import YourModel
Expand Down
120 changes: 120 additions & 0 deletions code/models/inception_time.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
from models.base_model import ClassificationModel
import tensorflow as tf
import numpy as np
import tensorflow_addons as tfa

class inception_time_model(ClassificationModel):
def __init__(self, name, n_classes, sampling_frequency, outputfolder, input_shape, epoch=30, batch_size=32, lr_init = 0.001, lr_red="yes", model_depth=6, loss="bce", kernel_size=40, bottleneck_size=32, nb_filters=32, clf="binary", verbose=1):
super(inception_time_model, self).__init__()
self.name = name
self.n_classes = n_classes
self.sampling_frequency = sampling_frequency
self.outputfolder = outputfolder
self.input_shape = input_shape
self.epoch = epoch
self.batch_size = batch_size
self.lr_red = lr_red
if loss == "bce":
self.loss = tf.keras.losses.BinaryCrossentropy()
elif loss == "wbce":
self.loss = tfa.losses.SigmoidFocalCrossEntropy() #focal instead of weighted bce
self.verbose = verbose
self.model = build_model((self.sampling_frequency*10,12),self.n_classes,lr_init = lr_init, depth=model_depth, kernel_size=kernel_size, bottleneck_size=bottleneck_size, nb_filters=nb_filters,clf=clf, loss = self.loss)


def fit(self, X_train, y_train, X_val, y_val):
if self.lr_red == "no":
self.model.fit(X_train, y_train, epochs=self.epoch, batch_size=self.batch_size,
validation_data=(X_val, y_val), verbose=self.verbose)
elif self.lr_red == "yes":
self.model.fit(X_train, y_train, epochs=self.epoch, batch_size=self.batch_size,
validation_data=(X_val, y_val), verbose=self.verbose,
callbacks = [tf.keras.callbacks.LearningRateScheduler(scheduler, verbose=0)])
else:
print("Error: wrong lr_red argument")
def predict(self, X):
return self.model.predict(X)


def _inception_module(input_tensor, stride=1, activation='linear', use_bottleneck=True, kernel_size=40, bottleneck_size=32, nb_filters=32):

if use_bottleneck and int(input_tensor.shape[-1]) > 1:
input_inception = tf.keras.layers.Conv1D(filters=bottleneck_size, kernel_size=1,
padding='same', activation=activation, use_bias=False)(input_tensor)
else:
input_inception = input_tensor

# kernel_size_s = [3, 5, 8, 11, 17]
kernel_size_s = [kernel_size // (2 ** i) for i in range(3)]

conv_list = []

for i in range(len(kernel_size_s)):
conv_list.append(tf.keras.layers.Conv1D(filters=nb_filters, kernel_size=kernel_size_s[i],
strides=stride, padding='same', activation=activation, use_bias=False)(
input_inception))

max_pool_1 = tf.keras.layers.MaxPool1D(pool_size=3, strides=stride, padding='same')(input_tensor)

conv_6 = tf.keras.layers.Conv1D(filters=nb_filters, kernel_size=1,
padding='same', activation=activation, use_bias=False)(max_pool_1)

conv_list.append(conv_6)

x = tf.keras.layers.Concatenate(axis=2)(conv_list)
x = tf.keras.layers.BatchNormalization()(x)
x = tf.keras.layers.Activation(activation='relu')(x)
return x

def _shortcut_layer(input_tensor, out_tensor):
shortcut_y = tf.keras.layers.Conv1D(filters=int(out_tensor.shape[-1]), kernel_size=1,
padding='same', use_bias=False)(input_tensor)
shortcut_y = tf.keras.layers.BatchNormalization()(shortcut_y)

x = tf.keras.layers.Add()([shortcut_y, out_tensor])
x = tf.keras.layers.Activation('relu')(x)
return x

def build_model(input_shape, nb_classes, depth=6, use_residual=True, lr_init = 0.001, kernel_size=40, bottleneck_size=32, nb_filters=32, clf="binary", loss= tf.keras.losses.BinaryCrossentropy()):
input_layer = tf.keras.layers.Input(input_shape)

x = input_layer
input_res = input_layer

for d in range(depth):

x = _inception_module(x,kernel_size = kernel_size, bottleneck_size=bottleneck_size, nb_filters=nb_filters)

if use_residual and d % 3 == 2:
x = _shortcut_layer(input_res, x)
input_res = x

gap_layer = tf.keras.layers.GlobalAveragePooling1D()(x)

output_layer = tf.keras.layers.Dense(units=nb_classes,activation='sigmoid')(gap_layer)
model = tf.keras.models.Model(inputs=input_layer, outputs=output_layer)
model.compile(loss=loss, optimizer=tf.keras.optimizers.Adam(learning_rate=lr_init),
metrics=[tf.keras.metrics.BinaryAccuracy(),
tf.keras.metrics.AUC(
num_thresholds=200,
curve='ROC',
summation_method='interpolation',
name="ROC",
multi_label=True,
),
tf.keras.metrics.AUC(
num_thresholds=200,
curve='PR',
summation_method='interpolation',
name="PRC",
multi_label=True,
)
])
print("Inception model built.")
return model

def scheduler(epoch, lr):
if epoch % 5 == 0:
return lr*0.1
else:
return lr
43 changes: 43 additions & 0 deletions code/reproduce_inception_time_results.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from experiments.scp_experiment import SCP_Experiment
from utils import utils
from code.configs.tf_inception_time_config import conf_tf_inception_all, conf_tf_inception_diagnostic, conf_tf_inception_form, conf_tf_inception_rhythm, conf_tf_inception_subdiagnostic, conf_tf_inception_superdiagnostic

def main():

datafolder = '../data/ptbxl/'
datafolder_icbeb = '../data/ICBEB/'
outputfolder = '../output/'

# perform each experiment one after another
e = SCP_Experiment('custom_exp_name_1', 'all', datafolder, outputfolder, [conf_tf_inception_all])
e.prepare()
e.perform()
e.evaluate()

e = SCP_Experiment('custom_exp_name_2', 'diagnostic', datafolder, outputfolder, [conf_tf_inception_diagnostic])
e.prepare()
e.perform()
e.evaluate()

e = SCP_Experiment('custom_exp_name_3', 'subdiagnostic', datafolder, outputfolder, [conf_tf_inception_subdiagnostic])
e.prepare()
e.perform()
e.evaluate()

e = SCP_Experiment('custom_exp_name_4', 'superdiagnostic', datafolder, outputfolder, [conf_tf_inception_superdiagnostic])
e.prepare()
e.perform()
e.evaluate()

e = SCP_Experiment('custom_exp_name_5', 'form', datafolder, outputfolder, [conf_tf_inception_form])
e.prepare()
e.perform()
e.evaluate()

e = SCP_Experiment('custom_exp_name_6', 'rhythm', datafolder, outputfolder, [conf_tf_inception_rhythm])
e.prepare()
e.perform()
e.evaluate()

if __name__ == "__main__":
main()
6 changes: 3 additions & 3 deletions code/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def apply_thresholds(preds, thresholds):
# DATA PROCESSING STUFF

def load_dataset(path, sampling_rate, release=False):
if path.split('/')[-2] == 'ptbxl':
if path.split('/')[-2] == 'ptb-xl-a-large-publicly-available-electrocardiography-dataset-1.0.1':
Bsingstad marked this conversation as resolved.
Show resolved Hide resolved
# load and convert annotation data
Y = pd.read_csv(path+'ptbxl_database.csv', index_col='ecg_id')
Y.scp_codes = Y.scp_codes.apply(lambda x: ast.literal_eval(x))
Expand Down Expand Up @@ -335,7 +335,7 @@ def apply_standardizer(X, ss):

# DOCUMENTATION STUFF

def generate_ptbxl_summary_table(selection=None, folder='../output/'):
def generate_ptbxl_summary_table(selection=None, folder: str="./your/path/to/ptbxl/"):
Bsingstad marked this conversation as resolved.
Show resolved Hide resolved

exps = ['exp0', 'exp1', 'exp1.1', 'exp1.1.1', 'exp2', 'exp3']
metric1 = 'macro_auc'
Expand Down Expand Up @@ -405,7 +405,7 @@ def generate_ptbxl_summary_table(selection=None, folder='../output/'):
md_source += '| ' + row[0].replace('fastai_', '') + ' | ' + row[1] + ' | [our work]('+our_work+') | [this repo]('+our_repo+')| \n'
print(md_source)

def ICBEBE_table(selection=None, folder='../output/'):
def ICBEBE_table(selection=None, folder:str="./your/path/to/icbeb/"):
Bsingstad marked this conversation as resolved.
Show resolved Hide resolved
cols = ['macro_auc', 'F_beta_macro', 'G_beta_macro']

if selection is None:
Expand Down