From 1cc76e0e5d415f38c3a9d30012a462f1b2bee0b0 Mon Sep 17 00:00:00 2001 From: phborba Date: Wed, 30 Sep 2020 14:36:40 -0300 Subject: [PATCH 01/10] adding base_path option to dataset --- requirements.txt | 2 +- .../dataset_loader/dataset.py | 39 ++++++++++++++----- segmentation_models_trainer/sample.json | 2 +- tests/test_training_script.py | 16 ++++++-- 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/requirements.txt b/requirements.txt index dfae55d..72ee475 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -tensorflow +tensorflow>=2.3.0 git+https://github.com/phborba/efficientnet git+https://github.com/phborba/segmentation_models pillow diff --git a/segmentation_models_trainer/dataset_loader/dataset.py b/segmentation_models_trainer/dataset_loader/dataset.py index 41607ea..db7d51b 100644 --- a/segmentation_models_trainer/dataset_loader/dataset.py +++ b/segmentation_models_trainer/dataset_loader/dataset.py @@ -19,12 +19,13 @@ * * **** """ - +import tensorflow as tf +import os from dataclasses import dataclass, field from dataclasses_jsonschema import JsonSchemaMixin from typing import Any, List from collections import OrderedDict -import tensorflow as tf + IMAGE_DTYPE = { 'float32' : tf.float32, @@ -121,10 +122,11 @@ def augment_image(self, input_image, input_mask): @dataclass class Dataset(JsonSchemaMixin): name: str - file_path: str n_classes: int dataset_size: int augmentation_list: List[ImageAugumentation] + file_path: str + base_path: str = '' cache: Any = True shuffle: bool = True shuffle_buffer_size: int = 10000 @@ -133,8 +135,8 @@ class Dataset(JsonSchemaMixin): num_paralel_reads: int = 4 img_dtype: str = 'float32' img_format: str = 'png' - img_width: int = 256 - img_length: int = 256 + img_width: int = 512 + img_length: int = 512 img_bands: int = 3 mask_bands: int = 1 use_ds_width_len: bool = False @@ -149,16 +151,31 @@ def get_img_input_shape(self): ) def get_tf_dataset(self, batch_size): + @tf.function def process_csv_entry(entry): width = entry['width'] if self.use_ds_width_len else self.img_width length = entry['length'] if self.use_ds_width_len else self.img_length label = tf.io.read_file( - entry['label_path'][0] + entry['label_path'][0] if self.base_path == '' \ + else tf.strings.join( + [ + self.base_path, + entry['label_path'][0] + ], + separator='' + ) ) label = decode_img(label, width, length, channels=1) # load the raw data from the file as a string img = tf.io.read_file( - entry['image_path'][0] + entry['label_path'][0] if self.base_path == '' \ + else tf.strings.join( + [ + self.base_path, + entry['label_path'][0] + ], + separator='' + ) ) img = decode_img(img, width, length) img, label = augment_image(img, label) @@ -186,7 +203,10 @@ def prepare_for_training(ds, batch_size): ds = ds.repeat() # `prefetch` lets the dataset fetch batches in the background while the model # is training. - ds = ds.prefetch(buffer_size=tf.data.experimental.AUTOTUNE if self.autotune == -1 else self.autotune) + ds = ds.prefetch( + buffer_size=tf.data.experimental.AUTOTUNE \ + if self.autotune == -1 else self.autotune + ) return ds def augment_image(img, label): @@ -205,7 +225,8 @@ def augment_image(img, label): ) labeled_ds = ds.map( process_csv_entry, - num_parallel_calls=tf.data.experimental.AUTOTUNE if self.autotune == -1 else self.autotune + num_parallel_calls=tf.data.experimental.AUTOTUNE if self.autotune == -1 \ + else self.autotune ) prepared_ds = prepare_for_training( labeled_ds, diff --git a/segmentation_models_trainer/sample.json b/segmentation_models_trainer/sample.json index 4d5744e..29c6adb 100644 --- a/segmentation_models_trainer/sample.json +++ b/segmentation_models_trainer/sample.json @@ -10,7 +10,7 @@ "optimizer": { "name": "Adam", "config": { - "learning_rate": 0.01 + "learning_rate": 0.001 } } }, diff --git a/tests/test_training_script.py b/tests/test_training_script.py index 8381259..a28271b 100644 --- a/tests/test_training_script.py +++ b/tests/test_training_script.py @@ -54,16 +54,21 @@ def setUp(self): os.path.join(current_dir, 'testing_data', 'data', 'labels'), '.png' ) + label_list = get_file_list( + os.path.join(current_dir, 'testing_data', 'data', 'labels'), + '.png' + ) self.csv_train_ds_file = create_csv_file( os.path.join(current_dir, 'testing_data', 'csv_train_ds.csv'), [image_list[0]], [label_list[0]] ) + self.csv_test_ds_file = create_csv_file( os.path.join(current_dir, 'testing_data', 'csv_test_ds.csv'), - [image_list[-1]], - [label_list[-1]] - ) + [os.path.join('/', *image_list[-1].split('/')[-2::])], + [os.path.join('/', *label_list[-1].split('/')[-2::])] + )#different label list procedure to test loading data with data path prefix self.experiment_path = os.path.join( current_dir, 'experiment_data' @@ -106,6 +111,11 @@ def setUp(self): self.settings_json = os.path.join( current_dir, 'testing_data', 'settings.json' ) + settings_dict['test_dataset']['base_path'] = os.path.join( + os.path.dirname(__file__), + 'testing_data', + 'data' + ) with open(self.settings_json, 'w') as json_file: json_file.write(json.dumps(settings_dict)) From 243ef903455ab16d6882bff7e1e548a4a34dc2cf Mon Sep 17 00:00:00 2001 From: phborba Date: Wed, 30 Sep 2020 17:53:49 -0300 Subject: [PATCH 02/10] improving callbacks --- .../callbacks_loader/callback.py | 15 ++- .../callbacks_loader/callback_factory.py | 14 ++ .../callbacks_loader/custom_callbacks.py | 34 +++-- .../experiment_builder/experiment.py | 57 ++++++-- segmentation_models_trainer/sample.json | 126 +++++++++++++++++- tests/test_callbacks.py | 15 ++- 6 files changed, 231 insertions(+), 30 deletions(-) diff --git a/segmentation_models_trainer/callbacks_loader/callback.py b/segmentation_models_trainer/callbacks_loader/callback.py index bf8dc20..4a7f258 100644 --- a/segmentation_models_trainer/callbacks_loader/callback.py +++ b/segmentation_models_trainer/callbacks_loader/callback.py @@ -93,4 +93,17 @@ def get_tf_objects(self): ] y z = CallbackList(y) - print(z.to_json()) \ No newline at end of file + print(z.to_json()) + w = Callback( + 'ImageHistory', + config={ + 'tensor_board_dir' : '/teste', + 'data' : None, + 'n_epochs' : 4, + 'draw_interval' : 1, + 'batch_size' : 1, + 'page_size' : 1, + 'report_dir': '/teste' + } + ) + w \ No newline at end of file diff --git a/segmentation_models_trainer/callbacks_loader/callback_factory.py b/segmentation_models_trainer/callbacks_loader/callback_factory.py index a7fa082..019aafd 100644 --- a/segmentation_models_trainer/callbacks_loader/callback_factory.py +++ b/segmentation_models_trainer/callbacks_loader/callback_factory.py @@ -54,3 +54,17 @@ def get_callback(name, parameters): return tf.keras.callbacks.TensorBoard( **parameters ) + +if __name__ == '__main__': + img_callback = CallbackFactory.get_callback( + 'ImageHistory', + parameters={ + 'tensor_board_dir' : '/teste', + 'data' : None, + 'n_epochs' : 4, + 'draw_interval' : 1, + 'batch_size' : 1, + 'page_size' : 1, + 'report_dir': '/teste' + } + ) \ No newline at end of file diff --git a/segmentation_models_trainer/callbacks_loader/custom_callbacks.py b/segmentation_models_trainer/callbacks_loader/custom_callbacks.py index f6c8dfb..5a98e4e 100644 --- a/segmentation_models_trainer/callbacks_loader/custom_callbacks.py +++ b/segmentation_models_trainer/callbacks_loader/custom_callbacks.py @@ -35,19 +35,29 @@ class ImageHistory(tf.keras.callbacks.Callback): def __init__(self, params, **kwargs): super(ImageHistory, self).__init__(**kwargs) - self.tensor_board_dir = params['tensor_board_dir'] - self.data = params['data'] - self.last_epoch = 0 if 'current_epoch' not in params \ - else params['current_epoch'] - self.n_epochs = params['n_epochs'] - self.draw_interval = params['draw_interval'] - self.batch_size = params['batch_size'] - self.page_size = params['page_size'] - self.report_dir = params['report_dir'] + self.tensorboard_dir = params['tensorboard_dir'] if 'tensorboard_dir' in params else None + self.dataset = params['dataset'] if 'dataset' in params else None + self.n_epochs = params['n_epochs'] if 'n_epochs' in params else 1 + self.draw_interval = params['draw_interval'] if 'draw_interval' in params else 1 + self.batch_size = params['batch_size'] if 'batch_size' in params else 1 + self.page_size = params['page_size'] if 'page_size' in params else self.batch_size + self.report_dir = params['report_dir'] if 'report_dir' in params else None + def set_params(self, params): + if 'dataset' in params: + self.dataset = params['dataset'] + if 'tensorboard_dir' in params: + self.tensorboard_dir = params['tensorboard_dir'] + if 'n_epochs' in params: + self.n_epochs = params['n_epochs'] + if 'batch_size' in params: + self.batch_size = params['batch_size'] + if 'report_dir' in params: + self.report_dir = params['report_dir'] + def on_epoch_end(self, epoch, logs=None): self.my_logs = logs or {} - self.last_epoch += 1 + self.last_epoch = epoch image_data, label_data, y_pred, data = self.predict_data() args = [ @@ -57,7 +67,7 @@ def on_epoch_end(self, epoch, logs=None): ] * self.page_size file_writer = tf.summary.create_file_writer( - self.tensor_board_dir + self.tensorboard_dir ) n_pages = np.ceil( self.batch_size / self.page_size @@ -108,7 +118,7 @@ def predict_data(self): predicted_images = [] ref_labels = [] image_data, label_data = list( - self.data.take(1) + self.dataset.take(1) )[0] #took one batch y_pred = self.model.predict(image_data) diff --git a/segmentation_models_trainer/experiment_builder/experiment.py b/segmentation_models_trainer/experiment_builder/experiment.py index 0de81c4..0f27a9f 100644 --- a/segmentation_models_trainer/experiment_builder/experiment.py +++ b/segmentation_models_trainer/experiment_builder/experiment.py @@ -61,19 +61,18 @@ def train(self): self.create_data_folders() - BATCH_SIZE = self.hyperparameters.batch_size * strategy.num_replicas_in_sync \ + self.BATCH_SIZE = self.hyperparameters.batch_size * strategy.num_replicas_in_sync \ if self.use_multiple_gpus else self.hyperparameters.batch_size n_classes = self.train_dataset.n_classes input_shape = self.train_dataset.get_img_input_shape() - train_ds = self.train_dataset.get_tf_dataset(BATCH_SIZE) - test_ds = self.test_dataset.get_tf_dataset(BATCH_SIZE) + train_ds = self.train_dataset.get_tf_dataset(self.BATCH_SIZE) + test_ds = self.test_dataset.get_tf_dataset(self.BATCH_SIZE) - training_steps_per_epoch = int( np.ceil(self.train_dataset.dataset_size / BATCH_SIZE) ) - test_steps_per_epoch = int( np.ceil(self.test_dataset.dataset_size / BATCH_SIZE) ) + self.training_steps_per_epoch = int( np.ceil(self.train_dataset.dataset_size / self.BATCH_SIZE) ) + self.test_steps_per_epoch = int( np.ceil(self.test_dataset.dataset_size / self.BATCH_SIZE) ) - def train_model(epochs, save_weights_path, encoder_freeze, load_weights=None): - callback_list = self.callbacks.get_tf_objects() + def train_model(epochs, save_weights_path, encoder_freeze, callback_list, load_weights=None): with strategy.scope(): model = self.model.get_model( n_classes, @@ -90,11 +89,11 @@ def train_model(epochs, save_weights_path, encoder_freeze, load_weights=None): ) model.fit( train_ds, - batch_size=BATCH_SIZE, - steps_per_epoch=training_steps_per_epoch, + batch_size=self.BATCH_SIZE, + steps_per_epoch=self.training_steps_per_epoch, epochs=epochs, validation_data=test_ds, - validation_steps=test_steps_per_epoch, + validation_steps=self.test_steps_per_epoch, callbacks=callback_list ) model.save_weights( @@ -107,6 +106,12 @@ def train_model(epochs, save_weights_path, encoder_freeze, load_weights=None): self.SAVE_PATH, 'warmup_experiment_{name}.h5'.format(name=self.name) ) + callback_list = self.get_initialized_callbacks( + epochs=self.warmup_epochs, + warmup=True, + data_ds=train_ds, + + ) model = train_model( epochs=self.warmup_epochs, save_weights_path=warmup_path, @@ -146,6 +151,38 @@ def create_data_folders(self): self.REPORT_DIR = self.test_and_create_folder( os.path.join(DATA_DIR, 'report_img') ) + + def get_initialized_callbacks(self, epochs, data_ds, warmup=False): + tf_callback_list = [] + for callback in self.callbacks: + if callback.name == 'BackupAndRestore': + callback.config.update({'backup_dir':self.CHECKPOINT_PATH}) + elif callback.name == 'ImageHistory': + callback.config.update( + { + 'dataset' : data_ds, + 'tensorboard_dir' : self.LOG_PATH, + 'n_epochs' : epochs, + 'batch_size' : self.BATCH_SIZE, + 'report_dir' : self.REPORT_DIR + } + ) + elif callback.name == 'ModelCheckpoint': + callback.config.update( + { + 'filepath': os.path.join( + self.CHECKPOINT_PATH, + "model{name}-{epoch:02d}-{".format( + name='_warmup' if warmup else '' + )+ callback.config.monitor+':.2f}.hdf5' + ), + 'save_freq': self.checkpoint_frequency * self.training_steps_per_epoch + } + ) + tf_callback_list.append( + callback.get_callback() + ) + return tf_callback_list @staticmethod def test_and_create_folder(path): diff --git a/segmentation_models_trainer/sample.json b/segmentation_models_trainer/sample.json index 29c6adb..7219468 100644 --- a/segmentation_models_trainer/sample.json +++ b/segmentation_models_trainer/sample.json @@ -1,6 +1,6 @@ { "name": "test", - "epochs": 2, + "epochs": 4, "experiment_data_path": "/data/test", "checkpoint_frequency": 10, "warmup_epochs": 2, @@ -10,7 +10,7 @@ "optimizer": { "name": "Adam", "config": { - "learning_rate": 0.001 + "learning_rate": 0.0001 } } }, @@ -39,8 +39,8 @@ "num_paralel_reads": 4, "img_dtype": "float32", "img_format": "png", - "img_width": 256, - "img_length": 256, + "img_width": 512, + "img_length": 512, "use_ds_width_len": false, "autotune": -1, "distributed_training": false @@ -57,6 +57,40 @@ "crop_height": 256 } }, + { + "name": "random_flip_left_right", + "parameters": {} + }, + { + "name": "random_flip_up_down", + "parameters": {} + }, + { + "name": "random_brightness", + "parameters": { + "max_delta": 0.1 + } + }, + { + "name": "random_contrast", + "parameters": { + "lower": 0.5, + "upper": 1.5 + } + }, + { + "name": "random_saturation", + "parameters": { + "lower": 0.5, + "upper": 1.5 + } + }, + { + "name": "random_hue", + "parameters": { + "max_delta": 0.01 + } + }, { "name": "per_image_standardization", "parameters": {} @@ -70,8 +104,8 @@ "num_paralel_reads": 4, "img_dtype": "float32", "img_format": "png", - "img_width": 256, - "img_length": 256, + "img_width": 512, + "img_length": 512, "use_ds_width_len": false, "autotune": -1, "distributed_training": false @@ -82,5 +116,85 @@ "architecture": "Unet", "activation": "sigmoid", "use_imagenet_weights": true + }, + "loss": { + "class_name": "bce_dice_loss", + "config": {}, + "framework": "sm" + }, + "callbacks": { + "items": [ + { + "name": "TensorBoard", + "config": { + "update_freq": "epoch" + } + }, + { + "name": "BackupAndRestore", + "config": {} + }, + { + "name": "ReduceLROnPlateau", + "config": { + "monitor": "val_loss", + "factor": 0.2, + "patience": 5, + "min_lr": 0.00000000001 + } + }, + { + "name": "ModelCheckpoint", + "config": { + "monitor": "iou_score", + "save_best_only": false, + "save_weights_only": false, + "verbose":1 + } + }, + { + "name": "ImageHistory", + "config": { + "draw_interval": 1, + "page_size": 10 + } + } + ] + }, + "metrics": { + "items": [ + { + "class_name": "iou_score", + "config": {}, + "framework": "sm" + }, + { + "class_name": "precision", + "config": {}, + "framework": "sm" + }, + { + "class_name": "recall", + "config": {}, + "framework": "sm" + }, + { + "class_name": "f1_score", + "config": {}, + "framework": "sm" + }, + { + "class_name": "f2_score", + "config": {}, + "framework": "sm" + }, + { + "class_name": "MeanIoU", + "config": { + "num_classes": 2 + }, + "framework": "tf.keras" + } + ] } } \ No newline at end of file diff --git a/tests/test_callbacks.py b/tests/test_callbacks.py index 7c9daff..492bd83 100644 --- a/tests/test_callbacks.py +++ b/tests/test_callbacks.py @@ -63,4 +63,17 @@ def test_import_instance(self): new_callback ) - \ No newline at end of file +class Test_TestCustomCallbacks(Test_TestCallbacks): + callback = Callback( + name='ImageHistory', + config= { + 'monitor' : 'val_loss', + 'factor' : 0.2, + 'patience' : 5, + 'min_lr' : 0.001 + } + ) + +if __name__ == '__main__': + x = Test_TestCustomCallbacks() + x \ No newline at end of file From f8c02840c69f2ca6e782ff584a9fba7401ee64e7 Mon Sep 17 00:00:00 2001 From: phborba Date: Wed, 30 Sep 2020 17:58:17 -0300 Subject: [PATCH 03/10] bug fix on callback iteration --- segmentation_models_trainer/experiment_builder/experiment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/segmentation_models_trainer/experiment_builder/experiment.py b/segmentation_models_trainer/experiment_builder/experiment.py index 0f27a9f..f0ab9ea 100644 --- a/segmentation_models_trainer/experiment_builder/experiment.py +++ b/segmentation_models_trainer/experiment_builder/experiment.py @@ -154,7 +154,7 @@ def create_data_folders(self): def get_initialized_callbacks(self, epochs, data_ds, warmup=False): tf_callback_list = [] - for callback in self.callbacks: + for callback in self.callbacks.items: if callback.name == 'BackupAndRestore': callback.config.update({'backup_dir':self.CHECKPOINT_PATH}) elif callback.name == 'ImageHistory': From 0f88eb8206b4e827ab323a753e6e4ce641b514e3 Mon Sep 17 00:00:00 2001 From: phborba Date: Wed, 30 Sep 2020 18:11:17 -0300 Subject: [PATCH 04/10] test update --- tests/test_callbacks.py | 8 ++++++++ tests/test_dataset.py | 2 +- tests/test_experiment.py | 8 +------- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/test_callbacks.py b/tests/test_callbacks.py index 492bd83..fcba622 100644 --- a/tests/test_callbacks.py +++ b/tests/test_callbacks.py @@ -73,6 +73,14 @@ class Test_TestCustomCallbacks(Test_TestCallbacks): 'min_lr' : 0.001 } ) + json_dict = json.loads('{"name": "ImageHistory", "config": {"monitor": "val_loss", "factor": 0.2, "patience": 5, "min_lr": 0.001}}') + def test_create_instance(self): + """[summary] + Tests instance creation + """ + self.assertEqual( + self.callback.name, 'ImageHistory' + ) if __name__ == '__main__': x = Test_TestCustomCallbacks() diff --git a/tests/test_dataset.py b/tests/test_dataset.py index 186d2c2..b668f01 100644 --- a/tests/test_dataset.py +++ b/tests/test_dataset.py @@ -50,7 +50,7 @@ class Test_TestDataset(unittest.TestCase): dataset_size=1000, augmentation_list=aug_list ) - json_dict = json.loads('{"name": "test", "file_path": "/data/test", "n_classes": 1, "dataset_size": 1000, "augmentation_list": [{"name": "random_crop", "parameters": {"crop_width": 256, "crop_height": 256}}, {"name": "per_image_standardization", "parameters": {}}], "cache": true, "shuffle": true, "shuffle_buffer_size": 10000, "shuffle_csv": true, "ignore_errors": true, "num_paralel_reads": 4, "img_dtype": "float32", "img_format": "png", "img_width": 256, "img_length": 256, "img_bands": 3, "mask_bands": 1, "use_ds_width_len": false, "autotune": -1, "distributed_training": false}') + json_dict = json.loads('{"name": "test", "n_classes": 1, "dataset_size": 1000, "augmentation_list": [{"name": "random_crop", "parameters": {"crop_width": 256, "crop_height": 256}}, {"name": "per_image_standardization", "parameters": {}}], "file_path": "/data/test", "base_path": "", "cache": true, "shuffle": true, "shuffle_buffer_size": 10000, "shuffle_csv": true, "ignore_errors": true, "num_paralel_reads": 4, "img_dtype": "float32", "img_format": "png", "img_width": 512, "img_length": 512, "img_bands": 3, "mask_bands": 1, "use_ds_width_len": false, "autotune": -1, "distributed_training": false}') def test_create_instance(self): """[summary] diff --git a/tests/test_experiment.py b/tests/test_experiment.py index f3eb5c2..89a638f 100644 --- a/tests/test_experiment.py +++ b/tests/test_experiment.py @@ -75,13 +75,7 @@ def test_create_instance(self): self.assertEqual( self.experiment.use_multiple_gpus, False ) - - def test_export_instance(self): - self.assertEqual( - self.experiment.to_dict(), - self.json_dict - ) - + def test_import_instance(self): new_experiment = Experiment.from_dict( self.json_dict From 0f920265f0af32748be9f1dc0759fe88a9e39896 Mon Sep 17 00:00:00 2001 From: phborba Date: Wed, 30 Sep 2020 18:15:10 -0300 Subject: [PATCH 05/10] string fix --- README.md | 134 ++++++++++++++++-- .../experiment_builder/experiment.py | 4 +- 2 files changed, 124 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index f801c38..3fc8e81 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,10 @@ Framework to train semantic segmentation models on TensorFlow using json files a ``` { - "name": "Example", - "epochs": 100, + "name": "test", + "epochs": 4, "experiment_data_path": "/data/test", - "checkpoint_frequency": 1, + "checkpoint_frequency": 10, "warmup_epochs": 2, "use_multiple_gpus": false, "hyperparameters": { @@ -19,16 +19,14 @@ Framework to train semantic segmentation models on TensorFlow using json files a "optimizer": { "name": "Adam", "config": { - "learning_rate": 0.01 + "learning_rate": 0.0001 } } }, "train_dataset": { "name": "train_ds", - "file_path": "/data/dataset_test.csv", + "file_path": "/data/train_ds.csv", "n_classes": 1, - "dataset_size": 1000, - "dataset_size": 60000, "augmentation_list": [ { "name": "random_crop", @@ -50,18 +48,16 @@ Framework to train semantic segmentation models on TensorFlow using json files a "num_paralel_reads": 4, "img_dtype": "float32", "img_format": "png", - "img_width": 256, - "img_length": 256, + "img_width": 512, + "img_length": 512, "use_ds_width_len": false, "autotune": -1, "distributed_training": false }, "test_dataset": { "name": "test_ds", - "file_path": "/data/dataset_test.csv", + "file_path": "/data/test_ds.csv", "n_classes": 1, - "dataset_size": 1000, - "dataset_size": 40000, "augmentation_list": [ { "name": "random_crop", @@ -70,6 +66,40 @@ Framework to train semantic segmentation models on TensorFlow using json files a "crop_height": 256 } }, + { + "name": "random_flip_left_right", + "parameters": {} + }, + { + "name": "random_flip_up_down", + "parameters": {} + }, + { + "name": "random_brightness", + "parameters": { + "max_delta": 0.1 + } + }, + { + "name": "random_contrast", + "parameters": { + "lower": 0.5, + "upper": 1.5 + } + }, + { + "name": "random_saturation", + "parameters": { + "lower": 0.5, + "upper": 1.5 + } + }, + { + "name": "random_hue", + "parameters": { + "max_delta": 0.01 + } + }, { "name": "per_image_standardization", "parameters": {} @@ -95,6 +125,86 @@ Framework to train semantic segmentation models on TensorFlow using json files a "architecture": "Unet", "activation": "sigmoid", "use_imagenet_weights": true + }, + "loss": { + "class_name": "bce_dice_loss", + "config": {}, + "framework": "sm" + }, + "callbacks": { + "items": [ + { + "name": "TensorBoard", + "config": { + "update_freq": "epoch" + } + }, + { + "name": "BackupAndRestore", + "config": {} + }, + { + "name": "ReduceLROnPlateau", + "config": { + "monitor": "val_loss", + "factor": 0.2, + "patience": 5, + "min_lr": 0.00000000001 + } + }, + { + "name": "ModelCheckpoint", + "config": { + "monitor": "iou_score", + "save_best_only": false, + "save_weights_only": false, + "verbose":1 + } + }, + { + "name": "ImageHistory", + "config": { + "draw_interval": 1, + "page_size": 10 + } + } + ] + }, + "metrics": { + "items": [ + { + "class_name": "iou_score", + "config": {}, + "framework": "sm" + }, + { + "class_name": "precision", + "config": {}, + "framework": "sm" + }, + { + "class_name": "recall", + "config": {}, + "framework": "sm" + }, + { + "class_name": "f1_score", + "config": {}, + "framework": "sm" + }, + { + "class_name": "f2_score", + "config": {}, + "framework": "sm" + }, + { + "class_name": "MeanIoU", + "config": { + "num_classes": 2 + }, + "framework": "tf.keras" + } + ] } } ``` diff --git a/segmentation_models_trainer/experiment_builder/experiment.py b/segmentation_models_trainer/experiment_builder/experiment.py index f0ab9ea..94968f4 100644 --- a/segmentation_models_trainer/experiment_builder/experiment.py +++ b/segmentation_models_trainer/experiment_builder/experiment.py @@ -172,9 +172,9 @@ def get_initialized_callbacks(self, epochs, data_ds, warmup=False): { 'filepath': os.path.join( self.CHECKPOINT_PATH, - "model{name}-{epoch:02d}-{".format( + "model{name}".format( name='_warmup' if warmup else '' - )+ callback.config.monitor+':.2f}.hdf5' + ) + "-{epoch:02d}-{" + callback.config.monitor+':.2f}.hdf5' ), 'save_freq': self.checkpoint_frequency * self.training_steps_per_epoch } From 9cb42fbac302691a5fcce5b04a65069c07369aa8 Mon Sep 17 00:00:00 2001 From: phborba Date: Wed, 30 Sep 2020 18:32:06 -0300 Subject: [PATCH 06/10] test fix --- .../callbacks_loader/callback.py | 3 --- .../experiment_builder/experiment.py | 16 ++++++++++++---- tests/test_training_script.py | 2 +- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/segmentation_models_trainer/callbacks_loader/callback.py b/segmentation_models_trainer/callbacks_loader/callback.py index 4a7f258..9dd7ed0 100644 --- a/segmentation_models_trainer/callbacks_loader/callback.py +++ b/segmentation_models_trainer/callbacks_loader/callback.py @@ -30,9 +30,6 @@ class Callback(JsonSchemaMixin): name: str config: dict - def __post_init__(self): - self.callback_obj = self.get_callback() - @staticmethod def validate_callback_name(name): if name not in [ diff --git a/segmentation_models_trainer/experiment_builder/experiment.py b/segmentation_models_trainer/experiment_builder/experiment.py index 94968f4..755af8a 100644 --- a/segmentation_models_trainer/experiment_builder/experiment.py +++ b/segmentation_models_trainer/experiment_builder/experiment.py @@ -87,6 +87,8 @@ def train_model(epochs, save_weights_path, encoder_freeze, callback_list, load_w loss=sm.losses.bce_jaccard_loss, metrics=metric_list ) + if load_weights is not None: + model.load_weights(load_weights) model.fit( train_ds, batch_size=self.BATCH_SIZE, @@ -108,14 +110,14 @@ def train_model(epochs, save_weights_path, encoder_freeze, callback_list, load_w ) callback_list = self.get_initialized_callbacks( epochs=self.warmup_epochs, - warmup=True, data_ds=train_ds, - + warmup=True ) model = train_model( epochs=self.warmup_epochs, save_weights_path=warmup_path, - encoder_freeze=True + encoder_freeze=True, + callback_list=callback_list ) final_save_path = os.path.join( self.SAVE_PATH, @@ -124,10 +126,16 @@ def train_model(epochs, save_weights_path, encoder_freeze, callback_list, load_w epochs=self.epochs ) ) + callback_list = self.get_initialized_callbacks( + epochs=self.warmup_epochs, + data_ds=train_ds, + warmup=True + ) model = train_model( epochs=self.epochs, save_weights_path=final_save_path, encoder_freeze=False, + callback_list=callback_list, load_weights=warmup_path if self.warmup_epochs > 0 else None ) @@ -174,7 +182,7 @@ def get_initialized_callbacks(self, epochs, data_ds, warmup=False): self.CHECKPOINT_PATH, "model{name}".format( name='_warmup' if warmup else '' - ) + "-{epoch:02d}-{" + callback.config.monitor+':.2f}.hdf5' + ) + "-{epoch:02d}-{" + callback.config["monitor"]+':.2f}.hdf5' ), 'save_freq': self.checkpoint_frequency * self.training_steps_per_epoch } diff --git a/tests/test_training_script.py b/tests/test_training_script.py index a28271b..cdfbf6b 100644 --- a/tests/test_training_script.py +++ b/tests/test_training_script.py @@ -105,7 +105,7 @@ def setUp(self): 'model' : json.loads('{"description": "test case", "backbone": "resnet18", "architecture": "Unet", "activation": "sigmoid", "use_imagenet_weights": true}'), 'loss' : json.loads('{"class_name": "bce_dice_loss", "config": {}, "framework": "sm"}'), - 'callbacks' : json.loads('{"items": [{"name": "ReduceLROnPlateau", "config": {"monitor": "val_loss", "factor": 0.2, "patience": 5, "min_lr": 0.001}}, {"name": "ModelCheckpoint", "config": {"filepath": "/data/teste/checkpoint.hdf5"}}]}'), + 'callbacks' : json.loads('{"items": [{"name": "ReduceLROnPlateau", "config": {"monitor": "val_loss", "factor": 0.2, "patience": 5, "min_lr": 0.001}}, {"name": "ModelCheckpoint", "config": {"monitor": "iou_score", "save_best_only": false, "save_weights_only": false, "verbose": 1}}]}'), 'metrics' : json.loads('{"items": [{"class_name": "iou_score", "config": {}, "framework": "sm"}, {"class_name": "precision", "config": {}, "framework": "sm"}, {"class_name": "recall", "config": {}, "framework": "sm"}, {"class_name": "f1_score", "config": {}, "framework": "sm"}, {"class_name": "f2_score", "config": {}, "framework": "sm"}, {"class_name": "LogCoshError", "config": {}, "framework": "tf.keras"}, {"class_name": "KLDivergence", "config": {}, "framework": "tf.keras"}, {"class_name": "MeanIoU", "config": {"num_classes": 2}, "framework": "tf.keras"}]}'), } self.settings_json = os.path.join( From dee58468db71e93784fa23cf1b57fe91f7977834 Mon Sep 17 00:00:00 2001 From: phborba Date: Wed, 30 Sep 2020 19:04:28 -0300 Subject: [PATCH 07/10] bug fixes --- segmentation_models_trainer/experiment_builder/experiment.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/segmentation_models_trainer/experiment_builder/experiment.py b/segmentation_models_trainer/experiment_builder/experiment.py index 755af8a..397844d 100644 --- a/segmentation_models_trainer/experiment_builder/experiment.py +++ b/segmentation_models_trainer/experiment_builder/experiment.py @@ -76,7 +76,7 @@ def train_model(epochs, save_weights_path, encoder_freeze, callback_list, load_w with strategy.scope(): model = self.model.get_model( n_classes, - encoder_freeze=True, + encoder_freeze=encoder_freeze, input_shape=input_shape ) opt = self.hyperparameters.optimizer.tf_object @@ -129,7 +129,7 @@ def train_model(epochs, save_weights_path, encoder_freeze, callback_list, load_w callback_list = self.get_initialized_callbacks( epochs=self.warmup_epochs, data_ds=train_ds, - warmup=True + warmup=False ) model = train_model( epochs=self.epochs, From e110f8b747d8faab7c8d219acaba99d1113c3f03 Mon Sep 17 00:00:00 2001 From: phborba Date: Wed, 30 Sep 2020 19:30:33 -0300 Subject: [PATCH 08/10] model checkpoint fix --- .../experiment_builder/experiment.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/segmentation_models_trainer/experiment_builder/experiment.py b/segmentation_models_trainer/experiment_builder/experiment.py index 397844d..a2724fe 100644 --- a/segmentation_models_trainer/experiment_builder/experiment.py +++ b/segmentation_models_trainer/experiment_builder/experiment.py @@ -80,11 +80,10 @@ def train_model(epochs, save_weights_path, encoder_freeze, callback_list, load_w input_shape=input_shape ) opt = self.hyperparameters.optimizer.tf_object - #TODO metrics and loss fields into compile metric_list = self.metrics.get_tf_objects() model.compile( opt, - loss=sm.losses.bce_jaccard_loss, + loss=self.loss.loss_obj, metrics=metric_list ) if load_weights is not None: @@ -180,9 +179,7 @@ def get_initialized_callbacks(self, epochs, data_ds, warmup=False): { 'filepath': os.path.join( self.CHECKPOINT_PATH, - "model{name}".format( - name='_warmup' if warmup else '' - ) + "-{epoch:02d}-{" + callback.config["monitor"]+':.2f}.hdf5' + "model-{epoch:04d}.ckpt" if not warmup else "warmup_model-{epoch:04d}.ckpt" ), 'save_freq': self.checkpoint_frequency * self.training_steps_per_epoch } From 2c76eee51effc41bf460f975eabed251b7638413 Mon Sep 17 00:00:00 2001 From: phborba Date: Wed, 30 Sep 2020 19:45:28 -0300 Subject: [PATCH 09/10] gpl2 license --- LICENSE | 360 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 339 insertions(+), 21 deletions(-) diff --git a/LICENSE b/LICENSE index c3b2f70..1b8a5cd 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,339 @@ -MIT License - -Copyright (c) 2020 Philipe Borba - -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. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. \ No newline at end of file From 0c9ce1645a2477d920572367b246472aa4d2e27d Mon Sep 17 00:00:00 2001 From: phborba Date: Wed, 30 Sep 2020 19:49:54 -0300 Subject: [PATCH 10/10] updates on requirements.txt --- requirements.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 72ee475..91b2a1e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,5 @@ tensorflow>=2.3.0 -git+https://github.com/phborba/efficientnet -git+https://github.com/phborba/segmentation_models +segmentation_models pillow matplotlib numpy