From 29d1a6fecad516ad2909ae55bd0146f60a2ed50f Mon Sep 17 00:00:00 2001 From: Natalia Shepeleva Date: Mon, 18 Mar 2019 15:31:40 +0100 Subject: [PATCH] Added: - pytorch training pipeline - pytorch metrics (losses, optimisers, etc.) - pytorch convnet - batch iteration fro detection (to be tested) Modified: - initial path preparation - toy ConvNet example --- .gitignore | 6 + configs/config_ConvNet.py | 6 +- main.py | 2 +- network/NetRunner.py | 52 +++-- network/TrainRunner.py | 234 +++++++++++++++++++- network/wrappers/ConvNet.py | 76 +++++-- network/wrappers/NetworkBase.py | 279 +++++++++++++++--------- requirments.txt | 1 + utils/BatchIterator.py | 27 +++ utils/DataParser.py | 276 +++++++++++++++--------- utils/metric.py | 365 ++++++++++++++++++++++++++++++++ utils/utils.py | 82 ++++--- 12 files changed, 1134 insertions(+), 272 deletions(-) create mode 100644 utils/metric.py diff --git a/.gitignore b/.gitignore index 894a44c..d566a8c 100644 --- a/.gitignore +++ b/.gitignore @@ -102,3 +102,9 @@ venv.bak/ # mypy .mypy_cache/ + +# idea +.idea/ + +# custom folders +experiments/ \ No newline at end of file diff --git a/configs/config_ConvNet.py b/configs/config_ConvNet.py index a298b6c..4b23a05 100644 --- a/configs/config_ConvNet.py +++ b/configs/config_ConvNet.py @@ -21,7 +21,7 @@ def load_config(): config = ConfigFlags().return_flags() config.net = 'ConvNet' - config.training_mode = False + config.training_mode = True config.data_set = 'MNIST' config.image_size = [28, 28, 1] @@ -31,7 +31,7 @@ def load_config(): config.ref_patience = 1 config.batch_size = 32 config.num_epochs = 1000 - config.loss = 'mse' + config.loss = 'softmax' config.optimizer = 'adam' config.gradcam_record = True config.gradcam_layers = 6 @@ -44,7 +44,7 @@ def load_config(): config.upconv = 'upconv' config.nonlin = 'relu' config.task_type = 'classification' - config.accuracy = 'mse' + config.accuracy = 'percent' config.augmentation = {'flip_hor': False, 'flip_vert': False} config.data_split = 0.7 diff --git a/main.py b/main.py index 1111911..efcda3f 100644 --- a/main.py +++ b/main.py @@ -20,7 +20,7 @@ from network.TrainRunner import TrainRunner from network.InferenceRunner import InferenceRunner -EXPERIMENT_ID = 'ToyNet' +EXPERIMENT_ID = 'ConvNet' def run(experiment_id): diff --git a/network/NetRunner.py b/network/NetRunner.py index 5c0ead4..b54463b 100644 --- a/network/NetRunner.py +++ b/network/NetRunner.py @@ -17,7 +17,7 @@ import tensorflow as tf from utils.DataParser import DataParser -from network.wrappers import ConvNet, UNet, VGG16 +from network.wrappers import ConvNet, UNet, FakeDetection class NetRunner: @@ -152,8 +152,10 @@ def build_tensorflow_pipeline(self): in_shape = NotImplementedError gt_shape = NotImplementedError elif self.task_type is 'detection': - in_shape = NotImplementedError - gt_shape = NotImplementedError + in_shape = [None, self.img_size[0], self.img_size[1], self.img_size[2]] + gt_shape = [None, 4+self.num_classes, None] + self._in_data = tf.placeholder(tf.float32, shape=in_shape, name='Input_train') + self._gt_data = tf.placeholder(tf.float32, shape=gt_shape, name='GT_train') else: raise ValueError('Task not supported') @@ -177,12 +179,12 @@ def build_tensorflow_pipeline(self): self.update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) self.network = self._pick_model() - self.pred_output = self.network.build_net_tf(self.in_data) + self.pred_output = self.network.build_net(self.in_data) self.global_step = tf.train.get_or_create_global_step() with tf.control_dependencies(self.update_ops): self.loss = self.network.return_loss(y_pred=self.pred_output, y_true=self.gt_data) - self.train_op = self.network.return_optimizer(self.global_step) + self.train_op = self.network.return_optimizer(global_step=self.global_step) self.accuracy = self.network.return_accuracy(y_pred=self.pred_output, y_true=self.gt_data, b_s=self.batch_size, net_type=self.task_type, loss_type=self.loss_type) @@ -240,6 +242,11 @@ def build_tensorflow_pipeline(self): self.graph_op = tf.global_variables_initializer() def build_pytorch_pipeline(self): + self.learning_rate = self.lr + self.network = self._pick_model() + + self.train_op = self.network.return_optimizer(net_param=self.network.parameters()) + return NotImplementedError @@ -248,17 +255,30 @@ def _pick_model(self): Pick a deep model specified by self.network_type string :return: """ - if self.network_type == 'ConvNet': - return ConvNet.ConvNet(self.network_type, self.loss_type, self.accuracy_type, self.learning_rate, - training=self.training_mode, framework=self.framework, num_classes=self.num_classes, trainable_layers=self.trainable_layers) - elif self.network_type == 'UNet': - return UNet.UNet(self.network_type, self.loss_type, self.accuracy_type, self.learning_rate, training=self.training_mode, framework=self.framework, - trainable_layers=self.trainable_layers, num_classes=self.num_classes) - elif self.network_type == 'VGG16': - return VGG16.VGG16(self.network_type, self.loss_type, self.accuracy_type, self.learning_rate, - training=self.training_mode, framework=self.framework, num_classes=self.num_classes, trainable_layers=self.trainable_layers) - else: - return ValueError('Architecture does not exist') + if self.framework == "tensorflow": + if self.network_type == 'ConvNet': + return ConvNet.ConvNet(self.network_type, self.loss_type, self.accuracy_type, self.learning_rate, framework=self.framework, + training=self.is_training, num_filters=self.num_filters, nonlin=self.nonlin, num_classes=self.num_classes, + trainable_layers=self.trainable_layers) + elif self.network_type == 'UNet': + return UNet.UNet(self.network_type, self.loss_type, self.accuracy_type, self.learning_rate, framework=self.framework, training=self.training_mode, + trainable_layers=self.trainable_layers, num_classes=self.num_classes) + # elif self.network_type == 'VGG16': + # # return VGG16.VGG16(self.network_type, self.loss_type, self.accuracy_type, self.learning_rate, + # # training=self.training_mode, framework=self.framework, num_classes=self.num_classes, trainable_layers=self.trainable_layers) + elif self.network_type == 'FakeDetection': + return FakeDetection.FakeDetection(self.network_type, self.loss_type, self.accuracy_type, self.learning_rate, training=self.training_mode, + trainable_layers=self.trainable_layers, num_classes=self.num_classes) + else: + raise ValueError('Architecture does not exist') + elif self.framework == "pytorch": + if self.network_type == 'ConvNet': + return ConvNet.ConvNet_pt(self.network_type, self.loss_type, self.accuracy_type, self.learning_rate, framework=self.framework, + training=self.is_training, num_filters=self.num_filters, nonlin=self.nonlin, num_classes=self.num_classes, + trainable_layers=self.trainable_layers) + else: + raise ValueError('Architecture does not exist') + def _initialize_short_summary(self): """ diff --git a/network/TrainRunner.py b/network/TrainRunner.py index 9c454ee..b16957e 100644 --- a/network/TrainRunner.py +++ b/network/TrainRunner.py @@ -30,6 +30,13 @@ from utils.gradcam.GradCAM import grad_cam_plus_plus as gradcam +import torch +from torch.autograd import Variable +from torch.optim.lr_scheduler import ReduceLROnPlateau, StepLR +import torch.onnx as torch_onnx + + + class TrainRunner(NetRunner): def __init__(self, args=None, experiment_id=None): super().__init__(args, experiment_id) @@ -46,11 +53,12 @@ def start_training(self): self._initialize_training() def _initialize_training(self): - if self.framework is 'tensorflow': + if self.framework == 'tensorflow': self.build_tensorflow_pipeline() self._run_tensorflow_pipeline() - elif self.framework is 'pytorch': + elif self.framework == 'pytorch': self.build_pytorch_pipeline() + self._run_pytorch_pipeline() else: raise ValueError('Framework is not supported') @@ -97,13 +105,13 @@ def _run_tensorflow_pipeline(self): self.net_params.append(long_summ) short_summ = self._initialize_short_summary() self.summ_params.append(short_summ) - if not os.path.isdir(os.path.join(self.tr_path, self.data_set, self.timestamp)): - os.mkdir(os.path.join(self.tr_path, self.data_set, self.timestamp)) + # if not os.path.isdir(os.path.join(self.tr_path)): + # os.mkdir(os.path.join(self.tr_path, self.data_set, self.timestamp)) - summary_log_file_train = os.path.join(self.tr_path, self.data_set, self.timestamp, 'train', self.timestamp + '_split_' + str(split)) + summary_log_file_train = os.path.join(self.tr_path, 'train', self.timestamp + '_split_' + str(split)) train_summary_writer = tf.summary.FileWriter(summary_log_file_train, sess.graph) - summary_log_file_valid = os.path.join(self.tr_path, self.data_set, self.timestamp, 'valid', self.timestamp + '_split_' + str(split)) + summary_log_file_valid = os.path.join(self.tr_path, 'valid', self.timestamp + '_split_' + str(split)) valid_summary_writer = tf.summary.FileWriter(summary_log_file_valid, sess.graph) saver = tf.train.Saver(save_relative_paths=True) @@ -272,10 +280,16 @@ def _run_tensorflow_pipeline(self): .format(epoch, train_aver_loss, epoch_acc_str_tr, epoch_duration_train, valid_aver_loss, epoch_acc_str_val, epoch_duration_valid)) if prev_loss > valid_aver_loss: - if not os.path.isdir(os.path.join(self.ckpnt_path, self.data_set, self.timestamp)): - os.mkdir(os.path.join(self.ckpnt_path, self.data_set, self.timestamp)) + # if not os.path.isdir(os.path.join(self.ckpnt_path, self.data_set, self.timestamp)): + # os.mkdir(os.path.join(self.ckpnt_path, self.data_set, self.timestamp)) + # + # model_file_path = os.path.join('experiments', 'ckpnt_logs', self.data_set, self.timestamp, self.timestamp + '_split_' + str(split)) + + model_file_path = os.path.join(self.ckpnt_path, + self.timestamp + '_split_' + str(split)) + if not os.path.exists(model_file_path): + os.makedirs(model_file_path) - model_file_path = os.path.join('experiments', 'ckpnt_logs', self.data_set, self.timestamp, self.timestamp + '_split_' + str(split)) saver.save(sess, "{}/model.ckpt".format(model_file_path), global_step=epoch) # onnx_graph = tf2onnx.tfonnx.process_tf_graph(sess.graph, output_names=["output:0"]) @@ -304,4 +318,204 @@ def _run_tensorflow_pipeline(self): coord.request_stop() coord.join(enqueue_threads) - h5_file.close() \ No newline at end of file + h5_file.close() + + + def _run_pytorch_pipeline(self): + h5_file = h5py.File(self.h5_data_file, 'r') + + with open(self.json_log, 'r') as f: + json_log = json.load(f) + + train_split = [] + valid_split = [] + cross_val_split = json_log['data_log']['cross_val_split'] + if len(cross_val_split) == 2: + train_split.append(cross_val_split[0]) + valid_split.append(cross_val_split[1]) + else: + for i in range(len(cross_val_split)): + valid_split.append(cross_val_split[i]) + train_split.append([].extend(cross_val_split[j]) for j in range(len(cross_val_split)) if j != i) + + for split in range(len(cross_val_split) - 1): + print("Cross validation split {} of {}".format(str(split + 1), str(len(cross_val_split) - 1))) + event_time = time.time() + global_step = 0 + + # TODO: add training and validation log summary + + learn_rate = self.lr + prev_loss = np.inf + ref_counter = 0 + ref_iter_count = 0 + total_recall_counter_train = 0 + total_recall_counter_valid = 0 + + # liveloss = PlotLosses() + + # loss_plot_path = os.path.join(self.checkpoint_dir, 'prj_'+str(self.project_id), 'training_loss') + # loss_plot_path = os.path.join(self.experiment_path, 'training_loss') + # if not os.path.exists(loss_plot_path): + # os.makedirs(loss_plot_path) + + + rp = ReduceLROnPlateau(self.train_op, factor=self.lr_decay, patience=self.ref_patience) + + for epoch in range(1, self.num_epochs + 1): + train_generator = BatchIterator(h5_file, train_split[split], self.img_size, self.num_classes, self.batch_size, + self.task_type, self.is_training) + + train_lim = train_generator.get_max_lim() + valid_generator = BatchIterator(h5_file, valid_split[split], self.img_size, self.num_classes, self.batch_size, + self.task_type, self.is_training) + valid_lim = valid_generator.get_max_lim() + epoch_loss_train = 0 + epoch_loss_valid = 0 + + epoch_accur_train = 0 + epoch_accur_valid = 0 + + epoch_duration_train = 0 + epoch_duration_valid = 0 + + print("image dimentions") + print([self.img_size[0], self.img_size[1], self.img_size[2]]) + + for i in tqdm(train_generator, total=train_lim, unit=' steps', desc='Epoch {:d} train'.format(epoch)): + total_recall_counter_train += 1 + # start_time = time.time() + ff = list(i) + + outputs = self.network(torch.from_numpy(np.array([np.reshape(f[0], [self.img_size[0], self.img_size[1], self.img_size[2]]) for f + in ff]).transpose((0,3,1,2))).float()) + loss = self.network.return_loss(outputs, torch.from_numpy(np.array([f[1] for f in ff])).long()) + self.train_op.zero_grad() + loss.backward() + self.train_op.step() + + train_step_accuracy = int(self.network.return_accuracy(outputs, torch.from_numpy(np.array([f[1] for f in ff])), self.batch_size, + self.task_type)) + + epoch_loss_train += loss.item() + epoch_accur_train += (100*train_step_accuracy)/self.batch_size + + for i in tqdm(valid_generator, total=valid_lim, unit=' steps', desc='Epoch {:d} valid'.format(epoch)): + total_recall_counter_valid += 1 + # start_time = time.time() + ff = list(i) + + outputs = self.network(torch.from_numpy(np.array([np.reshape(f[0], [self.img_size[0], self.img_size[1], self.img_size[2]]) for f + in ff]).transpose((0,3,1,2))).float()) + loss = self.network.return_loss(outputs, torch.from_numpy(np.array([f[1] for f in ff])).long()) + + valid_step_accuracy = int(self.network.return_accuracy(outputs, torch.from_numpy(np.array([f[1] for f in ff])), 1, + self.task_type)) + + epoch_loss_valid += loss.item() + epoch_accur_valid += (100*valid_step_accuracy)/self.batch_size + + train_aver_loss = epoch_loss_train / train_lim + valid_aver_loss = epoch_loss_valid / valid_lim + + print(epoch_accur_train) + + if self.task_type == 'classification': + epoch_accur_train = epoch_accur_train / train_lim + epoch_accur_valid = epoch_accur_valid / valid_lim + # epoch_acc_str_tr = log_loss_accuracy(epoch_accur_train, self.accuracy_type, self.task_type, + # self.num_classes) + # epoch_acc_str_val = log_loss_accuracy(epoch_accur_valid, self.accuracy_type, self.task_type, + # self.num_classes) + elif self.task_type == 'segmentation': + epoch_accur_train = epoch_accur_train / train_lim + epoch_accur_valid = epoch_accur_valid / valid_lim + # epoch_acc_str_tr = log_loss_accuracy(epoch_accur_train, self.accuracy_type, self.task_type, + # self.num_classes) + # epoch_acc_str_val = log_loss_accuracy(epoch_accur_valid, self.accuracy_type, self.task_type, + # self.num_classes) + + # print( + # '\nRESULTS: epoch {:d} train loss = {:.3f}, train accuracy : {:.3f} ({:.2f} sec) || ' + # 'valid loss = {:.3f}, valid accuracy : {:.3f} ({:.2f} sec)' + # .format(epoch, train_aver_loss, epoch_accur_train, epoch_duration_train, valid_aver_loss, + # epoch_accur_valid, epoch_duration_valid)) + + prev_lr = self.train_op.param_groups[0]['lr'] + rp.step(valid_aver_loss) + curr_lr = self.train_op.param_groups[0]['lr'] + # print("Prev lr: {} Curr lr: {}".format(prev_lr, curr_lr)) + # + # print('update loss') + # liveloss.update({ + # 'log loss': train_aver_loss, + # 'val_log loss': valid_aver_loss, + # 'accuracy': epoch_accur_train, + # 'val_accuracy': epoch_accur_valid}) + # # print('draw loss') + # figure = liveloss.draw() + # print(figure) + # print("saving figure") + # # print(figure) + + + + # figure_file = os.path.join(loss_plot_path,'train_acur_plot.jpg') + # figure.savefig(figure_file) + + print( + '\nRESULTS: epoch {:d} lr {:6f} train loss = {:.3f}, train accuracy : {:.3f} ({:.2f} sec) || ' + 'valid loss = {:.3f}, valid accuracy : {:.3f} ({:.2f} sec)' + .format(epoch, curr_lr, train_aver_loss, epoch_accur_train, epoch_duration_train, valid_aver_loss, + epoch_accur_valid, epoch_duration_valid)) + + if prev_loss > valid_aver_loss: + # if not os.path.isdir(self.checkpoint_dir): + # os.mkdir(self.checkpoint_dir) + + # if not os.path.isdir(os.path.join(self.tr_path, self.data_set, self.timestamp)): + # os.mkdir(os.path.join(self.tr_path, self.data_set, self.timestamp)) + + model_file_path = os.path.join(self.ckpnt_path, + self.timestamp + '_split_' + str(split)) + if not os.path.exists(model_file_path): + os.makedirs(model_file_path) + + torch.save(self.network, "{}/model.pth".format(model_file_path)) + + + # model_file_path = create_ckpt_data_pytorch(self.ckpnt_path, self.network_type, event_time) + # torch.save(self.network, model_file_path) + print('[SESSION SAVE] Epoch {:d}, loss: {:2f}, accur: {:2f}, lr: {:4f}'.format(epoch, valid_aver_loss, epoch_accur_valid, curr_lr)) + prev_loss = valid_aver_loss + # ref_counter = 0 + if curr_lr < prev_lr: + ref_iter_count += 1 + if ref_iter_count == self.ref_steps+1: + print('\nEarly stopping\n Saving last best model:') + final_network = torch.load(model_file_path) + # pprint.pprint(final_network.state_dict()) + print("Importing model to ONNX") + # model_file_path_onnx = create_ckpt_data_onnx(self.ckpnt_path, self.network_type, event_time) + + + + dummy_input = Variable(torch.randn(1, *[self.img_size[2], self.img_size[0], self.img_size[1]])) + torch_onnx.export(final_network, dummy_input, model_file_path, verbose=False) + + print('Done') + status = "FINISHED: Early stopping" + return status + gc.collect() + print("Finalizing training") + final_network = torch.load(model_file_path) + # pprint.pprint(final_network.state_dict()) + print("Importing model to ONNX") + # model_file_path_onnx = create_ckpt_data_onnx(self.ckpnt_path, self.network_type, event_time) + dummy_input = Variable(torch.randn(1, *[self.img_size[2], self.img_size[0], self.img_size[1]])) + torch_onnx.export(final_network, dummy_input, model_file_path, verbose=False) + + print('Done') + status = 'FINISHED' + h5_file.close() + return status \ No newline at end of file diff --git a/network/wrappers/ConvNet.py b/network/wrappers/ConvNet.py index 9bd231d..66a8bcb 100644 --- a/network/wrappers/ConvNet.py +++ b/network/wrappers/ConvNet.py @@ -13,14 +13,15 @@ # # --- imports ----------------------------------------------------------------- - +import torch.nn as nn import tensorflow as tf +import torch.nn.functional as F from network.wrappers.NetworkBase import NetworkBase class ConvNet(NetworkBase): - def __init__(self, network_type, loss, accuracy, lr, training, framework, trainable_layers=None, num_filters=16, + def __init__(self, network_type, loss, accuracy, lr, framework, training, trainable_layers=None, num_filters=16, optimizer='adam', nonlin='elu', num_classes=2, dropout=0.25): """ Convolutional Neural Network constructor @@ -33,12 +34,13 @@ def __init__(self, network_type, loss, accuracy, lr, training, framework, traina :param num_classes: number of classes/labels :param dropout: dropout ratio """ - super().__init__(network_type, loss, accuracy, lr, training, framework, trainable_layers=trainable_layers, - optimizer=optimizer, nonlin=nonlin, num_filters=num_filters, - num_classes=num_classes, dropout=dropout) + super().__init__(network_type=network_type, loss=loss, accuracy=accuracy, framework=framework, lr=lr, training=training, + trainable_layers=trainable_layers, num_filters=num_filters, optimizer=optimizer, nonlin=nonlin, + num_classes=num_classes, dropout=dropout) self.weights, self.biases, self.nets = [], [], [] - def build_net_tf(self, X): + + def build_net(self, X): """ Build the Convolutional Neural Network :param X: input tensor @@ -46,10 +48,10 @@ def build_net_tf(self, X): """ with tf.name_scope('s_conv_1'): - conv_1_1, batch_1_1, activ_1_1 = self._conv_bn_layer(X, n_filters=self.num_filters, filter_size=3, + conv_1_1, batch_1_1, activ_1_1 = self._conv_bn_layer_tf(X, n_filters=self.num_filters, filter_size=3, is_training=self.is_training, nonlin_f=self.nonlin_f, name_postfix='1_1') - conv_1_2, batch_1_2, activ_1_2 = self._conv_bn_layer(activ_1_1, n_filters=self.num_filters, + conv_1_2, batch_1_2, activ_1_2 = self._conv_bn_layer_tf(activ_1_1, n_filters=self.num_filters, filter_size=3, is_training=self.is_training, nonlin_f=self.nonlin_f, name_postfix='1_2') pooling_1 = tf.layers.max_pooling2d(activ_1_2, pool_size=2, strides=2, padding='same', name='pooling_1') @@ -57,10 +59,10 @@ def build_net_tf(self, X): self.nets.extend([conv_1_1, conv_1_2]) with tf.name_scope('s_conv_2'): - conv_2_1, batch_2_1, activ_2_1 = self._conv_bn_layer(drop_1, n_filters=self.num_filters, filter_scale=2, + conv_2_1, batch_2_1, activ_2_1 = self._conv_bn_layer_tf(drop_1, n_filters=self.num_filters, filter_scale=2, filter_size=3, is_training=self.is_training, nonlin_f=self.nonlin_f, name_postfix='2_1') - conv_2_2, batch_2_2, activ_2_2 = self._conv_bn_layer(activ_2_1, n_filters=self.num_filters, filter_scale=2, + conv_2_2, batch_2_2, activ_2_2 = self._conv_bn_layer_tf(activ_2_1, n_filters=self.num_filters, filter_scale=2, filter_size=3, is_training=self.is_training, nonlin_f=self.nonlin_f, name_postfix='2_2') pooling_2 = tf.layers.max_pooling2d(activ_2_2, pool_size=2, strides=2, padding='same', name='pooling_2') @@ -68,10 +70,10 @@ def build_net_tf(self, X): self.nets.extend([conv_2_1, conv_2_2]) with tf.name_scope('s_conv_3'): - conv_3_1, batch_3_1, activ_3_1 = self._conv_bn_layer(drop_2, n_filters=self.num_filters, filter_scale=4, + conv_3_1, batch_3_1, activ_3_1 = self._conv_bn_layer_tf(drop_2, n_filters=self.num_filters, filter_scale=4, filter_size=3, is_training=self.is_training, nonlin_f=self.nonlin_f, name_postfix='3_1') - conv_3_2, batch_3_2, activ_3_2 = self._conv_bn_layer(activ_3_1, n_filters=self.num_filters, filter_scale=4, + conv_3_2, batch_3_2, activ_3_2 = self._conv_bn_layer_tf(activ_3_1, n_filters=self.num_filters, filter_scale=4, filter_size=3, is_training=self.is_training, nonlin_f=self.nonlin_f, name_postfix='3_2') pooling_3 = tf.layers.max_pooling2d(activ_3_2, pool_size=3, strides=2, padding='same', name='pooling_3') @@ -88,3 +90,53 @@ def build_net_tf(self, X): drop_6 = tf.layers.dropout(fc_2, rate=self.dropout, name='drop_6') output_p = tf.layers.dense(drop_6, units=self.num_classes, activation=None, name='output') return output_p + + +class ConvNet_pt(NetworkBase, nn.Module): + def __init__(self, network_type, loss, accuracy, lr, framework, training, trainable_layers=None, num_filters=16, + optimizer='adam', nonlin='elu', num_classes=2, dropout=0.25): + NetworkBase.__init__(self, network_type=network_type, loss=loss, accuracy=accuracy, framework=framework, lr=lr, training=training, + trainable_layers=trainable_layers, num_filters=num_filters, optimizer=optimizer, nonlin=nonlin, + num_classes=num_classes, dropout=dropout) + nn.Module.__init__(self) + + self.conv_1_1 = self._conv_bn_layer_pt(1, self.num_filters, filter_size=5, stride=1, is_training=True, + nonlin_f=self.nonlin_f, padding='same', name_postfix='1_1') + self.pool_1 = nn.MaxPool2d(kernel_size=2, stride=2) + self.drop_1 = nn.Dropout2d(p=self.dropout) + + self.conv_2_1 = self._conv_bn_layer_pt(self.num_filters, self.num_filters*2, filter_size=5, stride=1, is_training=True, + nonlin_f=self.nonlin_f, padding='same', name_postfix='1_1') + self.pool_2 = nn.MaxPool2d(kernel_size=2, stride=2) + self.drop_2 = nn.Dropout2d(p=self.dropout) + + + # self.fc_1 = nn.Linear(1024, 512) + # self.drop_4 = nn.Dropout2d(p=self.dropout) + + self.fc_2 = nn.Linear(512, 128) + self.drop_5 = nn.Dropout2d(p=self.dropout) + + self.out = nn.Linear(128, self.num_classes) + + def forward(self, X): + x = self.conv_1_1(X) + # x = self.conv_1_2(x) + x = self.pool_1(x) + x = self.drop_1(x) + x = self.conv_2_1(x) + # x = self.conv_2_2(x) + x = self.pool_2(x) + x = self.drop_2(x) + # x = self.conv_3_1(x) + # x = self.conv_3_2(x) + # x = self.pool_3(x) + # x = self.drop_3(x) + x = x.view(x.size(0), -1) + # x = self.fc_1(x) + # x = self.drop_4(x) + x = self.fc_2(x) + x = self.drop_5(x) + x = self.out(x) + + return x diff --git a/network/wrappers/NetworkBase.py b/network/wrappers/NetworkBase.py index 3a16619..606f943 100644 --- a/network/wrappers/NetworkBase.py +++ b/network/wrappers/NetworkBase.py @@ -14,9 +14,10 @@ # --- imports ----------------------------------------------------------------- -from utils.metric_v03 import * +from utils.metric import * import numpy as np - +import torch.nn as nn +import torch.optim as optim class NetworkBase: """ @@ -37,7 +38,7 @@ def __init__(self, network_type, loss, accuracy, lr, training, framework, optimi :param num_classes: number of classes/labels :param dropout: dropout ratio """ - self.loss_f = self._pick_loss_func(loss) + self.loss_f = self._pick_loss_func(loss, framework) # self.accuracy_f_list = self._pick_accuracy_function(accuracy) self.is_training = training self.learning_rate = lr @@ -45,11 +46,11 @@ def __init__(self, network_type, loss, accuracy, lr, training, framework, optimi self.framework = framework self.network_type = network_type if optimizer: - self.optimizer_f = self._pick_optimizer_func(optimizer) + self.optimizer_f = self._pick_optimizer_func(optimizer, framework) if nonlin: - self.nonlin_f = self._pick_nonlin_func(nonlin) + self.nonlin_f = self._pick_nonlin_func(nonlin, framework) if upconv: - self.upconv_f = self._pick_upconv_func(upconv) + self.upconv_f = self._pick_upconv_func(upconv, framework) if num_filters: self.num_filters = num_filters if num_classes: @@ -65,9 +66,9 @@ def __init__(self, network_type, loss, accuracy, lr, training, framework, optimi self.trainable_layers = 'all' if isinstance(accuracy, list): - self.accuracy_f_list = [self._pick_accuracy_function(acc) for acc in accuracy] + self.accuracy_f_list = [self._pick_accuracy_function(acc, framework) for acc in accuracy] else: - self.accuracy_f_list = self._pick_accuracy_function(accuracy) + self.accuracy_f_list = self._pick_accuracy_function(accuracy, framework) def _loss_function(self, y_pred, y_true): """ @@ -105,128 +106,188 @@ def _accuracy_function(self, y_pred, y_true, b_s, net_type, loss_type): else: raise ValueError('Unexpected network task %s' % net_type) - def _optimizer_function(self, global_step): + def _optimizer_function(self, global_step=None, net_param=None): """ Return the optimizer function :param global_step: current global step :return: optimizer """ - if self.trainable_layers == 'all': - return self.optimizer_f(self.learning_rate).minimize(self.loss, global_step=global_step) + if global_step: + if self.trainable_layers == 'all': + return self.optimizer_f(self.learning_rate).minimize(self.loss, global_step=global_step) + else: + first_train_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, + self.trainable_layers) + return self.optimizer_f(self.learning_rate).minimize(self.loss, var_list=first_train_vars, + global_step=global_step) else: - first_train_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, - self.trainable_layers) - return self.optimizer_f(self.learning_rate).minimize(self.loss, var_list=first_train_vars, - global_step=global_step) + return self.optimizer_f(net_param, self.learning_rate) @staticmethod - def _pick_nonlin_func(key): + def _pick_nonlin_func(key, framework): """ Select a nonlinearity/activation function :param key: nonliniearity identifier :return: nonliniearity/activation function """ - if key == 'elu': - return tf.nn.elu - elif key == 'relu': - return tf.nn.relu - elif key == 'lrelu': - return tf.nn.leaky_relu - elif key == 'tanh': - return tf.nn.tanh - else: - raise ValueError('Unexpected nonlinearity function %s' % key) + if framework == "tensorflow": + if key == 'elu': + return tf.nn.elu + elif key == 'relu': + return tf.nn.relu + elif key == 'lrelu': + return tf.nn.leaky_relu + elif key == 'tanh': + return tf.nn.tanh + else: + raise ValueError('Unexpected nonlinearity function %s' % key) + elif framework == "pytorch": + if key == 'elu': + return nn.ELU + elif key == 'relu': + return nn.ReLU + elif key == 'lrelu': + return nn.LeakyReLU + elif key == 'tanh': + return nn.Tanh + else: + raise ValueError('Unexpected nonlinearity function %s' % key) @staticmethod - def _pick_upconv_func(key): + def _pick_upconv_func(key, framework): """ Select either deconvolution or upsampling :param key: identifier :return: upconv or upsampling """ - if key == 'upconv': - return tf.layers.conv2d_transpose - if key == 'upsampling': - - def upconv2d(input_, output_shape, filters, kernel_size=4, strides=(1, 1), name="upconv2d"): - """ - Pure tensorflow 2d upconvolution/upsampling layer using tf.image.resize_images - :param input_: - :param output_shape: - :param filters: - :param kernel_size: - :param strides: - :param name: - :return: - """ - with tf.variable_scope(name): - resized = tf.image.resize_images(input_, output_shape[1:3]) - upconv = tf.layers.conv2d(resized, filters=filters, kernel_size=kernel_size, strides=strides, - padding='same', name='conv') - # upconv = tf.nn.conv2d(resize, w, strides=[1, d_h, d_w, 1], padding='SAME') - - return upconv - - return upconv2d - else: - raise ValueError('Unexpected upconvolution function %s' % key) + if framework == "tensorflow": + if key == 'upconv': + return tf.layers.conv2d_transpose + if key == 'upsampling': + + def upconv2d(input_, output_shape, filters, kernel_size=4, strides=(1, 1), name="upconv2d"): + """ + Pure tensorflow 2d upconvolution/upsampling layer using tf.image.resize_images + :param input_: + :param output_shape: + :param filters: + :param kernel_size: + :param strides: + :param name: + :return: + """ + with tf.variable_scope(name): + resized = tf.image.resize_images(input_, output_shape[1:3]) + upconv = tf.layers.conv2d(resized, filters=filters, kernel_size=kernel_size, strides=strides, + padding='same', name='conv') + # upconv = tf.nn.conv2d(resize, w, strides=[1, d_h, d_w, 1], padding='SAME') + + return upconv + + return upconv2d + else: + raise ValueError('Unexpected upconvolution function %s' % key) + elif framework == "pytorch": + if key == 'upconv': + return nn.ConvTranspose2d + else: + raise ValueError('Unexpected upconvolution function %s' % key) @staticmethod - def _pick_loss_func(key): + def _pick_loss_func(key, framework): """ Select loss function :param key: loss function identifier :return: loss function """ - if key == 'softmax': - return softmax - if key == 'sigmoid': - return sigmoid - if key == 'margin': - return margin - if key == 'mse': - return mse - if key == 'mse_loss': - return mse_loss - else: - raise ValueError('Unexpected metric function %s' % key) + if framework == "tensorflow": + if key == 'softmax': + return softmax_tf + if key == 'sigmoid': + return sigmoid_tf + if key == 'margin': + return margin_tf + if key == 'mse': + return mse_tf + if key == 'mse_loss': + return mse_loss_tf + else: + raise ValueError('Unexpected metric function %s' % key) + elif framework == "pytorch": + if key == 'softmax': + return softmax_pt + if key == 'sigmoid': + return sigmoid_pt + if key == 'margin': + return margin_pt + if key == 'mse': + return mse_pt + if key == 'mse_loss': + return mse_loss_pt + else: + raise ValueError('Unexpected metric function %s' % key) @staticmethod - def _pick_accuracy_function(key): - if key == 'IoU': - return IoU - elif key == 'dice_sorensen': - return dice_sorensen - elif key == 'dice_jaccard': - return dice_jaccard - elif key == 'mse': - return mse - elif key == 'hinge': - return hinge - elif key == 'percent': - return percentage - else: - raise ValueError('Unexpected metric function %s' % key) + def _pick_accuracy_function(key, framework): + if framework == "tensorflow": + if key == 'IoU': + return IoU_tf + elif key == 'dice_sorensen': + return dice_sorensen_tf + elif key == 'dice_jaccard': + return dice_jaccard_tf + elif key == 'mse': + return mse_tf + elif key == 'hinge': + return hinge_tf + elif key == 'percent': + return percentage_tf + else: + raise ValueError('Unexpected metric function %s' % key) + elif framework == "pytorch": + if key == 'IoU': + return IoU_pt + elif key == 'dice_sorensen': + return dice_sorensen_pt + elif key == 'dice_jaccard': + return dice_jaccard_pt + elif key == 'mse': + return mse_pt + elif key == 'hinge': + return hinge_pt + elif key == 'percent': + return percentage_pt + else: + raise ValueError('Unexpected metric function %s' % key) @staticmethod - def _pick_optimizer_func(key): - if key == 'adam': - return tf.train.AdamOptimizer - if key == 'amsgrad': - from network.third_parties.amsgrad.amsgrad import AMSGrad - return AMSGrad - if key == 'momentum': - return tf.train.MomentumOptimizer - if key == 'gradient': - return tf.train.GradientDescentOptimizer - if key == 'proximalgrad': - return tf.train.ProximalGradientDescentOptimizer - else: - raise ValueError('Unexpected optimizer function %s' % key) + def _pick_optimizer_func(key, framework): + if framework == "tensorflow": + if key == 'adam': + return tf.train.AdamOptimizer + if key == 'amsgrad': + from network.third_parties.amsgrad.amsgrad import AMSGrad + return AMSGrad + if key == 'momentum': + return tf.train.MomentumOptimizer + if key == 'gradient': + return tf.train.GradientDescentOptimizer + if key == 'proximalgrad': + return tf.train.ProximalGradientDescentOptimizer + else: + raise ValueError('Unexpected optimizer function %s' % key) + elif framework == "pytorch": + if key == 'adam': + return optim.Adam + if key == 'gradient': + return optim.SGD + else: + raise ValueError('Unexpected optimizer function %s' % key) + @staticmethod - def _conv_bn_layer(input_layer, n_filters, filter_scale=1, filter_size=3, is_training=True, nonlin_f=None, + def _conv_bn_layer_tf(input_layer, n_filters, filter_scale=1, filter_size=3, is_training=True, nonlin_f=None, padding='same', name='s_conv_bn', name_postfix='1_1'): """ Convolution layer with batch normalization @@ -246,14 +307,28 @@ def _conv_bn_layer(input_layer, n_filters, filter_scale=1, filter_size=3, is_tra activation=None, padding=padding, kernel_regularizer=tf.contrib.layers.l2_regularizer(scale=0.1), name='conv_' + name_postfix) batch_norm = tf.layers.batch_normalization(conv, training=is_training, fused=False, name='batch_' + name_postfix) - if nonlin_f: # weights, biases = NetworkBase.weights_and_biases(tf.shape(batch_norm), tf.shape(batch_norm)[-1]) # nonlin = nonlin_f(tf.matmul(batch_norm, weights) + biases, name='activation_' + name_postfix) - nonlin = nonlin_f(batch_norm, name='activation_' + name_postfix) - else: - nonlin = None + nonlin = nonlin_f(batch_norm, name='activation_' + name_postfix) return conv, batch_norm, nonlin + + @staticmethod + def _conv_bn_layer_pt(n_in, n_out, filter_size=3, stride=1, is_training=True, nonlin_f=None, + padding='same', name_postfix='1_1'): + m = nn.Sequential( + nn.Conv2d(n_in, n_out, filter_size, stride), + nn.BatchNorm2d(n_out), + nonlin_f() + ) + return m + # conv = nn.Conv2d(n_filters * filter_scale, input_layer, kernel_size=filter_size, stride=2) + # batch_norm = nn.BatchNorm2d(conv) + # nonlin = nonlin_f(batch_norm) + # return conv, batch_norm, nonlin + + + @staticmethod def _sep_conv_bn_layer(input_layer, n_filters, filter_scale=1, filter_size=3, is_training=True, nonlin_f=None, padding='same', name='s_conv_bn', name_postfix='1_1'): @@ -342,13 +417,13 @@ def return_loss(self, y_pred, y_true): """ return self._loss_function(y_pred=y_pred, y_true=y_true) - def return_optimizer(self, global_step): + def return_optimizer(self, global_step=None, net_param=None): """ Returns the optimizer function :param global_step: current global step :return: optimizer """ - return self._optimizer_function(global_step) + return self._optimizer_function(global_step, net_param) def return_nets(self): """ diff --git a/requirments.txt b/requirments.txt index 5dc92b2..1d50914 100644 --- a/requirments.txt +++ b/requirments.txt @@ -8,3 +8,4 @@ matplotlib seaborn tqdm numba +psutil diff --git a/utils/BatchIterator.py b/utils/BatchIterator.py index 1b85edb..0d43ee8 100644 --- a/utils/BatchIterator.py +++ b/utils/BatchIterator.py @@ -92,6 +92,9 @@ def __iter__(self): elif self.task_type == 'prediction': while self.on_going: yield self._next_batch_prediction() + elif self.task_type == 'detection': + while self.on_going: + yield self._next_batch_detection() # @jit(nopython=True) @@ -180,6 +183,30 @@ def _next_batch_prediction(self): else: self.on_going = False + def _next_batch_detection(self): + imgs = self.file_name[self.X_key][self.current] + pre_lbls = self.file_name[self.y_key][self.current] + lbls = [[lst[0:3], lst[4::]]for lst in pre_lbls if sum(lst) != 0] + if not list(self.img_size) == imgs[0].shape: + imgs = self._resize_data(imgs) + if self.augment_dict: + imgs = self._augment_data(imgs) + yield imgs, lbls + for num, item in enumerate(self.iterator, 1): + imgs = self.file_name[self.X_key][item] + pre_lbls = self.file_name[self.y_key][self.current] + lbls = [[lst[0:3], lst[4::]] for lst in pre_lbls if sum(lst) != 0] + if not list(self.img_size) == imgs[0].shape: + imgs = self._resize_data(imgs) + if self.augment_dict: + imgs = self._augment_data(imgs) + self.current = item + if num == self.batch_size: + break + yield imgs, lbls + else: + self.on_going = False + # @jit(nopython=True) def _resize_data(self, imgs, msks=None): """ diff --git a/utils/DataParser.py b/utils/DataParser.py index d576a60..aa54aac 100644 --- a/utils/DataParser.py +++ b/utils/DataParser.py @@ -14,10 +14,13 @@ # --- imports ----------------------------------------------------------------- +import gc import ast import json import h5py -import numpy as np +import multiprocessing as mp +from functools import partial +from psutil import virtual_memory from sklearn.model_selection import train_test_split import time from utils.utils import * @@ -71,8 +74,29 @@ def get_ckpnt_path(self): def get_tr_path(self): return self.tr_path + + def _path_preparation(self): + if self.experiment_path is None: + self.experiment_path = os.path.join(os.path.dirname(os.path.abspath('utils')), "experiments") + if not os.path.exists(self.experiment_path): + os.makedirs(self.experiment_path) + self.data_path = os.path.join(self.experiment_path, "datasets", self.data_set) + if not os.path.exists(self.data_path): + os.makedirs(self.data_path) + self.info_path = os.path.join(self.experiment_path, "info_logs", self.data_set) + if not os.path.exists(self.info_path): + os.makedirs(self.info_path) + self.tr_path = os.path.join(self.experiment_path, "train_logs", self.data_set, self.timestamp) + if not os.path.exists(self.tr_path): + os.makedirs(self.tr_path) + self.ckpnt_path = os.path.join(self.experiment_path, "ckpnt_logs", self.data_set, self.timestamp) + if not os.path.exists(self.ckpnt_path): + os.makedirs(self.ckpnt_path) + + + + def _parse_data(self): - self._path_preparation() file_name = self.data_set if self.img_size: @@ -83,20 +107,10 @@ def _parse_data(self): file_name += '_cent' file_name += '.hdf5' - if not os.path.exists(os.path.join(self.data_path, self.data_set)): - os.makedirs(os.path.join(self.data_path, self.data_set)) - - if not os.path.exists(os.path.join(self.info_path, self.data_set)): - os.makedirs(os.path.join(self.info_path, self.data_set)) - - if not os.path.exists(os.path.join(self.tr_path, self.data_set)): - os.makedirs(os.path.join(self.tr_path, self.data_set)) - - if not os.path.exists(os.path.join(self.ckpnt_path, self.data_set)): - os.makedirs(os.path.join(self.ckpnt_path, self.data_set)) + self._path_preparation() - h5py_file_name = os.path.join(self.data_path, self.data_set, file_name) - log_file_name = os.path.join(self.info_path, self.data_set, self.timestamp + ".json") + h5py_file_name = os.path.join(self.data_path, file_name) + log_file_name = os.path.join(self.info_path, self.timestamp + ".json") if self.data_set is "MNIST": @@ -116,9 +130,9 @@ def _parse_data(self): raise ValueError('No data presented') def _data_file_parse(self, h5py_file_name, log_file_name): - if os.path.splitext(self.data_file)[1] is '.txt': + if os.path.splitext(self.data_file)[1] == '.txt': x_list, y_list = self._process_txt() - elif os.path.splitext(self.data_file)[1] is '.json': + elif os.path.splitext(self.data_file)[1] == '.json': x_list, y_list = self._process_json() else: raise ValueError('Data format is not supported. Check documentation for data type support.') @@ -128,7 +142,7 @@ def _data_file_parse(self, h5py_file_name, log_file_name): try: self.slice_split = self._cross_val_split(len(x_list)) self.data_size = len(x_list) - self._dump_h5py(h5py_file_name, x_list, one_hot_encode(y_list)) + self._dump_h5py(h5py_file_name, x_list, y_list) self._dump_json_logs(h5py_file_name, log_file_name) self.dict_data_path = h5py_file_name self.log_info_path = log_file_name @@ -159,25 +173,25 @@ def _process_txt(self): y_data_ = [] else: if self.task_key is "classification": - if (os.path.splitext(line.split()[0])[1].lower() in ['.jpg', '.jpeg', '.png']) and isinstance(ast.literal_eval(line.rstrip().split()[1]), int): + if (os.path.splitext(line.split()[0])[1].lower() in ['.jpg', '.jpeg', '.png', '.bmp']) and line.rstrip().split()[1].isdigit(): print("Classificaiton data detected. Integer encoded") X_data_ = [line.rstrip().split()[0] for line in file_lines] y_data_ = [int(line.rstrip().split()[1]) for line in file_lines] - elif (os.path.splitext(line.split()[0])[1].lower() in ['.jpg', '.jpeg', '.png']) and isinstance(ast.literal_eval(''.join(line.rstrip().split()[1:])), list) and (sum(ast.literal_eval(''.join(line.rstrip().split()[1:]))) == 1): + elif (os.path.splitext(line.split()[0])[1].lower() in ['.jpg', '.jpeg', '.png', '.bmp']) and isinstance(ast.literal_eval(''.join(line.rstrip().split()[1:])), list) and (sum(ast.literal_eval(''.join(line.rstrip().split()[1:]))) == 1): print("Classificaiton data detected. One hot encoded") X_data_ = [line.rstrip().split()[0] for line in file_lines] y_data_ = [ast.literal_eval(''.join(line.rstrip().split()[1:])) for line in file_lines] else: raise ValueError("Incorrect data representation") elif (self.task_key is "segmentation") or (self.task_key is "gan"): - if (os.path.splitext(line.split()[0])[1].lower() in ['.jpg', '.jpeg', '.png']) and ( - os.path.splitext(line.rstrip().split()[1])[1].lower() in ['.jpg', '.jpeg', '.png']): + if (os.path.splitext(line.split()[0])[1].lower() in ['.jpg', '.jpeg', '.png', '.bmp']) and ( + os.path.splitext(line.rstrip().split()[1])[1].lower() in ['.jpg', '.jpeg', '.png', '.bmp']): X_data_ = [line.rstrip().split()[0] for line in file_lines] y_data_ = [line.rstrip().split()[1] for line in file_lines] else: raise ValueError("Incorrect data representation") elif self.task_key is "detection": - if (os.path.splitext(line.split()[0])[1].lower() in ['.jpg', '.jpeg', '.png']) and isinstance(ast.literal_eval(''.join(line.rstrip().split()[1:])), list): + if (os.path.splitext(line.split()[0])[1].lower() in ['.jpg', '.jpeg', '.png', '.bmp']) and isinstance(ast.literal_eval(''.join(line.rstrip().split()[1:])), list): X_data_ = [line.rstrip().split()[0] for line in file_lines] y_data_ = [ast.literal_eval(''.join(line.rstrip().split()[1:])) for line in file_lines] else: @@ -191,22 +205,39 @@ def _process_json(self): with open(self.data_file, 'r') as f: file_lines = json.load(f) - if file_lines['source_path'] is [None, 'null', '']: + if file_lines['source_path'] in [None, 'null', '']: raise ValueError("Source path is not specified") else: - if file_lines['mask_path'] is [None, 'null', '']: - if self.task_key is "classification": - X_data_ = [d['frame'] for d in file_lines['meta']] - y_data_ = [o['object_class'] for d in file_lines['meta'] for o in d['frame']] - elif self.task_key is "detection": - X_data_ = [d['frame'] for d in file_lines['meta']] - y_data_ = [o['bb'] for d in file_lines['meta'] for o in d['frame']] + if file_lines['mask_path'] in [None, 'null', '']: + if self.task_key == "classification": + X_data_ = [os.path.join(file_lines['source_path'], d['frame']) for d in file_lines['meta']] + y_data_ = [o['object_class'] for d in file_lines['meta'] for o in d['objects']] + elif self.task_key == "tracking": + X_data_ = [os.path.join(file_lines['source_path'], d['frame']) for d in file_lines['meta']] + y_data_ = [o['bb'] for d in file_lines['meta'] for o in d['objects']] + elif self.task_key == "detection": + X_data_ = [os.path.join(file_lines['source_path'], d['frame']) for d in file_lines['meta']] + y_list = [] + for d in file_lines['meta']: + lst = [] + for o in d['objects']: + lst.append([o['bb'] + one_to_onehot(o['object_class'], self.num_classes)]) + y_list.append(lst) + + ll = max([len(l) for l in y_list]) + y_data_ = [] + for l in y_list: + m = l + for i in range(ll - len(m)): + m.append([[0, 0, 0, 0] + [0 for k in range(10)]]) + y_data_.append(m) + y_data_ = np.array(y_data_) else: raise ValueError('Such task not supported') else: - if (self.task_key is "segmentation") or (self.task_key is "gan"): - X_data_ = [d['frame'] for d in file_lines['meta']] - y_data_ = [d['mask'] for d in file_lines['meta']] + if (self.task_key == "segmentation") or (self.task_key == "gan"): + X_data_ = [os.path.join(file_lines['source_path'], d['frame']) for d in file_lines['meta']] + y_data_ = [os.path.join(file_lines['mask_path'], d['mask']) for d in file_lines['meta']] else: raise ValueError('Such task not supported') @@ -228,9 +259,11 @@ def _data_folder_parse(self, h5py_file_name, log_file_name): for i in range(len(path_list)): for dirpath, _, filenames in os.walk(path_list[i]): for f in filenames: - if f.lower().endswith(tuple(['.jpg', '.jpeg', '.png'])): + if f.lower().endswith(tuple(['.jpg', '.jpeg', '.png', '.bmp'])): x_list.append(os.path.abspath(os.path.join(dirpath, f))) y_list.append(i) + else: + raise ValueError("Not supported data format") elif self.task_key is "segmentation": if "images" in path_list and "masks" in path_list: x_list = path_walk(os.path.join(self.data_folder, 'images')) @@ -252,7 +285,7 @@ def _data_folder_parse(self, h5py_file_name, log_file_name): try: self.slice_split = self._cross_val_split(len(x_list)) self.data_size = len(x_list) - self._dump_h5py(h5py_file_name, x_list, one_hot_encode(y_list)) + self._dump_h5py(h5py_file_name, x_list, y_list) self._dump_json_logs(h5py_file_name, log_file_name) self.dict_data_path = h5py_file_name self.log_info_path = log_file_name @@ -273,7 +306,7 @@ def _data_folder_parse(self, h5py_file_name, log_file_name): def _load_mnist(self, h5py_file_name, log_file_name): print('Creating h5py file for MNIST') - if self.framework is 'tensorflow': + if self.framework == 'tensorflow': import tensorflow as tf mnist = tf.keras.datasets.mnist if self.is_training: @@ -281,25 +314,25 @@ def _load_mnist(self, h5py_file_name, log_file_name): else: _, (self.X_data, self.y_data) = mnist.load_data() # y_data = one_hot_encode(y_data) - elif self.framework is 'pytorch': + elif self.framework == 'pytorch': import torchvision.datasets as datasets - mnist_trainset = datasets.MNIST(root=os.path.join(self.info_path, self.data_set), train=True, download=True, transform=None) + mnist_trainset = datasets.MNIST(root=os.path.join(self.data_path, self.data_set), train=True, download=True, transform=None) import struct if self.is_training: - with open(os.path.join(self.info_path, self.data_set + r'/raw/train-labels-idx1-ubyte'), 'rb') as lbpath: + with open(os.path.join(self.data_path, self.data_set + r'/raw/train-labels-idx1-ubyte'), 'rb') as lbpath: magic, n = struct.unpack('>II', lbpath.read(8)) y_data = np.fromfile(lbpath, dtype=np.uint8).tolist() - with open(os.path.join(self.info_path, self.data_set + r'/raw/train-images-idx3-ubyte'), 'rb') as imgpath: + with open(os.path.join(self.data_path, self.data_set + r'/raw/train-images-idx3-ubyte'), 'rb') as imgpath: magic, num, rows, cols = struct.unpack(">IIII", imgpath.read(16)) X_data = np.fromfile(imgpath, dtype=np.uint8).reshape((-1, 28, 28, 1)) else: - with open(os.path.join(self.info_path, self.data_set + r'/raw/test-labels-idx1-ubyte'), + with open(os.path.join(self.data_path, self.data_set + r'/raw/test-labels-idx1-ubyte'), 'rb') as lbpath: magic, n = struct.unpack('>II', lbpath.read(8)) self.y_data = np.fromfile(lbpath, dtype=np.uint8).tolist() - with open(os.path.join(self.info_path, self.data_set + r'/raw/test-images-idx3-ubyte'), + with open(os.path.join(self.data_path, self.data_set + r'/raw/test-images-idx3-ubyte'), 'rb') as imgpath: magic, num, rows, cols = struct.unpack(">IIII", imgpath.read(16)) self.X_data = np.fromfile(imgpath, dtype=np.uint8).reshape((-1, 28, 28, 1)) @@ -313,7 +346,6 @@ def _load_mnist(self, h5py_file_name, log_file_name): try: self.data_size = len(X_data) self.slice_split = self._cross_val_split(len(y_data)) - # self._dump_h5py(h5py_file_name, X_data, one_hot_encode(y_data)) with h5py.File(h5py_file_name, 'a') as f: f.create_dataset('X_data', data=X_data) f.create_dataset('y_data', data=one_hot_encode(y_data)) @@ -344,13 +376,19 @@ def _load_cifar10(self, h5py_file_name, log_file_name): elif self.framework is "pytorch": import torchvision.datasets as datasets if self.is_training: - cifar_trainset = datasets.CIFAR10(root=os.path.join(self.info_path, self.data_set), train=True, download=True, transform=None) - X_data = cifar_trainset.train_data - y_data = cifar_trainset.train_labels + cifar_trainset = datasets.CIFAR10(root=os.path.join(self.data_path, self.data_set), train=True, download=True, transform=None) + # works ONLY for Windows. I have NO idea why + # X_data = cifar_trainset.train_data + # y_data = cifar_trainset.train_labels + y_data = [x for _, x in cifar_trainset] + [np.asarray(x) for x, _ in cifar_trainset] + else: - cifar_trainset = datasets.CIFAR10(root=os.path.join(self.info_path, self.data_set), train=False, download=True, transform=None) - self.X_data = cifar_trainset.test_data - self.y_data = cifar_trainset.test_labels + cifar_trainset = datasets.CIFAR10(root=os.path.join(self.data_path, self.data_set), train=False, download=True, transform=None) + # self.X_data = cifar_trainset.test_data + # self.y_data = cifar_trainset.test_labels + self.X_data = [np.asarray(x) for x, _ in cifar_trainset] + self.y_data = [x for _, x in cifar_trainset] else: raise ValueError('Framework does not exist') @@ -361,7 +399,9 @@ def _load_cifar10(self, h5py_file_name, log_file_name): try: self.data_size = len(X_data) self.slice_split = self._cross_val_split(len(y_data)) - self._dump_h5py(h5py_file_name, X_data, one_hot_encode(y_data)) + with h5py.File(h5py_file_name, 'a') as f: + f.create_dataset('X_data', data=X_data) + f.create_dataset('y_data', data=one_hot_encode(y_data)) self._dump_json_logs(h5py_file_name, log_file_name) self.dict_data_path = h5py_file_name self.log_info_path = log_file_name @@ -391,13 +431,17 @@ def _load_cifar100(self, h5py_file_name, log_file_name): elif self.framework is "pytorch": import torchvision.datasets as datasets if self.is_training: - cifar_trainset = datasets.CIFAR100(root=os.path.join(self.info_path, self.data_set), train=True, download=True, transform=None) - X_data = cifar_trainset.train_data - y_data = cifar_trainset.train_labels + cifar_trainset = datasets.CIFAR100(root=os.path.join(self.data_path, self.data_set), train=True, download=True, transform=None) + y_data = [x for _, x in cifar_trainset] + [np.asarray(x) for x, _ in cifar_trainset] + # X_data = cifar_trainset.train_data + # y_data = cifar_trainset.train_labels else: - cifar_trainset = datasets.CIFAR100(root=os.path.join(self.info_path, self.data_set), train=True, download=True, transform=None) - self.X_data = cifar_trainset.test_data - self.y_data = cifar_trainset.test_labels + cifar_trainset = datasets.CIFAR100(root=os.path.join(self.data_path, self.data_set), train=True, download=True, transform=None) + # self.X_data = cifar_trainset.test_data + # self.y_data = cifar_trainset.test_labels + self.X_data = [np.asarray(x) for x, _ in cifar_trainset] + self.y_data = [x for _, x in cifar_trainset] else: raise ValueError('Framework does not exist') @@ -407,7 +451,9 @@ def _load_cifar100(self, h5py_file_name, log_file_name): try: self.data_size = len(X_data) self.slice_split = self._cross_val_split(len(y_data)) - self._dump_h5py(h5py_file_name, X_data, one_hot_encode(y_data)) + with h5py.File(h5py_file_name, 'a') as f: + f.create_dataset('X_data', data=X_data) + f.create_dataset('y_data', data=one_hot_encode(y_data)) self._dump_json_logs(h5py_file_name, log_file_name) self.dict_data_path = h5py_file_name self.log_info_path = log_file_name @@ -443,41 +489,83 @@ def _dump_json_logs(self, h5py_file_name, log_file_name): raise ValueError("Unable to save logs") def _dump_h5py(self, h5py_file_name, x_list, y_list): + mem = virtual_memory() + free_mem = int(mem.free * 0.7) if (self.task_key is 'segmentation') or (self.task_key is 'gan'): + pool = mp.Pool(mp.cpu_count()) + data_mem_x = sum(pool.map(count_size, x_list)) + pool.close() + pool.join() + pool = mp.Pool(mp.cpu_count()) + data_mem_y = sum(pool.map(count_size, y_list)) + pool.close() + pool.join() + + + x_list_split = chunk_split(x_list, data_mem_x//free_mem + 1) + y_list_split = chunk_split(y_list, data_mem_y//free_mem + 1) + with h5py.File(h5py_file_name, 'a') as f: - dset_x = f.create_dataset('X_data', (1, self.img_size[0], self.img_size[1], self.img_size[2]), + dset_x = f.create_dataset('X_data', (0, self.img_size[0], self.img_size[1], self.img_size[2]), maxshape=(None, self.img_size[0], self.img_size[1], self.img_size[2]), chunks=True) - dset_y = f.create_dataset('y_data', (1, self.img_size[0], self.img_size[1], self.num_classes), - maxshape=(None, self.img_size[0], self.img_size[1], self.num_classes), + dset_y = f.create_dataset('y_data', (0, self.img_size[0], self.img_size[1], self.img_size[2]), + maxshape=(None, self.img_size[0], self.img_size[1], self.img_size[2]), chunks=True) - if isinstance(x_list[0], str): - for i in range(0, len(x_list)): - dset_x.resize(dset_x.shape[0] + 1, axis=0) - dset_x[-dset_x.shape[0]:] = expand_dims(x_list[i], self.img_size) - dset_y.resize(dset_y.shape[0] + 1, axis=0) - dset_y[-dset_y.shape[0]:] = expand_dims(y_list[i], self.img_size) - else: - for i in range(0, len(x_list)): - dset_x.resize(dset_x.shape[0] + 1, axis=0) - dset_x[-dset_x.shape[0]:] = expand_dims(x_list[i], self.img_size) - dset_y.resize(dset_y.shape[0] + 1, axis=0) - dset_y[-dset_y.shape[0]:] = expand_dims(y_list[i], self.img_size) + for i in range(len(x_list_split)): + temp_map = np.zeros([len(x_list_split), self.img_size[0], self.img_size[1], self.img_size[2]]) + pool = mp.Pool(mp.cpu_count()) + func = partial(bulk_process, self.img_size) + temp_map = np.expand_dims(np.array(pool.map(func, x_list_split[i])), -1) + dset_x.resize(dset_x.shape[0] + len(x_list_split[i]), axis=0) + dset_x[-len(x_list_split[i]):] = temp_map + pool.close() + pool.join() + for i in range(len(y_list_split)): + temp_map = np.zeros([len(x_list_split), self.img_size[0], self.img_size[1], self.img_size[2]]) + pool = mp.Pool(mp.cpu_count()) + func = partial(bulk_process, self.img_size) + temp_map = np.expand_dims(np.array(pool.map(func, x_list_split[i])), -1) + dset_y.resize(dset_y.shape[0] + len(y_list_split[i]), axis=0) + dset_y[-len(y_list_split[i]):] = temp_map + pool.close() + pool.join() + else: + start_process = time.time() with h5py.File(h5py_file_name, 'a') as f: - dset_x = f.create_dataset('X_data', (1, self.img_size[0], self.img_size[1], self.img_size[2]), + pool = mp.Pool(mp.cpu_count()) + start_time = time.time() + data_mem_x = sum(pool.map(count_size, x_list)) + pool.close() + pool.join() + elapsed_time = time.time() - start_time + print("Data size count finished in") + print(time.strftime("%H:%M:%S", time.gmtime(elapsed_time))) + x_list_split = chunk_split(x_list, data_mem_x // free_mem + 1) + + print("chunk split finished") + dset_x = f.create_dataset('X_data', (0, self.img_size[0], self.img_size[1], self.img_size[2]), maxshape=(None, self.img_size[0], self.img_size[1], self.img_size[2]), - chunks=True) - if isinstance(x_list[0], str): - for i in range(0, len(x_list)): - dset_x.resize(dset_x.shape[0] + 1, axis=0) - dset_x[-dset_x.shape[0]:] = expand_dims(x_list[i], self.img_size) - f.create_dataset('y_data', data=y_list) - else: - for i in range(0, len(x_list)): - dset_x.resize(dset_x.shape[0] + 1, axis=0) - dset_x[-dset_x.shape[0]:] = expand_dims(x_list[i], self.img_size) - f.create_dataset('y_data', data=y_list) + chunks=True, compression='gzip') + for i in range(len(x_list_split)): + temp_map = np.zeros([len(x_list_split), self.img_size[0], self.img_size[1], self.img_size[2]]) + print("Start data process loop " + str(i)) + start_time = time.time() + pool = mp.Pool(mp.cpu_count()) + func = partial(bulk_process, self.img_size) + temp_map = np.expand_dims(np.array(pool.map(func, x_list_split[i])),-1) + dset_x.resize(dset_x.shape[0] + len(x_list_split[i]), axis=0) + dset_x[-len(x_list_split[i]):] = temp_map + pool.close() + pool.join() + elapsed_time = time.time() - start_time + print(time.strftime("%H:%M:%S", time.gmtime(elapsed_time))) + + f.create_dataset('y_data', data=y_list) + end_process = time.time() - start_process + print('full time process: ' + time.strftime("%H:%M:%S", time.gmtime(end_process))) + gc.collect() def _check_data_split(self): """ @@ -509,24 +597,6 @@ def _cross_val_split(self, data_dim): return slices - def _path_preparation(self): - if self.experiment_path is None: - self.experiment_path = os.path.join(os.path.dirname(os.path.abspath('utils')), "experiments") - if not os.path.isdir(self.experiment_path): - os.mkdir(self.experiment_path) - self.data_path = os.path.join(self.experiment_path, "datasets") - if not os.path.isdir(self.data_path): - os.mkdir(self.data_path) - self.info_path = os.path.join(self.experiment_path, "info_logs") - if not os.path.isdir(self.info_path): - os.mkdir(self.info_path) - self.tr_path = os.path.join(self.experiment_path, "train_logs") - if not os.path.isdir(self.tr_path): - os.mkdir(self.tr_path) - self.ckpnt_path = os.path.join(self.experiment_path, "ckpnt_logs") - if not os.path.isdir(self.ckpnt_path): - os.mkdir(self.ckpnt_path) - @staticmethod def _preprocess_data_scope(img, msk=None, lbl=None, img_size=None, normalize=False, zero_center=False): diff --git a/utils/metric.py b/utils/metric.py new file mode 100644 index 0000000..adc524a --- /dev/null +++ b/utils/metric.py @@ -0,0 +1,365 @@ +#!/usr/bin/env python +# ----------------------------------------------------------------------------- +# Copyright (C) Software Competence Center Hagenberg GmbH (SCCH) +# All rights reserved. +# ----------------------------------------------------------------------------- +# This document contains proprietary information belonging to SCCH. +# Passing on and copying of this document, use and communication of its +# contents is not permitted without prior written authorization. +# ----------------------------------------------------------------------------- +# Created on : 11/07/2017 08:10 $ +# by : shepeleva $ +# SVN : $ +# + +# --- imports ----------------------------------------------------------------- + +import torch.nn as nn +import tensorflow as tf +import numpy as np + + + +def margin_tf(y_pred,y_true, margin=0.4, downweight=0.5): + """Penalizes deviations from margin for each logit. + + Each wrong logit costs its distance to margin. For negative logits margin is + 0.1 and for positives it is 0.9. First subtract 0.5 from all logits. Now + margin is 0.4 from each side. + + Args: + y_true: tensor, one hot encoding of ground truth. + y_pred: tensor, model predictions in range [0, 1] + margin: scalar, the margin after subtracting 0.5 from raw_logits. + downweight: scalar, the factor for negative cost. + + Returns: + A tensor with cost for each data point of shape [batch_size]. + """ + logits = y_pred - 0.5 + positive_cost = y_true * tf.cast(tf.less(logits, margin), + tf.float32) * tf.pow(logits - margin, 2) + negative_cost = (1 - y_true) * tf.cast( + tf.greater(logits, -margin), tf.float32) * tf.pow(logits + margin, 2) + return tf.reduce_mean(0.5 * positive_cost + downweight * 0.5 * negative_cost) + + +def IoU_tf(y_pred, y_true): + """Returns a (approx) IOU score + intersection = y_pred.flatten() * y_true.flatten() + Then, IOU = intersection / (y_pred.sum() + y_true.sum() - intersection) + Args: + :param y_pred: predicted labels (4-D array): (N, H, W, 1) + :param y_true: groundtruth labels (4-D array): (N, H, W, 1) + :return + float: IOU score + """ + threshold = 0.5 + axis = (0, 1, 2, 3) + smooth = 1e-5 + pre = tf.cast(y_pred > threshold, dtype=tf.float32) + truth = tf.cast(y_true > threshold, dtype=tf.float32) + inse = tf.reduce_sum(tf.multiply(pre, truth), axis=axis) # AND + union = tf.reduce_sum(tf.cast(tf.add(pre, truth) >= 1, dtype=tf.float32), axis=axis) # OR + batch_iou = (inse + smooth) / (union + smooth) + iou = tf.reduce_mean(batch_iou) + return iou + + # b_iou = [] + # for i in range(b_s): + # lbl_iou = [] + # for k in range(int(y_pred.shape[-1])): + # logits = tf.slice(y_pred, (i, 0, 0, k), (1, -1, -1, 1)) + # trn_labels = tf.slice(y_true, (i, 0, 0, k), (1, -1, -1, 1)) + # logits = tf.reshape(logits, [-1]) + # trn_labels = tf.reshape(trn_labels, [-1]) + # inter = tf.reduce_sum(tf.multiply(logits, trn_labels)) + # denom = tf.reduce_sum(tf.subtract(tf.add(logits, trn_labels), tf.multiply(logits, trn_labels))) + # lbl_iou.append(tf.div(inter, denom)) + # b_iou.append(lbl_iou) + # return tf.div(tf.reduce_sum(b_iou, axis=0), b_s) + + +def dice_jaccard_tf(y_pred, y_true): + """Returns a (approx) dice score + intesection = y_pred.flatten() * y_true.flatten() + Then, dice = 2 * intersection / (y_pred.sum() + y_true.sum()) + :param y_pred: predicted labels (4-D array): (N, H, W, 1) + :param y_true: groundtruth labels (4-D array): (N, H, W, 1) + :return + float: dice score + """ + smooth = 1e-5 + inse = tf.reduce_sum(y_true * y_pred, axis=(0, 1, 2, 3)) + l = tf.reduce_sum(y_true * y_true, axis=(0, 1, 2, 3)) + r = tf.reduce_sum(y_pred * y_pred, axis=(0, 1, 2, 3)) + dice = (2. * inse + smooth) / (l + r + smooth) + dice = tf.reduce_mean(dice) + return dice + + +def dice_sorensen_tf(y_pred, y_true): + smooth = 1e-5 + inse = tf.reduce_sum(y_true * y_pred, axis=(0, 1, 2, 3)) + l = tf.reduce_sum(y_true, axis=(0, 1, 2, 3)) + r = tf.reduce_sum(y_pred, axis=(0, 1, 2, 3)) + dice = (2. * inse + smooth) / (l + r + smooth) + dice = tf.reduce_mean(dice) + return dice + + +def softmax_tf(y_pred, y_true, epsilon=1e-10): + """ + Computes cross entropy with included softmax - DO NOT provide outputs from softmax layers to this function! + + For brevity, let `x = output`, `z = target`. The binary cross entropy loss is + loss(x, z) = - sum_i (x[i] * log(z[i]) + (1 - x[i]) * log(1 - z[i])) + + :param y_pred: predicted labels + :param y_true: groundtruth labels + :return + cross entropy with included softmax + """ + print(y_true) + print(y_pred) + + # classification + if len(y_true.shape) <= 2: + # labels onehot + if int(y_true.get_shape()[1]) > 1: + return tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(labels=y_true, logits=y_pred)) + # labels encoded as integers + else: + return tf.reduce_mean(tf.losses.sparse_softmax_cross_entropy(labels=y_true, logits=y_pred)) + # segmentation + else: + # print(y_true) + # print(y_pred) + y_true_flat = tf.reshape(y_true, [-1, int(y_true.shape[3])]) + y_pred_flat = tf.reshape(y_pred, [-1, int(y_true.shape[3])]) + return tf.reduce_mean(tf.losses.softmax_cross_entropy(onehot_labels=y_true_flat, logits=y_pred_flat)) + + +def sigmoid_tf(y_pred, y_true): + """ + Computes Sigmoid cross entropy + :param y_pred: + :param y_true: + :return: + """ + return tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(labels=y_true, logits=y_pred)) + + +def hinge_tf(y_pred, y_true): + """ + Computes Hinge Loss + :param y_pred: predicted labels + :param y_true: groundtruth labels + :return: + Hinge Loss + """ + return tf.losses.hinge_loss(labels=y_true, logits=y_pred) + + +def mse_tf(y_pred, y_true): + """ + Computes Sum-of-Squares loss + :param y_pred: predicted labels + :param y_true: groundtruth labels + :return: + Sum-of-Squares loss + """ + # y_p_s = tf.nn.sigmoid(y_pred) + # return tf.losses.mean_squared_error(labels=y_true, predictions=y_p_s) + tf.losses.get_regularization_loss() + + return tf.losses.mean_squared_error(labels=y_true, predictions=y_pred) + + +def mse_loss_tf(y_pred, y_true): + """ + Computes Sum-of-Squares loss + :param y_pred: predicted labels + :param y_true: groundtruth labels + :return: + Sum-of-Squares loss + """ + + # init_shape = y_true.get_shape().as_list() + + y_true_flat = tf.reshape(y_true, [-1]) + y_pred_flat = tf.reshape(y_pred, [-1]) + + aa_list = ((y_true_flat - y_pred_flat) * (y_true_flat - y_pred_flat))/2 + + aa_reshaped = tf.reshape(aa_list, tf.shape(y_true)) + + lst = tf.sqrt(tf.cast(tf.reduce_sum(aa_reshaped, axis=1), tf.float16)) + + + + + # import numpy as np + # lst = [] + # dim = y_true.get_shape().as_list() + # + # for item in range(0,dim[0]): + # aa_list = [(i-j)^2 for i, j in zip(y_true[item], y_pred[item])] + # lst.append(np.sqrt(sum(aa_list)/2)) + + return tf.reduce_mean(tf.cast(lst, dtype=tf.float32)) + # return tf.losses.mean_squared_error(labels=y_true, predictions=y_pred) + + +def percentage_tf(y_pred, y_true): + """ + Computes percentage of correct predictions + :param y_pred: + :param y_true: + :return: + """ + print(y_pred) + print(y_true) + return tf.reduce_mean(tf.cast(tf.equal(tf.argmax(y_pred, 1), tf.argmax(y_true, 1)), tf.float32)) + + +# pytorch metrics + +def margin_pt(y_pred,y_true, margin=0.4, downweight=0.5): + """Penalizes deviations from margin for each logit. + + Each wrong logit costs its distance to margin. For negative logits margin is + 0.1 and for positives it is 0.9. First subtract 0.5 from all logits. Now + margin is 0.4 from each side. + + Args: + y_true: tensor, one hot encoding of ground truth. + y_pred: tensor, model predictions in range [0, 1] + margin: scalar, the margin after subtracting 0.5 from raw_logits. + downweight: scalar, the factor for negative cost. + + Returns: + A tensor with cost for each data point of shape [batch_size]. + """ + true = y_true.max(dim=1)[1] + return nn.SoftMarginLoss()(y_pred, true) + + +def IoU_pt(y_pred, y_true): + """Returns a (approx) IOU score + intersection = y_pred.flatten() * y_true.flatten() + Then, IOU = intersection / (y_pred.sum() + y_true.sum() - intersection) + Args: + :param y_pred: predicted labels (4-D array): (N, H, W, 1) + :param y_true: groundtruth labels (4-D array): (N, H, W, 1) + :return + float: IOU score + """ + raise NotImplementedError + # ious = [] + # pred = pred.view(-1) + # target = target.view(-1) + # + # # Ignore IoU for background class ("0") + # for cls in range(1, n_classes): # This goes from 1:n_classes-1 -> class "0" is ignored + # pred_inds = pred == cls + # target_inds = target == cls + # intersection = (pred_inds[target_inds]).long().sum().data.cpu()[0] # Cast to long to prevent overflows + # union = pred_inds.long().sum().data.cpu()[0] + target_inds.long().sum().data.cpu()[0] - intersection + # if union == 0: + # ious.append(float('nan')) # If there is no ground truth, do not include in evaluation + # else: + # ious.append(float(intersection) / float(max(union, 1))) + # return np.array(ious) + + + + +def dice_jaccard_pt(y_pred, y_true): + """Returns a (approx) dice score + intesection = y_pred.flatten() * y_true.flatten() + Then, dice = 2 * intersection / (y_pred.sum() + y_true.sum()) + :param y_pred: predicted labels (4-D array): (N, H, W, 1) + :param y_true: groundtruth labels (4-D array): (N, H, W, 1) + :return + float: dice score + """ + raise NotImplementedError + + +def dice_sorensen_pt(y_pred, y_true): + raise NotImplementedError + + +def softmax_pt(y_pred, y_true, epsilon=1e-10): + """ + Computes cross entropy with included softmax - DO NOT provide outputs from softmax layers to this function! + + For brevity, let `x = output`, `z = target`. The binary cross entropy loss is + loss(x, z) = - sum_i (x[i] * log(z[i]) + (1 - x[i]) * log(1 - z[i])) + + :param y_pred: predicted labels + :param y_true: groundtruth labels + :return + cross entropy with included softmax + """ + true = y_true.max(dim=1)[1] + return nn.CrossEntropyLoss()(y_pred, true) + + +def sigmoid_pt(y_pred, y_true): + """ + Computes Sigmoid cross entropy + :param y_pred: + :param y_true: + :return: + """ + true = y_true.max(dim=1)[1] + return nn.BCEWithLogitsLoss()(y_pred, true) + + +def hinge_pt(y_pred, y_true): + """ + Computes Hinge Loss + :param y_pred: predicted labels + :param y_true: groundtruth labels + :return: + Hinge Loss + """ + raise NotImplementedError + + +def mse_pt(y_pred, y_true): + """ + Computes Sum-of-Squares loss + :param y_pred: predicted labels + :param y_true: groundtruth labels + :return: + Sum-of-Squares loss + """ + raise NotImplementedError + + +def mse_loss_pt(y_pred, y_true): + """ + Computes Sum-of-Squares loss + :param y_pred: predicted labels + :param y_true: groundtruth labels + :return: + Sum-of-Squares loss + """ + + raise NotImplementedError + + +def percentage_pt(y_pred, y_true): + """ + Computes percentage of correct predictions + :param y_pred: + :param y_true: + :return: + """ + y_pred_soft = y_pred.exp() / (y_pred.exp().sum(-1)).unsqueeze(-1) + + perc = (y_pred_soft.max(dim=1)[1] == y_true.max(dim=1)[1]).sum() + return perc + diff --git a/utils/utils.py b/utils/utils.py index 7f4317b..f855460 100644 --- a/utils/utils.py +++ b/utils/utils.py @@ -15,11 +15,12 @@ # --- imports ----------------------------------------------------------------- import os -import cv2 import time -import cython -from scipy.misc import imresize -# from skimage.transform import resize +import cv2 +from PIL import Image +import pycuda.driver as cuda +import pycuda.autoinit +from pycuda.compiler import SourceModule import numpy as np from numba import jit, vectorize, cuda @@ -42,34 +43,53 @@ def one_hot_encode(labels): return labels -@jit +def one_to_onehot(label, max_label): + y = [0 for i in range(max_label)] + y[label] = 1 + return y + + +# @jit(parallel=True) def resize_images_gpu(x, img_size): - # return cv2.resize(x, (img_size[1], img_size[0]), interpolation=cv2.INTER_LINEAR) - if x.shape == img_size: - return x - else: - return imresize(x, (img_size[0], img_size[1]), interp='nearest') -@jit + return cv2.resize(x, (img_size[1], img_size[0]), interpolation=cv2.INTER_LINEAR) + # if x.size == img_size[0:2]: + # return x + # else: + # return np.rollaxis(np.array(x.resize(img_size[0:2], Image.ANTIALIAS)), 1, 0) + + # return x + +# @jit def expand_dims(x, img_size): # return np.expand_dims(np.expand_dims(resize_images_gpu(x, img_size), axis=0), axis=-1) return np.expand_dims(np.expand_dims(x, axis=0), axis=-1) -@jit +# @jit def resize_images_cpu(x, img_size): return cv2.resize(x, (img_size[1], img_size[0]), interpolation=cv2.INTER_LINEAR) +# @jit(parallel=True) +def preprocess_image(img_size, im_file): -@cuda.jit -def preprocess_image(im_file, img_size): - if img_size[2] == 3: - img = cv2.imread(im_file, cv2.IMREAD_COLOR) - else: - img = cv2.imread(im_file, cv2.IMREAD_GRAYSCALE) - image = resize_images_gpu(img, img_size) + img = cv2.imread(im_file, 0) + image = cv2.resize(img, (img_size[1], img_size[0]), interpolation=cv2.INTER_LINEAR) + # img = Image.open(im_file) + # img = np.rollaxis(Image.open(im_file), 1, 0) + # image = resize_images_gpu(img_size,img) return image + +# @jit(parallel=True) +def bulk_process(img_size, im): + # img_out = [] + # for im in im_list: + # img_out.append(np.expand_dims(preprocess_image(im, img_size), axis=-1)) + # return img_out + return preprocess_image(img_size, im) + + def path_walk(path): path_list = [] for dirpath, _, filenames in os.walk(path): @@ -103,9 +123,21 @@ def log_loss_accuracy(accuracy, accuracy_type, task_type, num_classes, multi_tas return acc_str -# def create_ckpt_data(file_path): -# if not os.path.exists(file_path): -# # create directory -# os.makedirs(file_path) -# -# return "{}/model.ckpt".format(file_path) \ No newline at end of file +# @jit(parallel=True) +def count_size(file): + return os.stat(file).st_size + + +@jit +def chunk_split(data_list, splits): + avg = len(data_list) / float(splits) + out = [] + last = 0.0 + + while last < len(data_list): + out.append(data_list[int(last):int(last + avg)]) + last += avg + + return out + +