diff --git a/photonai/base/PhotonBase.py b/photonai/base/PhotonBase.py index 4f2dc863..0b770360 100644 --- a/photonai/base/PhotonBase.py +++ b/photonai/base/PhotonBase.py @@ -34,6 +34,11 @@ from .PhotonPipeline import PhotonPipeline +class PhotonNative: + """only for checking if code is meeting requirements""" + pass + + class OutputSettings: """ Configuration class that specifies the format in which the results are saved. Results can be saved to a MongoDB @@ -93,11 +98,11 @@ def __init__(self, mongodb_connect_url: str = None, self.save_output = save_output if self.save_output: - local_file: str = 'photon_result_file.p' - log_filename: str = 'photon_output.log' - summary_filename: str = 'photon_summary.txt' - pretrained_model_filename: str = 'photon_best_model.photon' - predictions_filename: str = 'outer_fold_predictions.csv' + local_file = 'photon_result_file.p' + log_filename = 'photon_output.log' + summary_filename = 'photon_summary.txt' + pretrained_model_filename = 'photon_best_model.photon' + predictions_filename = 'outer_fold_predictions.csv' self.local_file = os.path.join(project_folder, local_file) self.log_file = os.path.join(project_folder, log_filename) self.summary_filename = os.path.join(project_folder, summary_filename) @@ -428,7 +433,7 @@ def __iadd__(self, pipe_element): if isinstance(pipe_element, PreprocessingPipe): self.preprocessing_pipe = pipe_element else: - if isinstance(pipe_element, PipelineElement): + if isinstance(pipe_element, PipelineElement) or issubclass(type(pipe_element), PhotonNative): self.pipeline_elements.append(pipe_element) # Todo: is repeated each time element is added.... self._prepare_pipeline() @@ -749,8 +754,14 @@ def fit(self, data, targets, **kwargs): # do the optimizing for current_config in self.optimizer.ask: + + if hasattr(self.optimizer, 'ask_for_pipe'): + pipe_ctor = self.optimizer.ask_for_pipe() + else: + pipe_ctor = self._copy_pipeline + self.__distribute_cv_info_to_hyperpipe_children(reset=True, config_counter=tested_config_counter) - hp = TestPipeline(self._copy_pipeline, current_config, self.metrics, self.update_mother_inner_fold_nr, + hp = TestPipeline(pipe_ctor, current_config, self.metrics, self.update_mother_inner_fold_nr, mongo_db_settings=self.output_settings, callback_function=self.inner_cv_callback_function) Logger().debug('optimizing of:' + self.name) @@ -1236,17 +1247,17 @@ def load_optimum_pipe(file, password=None): def run_dummy_estimator(self): if hasattr(self.pipeline_elements[-1].base_element, '_estimator_type'): - type = self.pipeline_elements[-1].base_element._estimator_type + est_type = self.pipeline_elements[-1].base_element._estimator_type else: if isinstance(self.pipeline_elements[-1], PipelineSwitch): - type = self.pipeline_elements[-1].base_element.base_element._estimator_type + est_type = self.pipeline_elements[-1].base_element.base_element._estimator_type else: - type = None + est_type = None - if type == 'regressor': + if est_type == 'regressor': strategy = 'mean' dummy = DummyRegressor(strategy=strategy) - elif type == 'classifier': + elif est_type == 'classifier': strategy = 'most_frequent' dummy = DummyClassifier(strategy=strategy) else: @@ -1263,6 +1274,13 @@ def run_dummy_estimator(self): for train, test in self.data_test_cases: train_X, train_y = self.X[train], self.y[train] + + if isinstance(train_X, np.ndarray): + if len(train_X.shape) > 2: + Logger().info("Skipping dummy estimator because of too much dimensions") + break + + # dummy.fit(train_X, train_y) dummy.fit(train_X, train_y) train_scores = TestPipeline.score(dummy, train_X, train_y, metrics=self.metrics) @@ -1276,12 +1294,14 @@ def run_dummy_estimator(self): inner_fold.validation = test_scores fold_list.append(inner_fold) - config_item.inner_folds = fold_list - config_item.metrics_train, config_item.metrics_test = MDBHelper.aggregate_metrics(config_item, self.metrics) + dummy_results = DummyResults() - dummy_results.strategy = strategy - dummy_results.train = config_item.metrics_train - dummy_results.test = config_item.metrics_test + if len(fold_list) > 0: + config_item.inner_folds = fold_list + config_item.metrics_train, config_item.metrics_test = MDBHelper.aggregate_metrics(config_item, self.metrics) + dummy_results.strategy = strategy + dummy_results.train = config_item.metrics_train + dummy_results.test = config_item.metrics_test return dummy_results @@ -1437,6 +1457,8 @@ def __init__(self, name, hyperparameters: dict=None, test_disabled: bool=False, self.is_transformer = hasattr(self.base_element, "transform") self.is_estimator = hasattr(self.base_element, "predict") + self.kwargs = kwargs + # Todo: check if hyperparameters are members of the class # Todo: write method that returns any hyperparameter that could be optimized --> sklearn: get_params.keys # Todo: map any hyperparameter to a possible default list of values to try @@ -1465,7 +1487,12 @@ def __init__(self, name, hyperparameters: dict=None, test_disabled: bool=False, self.needs_covariates = False def copy_me(self): - return deepcopy(self) + if hasattr(self.base_element, 'copy_me'): + # new_base_element = self.base_element.copy_me() + # TODO !!!!!!! + return PipelineElement(self.name, self.hyperparameters, **self.kwargs) + else: + return deepcopy(self) @classmethod def create(cls, name, base_element, hyperparameters: dict, test_disabled=False, disabled=False, **kwargs): @@ -1606,6 +1633,8 @@ def transform(self, X, y=None, **kwargs): if hasattr(self.base_element, 'transform'): return self.adjusted_delegate_call(self.base_element.transform, X, y, **kwargs) elif hasattr(self.base_element, 'predict', **kwargs): + # Logger().warn("used prediction instead of transform " + self.name) + # raise Warning() return self.base_element.predict(X) else: Logger().error('BaseException: transform-predict-mess') @@ -1835,7 +1864,7 @@ def __init__(self, name: str, stacking_elements=None, voting: bool=False): self.__iadd__(item_to_stack) # in case any of the children needs y or the covariates, we have to request them - self.needs_y = True + self.needs_y = False self.needs_covariates = True def __iadd__(self, item): @@ -1916,7 +1945,7 @@ def fit(self, data, targets=None, **kwargs): """ for name, element in self.pipe_elements.items(): # Todo: parallellize fitting - element.fit(data, targets) + element.fit(data, targets, **kwargs) return self def predict(self, data, targets=None, **kwargs): @@ -1981,7 +2010,7 @@ def stack_data(cls, a, b): New matrix, that is a and b horizontally joined """ - if a.size == 0: + if a is None or (isinstance(a, np.ndarray) and a.size == 0): a = b else: # Todo: check for right dimensions! diff --git a/photonai/base/PhotonBatchElement.py b/photonai/base/PhotonBatchElement.py new file mode 100644 index 00000000..fb417bd4 --- /dev/null +++ b/photonai/base/PhotonBatchElement.py @@ -0,0 +1,99 @@ +from .PhotonBase import PipelineElement +from ..photonlogger import Logger +import numpy as np + + +class PhotonBatchElement(PipelineElement): + + def __init__(self, name, hyperparameters: dict=None, test_disabled: bool=False, disabled: bool =False, + base_element=None, batch_size: int = 10, **kwargs): + + super(PhotonBatchElement, self).__init__(name, hyperparameters, test_disabled, disabled, base_element, **kwargs) + # self.base_element = PipelineElement(base_element_name, hyperparameters=hyperparameters, **kwargs) + + self.batch_size = batch_size + + @staticmethod + def chunker(nr_items, size): + return [(pos, pos + size) for pos in range(0, nr_items, size)] + + def batch_call(self, delegate, X, y=None, call_with_y=True, **kwargs): + + # initialize return values + processed_X = None + processed_y = None + processed_kwargs = dict() + + # iterate through data batchwise + if isinstance(X, np.ndarray): + nr = X.shape[0] + dim = len(X.shape) + else: + nr = len(X) + dim = 1 + + batch_idx = 0 + for start, stop in PhotonBatchElement.chunker(nr, self.batch_size): + + batch_idx += 1 + Logger().debug(self.name + " is processing batch nr " + str(batch_idx)) + + # split data in batches + if dim > 1: + X_batched = X[start:stop, :] + else: + X_batched = X[start:stop] + + # we are probably None anyway + y_batched = y + # if we are to batch then apply it + if call_with_y and y is not None: + y_batched = y[start:stop] + + kwargs_dict_batched = dict() + for key, kwargs_list in kwargs.items(): + if not isinstance(kwargs_list, np.ndarray): + kwargs_list = np.array(kwargs_list) + if len(kwargs_list.shape) > 1: + kwargs_dict_batched[key] = kwargs_list[start:stop, :] + else: + kwargs_dict_batched[key] = kwargs_list[start:stop] + + # call the delegate + X_new, y_new, kwargs_new = self.adjusted_delegate_call(delegate, X_batched, y_batched, **kwargs_dict_batched) + + # stack results + processed_X = PhotonBatchElement.stack_results(X_new, processed_X) + + if call_with_y: + processed_y = PhotonBatchElement.stack_results(y_new, processed_y) + for proc_key, proc_values in kwargs_new.items(): + new_kwargs_data = kwargs_new[proc_key] + if proc_key not in processed_kwargs: + processed_kwargs[proc_key] = new_kwargs_data + else: + processed_kwargs[proc_key] = PhotonBatchElement.stack_results(new_kwargs_data, processed_kwargs[proc_key]) + else: + processed_kwargs = kwargs + processed_y = y + return processed_X, processed_y, processed_kwargs + + @staticmethod + def stack_results(new_a, existing_a): + if existing_a is not None: + if isinstance(new_a, list) or (isinstance(new_a, np.ndarray) and len(new_a.shape) < 2): + if isinstance(existing_a, list): + existing_a = existing_a + new_a + else: + existing_a = np.hstack((existing_a, new_a)) + else: + existing_a = np.vstack((existing_a, new_a)) + else: + existing_a = new_a + return existing_a + + def transform(self, X, y=None, **kwargs): + return self.batch_call(self.base_element.transform, X, y, **kwargs) + + def predict(self, X, y=None, **kwargs): + return self.batch_call(self.base_element.predict, X, y, call_with_y=False, **kwargs) diff --git a/photonai/configuration/PhotonNeuro.json b/photonai/configuration/PhotonNeuro.json index ce5ae0be..173a9cb0 100644 --- a/photonai/configuration/PhotonNeuro.json +++ b/photonai/configuration/PhotonNeuro.json @@ -1,14 +1,18 @@ { - "ResampleImgs":[ - "photonai.neuro.ImageBasics.ResamplingImgs", + "ResampleImages":[ + "photonai.neuro.ImageBasics.ResampleImages", "Transformer" ], - "SmoothImgs":[ - "photonai.neuro.ImageBasics.SmoothImgs", + "SmoothImages":[ + "photonai.neuro.ImageBasics.SmoothImages", "Transformer" ], "BrainAtlas":[ "photonai.neuro.BrainAtlas.BrainAtlas", "Transformer" + ], + "PatchImages":[ + "photonai.neuro.ImageBasics.PatchImages", + "Transformer" ] -} \ No newline at end of file +} diff --git a/photonai/examples/Brain_Age_Master.py b/photonai/examples/Brain_Age_Master.py new file mode 100644 index 00000000..5108aa8e --- /dev/null +++ b/photonai/examples/Brain_Age_Master.py @@ -0,0 +1,101 @@ +import numpy as np +import pandas as pd +import tensorflow as tf +from sklearn.model_selection import KFold +#from skopt import Optimizer +#from skopt.optimizer import dummy_minimize +#from skopt import dummy_minimize +import scipy.io as sio +import keras +from photonai.base.PhotonBase import Hyperpipe, PipelineElement, PhotonRegister +from photonai.base.PhotonBatchElement import PhotonBatchElement +from photonai.validation import ResultsTreeHandler +from photonai.neuro.BrainAtlas import AtlasLibrary +from scipy.stats import itemfreq +from photonai.investigator.Investigator import Investigator +import matplotlib.pyplot as plt +import pandas as pd +from nilearn import image +import time + + +import os +os.environ["CUDA_DEVICE_ORDER"]="PCI_BUS_ID" +os.environ["CUDA_VISIBLE_DEVICES"]="2" + + +# RandomCtrlData = np.ones((1792, 121, 145, 121)) +# RandomCtrlData = np.ones((172, 121, 145, 121)) +# RandomCtrlLabels = np.random.randn((RandomCtrlData.shape[0])) + +root_folder = '/spm-data/Scratch/spielwiese_ramona/PAC2018/' +filename = 'PAC2018_age.csv' +df = pd.read_csv(os.path.join(root_folder, filename)) + +X = df["PAC_ID"] +X = [os.path.join(root_folder, 'data_all/' + x + ".nii") for x in X] +y = df["Age"].values + +X = X[0:1500] +y = y[0:1500] + +# +PhotonRegister.save(photon_name='Brain_Age_Splitting_Wrapper', + class_str='photonai.modelwrapper.Brain_Age_Splitting_Wrapper.Brain_Age_Splitting_Wrapper', element_type="Transformer") +# +# PhotonRegister.save(photon_name='Brain_Age_Splitting_CNN', +# class_str='photonai.modelwrapper.Brain_Age_Splitting_CNN.Brain_Age_Splitting_CNN', element_type="Estimator") +# +PhotonRegister.save(photon_name='Brain_Age_Random_Forest', + class_str='photonai.modelwrapper.Brain_Age_Random_Forest.Brain_Age_Random_Forest', element_type="Estimator") + +my_pipe = Hyperpipe('BrainAgePipe', + optimizer='grid_search', + metrics=['mean_absolute_error'], + best_config_metric='mean_absolute_error', + inner_cv=KFold(n_splits=5, shuffle=True, random_state=42), + outer_cv=KFold(n_splits=5, shuffle=True, random_state=42), + eval_final_performance=False, + verbosity=2) + +# transformer = PipelineElement(, hyperparameters={}) +# base_element=transformer +batched_transformer = PhotonBatchElement("PatchImages", hyperparameters={'patch_size': [10, 25, 50, 75, 100]}, + batch_size=100, + nr_of_processes=10, + cache_folder='/spm-data/vault-data1/tmp/photon_cache_vincent/') +my_pipe += batched_transformer + + +#my_pipe += PipelineElement('Brain_Age_Splitting_Wrapper') + +my_pipe += PipelineElement('Brain_Age_Random_Forest') + +my_pipe.fit(X, y) + +batched_transformer.base_element.clear_cache() + + + + + + + + + +inner_performances = list() +for i, fold in enumerate(my_pipe.result_tree.outer_folds[0].tested_config_list): + inner_performances.append((fold.config_dict, fold.metrics_test[0].value)) +print(inner_performances) + +plt.ylim(0.2, 0.8) +plt.xticks(rotation=90) +plt.margins(0.3) + +for i, lelles in inner_performances: + print(i, lelles) + Benis = ",".join(("{}={}".format(*p) for p in i.items())) + plt.plot(Benis, lelles, 'ro') + + +plt.show() diff --git a/photonai/examples/Investigator.py b/photonai/examples/Investigator.py new file mode 100644 index 00000000..037ab078 --- /dev/null +++ b/photonai/examples/Investigator.py @@ -0,0 +1,13 @@ + +from photonai.investigator.Investigator import Investigator + +# from mongodb +Investigator.load_from_db('mongodb://server:27017/photon_results', 'my_pipe') + +# from working memory +my_pipe = Hyperpipe(...) +my_pipe.fit(X, y) +Investigator.show(my_pipe) + +# from file +Investigator.load_from_file('my_pipe_name', '/home/usr123/proj45/my_pipe_results2019-08-01/photon_result_file.p') diff --git a/photonai/examples/basic_pipeline.py b/photonai/examples/basic_pipeline.py index 185434b5..fb920aa5 100644 --- a/photonai/examples/basic_pipeline.py +++ b/photonai/examples/basic_pipeline.py @@ -12,14 +12,15 @@ X, y = load_breast_cancer(True) # YOU CAN SAVE THE TRAINING AND TEST RESULTS AND ALL THE PERFORMANCES IN THE MONGODB -mongo_settings = OutputSettings(mongodb_connect_url="mongodb://trap-umbriel:27017/photon_results", save_predictions='best') +mongo_settings = OutputSettings(mongodb_connect_url="mongodb://trap-umbriel:27017/photon_results", + save_predictions='best') # save_options = OutputSettings(local_file="/home/photon_user/photon_test/test_item.p") # DESIGN YOUR PIPELINE -my_pipe = Hyperpipe('basic_svm_pipe_no_performance', # the name of your pipeline +my_pipe = Hyperpipe('basic_svm_pipe', # the name of your pipeline optimizer='grid_search', # which optimizer PHOTON shall use metrics=['accuracy', 'precision', 'recall', 'balanced_accuracy'], # the performance metrics of your interest best_config_metric='accuracy', # after hyperparameter search, the metric declares the winner config @@ -27,7 +28,6 @@ inner_cv=KFold(n_splits=5), # test each configuration ten times respectively, verbosity=1, output_settings=mongo_settings) # get error, warn and info message - #imbalanced_data_strategy_filter='RandomUnderSampler' # skips next folds of inner cv if accuracy and precision in first fold are below 0.96. # performance_constraints = [MinimumPerformance('accuracy', 0.96), # MinimumPerformance('precision', 0.96)] @@ -47,7 +47,7 @@ # ADD ELEMENTS TO YOUR PIPELINE # first normalize all features -my_pipe += PipelineElement('StandardScaler') +my_pipe.add(PipelineElement('StandardScaler')) # then do feature selection using a PCA, specify which values to try in the hyperparameter search my_pipe += PipelineElement('PCA', hyperparameters={'n_components': IntegerRange(5, 20)}, test_disabled=True) # engage and optimize the good old SVM for Classification @@ -60,7 +60,7 @@ start_time = time.time() # NOW TRAIN YOUR PIPELINE -# my_pipe.fit(X, y) +my_pipe.fit(X, y) elapsed_time = time.time() - start_time print(time.strftime("%H:%M:%S", time.gmtime(elapsed_time))) # AND SHOW THE RESULTS IN THE WEBBASED PHOTON INVESTIGATOR TOOL diff --git a/photonai/examples/custom_estimator.py b/photonai/examples/custom_estimator.py new file mode 100644 index 00000000..f5379157 --- /dev/null +++ b/photonai/examples/custom_estimator.py @@ -0,0 +1,29 @@ +from sklearn.base import BaseEstimator + + +class CustomEstimator(BaseEstimator): + + def __init__(self, param1=0, param2=None): + # it is important that you name your params the same in the constructor + # stub as well as in your class variables! + self.param1 = param1 + self.param2 = param2 + + def fit(self, data, targets=None, **kwargs): + """ + Adjust the underlying model or method to the data. + + Returns + ------- + IMPORTANT: must return self! + """ + return self + + def predict(self, data): + """ + Use the learned model to make predictions. + """ + my_predictions = [] + return my_predictions + + diff --git a/photonai/examples/custom_transformer.py b/photonai/examples/custom_transformer.py new file mode 100644 index 00000000..a527fde1 --- /dev/null +++ b/photonai/examples/custom_transformer.py @@ -0,0 +1,28 @@ +from sklearn.base import BaseEstimator + + +class CustomTransformer(BaseEstimator): + + def __init__(self, param1=0, param2=None): + # it is important that you name your params the same in the constructor + # stub as well as in your class variables! + self.param1 = param1 + self.param2 = param2 + + def fit(self, data, targets=None, **kwargs): + """ + Adjust the underlying model or method to the data. + + Returns + ------- + IMPORTANT: must return self! + """ + return self + + def transform(self, data, targets=None, **kwargs): + """ + Apply the method's logic to the data. + """ + transformed_data = [] + return transformed_data + diff --git a/photonai/examples/downsample_mnc.py b/photonai/examples/downsample_mnc.py new file mode 100644 index 00000000..066a6a2b --- /dev/null +++ b/photonai/examples/downsample_mnc.py @@ -0,0 +1,88 @@ +from photonai.neuro.ImageBasics import ResampleImages, SmoothImages, PatchImages +import numpy as np +import pickle +import time + +file = ["/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0001.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0009.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0020.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0070.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0073.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0071.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0072.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0074.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0075.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0076.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0077.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0078.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0080.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0081.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0082.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0083.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0084.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0085.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0086.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0087.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0001.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0009.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0020.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0070.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0073.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0071.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0072.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0074.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0075.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0076.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0077.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0078.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0080.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0081.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0082.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0083.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0084.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0085.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0086.nii", + "/spm-data/Scratch/spielwiese_ramona/PAC2018/data/PAC2018_0087.nii" + ] + +start_time = time.time() +# t = ResampleImages(voxel_size=[1, 1, 1], nr_of_processes=10) +t = PatchImages(nr_of_processes=10) +resampled_images = t.transform(file) +elapsed_time = time.time() - start_time +print(time.strftime("%H:%M:%S", time.gmtime(elapsed_time))) + + +start_time = time.time() +# t = ResampleImages(voxel_size=[1, 1, 1], nr_of_processes=1) +t = PatchImages(nr_of_processes=1) +resampled_images = t.transform(file) +elapsed_time = time.time() - start_time +print(time.strftime("%H:%M:%S", time.gmtime(elapsed_time))) + + +# patched_image = PatchImages.draw_patches(resampled_images[0].dataobj, 25) + +# patcher = PatchImages() +# patched_images = patcher.transform(resampled_images, nr_of_processes = 10) + +debug = True +# +# # # pickle.dump(resampled_images, open('resampled_imgs.p', 'wb')) +# # +# # resampled_images = pickle.load(open('resampled_imgs.p', 'rb')) +# t2 = SmoothImages(fwhm=[2, 2, 2], nr_of_processes=3) +# print("Now should smooth images") +# smoothed_images = t2.transform(resampled_images) +# print("Images smoothed") +# +# print(len(smoothed_images)) +# t3 = ResampleImages(voxel_size=[3, 5, 10], nr_of_processes=5) +# res_smoothed_images = t3.transform(smoothed_images) + + +# downsampled_file = resample_img(file[0], target_affine=np.diag([1, 1, 1]), interpolation='nearest') +# downsampled_file.to_filename('/spm-data/Scratch/spielwiese_ramona/PAC2019/raw_data/N3107/N3107_downsampled.nii') +debug = True + + diff --git a/photonai/examples/neuro_image_transformation.py b/photonai/examples/neuro_image_transformation.py new file mode 100644 index 00000000..cef6b677 --- /dev/null +++ b/photonai/examples/neuro_image_transformation.py @@ -0,0 +1,30 @@ +from photonai.base.PhotonBase import Hyperpipe, PipelineElement +from photonai.base.PhotonBatchElement import PhotonBatchElement +from sklearn.model_selection import KFold +import glob +import numpy as np + + +my_pipe = Hyperpipe('BatchAndSmooth', # the name of your pipeline + optimizer='grid_search', # which optimizer PHOTON shall use + metrics=['mean_absolute_error'], # the performance metrics of your interest + best_config_metric='mean_absolute_error', # after hyperparameter search, the metric declares the winner config + outer_cv=KFold(n_splits=2), # repeat hyperparameter search three times + inner_cv=KFold(n_splits=2), # test each configuration ten times respectively, + verbosity=2) + + +root_folder = "/spm-data/Scratch/spielwiese_ramona/test_photon_neuro/*.nii" +file_list = glob.glob(root_folder) +y = np.random.randint(145, size=len(file_list)).astype(np.float) + +my_pipe += PhotonBatchElement("SmoothImages", hyperparameters={'fwhm': [[2, 2, 2], [3, 3, 3], [4, 4, 4]]}, + batch_size=2) +my_pipe += PhotonBatchElement("ResampleImages", hyperparameters={'voxel_size': [[3, 3, 3], [2, 2, 2], [5, 5, 5]]}, + batch_size=2, output_img=False) + +my_pipe += PipelineElement('SVR') + +my_pipe.fit(file_list[:10], y[:10]) + +debug = True diff --git a/photonai/examples/stacking_example.py b/photonai/examples/stacking_example.py index a3be3ccb..2a01ea58 100644 --- a/photonai/examples/stacking_example.py +++ b/photonai/examples/stacking_example.py @@ -1,7 +1,5 @@ -from photonai.base.PhotonBase import Hyperpipe, PipelineElement, PipelineStacking, PipelineBranch, OutputSettings +from photonai.base.PhotonBase import Hyperpipe, PipelineElement, PipelineStacking, from photonai.optimization.Hyperparameters import FloatRange, IntegerRange, Categorical -from photonai.investigator.Investigator import Investigator -from photonai.configuration.Register import PhotonRegister from sklearn.model_selection import KFold from sklearn.datasets import load_breast_cancer @@ -9,34 +7,22 @@ my_pipe = Hyperpipe('basic_stacking', - optimizer='grid_search', + optimizer='sk_opt', metrics=['accuracy', 'precision', 'recall'], best_config_metric='accuracy', outer_cv=KFold(n_splits=3), inner_cv=KFold(n_splits=10), verbosity=1) -tree_branch = PipelineBranch('first_branch') -tree_branch += PipelineElement('StandardScaler') -tree_branch += PipelineElement('DecisionTreeClassifier', {'criterion': ['gini'], - 'min_samples_split': IntegerRange(2, 4)}) - -svm_branch = PipelineBranch('svm_branch') -svm_branch += PipelineElement('StandardScaler') -svm_branch += PipelineElement('SVC', {'kernel': Categorical(['rbf', 'linear']), - 'C': FloatRange(0.5, 2, "linspace", num=3)}) - - +my_pipe += PipelineElement('StandardScaler') my_pipe_stack = PipelineStacking('final_stack', voting=False) -my_pipe_stack += svm_branch -my_pipe_stack += tree_branch +my_pipe_stack += PipelineElement('DecisionTreeClassifier', hyperparameters={'criterion': ['gini'], + 'min_samples_split': IntegerRange(2, 4)}) +my_pipe_stack += PipelineElement('LinearSVC', hyperparameters={'C': FloatRange(0.5, 25)}) my_pipe += my_pipe_stack my_pipe += PipelineElement('SVC', {'kernel': Categorical(['rbf', 'linear'])}) - my_pipe.fit(X, y) -# Investigator.load_from_file("basic_svm_pipe", 'my_tree.p') - debug = True diff --git a/photonai/examples/subpipelines.py b/photonai/examples/subpipelines.py new file mode 100644 index 00000000..ff7fdec0 --- /dev/null +++ b/photonai/examples/subpipelines.py @@ -0,0 +1,45 @@ +from photonai.base.PhotonBase import Hyperpipe, PipelineElement, PipelineStacking, PipelineBranch +from photonai.optimization.Hyperparameters import FloatRange, IntegerRange, Categorical +from photonai.investigator.Investigator import Investigator +from photonai.configuration.Register import PhotonRegister +from sklearn.model_selection import KFold +from sklearn.datasets import load_breast_cancer + +X, y = load_breast_cancer(True) + + +my_pipe = Hyperpipe('basic_stacking', + optimizer='grid_search', + metrics=['accuracy', 'precision', 'recall'], + best_config_metric='f1_score', + outer_cv=KFold(n_splits=3), + inner_cv=KFold(n_splits=10), + verbosity=1) + +# BRANCH WITH QUANTILTRANSFORMER AND DECISIONTREECLASSIFIER +tree_qua_branch = PipelineBranch('tree_branch') +tree_qua_branch += PipelineElement('QuantileTransformer') +tree_qua_branch += PipelineElement('DecisionTreeClassifier',{'min_samples_split': IntegerRange(2, 4)},criterion='gini') + +# BRANCH WITH MinMaxScaler AND DecisionTreeClassifier +svm_mima_branch = PipelineBranch('svm_branch') +svm_mima_branch += PipelineElement('MinMaxScaler') +svm_mima_branch += PipelineElement('SVC', {'kernel': Categorical(['rbf', 'linear']), + 'C':2.0},gamma='auto') + +# BRANCH WITH StandardScaler AND KNeighborsClassifier +knn_sta_branch = PipelineBranch('neighbour_branch') +knn_sta_branch += PipelineElement('StandardScaler') +knn_sta_branch += PipelineElement('KNeighborsClassifier') + +# voting = True to mean the result of every branch +my_pipe_stack = PipelineStacking('final_stack', voting=True) +my_pipe_stack += tree_qua_branch +my_pipe_stack += svm_mima_branch +my_pipe_stack += knn_sta_branch + +my_pipe += my_pipe_stack + +my_pipe += PipelineElement('LogisticRegression', solver='lbfgs') + +my_pipe.fit(X, y) diff --git a/photonai/modelwrapper/Biclustering2d.py b/photonai/modelwrapper/Biclustering2d.py index 1e359771..ea6b9de9 100644 --- a/photonai/modelwrapper/Biclustering2d.py +++ b/photonai/modelwrapper/Biclustering2d.py @@ -2,12 +2,13 @@ from sklearn.cluster import SpectralBiclustering import numpy as np import os +from matplotlib import pyplot as plt #from photonai.photonlogger.Logger import Logger class Biclustering2d(BaseEstimator, TransformerMixin): _estimator_type = "transformer" - def __init__(self, n_clusters, random_state=42, scale='bistochastic', n_components=6, + def __init__(self, n_clusters=4, random_state=42, scale='bistochastic', n_components=6, n_best=3, logs=''): self.n_clusters = n_clusters self.random_state = random_state @@ -18,25 +19,44 @@ def __init__(self, n_clusters, random_state=42, scale='bistochastic', n_componen self.logs = logs else: self.logs = os.getcwd() - self.biclustModel = SpectralBiclustering(n_clusters=self.n_clusters, random_state=self.random_state, - method=self.scale, n_components=self.n_components, - n_best=self.n_best) - def fit(self, X, y=None): + def fit(self, X, y): # Biclustering of the mean 2d image of all samples - X_mean = np.squeeze(np.mean(X, axis=0)) + HC = X[y==0,:] + print(HC.shape) + X_mean = np.squeeze(np.mean(HC, axis=0)) + #X_mean = np.squeeze(np.mean(X, axis=0)) + self.biclustModel = self.create_model() self.biclustModel.fit(X_mean) + + # Plotting the clustered matrix + fit_data = X_mean[np.argsort(self.biclustModel.row_labels_)] + fit_data = fit_data[:, np.argsort(self.biclustModel.column_labels_)] + plt.matshow(fit_data) + plt.title(self.n_clusters) + plt.show() + return self def transform(self, X): X_reordered = np.empty(X.shape) - for i in range(len(X[0])): + for i in range(X.shape[0]): x = np.squeeze(X[i,:,:]) x_clust = x[np.argsort(self.biclustModel.row_labels_)] x_clust = x_clust[:, np.argsort(self.biclustModel.column_labels_)] X_reordered[i, :, :] = x_clust return X_reordered + def create_model(self): + + biclustModel = SpectralBiclustering(n_clusters=self.n_clusters, random_state=self.random_state, + method=self.scale, n_components=self.n_components, + n_best=self.n_best) + + + + return biclustModel + # ToDo: add these functions again # def set_params(self, **params): # if 'n_components' in params: @@ -63,4 +83,3 @@ def transform(self, X): # X_new = bcm.transform(X) # plt.matshow(np.squeeze(X_new[0]), cmap=plt.cm.Blues) # plt.show() - diff --git a/photonai/modelwrapper/BrainAgeNeuralNet.py b/photonai/modelwrapper/BrainAgeNeuralNet.py new file mode 100644 index 00000000..ada9ace7 --- /dev/null +++ b/photonai/modelwrapper/BrainAgeNeuralNet.py @@ -0,0 +1,42 @@ +import numpy as np +from sklearn.base import BaseEstimator, RegressorMixin +from sklearn.utils import shuffle +from photonai.modelwrapper.KerasDNNRegressor import KerasDNNRegressor + + +class BrainAgeNeuralNet(BaseEstimator, RegressorMixin): + + def __init__(self, **kwargs): + self.estimator = KerasDNNRegressor(**kwargs) + + def fit(self, X, y): + + y = np.repeat(y, X.shape[1]) + # make patches per person a training case + print(X.shape) + X = np.reshape(X, (-1, X.shape[2], X.shape[3])) + # flatten training cases for reshape + X = np.reshape(X, (X.shape[0]), -1) + # shuffle the the data so there won't be long strands of same-aged people + X, y = shuffle(X, y, random_state=self.random_state) + + # 1. make model + self.estimator.fit(X, y) + return self + + def predict(self, X): + if not isinstance(X, np.ndarray): + print("Loading data") + X = np.asarray(X) + + X_to_predict = X.reshape(X.shape[0], X.shape[1], -1) + predict_result = [] + for i in range(X.shape[0]): + predict_interim_result = np.squeeze(self.estimator.predict(X_to_predict[i, :, :], batch_size=self.batch_size)) + # predict_interim_result = self.photon_rfr.predict(X_to_predict[i, :, :]) + predict_result_to_append = np.mean(predict_interim_result) + predict_result.append(predict_result_to_append) + return predict_result + + + diff --git a/photonai/modelwrapper/Brain_Age_Random_Forest.py b/photonai/modelwrapper/Brain_Age_Random_Forest.py new file mode 100644 index 00000000..e0aff003 --- /dev/null +++ b/photonai/modelwrapper/Brain_Age_Random_Forest.py @@ -0,0 +1,77 @@ +import numpy as np +import tensorflow as tf +from sklearn.base import BaseEstimator, ClassifierMixin +from sklearn.model_selection import ShuffleSplit +from sklearn.ensemble import RandomForestRegressor +from sklearn.svm import LinearSVR +from sklearn.utils import shuffle + +class Brain_Age_Random_Forest(BaseEstimator, ClassifierMixin): + # todo: BUGFIX --> pooling doesnt work + def __init__(self, target_dimension=2, + loss='mse', metrics=['accuracy'], + gpu_device='/gpu:0', random_state = 42, + early_stopping_flag=True, eaSt_patience=20, + reLe_factor=0.4, reLe_patience=5): + + self.target_dimension = target_dimension + self.loss = loss + self.metrics = metrics + self.random_state = random_state + self.gpu_device = gpu_device + self.early_stopping_flag = early_stopping_flag + self.eaSt_patience = eaSt_patience + self.reLe_factor = reLe_factor + self.reLe_patience = reLe_patience + + self.x = None + self.y_ = None + self.model = None + + def fit(self, X, y): + # Reshape X to add dimension for CNN (RGB channel) + if not isinstance(X, np.ndarray): + print("Loading data") + X = np.asarray(X) + + print("Starting Fitting") + y = np.repeat(y, X.shape[1]) + #make patches per person a training case + print(X.shape) + X = np.reshape(X, (-1, X.shape[2], X.shape[3])) + #flatten training cases for reshape + X = np.reshape(X, (X.shape[0], -1)) + #shuffle the the data so there won't be long strands of same-aged people + X, y = shuffle(X, y, random_state=42) + + #model is a random forest regressor + self.photon_rfr = LinearSVR() + # self.photon_rfr = RandomForestRegressor() + self.photon_rfr.fit(X, y) + print("Fitting done") + + return self + + def predict(self, X): + + if not isinstance(X, np.ndarray): + print("Loading data") + X = np.asarray(X) + + X_to_predict = X.reshape(X.shape[0], X.shape[1], -1) + predict_result = [] + for i in range(X.shape[0]): + predict_interim_result = self.photon_rfr.predict(X_to_predict[i, :, :]) + predict_result_to_append = np.mean(predict_interim_result) + predict_result.append(predict_result_to_append) + return predict_result + + + @staticmethod + def dense_to_one_hot(labels_dense, num_classes): + """Convert class labels from scalars to one-hot vectors.""" + num_labels = labels_dense.shape[0] + index_offset = np.arange(num_labels) * num_classes + labels_one_hot = np.zeros((num_labels, num_classes)) + labels_one_hot.flat[index_offset + labels_dense.ravel()] = 1 + return labels_one_hot diff --git a/photonai/modelwrapper/Brain_Age_Splitting_Wrapper.py b/photonai/modelwrapper/Brain_Age_Splitting_Wrapper.py new file mode 100644 index 00000000..6251b3bc --- /dev/null +++ b/photonai/modelwrapper/Brain_Age_Splitting_Wrapper.py @@ -0,0 +1,92 @@ +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.cluster import SpectralBiclustering +from skimage.util.shape import view_as_windows +import math +import numpy as np +import os +#from photonai.photonlogger.Logger import Logger + +class Brain_Age_Splitting_Wrapper(BaseEstimator, TransformerMixin): + _estimator_type = "transformer" + + def __init__(self, patch_size = 25, random_state=42, logs=''): + self.patch_size = patch_size + self.random_state = random_state + if logs: + self.logs = logs + else: + self.logs = os.getcwd() + + def fit(self, X, y=None): + pass + + def transform(self, X): + + BenisLänge = X.shape[1] - (self.patch_size - 1) + BenisBreite = X.shape[2]- (self.patch_size - 1) + + BenisLängsSchritte = BenisLänge / self.patch_size + BenisBreitenSchritte = BenisBreite / self.patch_size + + KleineBenisLängsSchritte = int(np.ceil(BenisLängsSchritte)) + KleineBenisBreitenSchritte = int(np.ceil(BenisBreitenSchritte)) + BenisSteppos = KleineBenisLängsSchritte * KleineBenisBreitenSchritte + + MegaBenis = BenisSteppos * X.shape[3] + Beniswertos = np.ones((MegaBenis, self.patch_size, self.patch_size, 1)) + print(Beniswertos.shape) + + for i in range(X.shape[0]): + # print(brain_scans[i,:,:,:].shape) + # make brain scans a new variable or try making the statement for the ith one + # x.reshape(x.shape[0] // 2, 2, x.shape[1] // 2, 2).swapaxes(1, 2).reshape(-1, 2, 2) + Benis = view_as_windows(X[i, :, :, :], (self.patch_size, self.patch_size, 1), step=1) + #print(Benis.shape) + + BenisLänge = Benis.shape[0] + BenisBreite = Benis.shape[1] + #BenisSchritte = BenisLänge / self.patch_size + + BenisMatrix = Benis[0:BenisLänge:self.patch_size, 0:BenisBreite:self.patch_size, :, :] + #print(BenisMatrix.shape) + + # TODO: Reshape First 3 Matrix Dimensions into 1, which will give 900 images + BenisMatrix = BenisMatrix.reshape((-1, BenisMatrix.shape[3], BenisMatrix.shape[4], BenisMatrix.shape[5])) + #print(BenisMatrix.shape) + + Beniswertos = np.append(Beniswertos, BenisMatrix, axis=3) + #print(Beniswertos.shape) + + #TODO: Drop first row + Beniswertos = np.delete(Beniswertos, 0, 3) + Beniswertos = np.moveaxis(Beniswertos, 3, 0) + print(Beniswertos.shape) + + return Beniswertos + + # ToDo: add these functions again + # def set_params(self, **params): + # if 'n_components' in params: + # self.n_clusters = params['n_components'] + # if 'logs' in params: + # self.logs = params.pop('logs', None) + # + # if not self.biclustModel: + # self.biclustModel = self.createBiclustering() + # self.biclustModel.set_params(**params) + # + # def get_params(self, deep=True): + # if not self.biclustModel: + # self.biclustModel = self.createBiclustering() + # biclust_dict = self.biclustModel.get_params(deep) + # biclust_dict['logs'] = self.logs + # return biclust_dict + +# if __name__ == "__main__": +# from matplotlib import pyplot as plt +# X = np.random.rand(100, 30, 30) +# bcm = Biclustering2d(n_clusters=3) +# bcm.fit(X) +# X_new = bcm.transform(X) +# plt.matshow(np.squeeze(X_new[0]), cmap=plt.cm.Blues) +# plt.show() diff --git a/photonai/modelwrapper/SamplePairing.py b/photonai/modelwrapper/SamplePairing.py index ea8abaca..602aff5d 100644 --- a/photonai/modelwrapper/SamplePairing.py +++ b/photonai/modelwrapper/SamplePairing.py @@ -157,6 +157,7 @@ def _get_samples(self, X, y, generator, distance_metric, draw_limit, rand_seed, else: return X_new, None, kwargs + class SamplePairingRegression(SamplePairingBase): _estimator_type = "transformer" @@ -185,7 +186,6 @@ def transform(self, X, y=None, **kwargs): return self._get_samples(X, y, self.generator, self.distance_metric, self.draw_limit, self.rand_seed, **kwargs) - class SamplePairingClassification(SamplePairingBase): _estimator_type = "transformer" @@ -227,17 +227,18 @@ def transform(self, X, y=None, **kwargs): X_new = list() y_new = list() + for t, limit in zip(np.unique(y), nDiff): X_new_class, y_new_class, kwargs = self._get_samples(X[y == t], y[y == t], - generator=self.generator, distance_metric=self.distance_metric, - draw_limit=limit, rand_seed=self.rand_seed, **kwargs) + generator=self.generator, distance_metric=self.distance_metric, + draw_limit=limit, rand_seed=self.rand_seed, **kwargs) X_new.append(X_new_class) y_new.append(y_new_class) + # get the corresponding kwargs if kwargs: for name, kwarg in kwargs.items(): kwargs_new[name] = np.concatenate((np.asarray(kwargs_new[name]), kwarg)) - X_new = np.concatenate(X_new) - y_new = np.concatenate(y_new) + return X_new, y_new, kwargs_new diff --git a/photonai/requirements.txt b/photonai/requirements.txt index 8251d9d3..aff80ccb 100644 --- a/photonai/requirements.txt +++ b/photonai/requirements.txt @@ -19,6 +19,7 @@ scipy statsmodels flask prettytable +seaborn # sk optimize scikit-optimize # fabolas diff --git a/photonai/test/NeuroTest.py b/photonai/test/NeuroTest.py new file mode 100644 index 00000000..16063c46 --- /dev/null +++ b/photonai/test/NeuroTest.py @@ -0,0 +1,106 @@ +import unittest, os, inspect +from ..neuro.BrainAtlas import AtlasLibrary, BrainAtlas +from ..base.PhotonBatchElement import PhotonBatchElement +from ..neuro.ImageBasics import ResampleImages, SmoothImages +from nilearn import image +from nibabel.nifti1 import Nifti1Image +import numpy as np + + +class NeuroTest(unittest.TestCase): + + def setUp(self): + self.test_folder = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'test_data/') + self.atlas_name = "AAL" + self.X = AtlasLibrary().get_nii_files_from_folder(self.test_folder, extension=".nii") + + def tearDown(self): + pass + + def test_brain_atlas_load(self): + + brain_atlas = AtlasLibrary().get_atlas(self.atlas_name) + + # manually load brain atlas + man_map = image.load_img(os.path.dirname(inspect.getfile(BrainAtlas)) + '/Atlases/AAL_SPM12/AAL.nii.gz').get_data() + self.assertTrue(np.array_equal(man_map, brain_atlas.map)) + + def test_brain_atlas(self): + + + # 4101 + # Hippocampus_L + # 4102 + # Hippocampus_R + # 4201 + # Amygdala_L + # 4202 + # Amygdala_R + + brain_atlas = BrainAtlas("AAL", "vec", rois=["Hippocampus_R", "Hippocampus_L", "Amygdala_L", "Amygdala_R"]) + new_data = brain_atlas.transform(self.X) + self.assertTrue(len(self.X), len(brain_atlas.rois)) + + brain_atlas_mean = BrainAtlas("AAL", "mean", rois='all') + brain_atlas_mean.transform(self.X) + debug = True + + def test_resampling_and_smoothing(self): + + testsuite = ["Testing Method on Single Core", + "Testing Method on Multi Core", + "Testing Method on Single Core Batched", + "Testing Method on Multi Core Batched"] + + def create_instances_and_transform(neuro_class, neuro_class_str, param_dict, transformed_X): + instance_list = [] + + instance_list.append(neuro_class(nr_of_processes=1, **param_dict)) + instance_list.append(neuro_class(nr_of_processes=3, + cache_folder=os.path.join(self.test_folder, "cache_folder"), **param_dict)) + instance_list.append(PhotonBatchElement(neuro_class_str, batch_size=5, nr_of_processes=1, **param_dict)) + instance_list.append(PhotonBatchElement(neuro_class_str, batch_size=5, nr_of_processes=3, + cache_folder=os.path.join(self.test_folder, "cache_folder"), + **param_dict)) + + for test, obj in enumerate(instance_list): + print(testsuite[test]) + + # transform data + if isinstance(obj, PhotonBatchElement): + new_X, _, _ = obj.transform(self.X) + obj.base_element.clear_cache() + else: + new_X = obj.transform(self.X) + obj.clear_cache() + + # compare output to nilearn version + for index, nilearn_nifti in enumerate(transformed_X): + photon_nifti = new_X[index] + if isinstance(photon_nifti, Nifti1Image): + self.assertTrue(np.array_equal(photon_nifti.dataobj, nilearn_nifti.dataobj)) + else: + self.assertTrue(np.array_equal(np.asarray(photon_nifti), nilearn_nifti.dataobj)) + + print("finished testing object: all images are fine.") + + print("Testing Nifti Smoothing.") + smoothing_param_dict = {'fwhm': [3, 3, 3]} + nilearn_smoothed_X = [] + for element in self.X: + nilearn_smoothed_X.append(image.smooth_img(element, **smoothing_param_dict)) + create_instances_and_transform(SmoothImages, 'SmoothImages', smoothing_param_dict, nilearn_smoothed_X) + + print("Testing Nifti Resampling.") + target_affine = np.diag([5, 5, 5]) + resample_param_dict = {'target_affine': target_affine, 'interpolation': 'nearest'} + nilearn_resampled_X = [] + for element in self.X: + nilearn_resampled_X.append(image.resample_img(element, **resample_param_dict)) + create_instances_and_transform(ResampleImages, 'ResampleImages', {'voxel_size': [5, 5, 5]}, nilearn_resampled_X) + + + + + + diff --git a/photonai/test/PhotonBatchTests.py b/photonai/test/PhotonBatchTests.py new file mode 100644 index 00000000..b2dfacca --- /dev/null +++ b/photonai/test/PhotonBatchTests.py @@ -0,0 +1,73 @@ +import unittest +import numpy as np +from ..base.PhotonBatchElement import PhotonBatchElement + + +class DummyBatchTransformer: + + def __init__(self): + self.needs_y = False + self.needs_covariates = True + self.predict_count = 0 + + def fit(self, X, y, **kwargs): + pass + + def transform(self, X, y, **kwargs): + X_new = [] + for i, x in enumerate(X): + X_new.append([str(sub_x) + str(y[i]) for sub_x in x]) + + if not np.array_equal(y, kwargs["animals"]): + raise Exception("Batching y and kwargs delivery is strange") + + kwargs["animals"] = [i[::-1] for i in kwargs["animals"]] + + return X_new, y, kwargs + + def predict(self, X, y=None, **kwargs): + + self.predict_count += 1 + predictions = np.ones(X.shape) * self.predict_count + return predictions + + +class NeuroBatchTests(unittest.TestCase): + + def setUp(self): + self.batch_size = 10 + nr_features = 3 + origin_list = ["affe", "tiger", "schwein", "giraffe", "löwe"] + self.data = None + self.targets = None + + self.neuro_batch = PhotonBatchElement("dummy_batch", batch_size=self.batch_size, + base_element=DummyBatchTransformer()) + + for element in origin_list: + features = [element + str(i) for i in range(0, nr_features)] + if self.data is None: + self.data = np.array([features] * self.batch_size) + else: + self.data = np.vstack((self.data, [features] * self.batch_size)) + if self.targets is None: + self.targets = np.array([element] * self.batch_size) + else: + self.targets = np.hstack((self.targets, [element] * self.batch_size)) + + self.data = np.array(self.data) + self.targets = np.array(self.targets) + self.kwargs = {"animals": self.targets} + + def test_transform(self): + X_new, y_new, kwargs_new = self.neuro_batch.transform(self.data, self.targets, **self.kwargs) + self.assertListEqual(X_new[0, :].tolist(), ["affe0affe", "affe1affe", "affe2affe"]) + self.assertListEqual(X_new[49, :].tolist(), ["löwe0löwe", "löwe1löwe", "löwe2löwe"]) + self.assertEqual(kwargs_new["animals"][0], "effa") + self.assertEqual(kwargs_new["animals"][49], "ewöl") + + def test_predict(self): + X_predicted, _, _ = self.neuro_batch.predict(self.data, **self.kwargs) + # assure that predict is batch wisely called + self.assertTrue(X_predicted[0][0] == 1) + self.assertTrue(X_predicted[-1][0] == (self.data.shape[0]/self.batch_size)) diff --git a/photonai/validation/PermutationTest.py b/photonai/validation/PermutationTest.py index ffed836f..5241557f 100644 --- a/photonai/validation/PermutationTest.py +++ b/photonai/validation/PermutationTest.py @@ -51,6 +51,7 @@ def fit(self, X, y): # Run with true labels + connect(self.pipe.output_settings.mongodb_connect_url, alias="photon_core") # Check if it already exists in DB try: existing_reference = MDBHyperpipe.objects.raw({'permutation_id': self.mother_permutation_id, diff --git a/photonai/validation/ResultsDatabase.py b/photonai/validation/ResultsDatabase.py index b49f7724..7117616f 100644 --- a/photonai/validation/ResultsDatabase.py +++ b/photonai/validation/ResultsDatabase.py @@ -141,6 +141,10 @@ class FoldOperations(Enum): STD = 1 RAW = 2 +class ParallelData(MongoModel): + + unprocessed_data = fields.ObjectIdField() + processed_data = fields.ObjectIdField() class MDBHelper: OPERATION_DICT = {FoldOperations.MEAN: np.mean, FoldOperations.STD: np.std} diff --git a/photonai/validation/Validate.py b/photonai/validation/Validate.py index c09814e7..224f9063 100644 --- a/photonai/validation/Validate.py +++ b/photonai/validation/Validate.py @@ -451,6 +451,7 @@ def calculate_metrics(y_true, y_pred, metrics): scorer = Scorer.create(metric) if scorer is not None: scorer_value = scorer(y_true, y_pred) + Logger().debug(str(scorer_value)) output_metrics[metric] = scorer_value else: output_metrics[metric] = np.nan diff --git a/setup.py b/setup.py index 363be11c..02e00b93 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ from setuptools import setup, find_packages -__version__ = '0.4.0' +__version__ = '0.5.0' setup( name='photonai',