From 583d4b5ec6f821b6b206edfc44b18dac60a17975 Mon Sep 17 00:00:00 2001 From: evankomp Date: Fri, 26 Feb 2021 18:34:30 -0800 Subject: [PATCH 01/99] test failure --- gandy/tests/test_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gandy/tests/test_test.py b/gandy/tests/test_test.py index 78e986a..661934c 100644 --- a/gandy/tests/test_test.py +++ b/gandy/tests/test_test.py @@ -1,4 +1,4 @@ class TestDumbFunction(unittest.TestCase): def test_dumb(self): - self.assertEqual(True, True) + self.assertEqual(True, False) From 6258ece32ae35248f764fc9760fc2f44aabfb9f2 Mon Sep 17 00:00:00 2001 From: evankomp Date: Mon, 1 Mar 2021 13:40:17 -0800 Subject: [PATCH 02/99] needed to change flake comand to the correct file --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e7a3a21..adf3b63 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,7 +31,7 @@ install: # a list of commands to run before the main script before_script: - - flake8 codebase + - flake8 gandy # the actual commands to run script: From d6b4bc5b1d24c872ba897ba57f32776c8fc3d6fc Mon Sep 17 00:00:00 2001 From: EvanKomp Date: Mon, 1 Mar 2021 14:11:55 -0800 Subject: [PATCH 03/99] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 2fb8d4e..de6861c 100644 --- a/README.md +++ b/README.md @@ -15,3 +15,6 @@ If any new installed development dependancies, add them to the devenv.yml enviro To update dev environment with new dependencies in the .yml file, conda env update --file devenv.yml ./working/ is a workspace for notebooks/testing. It will be ignored by git by default, and will be removed upon release. To specifically "save" your files to git or to share work with other developers, use git add --force working. + +## Testing +Tests located at gandy/tests From 2c6f963c4a1c9c06e91064ccfaedc12d71f2367f Mon Sep 17 00:00:00 2001 From: evankomp Date: Mon, 1 Mar 2021 18:53:38 -0800 Subject: [PATCH 04/99] initial format commit --- gandy/optimization/__init__.py | 0 gandy/{models => optimization}/hypersearch.py | 0 gandy/tests/test_hypersearch/__init__.py | 0 gandy/tests/test_hypersearch/test_hypersearch.py | 0 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 gandy/optimization/__init__.py rename gandy/{models => optimization}/hypersearch.py (100%) create mode 100644 gandy/tests/test_hypersearch/__init__.py create mode 100644 gandy/tests/test_hypersearch/test_hypersearch.py diff --git a/gandy/optimization/__init__.py b/gandy/optimization/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gandy/models/hypersearch.py b/gandy/optimization/hypersearch.py similarity index 100% rename from gandy/models/hypersearch.py rename to gandy/optimization/hypersearch.py diff --git a/gandy/tests/test_hypersearch/__init__.py b/gandy/tests/test_hypersearch/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gandy/tests/test_hypersearch/test_hypersearch.py b/gandy/tests/test_hypersearch/test_hypersearch.py new file mode 100644 index 0000000..e69de29 From 7532f172363c9b90910540f68ce199f8bb4b71ca Mon Sep 17 00:00:00 2001 From: Kyle Moskowitz Date: Tue, 2 Mar 2021 01:23:40 -0800 Subject: [PATCH 05/99] PEP8 style fixes --- gandy/quality_est/metrics.py | 232 ++++++++++++++++++----------------- 1 file changed, 122 insertions(+), 110 deletions(-) diff --git a/gandy/quality_est/metrics.py b/gandy/quality_est/metrics.py index 76f0a4e..2173c59 100644 --- a/gandy/quality_est/metrics.py +++ b/gandy/quality_est/metrics.py @@ -1,151 +1,163 @@ -'''Metrics module: contains some relevent metrics to assess the performance of machine learning models. - -This module implements a parent metric class that contains necessary initialization arguments and automatically -calls a calculate method to compute a given metric. Required intial arguments include the machine learning model -output predictions and real values for comparison. Optionally, the user may input uncertainties if a given model -outputs them. The properties of the parent class are then inhereted by individual children classes which define -the exact mathematical operations to compute a specific metric. Calling a specific metric child class will compute -a given metric and return the total value and/or invidual values of that metric based on the input data provided. +''' +Metrics module: contains some relevent metrics to assess the performance of +machine learning models. + +This module implements a parent metric class that contains necessary +initialization arguments and automatically calls a calculate method to +compute a given metric. Required intial arguments include the machine +learning model output predictions and real values for comparison. Optionally, +the user may input uncertainties if a given model outputs them. The +properties of the parent class are then inhereted by individual children +classes which define the exact mathematical operations to compute a specific +metric. Calling a specific metric child class will compute a given metric and +return the total value and/or invidual values of that metric based on the +input data provided. ''' -## Imports +# Imports from typing import Tuple, Iterable, Any, Object, Type, List import numpy as np -import math +import math -## Typing -Array = Type[numpy.ndarray] +# Typing +Array = Type[np.ndarray] -## Define dictionary of available metrics +# Define dictionary of available metrics Metric_codex = {} -## Parent class for metric + +# Parent class for metric class Metric: - ''' - - Implements metric parent class. This class will define the structure of various quality - evaluation techniques used for comparing the uncertainty model outputs to real - experimental data. Children classes will inherent properties of this class. - ''' - - def __init__(self, predictions: Array, real: Array, uncertainties = None): + Implements metric parent class. This class will define the structure of + various quality evaluation techniques used for comparing the uncertainty + model outputs to real experimental data. Children classes will inherent + properties of this class. + ''' + def __init__(self, predictions: Array, real: Array, uncertainties=None): ''' - - Initializes an instance of the metric class, including the predictions, uncertainties (optional), and real data - necessary for comparison. - + Initializes an instance of the metric class, including the + predictions, uncertainties (optional), and real data + necessary for comparison. + Arg: predictions(ndarray): - Array of predictions generated from the uncertainty model - + Array of predictions generated from the uncertainty model + real(ndarray): - Array of real values that you want to compare the uncertainty model ouput to (eg. experimental data) - + Array of real values that you want to compare the uncertainty + model ouput to (eg. experimental data) + uncertainties(ndarray): - Optional argument which contains array of uncertainty values generated from the uncertainty module - + Optional argument which contains array of uncertainty values + generated from the uncertainty module ''' - ## psuedocode - # set self.predictions - # set self.real - # set self.uncertainties + # psuedocode + # set self.predictions + # set self.real + # set self.uncertainties # call calculate function within init: self.calculate() - return def calculate(self, **kwargs): - '''Empty calculate function''' - return - - -## Children classes for each relevent metric -class MSE(Metric): - ''' - Mean Squared Error class which defines the structure used for + ''' + Empty calculate function + ''' + return + + +# Children classes for each relevent metric +class MSE(Metric): + ''' + Mean Squared Error class which defines the structure used for computing the MSE between the passed in datasets. Inherets the properties of the parent class Metrics. - ''' def calculate(self, **kwargs) -> Tuple[float, Array]: - ''' - Method that defines the mathematical formula necessary to compute the MSE. - - Args: - - **kwargs: - Necessary keyword arguments to be passed into calculate() method - - Returns: - - MSE_value(float): - Total value of the Mean Squared error computed - - MSE_values(ndarray): - An array of MSE scores for each prediction - - ''' - ## pseudocode - # define mathematical formula to carry out MSE calculations using self.args variables - # iteration over arrays likely and plug into defined formula + ''' + Method that defines the mathematical formula necessary to compute the + MSE. + + Args: + + **kwargs: + Necessary keyword arguments to be passed into calculate() + method + + Returns: + + MSE_value(float): + Total value of the MSE computed + + MSE_values(ndarray): + An array of MSE scores for each prediction + + ''' + # pseudocode + # define mathematical formula for MSE calculation using self.args + # iteration over arrays likely, then plug into defined formula return MSE_value, MSE_values + class RMSE(Metric): ''' - Root Mean Squared Error class which defines the structure used for + Root Mean Squared Error class which defines the structure used for computing the RMSE between the passed in datasets. Inherets the properties of the parent class Metrics. - ''' def calculate(self, **kwargs) -> Tuple[float, Array]: - ''' - Method that defines the mathematical formula necessary to compute the RMSE. - - Args: - - **kwargs: - necessary keyword arguments to be passed into calculate() method - - Returns: - - RMSE_value(float): - Total value of the RMSE computed - - RMSE_values(ndarray): - Array of RMSE values for each prediction - - ''' - ## pseudocode - # define mathematical formula to carry out RMSE calculations using self.args variables - # iteration over arrays and plug into defined formula + ''' + Method that defines the mathematical formula necessary to compute the + RMSE. + + Args: + + **kwargs: + Necessary keyword arguments to be passed into calculate() + method + + Returns: + + RMSE_value(float): + Total value of the RMSE computed + + RMSE_values(ndarray): + Array of RMSE values for each prediction + + ''' + # pseudocode + # define mathematical formula for RMSE calculations using self.args + # iteration over arrays and plug into defined formula return RMSE_value, RMSE_values + class F1(Metric): ''' - F1 score class which defines the structure used for - computing the F1 score between the passed in datasets. - Inherets the properties of the parent class Metrics. - + F1 score class which defines the structure used forcomputing the F1 score + between the passed in datasets. Inherets the properties of the parent + class Metrics. ''' def calculate(self, **kwargs) -> float: - ''' - Method that defines the mathematical formula necessary to compute the RMSE. - - Args: - - **kwargs: - Necessary keyword arguments to be passed into calculate() method - - Returns: - - F1_value(float): - Value of the F1 score computed - - ''' - ## pseudocode - # define mathematical formula to carry out F1 calculation using self.args variables - # iteration over arrays and plug into formula - return F1_value \ No newline at end of file + ''' + Method that defines the mathematical formula necessary to compute + the RMSE. + + Args: + + **kwargs: + Necessary keyword arguments to be passed into calculate() + method + + Returns: + + F1_value(float): + Value of the F1 score computed + + ''' + # pseudocode + # define mathematical formula for F1 calculation using self.args variables + # iteration over arrays and plug into formula + return F1_value From 3a86fe81c9af52d617f95424a03217b91d1afb22 Mon Sep 17 00:00:00 2001 From: evankomp Date: Thu, 4 Mar 2021 15:19:23 -0800 Subject: [PATCH 06/99] Auto pep8ed --- gandy/models/gps.py | 115 ++++++------ gandy/models/hypersearch.py | 286 +++++++++++++++--------------- gandy/models/models.py | 327 +++++++++++++++++------------------ gandy/quality_est/metrics.py | 164 +++++++++--------- 4 files changed, 451 insertions(+), 441 deletions(-) diff --git a/gandy/models/gps.py b/gandy/models/gps.py index 8fd0104..6ccc08b 100644 --- a/gandy/models/gps.py +++ b/gandy/models/gps.py @@ -1,24 +1,24 @@ """Gaussian Process uncertainty models. Available for classifiers and regressors, use gaussian processes to make predi- -ctions of target values and uncertainties. +ctions of target values and uncertainties. Typical usage: For a set of training data Xs, Ys as arrays with non zeroth dimension shapes xshape and yshape: create and train a classifier for one training session. - + cfr = ucGaussianProcess.C(xshape, yshape) cfr.train(Xs, Ys, session='first') - - Make predictions on a test set of data Xst, Yst with the same shapes as + + Make predictions on a test set of data Xst, Yst with the same shapes as training: predictions, uncertainties = cfr.predict(Xst) - + Score the model on the test set using an mse metric: score = cfr.evaluate(Xs, Ys, metric='mse') """ -## imports +# imports from typing import Type, Tuple, Union import sklearn.gaussian_process @@ -26,126 +26,129 @@ import gandy.models.models -## Typing +# Typing Model = Type[ucGaussianProcess] Array = Type[numpy.ndarray] Predictor = Type[sklearn.gaussian_process] -## The gaussian process uncertainty model +# The gaussian process uncertainty model + + class ucGaussianProcess(gandy.models.models.UncertaintyModel): """Gaussian Process Regressor/Classifier Uncertainty Model - + Utilizes sklearn's GP objects as an Uncertainty Model, able to make predic- tions and uncertainty predictions. - + Args: - xshape (tuple of int): + xshape (tuple of int): shape of example data, excluding the first dimension - yshape (tuple of int): + yshape (tuple of int): shape of target data, excluding the first dimension **kwargs: keyword arguments to pass to the build method """ - def _build(self, - model_type: str, + + def _build(self, + model_type: str, **kwargs) -> Predictor: - """Creates and returns a gaussian process predictor. - - Can be classifier or a regressor, chosen by specifying model type. + """Creates and returns a gaussian process predictor. + + Can be classifier or a regressor, chosen by specifying model type. Accesses scikit-learn for the guassian process predictors. - + Args: model_type (str): - Type ('classifier' or 'regressor') of model to assign as the + Type ('classifier' or 'regressor') of model to assign as the predictor. - **kwargs: + **kwargs: Keyword arguments to pass to `sklearn.gaussian_process- .GaussianProcessRegressor/Classifier` - + Returns: - instance of sklearn.gaussian_process: + instance of sklearn.gaussian_process: The built predictor. """ - ## psueudocode - #. if statement classifier or regressor + # psueudocode + # . if statement classifier or regressor # instatiate scikitlearn object with kwargs - #. else + # . else # raise not implimented error return model - - def _train(self, + + def _train(self, Xs: Array, Ys: Array, **kwargs): """Trains the gaussian process on training data via covariance kernel. - + Trains the predictor accoring to `sklearn.gaussian_process. - GaussianProcessRegressor/Classifier`. No training losses/metrics + GaussianProcessRegressor/Classifier`. No training losses/metrics associated with the covariance fit, so None is returned. - + Args: - Xs (Array): + Xs (Array): Examples data to train on. - Ys (Array): - Label data that is targeted for metrics for training. + Ys (Array): + Label data that is targeted for metrics for training. **kwargs: Keyword arguments passed to model's fit method. Returns: - None: + None: No losses to return for GP fitting. """ - ## pseudocode - #. fit self model with Xs, Ys + # pseudocode + # . fit self model with Xs, Ys return None - - def _predict(self, - Xs: Array, + + def _predict(self, + Xs: Array, **kwargs) -> Tuple[Array]: """Make predictions of target on given examples. - + Uses the sklearn gaussian process at self.model to make predictions, and predictions of uncertainty. Make predictions on unlabeled example data. - + Args: Xs (ndarray): Example data to make predictions on. - **kwargs: + **kwargs: keyword arguments passed to predictor's predict method - + Returns: tuple of ndarray: array of predictions of targets with the same length as Xs - array of prediction uncertainties of targets withthe same length + array of prediction uncertainties of targets withthe same length as Xs """ - ## pseudocode - #. get uncertainties and predictions by passing return_std to + # pseudocode + # . get uncertainties and predictions by passing return_std to # sklearn object's predict return predictions, uncertainties - + @classmethod def R(cls, *args, **kwargs) -> Model: - """Alternative to passing model_type as 'regressor' to object + """Alternative to passing model_type as 'regressor' to object initialization. - + Arguments: - *args: + *args: positional arguments to pass to init **kwargs: keyword arguments to pass to init and build methods. """ - return cls(*args, model_type = 'regressor', **kwargs) - + return cls(*args, model_type='regressor', **kwargs) + @classmethod def C(cls, *args, **kwargs) -> Model: - """Alternative to passing model_type as 'classifier' to object + """Alternative to passing model_type as 'classifier' to object initialization. - + Arguments: - *args: + *args: positional arguments to pass to init **kwargs: keyword arguments to pass to init and build methods. """ - return cls(*args, model_type = 'classifier', **kwargs) \ No newline at end of file + return cls(*args, model_type='classifier', **kwargs) diff --git a/gandy/models/hypersearch.py b/gandy/models/hypersearch.py index d551a5b..9acbdba 100644 --- a/gandy/models/hypersearch.py +++ b/gandy/models/hypersearch.py @@ -9,25 +9,25 @@ Typical usage: - Define `search_space` a dictionary of hyperparameters and their respective + Define `search_space` a dictionary of hyperparameters and their respective space to search. - + search_space = {'hyp1': [choice1, choice2, choice3], 'hyp2': (low, high, uniform)} - + For a set of development data Xs, Ys as arrays with non zeroth dimension shapes xshape and yshape: - - opt = OptRoutine(UncertaintyModel, Xs, Ys, search_space, xshape=xshape, + + opt = OptRoutine(UncertaintyModel, Xs, Ys, search_space, xshape=xshape, yshape=yshape) - + Optimize with 3 fold CV opt.optimize(k=3) best_params = opt.best_params # dict of the values in search_space for each - # hyp found to be best + # hyp found to be best """ -## imports +# imports from typing import Tuple, Iterable, Any, Object, Type, List import optuna @@ -35,59 +35,62 @@ import gandy.models.models -## Typing +# Typing Model = Type[gandy.models.models.UncertaintyModel] Array = Type[numpy.ndarray] Trial = Type[optuna.trials.Trial] -## class to specify optuna search space from python readable inputs +# class to specify optuna search space from python readable inputs + + class SearchableSpace: - """Wrapper to convert user specified search space into Optuna readable + """Wrapper to convert user specified search space into Optuna readable function. - + Args: - hypname (str): + hypname (str): Name of hyperparameter. space (tuple or list): The user defined hyperparameter space to search, determined by form Options - TBD - + Attributes: - func (optuna.trials.Trial method): + func (optuna.trials.Trial method): Function to be used for sampling of hyperparams. - hypname (str): + hypname (str): Name of hyperparameter. args (tuple): Positional arguments after name to be passed to func for sampling """ + def __init__(self, hypname, space): - ## pseudocode - #. if statement format of space + # pseudocode + # . if statement format of space # set self.func, self.args, and self.hypname return - -## object function class to be optimized + +# object function class to be optimized class SubjectObjective: - """Objective function definition in an Optuna study. - - Not meant to be interacted with directly. Supports trial pruning and cross + """Objective function definition in an Optuna study. + + Not meant to be interacted with directly. Supports trial pruning and cross validation. Define an objective function for hyperparameter optimization considering a subject class. - + Args: subject (UncertaintyModel): A class of UncertaintyModel, the subject of the study - Xs (ndarray): + Xs (ndarray): Examples feature data in the development set to use for study. Ys (ndarray): Examples label data in the development set to use for study. param_space (dict): - Pairs of {hypname: sample_function} where sample_function is a + Pairs of {hypname: sample_function} where sample_function is a optuna Trial method used for sampleing sessions (int or list of str): Number of training sessions to execute per model or names of sessions, checking for pruning if necessary - k (int or): + k (int or): Number of folds for cross validation or tuple of fold indexes in data. Default None, don't use cross validation. val_data (tuple of array): @@ -99,6 +102,7 @@ class SubjectObjective: **kwargs: Keyword arguments to pass to constructor, training, and scoring. """ + def __init__(self, subject: Model, Xs: Array, @@ -108,227 +112,227 @@ def __init__(self, k: Union[int, tuple] = None, val_data: Tuple[Array] = None, val_frac: float = None - **kwargs): - ## pseudocode - #. make sure no overlap of kwargs and param space - #. store kwargs - #. make sure only one of k, val_data, val_frac - #. test input type - #. set self attributes in proper form + ** kwargs): + # pseudocode + # . make sure no overlap of kwargs and param space + # . store kwargs + # . make sure only one of k, val_data, val_frac + # . test input type + # . set self attributes in proper form return - + def _sample_params(self, trial: Trial) -> dict: - """Sample the hyperparameters to be used for this trial. - - Uses space defined at self.param_space (dict of SearchableSpace instances) + """Sample the hyperparameters to be used for this trial. + + Uses space defined at self.param_space (dict of SearchableSpace instances) to return values for this trial. - + Args: trial (optuna Trial): Current trial. - + Returns: hyparms (dict): Mapping of hyperparameter values to use for this trial """ - ## pseudocode - #. hyparams = dict loop self.search_space trial.method(args) + # pseudocode + # . hyparams = dict loop self.search_space trial.method(args) return hyparams - - def _execute_instance(self, + + def _execute_instance(self, hyparams: dict, train_data: Tuple[Array], val_data: Tuple[Array]) -> float: """Train and score on validation data a single subject instance. - + Args: hyparms (dict): Mapping of hyperparameter values to use for this trial train_data (tuple of ndarray): - Training (examples, targets) to use for scoring. + Training (examples, targets) to use for scoring. val_data (tuple of ndarray) Validation (examples, targets) to use for scoring. - + Returns: - float: + float: The score of the model on the validation data. """ - ## pseudocode - #. construct model with hyparms and self kwargs - #. train model with hyparms and self kwargs - #. score model with self kwargs + # pseudocode + # . construct model with hyparms and self kwargs + # . train model with hyparms and self kwargs + # . score model with self kwargs return single_loss - - def __call__(self, trial: Trial) -> float: + + def __call__(self, trial: Trial) -> float: """Function used by optuna to run a single trial. Returns the score to minimize. - + Args: trial (optuna Trial): Current trial. - + Returns: - float: + float: The score of this trial, the quantity to minimize. """ - ## pseudocode - #. sample hypparams for this trial - #. depending on val_data, k, or val_frac - #. split data based on above - #. for each session, execute instances - #. check for prune + # pseudocode + # . sample hypparams for this trial + # . depending on val_data, k, or val_frac + # . split data based on above + # . for each session, execute instances + # . check for prune return loss - -## Hyperparameter search class wrapper for our models and optuna + +# Hyperparameter search class wrapper for our models and optuna class OptRoutine: - """Hyperparameter optimizing routine for uncertainty models. - + """Hyperparameter optimizing routine for uncertainty models. + Searches over hyperparemeters for a class of uncertainty model for the set - producing thelowest value of a passed loss metric. Uses a cross validation - routine and is capable of pruning non-promising models between training - sessions. Optimizes objective function of the form + producing thelowest value of a passed loss metric. Uses a cross validation + routine and is capable of pruning non-promising models between training + sessions. Optimizes objective function of the form `gandy.models.hypersearch.SubjectObjective`. - + Args: subject (UncertaintyModel): A class of UncertaintyModel, the subject of the study - Xs (Iterable): + Xs (Iterable): Examples feature data in the development set to use for study. Ys (Iterable): Examples label data in the development set to use for study. - search_space (dict): + search_space (dict): Mapping of the hyperparameters to search over as {name: space} where space represents a search space based on its format. Options - TBD """ - def __init__(self, - subject: Model, + + def __init__(self, + subject: Model, Xs: Iterable, Ys: Iterable, - search_space = None, + search_space=None, **kwargs): - ## pseudocode - #. assert class type is child of uncertainty model - #. set the class to self.subject - #. set self the Xs and Ys data after taking values - #. save all_kwargs + # pseudocode + # . assert class type is child of uncertainty model + # . set the class to self.subject + # . set self the Xs and Ys data after taking values + # . save all_kwargs return - + def _set_param_space(self, **kwargs): - """Define the search space from user input search space according to - gandy.models.hypersearch.SearchableSpace class. - - Not meant to be interacted with directly. Reassigns stored search_space + """Define the search space from user input search space according to + gandy.models.hypersearch.SearchableSpace class. + + Not meant to be interacted with directly. Reassigns stored search_space if specified. """ - ## pseudocode - #. check self.search_space input + # pseudocode + # . check self.search_space input # raise if None - #. create empty param_space - #. for loop self.param_space + # . create empty param_space + # . for loop self.param_space # param_space = SearchableSpace class # set self._param_space - return - + return + def _set_objective(self, **kwargs): """Define the objective function for optuna to target when optimizing - hyperparameters. - - Initates an instance of gandy.models.hypersearch.SubjectObjective, - capable of cross validation and pruning between sessions. Not meant to + hyperparameters. + + Initates an instance of gandy.models.hypersearch.SubjectObjective, + capable of cross validation and pruning between sessions. Not meant to be interacted with directly. - + Args: - **kwargs: + **kwargs: Keyword arguments to pass to SubjectObjective class, such as the number of cross validation folds or constructor kwargs """ - ## pseudocode - #. check self._param_space exists - #. try to set if not - #. define SubjectObjective( + # pseudocode + # . check self._param_space exists + # . try to set if not + # . define SubjectObjective( # self.subject, # self.Xs, # self.Ys, # **kwargs) - - #. set self.objective + + # . set self.objective return - + def _set_study(self, **kwargs): - """Define the optuna study with create_study. - + """Define the optuna study with create_study. + Not meant to be interacted with directly. Creates the study to be used - - Args: + + Args: **kwargs: Keyword arguments for optuna create study. """ - ## pseudocode - #. create study optuna.create_study - #. set to self.study + # pseudocode + # . create study optuna.create_study + # . set to self.study return - - def optimize(self, - search_space: dict = None, + + def optimize(self, + search_space: dict = None, **kwargs) -> float: """Run the optimization study and save the best parameters. Return the best model's score. - + Args: - search_space (dict): + search_space (dict): Mapping of the hyperparameters to search over as {name: space} where space represents a search space based on its format. Options - TBD **kwargs: Keyword arguments to pass to constructor, optimizer, etc. - + Returns: best_score (float): best score of all hyperparameters searched """ - ## psuedocode - #. if search_space specified set - #. update all kwargs with these - #. set optimizer, study with all kwargs - #. set self.best_params - #. get best_score + # psuedocode + # . if search_space specified set + # . update all kwargs with these + # . set optimizer, study with all kwargs + # . set self.best_params + # . get best_score return best_score - + def train_best(self, **kwargs) -> Model: """Train the subject on the entire dataset with the best found parameters. - + Requires self.optimize to have been executed or best_params to have been specified. - + Args: - **kwargs: + **kwargs: Keyword arguments to pass to the constructor and trainer - + Returns: best_model (UncertaintyModel): - Instance of subject with specified static and best searched + Instance of subject with specified static and best searched hyperparameters trained on entire dataset. """ - ## pseudocode - #. check best_params exist - #. update all kwargs - #. Initiate model with and best_params and kwargs - #. train model with best_params training and kwargs - #. set self.best_model + # pseudocode + # . check best_params exist + # . update all kwargs + # . Initiate model with and best_params and kwargs + # . train model with best_params training and kwargs + # . set self.best_model return best_model - + @property def search_space(self): """dict: hyperparameter name to search space parirings""" return self._search_space - + @search_space.setter def search_space(self, new_search_space): - ## pseudocode - #. check dict + # pseudocode + # . check dict self._search_space = new_search_space return - \ No newline at end of file diff --git a/gandy/models/models.py b/gandy/models/models.py index 486526b..d2b4c77 100644 --- a/gandy/models/models.py +++ b/gandy/models/models.py @@ -6,395 +6,394 @@ ish building, training, predicting, and evaluateing. Typical usage: - Not meant to be interacted with directly. Subclasses must define + Not meant to be interacted with directly. Subclasses must define `_build`, `_train`, and `_predict` in order to function properly. """ -## imports +# imports from typing import Tuple, Iterable, Any, Object, Type import numpy import gandy.metrics -## typing +# typing Array = Type[numpy.ndarray] + class NotImplimented(Warning): """Warning to indicate that a child class has not yet implimented necessary methods. """ - ## pseudocode - #. define the exception + # pseudocode + # . define the exception pass class UncertaintyModel: """Parent uncertainty model class structure. - + Defines the structure for uncertainty models, with method wrappers for eg. - training, predicting accessing the predictor itself in `self.model`. The + training, predicting accessing the predictor itself in `self.model`. The `build` method, ran in init, creates the predictor according to the user's kwargs and allows for the creation of different complex models eg. GAN vs BNN to fit the same parent format. The method also impliments data format checking. - + Class will raise NotImplimented exception on methods not defined as necess- ary in children: `_build`, `_train`, `_predict` - + Args: - xshape (tuple of int): + xshape (tuple of int): shape of example data, excluding the first dimension - yshape (tuple of int): + yshape (tuple of int): shape of target data, excluding the first dimension **kwargs: keyword arguments to pass to the build method """ - - ## to contain dictionary of callable metric classes from the metrics module - metrics = {} #gandy.metrics.metric_codex + + # to contain dictionary of callable metric classes from the metrics module + metrics = {} # gandy.metrics.metric_codex """Available metrics defined in gandy.metrics""" - - def __init__(self, - xshape: Tuple[int], - yshape: Tuple[int], + + def __init__(self, + xshape: Tuple[int], + yshape: Tuple[int], **kwargs): - ## pseudocode - #. set self shapes - #. assign self model by running build function - #. create empty sessions list + # pseudocode + # . set self shapes + # . assign self model by running build function + # . create empty sessions list return - + def check(self, Xs: Iterable, - Ys: Iterable = None, + Ys: Iterable = None, **kwargs) -> Tuple[Array]: """Attempt to format incoming data. - + Assures that a passed set of data has the correct datatypes and shapes for the model. Transforms it to numpy if not already. - + Args: Xs (iterable): examples data to check Ys (iterable): label data to check, if present. Default None. - + Returns: tuple of ndarrays: Xs, the formated X data Ys, the formated Y data if present """ - ## pseudocode - #. assert data type has shape attribute - #. check shapes of Xs and Ys against self shapes - #. raise error if do not match - #. convert to numpy + # pseudocode + # . assert data type has shape attribute + # . check shapes of Xs and Ys against self shapes + # . raise error if do not match + # . convert to numpy if Ys: return Xs, Ys else: return Xs - + def build(self, **kwargs): """Construct and store the predictor. - + Build a model according to `_build` and assign to `self.model` - + Args: **kwargs: keyword arguments to pass to `_build` """ - ## pseudocode - #. set self model to _build + # pseudocode + # . set self model to _build return - + def _build(self, *args, **kwargs) -> Object: """Construct and return the predictor. - + Must be implimented in child class. To creates and returns a predictor with keyword argument inputs. Raises not implimented warning. - + Args: - *args: + *args: arguments defined in child **kwargs: keyword arguments/hyperparemeters for predictor init. - + Raises: - NotImplimented: + NotImplimented: warning that child class has not overloaded this method Returns: None: children will return the predictor """ - ## pseudocode - #. raise not implimented - #. model is None + # pseudocode + # . raise not implimented + # . model is None return model - + def train(self, Xs: Iterable, - Ys: Iterable, - session: str = None, + Ys: Iterable, + session: str = None, metric: Union[str, Callable], **kwargs): """Train the predictor for one session, handled by `_train`. - + Trains the stored predictor for a single session according to the - protocol in `_train`. Stores any returned quantities eg. losses + protocol in `_train`. Stores any returned quantities eg. losses in the `sessions` attribute. - + Args: - Xs (Iterable): + Xs (Iterable): Examples data. - Ys (Iterable): - Label data that is targeted for metrics. - session (str): + Ys (Iterable): + Label data that is targeted for metrics. + session (str): Name of training session for storing in losses. default None, incriment new name. metric (str): Metric to use, a key in UncertaintyModel.metrics or a metric object that takes as input true, predicted, and uncertainty values. - **kwargs: + **kwargs: Keyword arguments to pass to `_train` and assign non-default \ training parameters. """ - ## pseudocode - #. check data inputs with check method - conver to numpy - #. get metric method - #. execute _train with formated data and metric (?) - #. update session losses with session _train losses - maybe create session name - return - + # pseudocode + # . check data inputs with check method - conver to numpy + # . get metric method + # . execute _train with formated data and metric (?) + # . update session losses with session _train losses - maybe create session name + return + def _train(self, Xs: Array, Ys: Array, *args, **kwargs) -> Any: """Train the predictor. - + Must be implimented in child class. Trains the stored predictor and returns any losses or desired metrics. Up to child to accept metric. - + Args: - Xs (Array): + Xs (Array): Examples data to train on. - Ys (Array): - Label data that is targeted for metrics for training. + Ys (Array): + Label data that is targeted for metrics for training. *args: Positional arguments to be defined by child. - **kwargs: - Keyword arguments to assign non-default training parameters or + **kwargs: + Keyword arguments to assign non-default training parameters or pass to nested functions. - + Returns: - any: + any: Desired tracking of losses during training. Not implimented here, and returns None. """ - ## psudocode - #. raise not implimented - #. losses is None + # psudocode + # . raise not implimented + # . losses is None return losses - + def predict(self, - Xs: Iterable, + Xs: Iterable, uc_threshold: float = None, **kwargs) -> Tuple[Array]: """Make predictions on a set of data and return predictions and uncertain- - ty arrays. - - For a set of incoming data, check it and make predictions with the stored + ty arrays. + + For a set of incoming data, check it and make predictions with the stored model according to `_predict`. Optionally flag predictions whose uncert- ainties excede a desired threshhold - + Args: - Xs (Iterable): + Xs (Iterable): Examples data to make predictions of. - uc_threshold (float): acceptible amount of uncertainty. Predictions of - higher ucertainty values will be flagged + uc_threshold (float): acceptible amount of uncertainty. Predictions of + higher ucertainty values will be flagged **kwargs: keyword arguments to pass to `_predict` - + Returns: tuple of ndarray: array of predictions of targets with the same length as Xs - array of prediction uncertainties of targets withthe same length + array of prediction uncertainties of targets withthe same length as Xs (optional) array of flags of uncertain predictions higher than thr- eshhold of same length as Xs """ - ## pseudocode - #. check X data with check function - #. run _predict to return predictions and uncertainties - #. if threshhold, return predictions, uncertainties, and flags + # pseudocode + # . check X data with check function + # . run _predict to return predictions and uncertainties + # . if threshhold, return predictions, uncertainties, and flags return predictions, uncertainties, flags - - def _predict(self, - Xs: Array, - *args, + + def _predict(self, + Xs: Array, + *args, **kwargs): """Make predictions on a set of data and return predictions and uncertain- ty arrays. - + Must be implimented by child class. Makes predictions on data using model at self.model and any other stored objects. - + Args: Xs (ndarray): Example data to make predictions on. *args: Positional arguments to be defined by child. - **kwargs: + **kwargs: Keyword arguments for predicting. - + Returns: tuple of ndarray: array of predictions of targets with the same length as Xs - array of prediction uncertainties of targets withthe same length + array of prediction uncertainties of targets withthe same length as Xs """ - ## psuedocode - #. raise not implimented - #. set pred, unc to None + # psuedocode + # . raise not implimented + # . set pred, unc to None return predictions, uncertainties - - def score(self, + + def score(self, Xs: Iterable, Ys: Iterable, - metric: Union[str, Callable], + metric: Union[str, Callable], **kwargs) -> Tuple[float, Array]: """Make predictions and score the results according to a defined metric. - + For a set of labeled example data, use the the defined `_predict` method to make predictions on the data. Then, compare them to the true labels according to a desired metric. - + Args: - Xs (Iterable): + Xs (Iterable): Examples data to make predictions on. - Ys (Iterable): + Ys (Iterable): Labels of data. - metric (str): + metric (str): Metric to use, a key in UncertaintyModel.metrics or a metric object that takes as input true, predicted, and uncertainty values. **kwargs: keyword arguments to pass to `_predict` - + Returns: float: Total score according to metric. ndarray: Score array for each prediction. """ - ## pseudocode - #. if statement to get metric object from metrics or specified - #. else raise undefined metric - #. check data - #. predictions, uncertainties = execute self._predict on Xs - #. pass predictions, uncertainties to metric get back costs + # pseudocode + # . if statement to get metric object from metrics or specified + # . else raise undefined metric + # . check data + # . predictions, uncertainties = execute self._predict on Xs + # . pass predictions, uncertainties to metric get back costs return metric_value, metric_values - - def save(self, - filename: str, + + def save(self, + filename: str, **kwargs): """Save the model out of memory to the hard drive by specified format. - - Save to model to hardrive as two files, "`filename`.json" and + + Save to model to hardrive as two files, "`filename`.json" and "`filename`.XX" where the XX is determined by the predictor type - + Args: - filename (str): + filename (str): path to save model to, no extension **kwargs: keyword arguments to pass to _save, child specified method """ - ## pseudocode - #. execute _save with filename - #. save json with xshape, yshape, sesssions, etc. + # pseudocode + # . execute _save with filename + # . save json with xshape, yshape, sesssions, etc. return - + def _save(filename: str, **kwargs): """Method defined by child to save the predictor. - + Method must save into memory the object at self.model - + Args: - filename (str): + filename (str): name of file to save model to """ ## raise not implimented return - + @classmethod - def load(cls, - filename: str, + def load(cls, + filename: str, **kwargs): """Load a model from hardrive at filename. - - From two files, "`filename`.json" and "`filename`.XX" where the XX is + + From two files, "`filename`.json" and "`filename`.XX" where the XX is determined by the predictor type, load the model into memory. - + Args: - filename (str): + filename (str): path of file to load **kwargs: keyword arguments to pass to _load - + Returns: instance of class: the loaded UncertaintyModel """ - ## pseudocode - #. load the json and run cls(args) - #. predictor = _load - #. instance._model = predictor + # pseudocode + # . load the json and run cls(args) + # . predictor = _load + # . instance._model = predictor return instance - + def _load(self, - filename: str, - **kwargs): + filename: str, + **kwargs): """Method defined by child to load a predictor into memory. - + Loads the object to be assigned to self.model. - + Args: - filename (str): + filename (str): path of file to load """ ## raise not implimented - #. model = None + # . model = None return model - + @property def model(self): """predictor: the overall predictor model""" return self._model - + @model.setter def model(self, new_model): - ## raise exception does not support direct setting, use build function + # raise exception does not support direct setting, use build function return - + @model.deleter def model(self): - ## print message about deleting model, build needs to be ran + # print message about deleting model, build needs to be ran return - + @property def xshape(self): """tuple of int: shape of example features""" return self._xshape - + @xshape.setter def xshape(self, new_xshape): - ## test new shape, delete model + # test new shape, delete model self._xshape = new_xshape return - + @property def yshape(self): """tuple of int: shape of example label""" return self._yshape - + @yshape.setter def yshape(self, new_yshape): - ## test new shape, delete model + # test new shape, delete model self._yshape = new_yshape return - - \ No newline at end of file diff --git a/gandy/quality_est/metrics.py b/gandy/quality_est/metrics.py index 76f0a4e..c79836c 100644 --- a/gandy/quality_est/metrics.py +++ b/gandy/quality_est/metrics.py @@ -1,151 +1,155 @@ -'''Metrics module: contains some relevent metrics to assess the performance of machine learning models. - -This module implements a parent metric class that contains necessary initialization arguments and automatically -calls a calculate method to compute a given metric. Required intial arguments include the machine learning model -output predictions and real values for comparison. Optionally, the user may input uncertainties if a given model -outputs them. The properties of the parent class are then inhereted by individual children classes which define -the exact mathematical operations to compute a specific metric. Calling a specific metric child class will compute -a given metric and return the total value and/or invidual values of that metric based on the input data provided. +'''Metrics module: contains some relevent metrics to assess the performance of machine learning models. + +This module implements a parent metric class that contains necessary initialization arguments and automatically +calls a calculate method to compute a given metric. Required intial arguments include the machine learning model +output predictions and real values for comparison. Optionally, the user may input uncertainties if a given model +outputs them. The properties of the parent class are then inhereted by individual children classes which define +the exact mathematical operations to compute a specific metric. Calling a specific metric child class will compute +a given metric and return the total value and/or invidual values of that metric based on the input data provided. ''' -## Imports +# Imports from typing import Tuple, Iterable, Any, Object, Type, List import numpy as np -import math +import math -## Typing +# Typing Array = Type[numpy.ndarray] -## Define dictionary of available metrics +# Define dictionary of available metrics Metric_codex = {} -## Parent class for metric +# Parent class for metric + + class Metric: - ''' - - Implements metric parent class. This class will define the structure of various quality + ''' + + Implements metric parent class. This class will define the structure of various quality evaluation techniques used for comparing the uncertainty model outputs to real experimental data. Children classes will inherent properties of this class. - + ''' - - def __init__(self, predictions: Array, real: Array, uncertainties = None): + + def __init__(self, predictions: Array, real: Array, uncertainties=None): ''' - + Initializes an instance of the metric class, including the predictions, uncertainties (optional), and real data - necessary for comparison. - + necessary for comparison. + Arg: predictions(ndarray): - Array of predictions generated from the uncertainty model - + Array of predictions generated from the uncertainty model + real(ndarray): - Array of real values that you want to compare the uncertainty model ouput to (eg. experimental data) - + Array of real values that you want to compare the uncertainty model ouput to (eg. experimental data) + uncertainties(ndarray): Optional argument which contains array of uncertainty values generated from the uncertainty module - + ''' - ## psuedocode - # set self.predictions - # set self.real - # set self.uncertainties + # psuedocode + # set self.predictions + # set self.real + # set self.uncertainties # call calculate function within init: self.calculate() - return + return def calculate(self, **kwargs): - '''Empty calculate function''' - return - - -## Children classes for each relevent metric -class MSE(Metric): - ''' - Mean Squared Error class which defines the structure used for + '''Empty calculate function''' + return + + +# Children classes for each relevent metric +class MSE(Metric): + ''' + Mean Squared Error class which defines the structure used for computing the MSE between the passed in datasets. Inherets the properties of the parent class Metrics. - + ''' def calculate(self, **kwargs) -> Tuple[float, Array]: - ''' - Method that defines the mathematical formula necessary to compute the MSE. - + ''' + Method that defines the mathematical formula necessary to compute the MSE. + Args: - + **kwargs: Necessary keyword arguments to be passed into calculate() method - Returns: + Returns: + + MSE_value(float): + Total value of the Mean Squared error computed - MSE_value(float): - Total value of the Mean Squared error computed - MSE_values(ndarray): - An array of MSE scores for each prediction + An array of MSE scores for each prediction ''' - ## pseudocode - # define mathematical formula to carry out MSE calculations using self.args variables - # iteration over arrays likely and plug into defined formula - return MSE_value, MSE_values + # pseudocode + # define mathematical formula to carry out MSE calculations using self.args variables + # iteration over arrays likely and plug into defined formula + return MSE_value, MSE_values + class RMSE(Metric): ''' - Root Mean Squared Error class which defines the structure used for + Root Mean Squared Error class which defines the structure used for computing the RMSE between the passed in datasets. Inherets the properties of the parent class Metrics. - + ''' def calculate(self, **kwargs) -> Tuple[float, Array]: ''' - Method that defines the mathematical formula necessary to compute the RMSE. - + Method that defines the mathematical formula necessary to compute the RMSE. + Args: - + **kwargs: necessary keyword arguments to be passed into calculate() method - Returns: + Returns: RMSE_value(float): - Total value of the RMSE computed + Total value of the RMSE computed RMSE_values(ndarray): - Array of RMSE values for each prediction - + Array of RMSE values for each prediction + ''' - ## pseudocode - # define mathematical formula to carry out RMSE calculations using self.args variables - # iteration over arrays and plug into defined formula - return RMSE_value, RMSE_values + # pseudocode + # define mathematical formula to carry out RMSE calculations using self.args variables + # iteration over arrays and plug into defined formula + return RMSE_value, RMSE_values + class F1(Metric): ''' - F1 score class which defines the structure used for - computing the F1 score between the passed in datasets. + F1 score class which defines the structure used for + computing the F1 score between the passed in datasets. Inherets the properties of the parent class Metrics. - + ''' def calculate(self, **kwargs) -> float: - ''' - Method that defines the mathematical formula necessary to compute the RMSE. - + ''' + Method that defines the mathematical formula necessary to compute the RMSE. + Args: - + **kwargs: Necessary keyword arguments to be passed into calculate() method - Returns: + Returns: F1_value(float): - Value of the F1 score computed + Value of the F1 score computed ''' - ## pseudocode - # define mathematical formula to carry out F1 calculation using self.args variables - # iteration over arrays and plug into formula - return F1_value \ No newline at end of file + # pseudocode + # define mathematical formula to carry out F1 calculation using self.args variables + # iteration over arrays and plug into formula + return F1_value From 061b0b70a38cb1f62c2447e0552be268d511dddd Mon Sep 17 00:00:00 2001 From: evankomp Date: Thu, 4 Mar 2021 18:57:10 -0800 Subject: [PATCH 07/99] Searchable space test written --- .../test_hypersearch/test_hypersearch.py | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/gandy/tests/test_hypersearch/test_hypersearch.py b/gandy/tests/test_hypersearch/test_hypersearch.py index e69de29..f7f2b18 100644 --- a/gandy/tests/test_hypersearch/test_hypersearch.py +++ b/gandy/tests/test_hypersearch/test_hypersearch.py @@ -0,0 +1,57 @@ +"""Testing hyperparameter optimization with optuna""" +import numpy +import unittest +import unittest.mock + +import optuna.trial + +import gandy.optimization.optimization as opt + +class TestSearchableSpace(unittest.TestCase): + + def test_class(self): + """try all possible searchable spaces""" + NAME = 'hypname' + + # uniform float + spaces = [(2.0, 4.0), (2.0, 4.0, 'uniform')] + for space in spaces: + subject = opt.SearchableSpace(NAME, space) + self.assertEqual(subject.name, NAME) + self.assertEqual(subject.args, (2.0, 4.0)) + self.assertTrue(subject.func is optuna.trial.Trial.suggest_uniform) + + # loguniform float + space = (3.0, 8.0, 'loguniform') + subject = opt.SearchableSpace(NAME, space) + self.assertEqual(subject.name, NAME) + self.assertEqual(subject.args, (3.0, 8.0)) + self.assertTrue(subject.func is optuna.trial.Trial.suggest_loguniform) + + # discrete uniform + space = (5.0, 10.0, 2.0) + subject = opt.SearchableSpace(NAME, space) + self.assertEqual(subject.name, NAME) + self.assertEqual(subject.args, (5.0, 10.0, 2.0)) + self.assertTrue(subject.func is + optuna.trial.Trial.suggest_discrete_uniform) + + # int + space = (2, 10) + subject = opt.SearchableSpace(NAME, space) + self.assertEqual(subject.name, NAME) + self.assertEqual(subject.args, (2, 10, 1)) + self.assertTrue(subject.func is optuna.trial.Trial.suggest_int) + space = (2, 10, 3) + subject = opt.SearchableSpace(NAME, space) + self.assertEqual(subject.name, NAME) + self.assertEqual(subject.args, (2, 10, 3)) + self.assertTrue(subject.func is optuna.trial.Trial.suggest_int) + + # catagorical + space = ['a', 'b', 'c'] + subject = opt.SearchableSpace(NAME, space) + self.assertEqual(subject.name, NAME) + self.assertEqual(subject.args, space) + self.assertTrue(subject.func is optuna.trial.Trial.suggest_catagorical) + return From f0ba1a15c02866c0c030b49544e8e2000fab878c Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Fri, 5 Mar 2021 14:56:52 -0800 Subject: [PATCH 08/99] :q --- gandy/tests/test_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gandy/tests/test_test.py b/gandy/tests/test_test.py index 78e986a..f88de47 100644 --- a/gandy/tests/test_test.py +++ b/gandy/tests/test_test.py @@ -1,3 +1,4 @@ +import unittest class TestDumbFunction(unittest.TestCase): def test_dumb(self): From c12986d8855f13d8790212cafc5a737f7a700da4 Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Fri, 5 Mar 2021 14:59:32 -0800 Subject: [PATCH 09/99] Removed unused metrics import --- gandy/models/gans.py | 1 - 1 file changed, 1 deletion(-) diff --git a/gandy/models/gans.py b/gandy/models/gans.py index 2da7717..428431c 100644 --- a/gandy/models/gans.py +++ b/gandy/models/gans.py @@ -7,7 +7,6 @@ # gandy imports import gandy.models.models -import gandy.metrics # deep learning imports import deepchem From e20918b1f88c348b0ba16c974f457ba4e5c639a5 Mon Sep 17 00:00:00 2001 From: Kyle Moskowitz Date: Fri, 5 Mar 2021 15:04:57 -0800 Subject: [PATCH 10/99] Typo fixed --- gandy/quality_est/metrics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gandy/quality_est/metrics.py b/gandy/quality_est/metrics.py index 2173c59..a6d4de6 100644 --- a/gandy/quality_est/metrics.py +++ b/gandy/quality_est/metrics.py @@ -10,7 +10,7 @@ properties of the parent class are then inhereted by individual children classes which define the exact mathematical operations to compute a specific metric. Calling a specific metric child class will compute a given metric and -return the total value and/or invidual values of that metric based on the +return the total value and/or individual values of that metric based on the input data provided. ''' From e4ebfd7d5a53c2223f28d6bec1b611926287cd68 Mon Sep 17 00:00:00 2001 From: evankomp Date: Fri, 5 Mar 2021 15:22:59 -0800 Subject: [PATCH 11/99] hypersearch tests, searchable space done, subject objective almost done --- gandy/optimization/hypersearch.py | 55 ++++- .../test_hypersearch/test_hypersearch.py | 223 +++++++++++++++++- 2 files changed, 275 insertions(+), 3 deletions(-) diff --git a/gandy/optimization/hypersearch.py b/gandy/optimization/hypersearch.py index d551a5b..9a50920 100644 --- a/gandy/optimization/hypersearch.py +++ b/gandy/optimization/hypersearch.py @@ -103,7 +103,7 @@ def __init__(self, subject: Model, Xs: Array, Ys: Array, - param_space: dict, + param_space: list, sessions: Union[int, List[str]] = None, k: Union[int, tuple] = None, val_data: Tuple[Array] = None, @@ -132,7 +132,7 @@ def _sample_params(self, trial: Trial) -> dict: Mapping of hyperparameter values to use for this trial """ ## pseudocode - #. hyparams = dict loop self.search_space trial.method(args) + #. hyparams = dict loop self.param_space trial.method(args) return hyparams def _execute_instance(self, @@ -179,6 +179,57 @@ def __call__(self, trial: Trial) -> float: #. check for prune return loss + @property + def k(self): + """list of fold indicies for incoming data, (train ind, val ind)""" + return self._k + + @k.setter + def k(self, new_k): + # if int convert to indexes + # otherwise check proper form + self._k = new_k + return + + @k.deleter + def k(self): + self._k = None + # print message + return + + @property + def val_data(self): + """tuple of ndarray, (train data, test data)""" + return self._val_data + + @val_data.setter + def val_data(self, new_val_data): + # check tuple of array + self._val_data = new_val_data + return + + @val_data.deleter + def val_data(self): + self._val_data = None + # print message + return + + @property + def val_frac(self): + """fraction of incoming data to use as validation data, + randomly sample""" + return self._val_frac + + @val_frac.setter + def val_frac(self, new_val_frac): + # check float + self._val_frac = new_val_frac + return + + @val_frac.deleter + def val_frac(self): + self._val_frac = None + return ## Hyperparameter search class wrapper for our models and optuna class OptRoutine: diff --git a/gandy/tests/test_hypersearch/test_hypersearch.py b/gandy/tests/test_hypersearch/test_hypersearch.py index f7f2b18..47fa015 100644 --- a/gandy/tests/test_hypersearch/test_hypersearch.py +++ b/gandy/tests/test_hypersearch/test_hypersearch.py @@ -1,11 +1,13 @@ """Testing hyperparameter optimization with optuna""" -import numpy +import itertools import unittest import unittest.mock +import numpy import optuna.trial import gandy.optimization.optimization as opt +import gandy.models.models class TestSearchableSpace(unittest.TestCase): @@ -55,3 +57,222 @@ def test_class(self): self.assertEqual(subject.args, space) self.assertTrue(subject.func is optuna.trial.Trial.suggest_catagorical) return + + +class TestSubjectObjective(unittest.TestCase): + + params = [opt.SearchableSpace('hyp1', (1,10)), + opt.SearchableSpace('hyp2', ['a', 'b'])] + inputs = {'subject': gandy.models.models.UncertaintyModel, + 'Xs': numpy.array(1), + 'Ys': numpy.array(1), + 'param_space': params, + } + def test___init__(self): + """Ensure only one validation option and proper saving of parameters""" + # expected success, no sessions specified, no val + subject = opt.SubjectObjective(**self.inputs) + self.assertTrue(subject.sessions is range(1)) + self.assertTrue(subject.param_space is params) + for att in ['k', 'val_data', 'val_frac']: + self.assertEqual(getattr(subject, att), None) + # specify sessions + subject = opt.SubjectObjective(**self.inputs, sessions=5) + self.assertTrue(subject.sessions is range(5)) + subject = opt.SubjectObjective(**self.inputs, sessions=['a', 'b']) + self.assertEqual(subject.sessions, ['a', 'b']) + # test proper validation handling + # k + subject = opt.SubjectObjective(**self.inputs, k=5) + self.assertTrue(subject.k is not None) + subject = opt.SubjectObjective(**self.inputs, + k=[(numpy.array(1),numpy.array(1))]) + self.assertTrue(subject.k is not None) + # val_data + subject = opt.SubjectObjective(**self.inputs, + val_data=(numpy.array(1), + numpy.array(1))) + self.assertTrue(subject.val_data is not None) + # val_frac + subject = opt.SubjectObjective(**self.inputs, + val_frac=0.5) + self.assertTrue(subject.val_frac is not None) + # test failure cases - cannot have two of these options + failure_cases = itertools.combinations( + ['k', 'val_data', 'val_frac'], 2 + ) + for fc in failure_cases: + kws = dict(zip(fc, ['keywordvalue1', 'keywordvalue2'])) + with self.assertRaises('ValueError'): + subject = opt.SubjectObjective(**self.inputs, **kws) + + # ensure proper saving of keyword arguments + subject = opt.SubjectObjective(**self.inputs, keyword1=5) + self.assertTrue('keyword1' in subject.kwargs.keys()) + + # kwarg and param overlap - would cause issues later + with self.assertRaises(ValueError): + subject = opt.SubjectObjective(**self.inputs, hyp1=5) + return + + @unittest.mock.patch('sklearn.model_selection.KFold') + def test_property_k(self, mocked_kfold): + """ensure proper handling of kfolds setting""" + mocked_kfold.split.return_value = ('train', 'test') + subject = opt.SubjectObjective(**self.inputs) + # int or iterable of tuple works + subject.k = 5 + mocked_kfold.assert_called() + mocked_kfold.split.called_with(subject.Xs) + self.assertTrue(mocked_kfold.split.call_count == 5) + self.assertTrue(type(subject.k) == list) + for f in subject.k: + self.assertTrue(type(f) == tuple and len(f) == 2) + test_folds = [(1,2), (1,2)] + subject.k = test_folds + self.assertEqual(test_folds, subject.k) + # failure case not either + with self.assertRaises(TypeError): + subject.k = 'str' + return + + def test_property_val_data(self): + """ability to check val data before saving""" + val_data_failures = [(1, 3), + [numpy.array(1), numpy.array(1)], + (numpy.array([1,2]), numpy.array(1))] + subject = opt.SubjectObjective(**self.inputs) + for val_data in val_data_failures: + with self.assertRaises(TypeError): + subject.val_data = val_data + + # success - tuple of arrays of the same length + val_data = (numpy.array([1,2,3]), numpy.array([1,2,3])) + subject.val_data = val_data + self.assertTrue(subject.val_data is not None) + return + + def test_property_val_frac(self): + """it should only accept float between 0 and 1""" + subject = opt.SubjectObjective(**self.inputs) + val_frac_failures = [0.0, 1.0, 2.0] + for val_frac in val_frac_failures: + with self.assertRaises(ValueError): + subject.val_frac = val_frac + with self.assertRaises(TypeError): + subject.val_frac = 'string' + return + + @unittest.mock.patch('optuna.trial.Trial'): + def test__sample_params(self, mocked_Trial): + """can the sampler get parameters from optuna methods""" + # prepare the mocked trial + trial = mocked_Trial() + subject = opt.SubjectObjective(**self.inputs) + # run the sample and test correct calls + params = subject._sample_params(trial) + mocked_Trial.suggest_int.assert_called_with( + trial, 'hyp1', 1, 10, 1 + ) + mocked_Trial.suggest_catagorical.assert_called_with( + trial, 'hyp2', ['a', 'b'] + ) + self.assertTrue(all(hyp in params.keys() for hyp in ['hyp1', 'hyp2'])) + return + + @unittest.mock.patch('import gandy.models.models.UncertaintyModel') + def test__execute_instance(self, mocked_UM): + """does the method instantialize and call the correct model methods""" + subject = opt.SubjectObjective(**self.inputs, xshape=(5,), keyword=5) + hyparams = {'hp1': 1, 'hp2': 2} + train_data = ('Xst', 'Yst') + val_data = ('Xsv', 'Ysv') + mocked_UM_in = unittest.mock.MagicMock() + mocked_UM.return_value = mocked_UM_in + mocked_UM_in.score.return_value = 'score' + # execute the instance + score = subject._execute_instance(hyparams, train_data, val_data) + mocked_UM.assert_called_with(xshape=(5,), + keyword=5, + hp1=1, + hp2=2) + mocked_UM_in.train.assert_called_with('Xst', 'Yst', + xshape=(5,), + keyword=5, + hp1=1, + hp2=2) + mocked_UM_in.score.assert_called_with('Xsv', 'Ysv', + xshape=(5,), + keyword=5, + hp1=1, + hp2=2) + self.assertTrue(score is 'score') + return + + @unittest.mock.patch('optuna.trial.Trial') + def test___call__(self, mocked_Trial): + """ability to identify different validation options and call the + correct methods""" + subject = opt.SubjectObjective(**self.inputs) + mocked_sample = unittest.mock.MagicMock( + return_value={'hp1': 1, 'hp2': 2} + ) + subject._sample_params = mocked_sample + mocked_execute = unittest.mock.MagicMock( + return_value='loss' + ) + subject._execute_instance = mocked_execute + trial = mocked_Trial() + + # start with k specifed - folds are arrays of indexes data len = 1 + first, second = (np.array(0), np.array(0)), (np.array(0), np.array(0)) + subject._k = [first, second] + subject.__call__(trial) + mocked_sample.assert_called_with(trial) + mocked_execute.assert_called_with( + {'hp1': 1, 'hp2': 2}, + (subject.Xs[second[0]], subject.Ys[second[0]]), + (subject.Xs[second[0]], subject.Ys[second[0]]) + ) + self.assertTrue(mocked_execute.call_count == 2) + trial.should_prune.assert_called() + # reset calls + mocked_execute.reset_mock() + mocked_sample.reset_mock() + trial.reset_mock() + + # val_data specifed + val_data = ('Xsv', 'Ysv') + subject._val_data = val_data + subject.__call__(trial) + mocked_sample.assert_called_with(trial) + mocked_execute.assert_called_with( + {'hp1': 1, 'hp2': 2}, + (subject.Xs, subject.Ys), + val_data + ) + self.assertTrue(mocked_execute.call_count == 1) + trial.should_prune.assert_called() + # reset calls + mocked_execute.reset_mock() + mocked_sample.reset_mock() + trial.reset_mock() + + # val frac specified + val_frac = 5.0 + subject._val_frac = val_frac + + with unittest.mock.patch('sklearn.model_selection.train_test_split', + return_value = 'Xt', 'Xv', 'Yt', 'Yv') as + mocked_tts: + subject.__call__(trial) + mocked_sample.assert_called_with(trial) + mocked_tts.assert_called_with(subject.Xs, subject.Ys, + test_size=val_frac) + mocked_execute.assert_called_with( + {'hp1': 1, 'hp2': 2}, + ('Xt', 'Yt'), + ('Xv', 'Yv') + ) + self.assertTrue(mocked_execute.call_count == 1) + trial.should_prune.assert_called() From e027f1e12f8474428e160a6ae5e6d42d8dea9913 Mon Sep 17 00:00:00 2001 From: evankomp Date: Fri, 5 Mar 2021 16:10:01 -0800 Subject: [PATCH 12/99] modelsm gps, hypersearch all pep8 compliant --- gandy/models/gps.py | 11 ++++--- gandy/models/hypersearch.py | 25 +++++++++------ gandy/models/models.py | 63 ++++++++++++++++++++----------------- 3 files changed, 58 insertions(+), 41 deletions(-) diff --git a/gandy/models/gps.py b/gandy/models/gps.py index 6ccc08b..e015796 100644 --- a/gandy/models/gps.py +++ b/gandy/models/gps.py @@ -19,7 +19,7 @@ score = cfr.evaluate(Xs, Ys, metric='mse') """ # imports -from typing import Type, Tuple, Union +from typing import Type, Tuple, Object import sklearn.gaussian_process import numpy @@ -27,7 +27,7 @@ import gandy.models.models # Typing -Model = Type[ucGaussianProcess] +Model = Type[Object] Array = Type[numpy.ndarray] Predictor = Type[sklearn.gaussian_process] @@ -74,6 +74,7 @@ def _build(self, # instatiate scikitlearn object with kwargs # . else # raise not implimented error + model = None return model def _train(self, @@ -119,12 +120,14 @@ def _predict(self, Returns: tuple of ndarray: array of predictions of targets with the same length as Xs - array of prediction uncertainties of targets withthe same length - as Xs + array of prediction uncertainties of targets withthe same + length as Xs """ # pseudocode # . get uncertainties and predictions by passing return_std to # sklearn object's predict + predictions = None + uncertainties = None return predictions, uncertainties @classmethod diff --git a/gandy/models/hypersearch.py b/gandy/models/hypersearch.py index 9acbdba..e6baa40 100644 --- a/gandy/models/hypersearch.py +++ b/gandy/models/hypersearch.py @@ -28,7 +28,7 @@ """ # imports -from typing import Tuple, Iterable, Any, Object, Type, List +from typing import Tuple, Iterable, Type, List, Union import optuna import numpy @@ -59,7 +59,8 @@ class SearchableSpace: Function to be used for sampling of hyperparams. hypname (str): Name of hyperparameter. - args (tuple): Positional arguments after name to be passed to func for sampling + args (tuple): Positional arguments after name to be passed to func for + sampling """ def __init__(self, hypname, space): @@ -111,8 +112,8 @@ def __init__(self, sessions: Union[int, List[str]] = None, k: Union[int, tuple] = None, val_data: Tuple[Array] = None, - val_frac: float = None - ** kwargs): + val_frac: float = None, + **kwargs): # pseudocode # . make sure no overlap of kwargs and param space # . store kwargs @@ -124,8 +125,8 @@ def __init__(self, def _sample_params(self, trial: Trial) -> dict: """Sample the hyperparameters to be used for this trial. - Uses space defined at self.param_space (dict of SearchableSpace instances) - to return values for this trial. + Uses space defined at self.param_space (dict of SearchableSpace + instances) to return values for this trial. Args: trial (optuna Trial): @@ -137,6 +138,7 @@ def _sample_params(self, trial: Trial) -> dict: """ # pseudocode # . hyparams = dict loop self.search_space trial.method(args) + hyparams = None return hyparams def _execute_instance(self, @@ -161,6 +163,7 @@ def _execute_instance(self, # . construct model with hyparms and self kwargs # . train model with hyparms and self kwargs # . score model with self kwargs + single_loss = None return single_loss def __call__(self, trial: Trial) -> float: @@ -181,6 +184,7 @@ def __call__(self, trial: Trial) -> float: # . split data based on above # . for each session, execute instances # . check for prune + loss = None return loss @@ -300,13 +304,15 @@ def optimize(self, # . set optimizer, study with all kwargs # . set self.best_params # . get best_score + best_score = None return best_score def train_best(self, **kwargs) -> Model: - """Train the subject on the entire dataset with the best found parameters. + """Train the subject on the entire dataset with the best found + parameters. - Requires self.optimize to have been executed or best_params to have been - specified. + Requires self.optimize to have been executed or best_params to have + been specified. Args: **kwargs: @@ -323,6 +329,7 @@ def train_best(self, **kwargs) -> Model: # . Initiate model with and best_params and kwargs # . train model with best_params training and kwargs # . set self.best_model + best_model = None return best_model @property diff --git a/gandy/models/models.py b/gandy/models/models.py index d2b4c77..099700b 100644 --- a/gandy/models/models.py +++ b/gandy/models/models.py @@ -10,11 +10,11 @@ `_build`, `_train`, and `_predict` in order to function properly. """ # imports -from typing import Tuple, Iterable, Any, Object, Type +from typing import Tuple, Iterable, Any, Type, Callable, Union import numpy -import gandy.metrics +# import gandy.metrics # typing Array = Type[numpy.ndarray] @@ -105,7 +105,7 @@ def build(self, **kwargs): # . set self model to _build return - def _build(self, *args, **kwargs) -> Object: + def _build(self, *args, **kwargs) -> Callable: """Construct and return the predictor. Must be implimented in child class. To creates and returns a predictor @@ -126,14 +126,14 @@ def _build(self, *args, **kwargs) -> Object: """ # pseudocode # . raise not implimented - # . model is None + model = None return model def train(self, Xs: Iterable, Ys: Iterable, - session: str = None, metric: Union[str, Callable], + session: str = None, **kwargs): """Train the predictor for one session, handled by `_train`. @@ -150,8 +150,9 @@ def train(self, Name of training session for storing in losses. default None, incriment new name. metric (str): - Metric to use, a key in UncertaintyModel.metrics or a metric object - that takes as input true, predicted, and uncertainty values. + Metric to use, a key in UncertaintyModel.metrics or a metric + objectthat takes as input true, predicted, and uncertainty + values. **kwargs: Keyword arguments to pass to `_train` and assign non-default \ training parameters. @@ -160,7 +161,7 @@ def train(self, # . check data inputs with check method - conver to numpy # . get metric method # . execute _train with formated data and metric (?) - # . update session losses with session _train losses - maybe create session name + # . update session losses with session _train losses return def _train(self, @@ -171,7 +172,8 @@ def _train(self, """Train the predictor. Must be implimented in child class. Trains the stored predictor - and returns any losses or desired metrics. Up to child to accept metric. + and returns any losses or desired metrics. Up to child to accept + metric. Args: Xs (Array): @@ -191,39 +193,40 @@ def _train(self, """ # psudocode # . raise not implimented - # . losses is None + losses = None return losses def predict(self, Xs: Iterable, uc_threshold: float = None, **kwargs) -> Tuple[Array]: - """Make predictions on a set of data and return predictions and uncertain- - ty arrays. + """Make predictions on a set of data and return predictions and + uncertainty arrays. - For a set of incoming data, check it and make predictions with the stored - model according to `_predict`. Optionally flag predictions whose uncert- - ainties excede a desired threshhold + For a set of incoming data, check it and make predictions with the + stored model according to `_predict`. Optionally flag predictions whose + uncertainties excede a desired threshhold. Args: Xs (Iterable): Examples data to make predictions of. - uc_threshold (float): acceptible amount of uncertainty. Predictions of - higher ucertainty values will be flagged + uc_threshold (float): acceptible amount of uncertainty. + Predictions of higher ucertainty values will be flagged **kwargs: keyword arguments to pass to `_predict` Returns: tuple of ndarray: array of predictions of targets with the same length as Xs - array of prediction uncertainties of targets withthe same length - as Xs - (optional) array of flags of uncertain predictions higher than thr- - eshhold of same length as Xs + array of prediction uncertainties of targets withthe same + length as Xs + (optional) array of flags of uncertain predictions higher + than threshhold of same length as Xs """ # pseudocode # . check X data with check function # . run _predict to return predictions and uncertainties # . if threshhold, return predictions, uncertainties, and flags + predictions, uncertainties, flags = None, None, None return predictions, uncertainties, flags def _predict(self, @@ -247,12 +250,13 @@ def _predict(self, Returns: tuple of ndarray: array of predictions of targets with the same length as Xs - array of prediction uncertainties of targets withthe same length - as Xs + array of prediction uncertainties of targets withthe same + length as Xs """ # psuedocode # . raise not implimented # . set pred, unc to None + predictions, uncertainties = None, None return predictions, uncertainties def score(self, @@ -272,8 +276,9 @@ def score(self, Ys (Iterable): Labels of data. metric (str): - Metric to use, a key in UncertaintyModel.metrics or a metric object - that takes as input true, predicted, and uncertainty values. + Metric to use, a key in UncertaintyModel.metrics or a metric + object that takes as input true, predicted, and uncertainty + values. **kwargs: keyword arguments to pass to `_predict` Returns: @@ -288,6 +293,7 @@ def score(self, # . check data # . predictions, uncertainties = execute self._predict on Xs # . pass predictions, uncertainties to metric get back costs + metric_value, metric_values = None, None return metric_value, metric_values def save(self, @@ -319,7 +325,7 @@ def _save(filename: str, filename (str): name of file to save model to """ - ## raise not implimented + # raise not implimented return @classmethod @@ -344,6 +350,7 @@ def load(cls, # . load the json and run cls(args) # . predictor = _load # . instance._model = predictor + instance = None return instance def _load(self, @@ -357,8 +364,8 @@ def _load(self, filename (str): path of file to load """ - ## raise not implimented - # . model = None + # raise not implimented + model = None return model @property From 145a80e50b04df8d0648dde49a38e82ab62399cf Mon Sep 17 00:00:00 2001 From: evankomp Date: Fri, 5 Mar 2021 16:41:26 -0800 Subject: [PATCH 13/99] test optimization progress --- .../test_hypersearch/test_hypersearch.py | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/gandy/tests/test_hypersearch/test_hypersearch.py b/gandy/tests/test_hypersearch/test_hypersearch.py index 47fa015..13ddc5e 100644 --- a/gandy/tests/test_hypersearch/test_hypersearch.py +++ b/gandy/tests/test_hypersearch/test_hypersearch.py @@ -276,3 +276,58 @@ def test___call__(self, mocked_Trial): ) self.assertTrue(mocked_execute.call_count == 1) trial.should_prune.assert_called() + # reset calls + mocked_execute.reset_mock() + mocked_sample.reset_mock() + trial.reset_mock() + subject._val_frac = None + + # None specified + with unittest.mock.patch('sklearn.model_selection.train_test_split', + return_value = 'Xt', 'Xv', 'Yt', 'Yv') as + mocked_tts: + subject.__call__(trial) + mocked_sample.assert_called_with(trial) + mocked_tts.assert_called_with(subject.Xs, subject.Ys, + test_size=0.8) + mocked_execute.assert_called_with( + {'hp1': 1, 'hp2': 2}, + ('Xt', 'Yt'), + ('Xv', 'Yv') + ) + self.assertTrue(mocked_execute.call_count == 1) + trial.should_prune.assert_called() + return + +class TestOptRoutine(unittest.TestCase): + """User interface class""" + + def test___init__(self): + """proper saving of keyword arguments and data saving""" + # failure case not correct model type + with self.assertRaises(TypeError): + subject = opt.OptRoutine(subject=opt.SearchableSpace, + Xs=numpy.array([1,2,3]), + Ys=numpy.array([1,2,3]), + search_space={'hyp1': (1,10), + 'hyp2': ['a', 'b']}, + keyword=5) + # failure case data not iterable + with self.assertRaises(TypeError): + subject = opt.OptRoutine(subject = gandy.models.models.\ + UncertaintyModel, + Xs='str', + Ys=numpy.array([1,2,3]), + search_space = {'hyp1': (1,10), + 'hyp2': ['a', 'b']}, + keyword=5) + with self.assertRaises(TypeError): + subject = opt.OptRoutine(subject=gandy.models.models.\ + UncertaintyModel, + Xs=numpy.array([1,2,3]), + Ys='str', + search_space={'hyp1': (1,10), + 'hyp2': ['a', 'b']}, + keyword=5) + + \ No newline at end of file From e288e7c8e5074f24f72c7fe6cab40d6be4d2fce2 Mon Sep 17 00:00:00 2001 From: Kyle Moskowitz Date: Fri, 5 Mar 2021 16:44:03 -0800 Subject: [PATCH 14/99] PEP8 style fixes --- gandy/quality_est/metrics.py | 154 ++++++++++++++++++----------------- 1 file changed, 81 insertions(+), 73 deletions(-) diff --git a/gandy/quality_est/metrics.py b/gandy/quality_est/metrics.py index c79836c..7360a5c 100644 --- a/gandy/quality_est/metrics.py +++ b/gandy/quality_est/metrics.py @@ -1,41 +1,40 @@ -'''Metrics module: contains some relevent metrics to assess the performance of machine learning models. - -This module implements a parent metric class that contains necessary initialization arguments and automatically -calls a calculate method to compute a given metric. Required intial arguments include the machine learning model -output predictions and real values for comparison. Optionally, the user may input uncertainties if a given model -outputs them. The properties of the parent class are then inhereted by individual children classes which define -the exact mathematical operations to compute a specific metric. Calling a specific metric child class will compute -a given metric and return the total value and/or invidual values of that metric based on the input data provided. +''' +Metrics module: contains some relevent metrics to assess the performance of +machine learning models. + +This module implements a parent metric class that contains necessary +initialization arguments and automatically calls a calculate method to +compute a given metric. Required intial arguments include the machine +learning model output predictions and real values for comparison. Optionally, +the user may input uncertainties if a given model outputs them. The +properties of the parent class are then inhereted by individual children +classes which define the exact mathematical operations to compute a specific +metric. Calling a specific metric child class will compute a given metric and +return the total value and/or individual values of that metric based on the +input data provided. ''' # Imports -from typing import Tuple, Iterable, Any, Object, Type, List +from typing import Type, Tuple import numpy as np -import math # Typing -Array = Type[numpy.ndarray] +Array = Type[np.ndarray] -# Define dictionary of available metrics -Metric_codex = {} # Parent class for metric - - class Metric: ''' - - Implements metric parent class. This class will define the structure of various quality - evaluation techniques used for comparing the uncertainty model outputs to real - experimental data. Children classes will inherent properties of this class. - + Implements metric parent class. This class will define the structure of + various quality evaluation techniques used for comparing the uncertainty + model outputs to real experimental data. Children classes will inherent + properties of this class. ''' - def __init__(self, predictions: Array, real: Array, uncertainties=None): ''' - - Initializes an instance of the metric class, including the predictions, uncertainties (optional), and real data + Initializes an instance of the metric class, including the + predictions, uncertainties (optional), and real data necessary for comparison. Arg: @@ -43,21 +42,22 @@ def __init__(self, predictions: Array, real: Array, uncertainties=None): Array of predictions generated from the uncertainty model real(ndarray): - Array of real values that you want to compare the uncertainty model ouput to (eg. experimental data) + Array of real values that you want to compare the uncertainty + model ouput to (eg. experimental data) uncertainties(ndarray): - Optional argument which contains array of uncertainty values generated from the uncertainty module - + Optional argument which contains array of uncertainty values + generated from the uncertainty module ''' # psuedocode # set self.predictions # set self.real # set self.uncertainties # call calculate function within init: self.calculate() - return - def calculate(self, **kwargs): - '''Empty calculate function''' + ''' + Empty calculate function + ''' return @@ -67,31 +67,34 @@ class MSE(Metric): Mean Squared Error class which defines the structure used for computing the MSE between the passed in datasets. Inherets the properties of the parent class Metrics. - ''' def calculate(self, **kwargs) -> Tuple[float, Array]: - ''' - Method that defines the mathematical formula necessary to compute the MSE. + ''' + Method that defines the mathematical formula necessary to compute the + MSE. - Args: + Args: - **kwargs: - Necessary keyword arguments to be passed into calculate() method + **kwargs: + Necessary keyword arguments to be passed into calculate() + method - Returns: + Returns: - MSE_value(float): - Total value of the Mean Squared error computed + MSE_value(float): + Total value of the MSE computed - MSE_values(ndarray): - An array of MSE scores for each prediction + MSE_values(ndarray): + An array of MSE scores for each prediction - ''' + ''' # pseudocode - # define mathematical formula to carry out MSE calculations using self.args variables - # iteration over arrays likely and plug into defined formula - return MSE_value, MSE_values + # define mathematical formula for MSE calculation using self.args + # iteration over arrays likely, then plug into defined formula + MSE_value = None + MSE_values = None + return MSE_value, MSE_values class RMSE(Metric): @@ -99,57 +102,62 @@ class RMSE(Metric): Root Mean Squared Error class which defines the structure used for computing the RMSE between the passed in datasets. Inherets the properties of the parent class Metrics. - ''' def calculate(self, **kwargs) -> Tuple[float, Array]: - ''' - Method that defines the mathematical formula necessary to compute the RMSE. + ''' + Method that defines the mathematical formula necessary to compute the + RMSE. - Args: + Args: - **kwargs: - necessary keyword arguments to be passed into calculate() method + **kwargs: + Necessary keyword arguments to be passed into calculate() + method - Returns: + Returns: - RMSE_value(float): - Total value of the RMSE computed + RMSE_value(float): + Total value of the RMSE computed - RMSE_values(ndarray): - Array of RMSE values for each prediction + RMSE_values(ndarray): + Array of RMSE values for each prediction - ''' + ''' # pseudocode - # define mathematical formula to carry out RMSE calculations using self.args variables + # define mathematical formula for RMSE calculations using self.args # iteration over arrays and plug into defined formula - return RMSE_value, RMSE_values + RMSE_value = None + RMSE_values = None + return RMSE_value, RMSE_values class F1(Metric): ''' - F1 score class which defines the structure used for - computing the F1 score between the passed in datasets. - Inherets the properties of the parent class Metrics. - + F1 score class which defines the structure used forcomputing the F1 score + between the passed in datasets. Inherets the properties of the parent + class Metrics. ''' def calculate(self, **kwargs) -> float: - ''' - Method that defines the mathematical formula necessary to compute the RMSE. + ''' + Method that defines the mathematical formula necessary to compute + the RMSE. - Args: + Args: - **kwargs: - Necessary keyword arguments to be passed into calculate() method + **kwargs: + Necessary keyword arguments to be passed into calculate() + method - Returns: + Returns: - F1_value(float): - Value of the F1 score computed + F1_value(float): + Value of the F1 score computed - ''' + ''' # pseudocode - # define mathematical formula to carry out F1 calculation using self.args variables + # define mathematical formula for F1 calculation using self.args variables # iteration over arrays and plug into formula - return F1_value + F1_value = None + return F1_value From b3b2b2b1b6998a2b989f4ea22ca7ad8c38d6b44f Mon Sep 17 00:00:00 2001 From: evankomp Date: Fri, 5 Mar 2021 16:54:46 -0800 Subject: [PATCH 15/99] removed useless tests --- gandy/tests/test_test.py | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 gandy/tests/test_test.py diff --git a/gandy/tests/test_test.py b/gandy/tests/test_test.py deleted file mode 100644 index 661934c..0000000 --- a/gandy/tests/test_test.py +++ /dev/null @@ -1,4 +0,0 @@ -class TestDumbFunction(unittest.TestCase): - - def test_dumb(self): - self.assertEqual(True, False) From 841afcead6803f78974f66d96f20b7964f4d4a64 Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Fri, 5 Mar 2021 17:13:03 -0800 Subject: [PATCH 16/99] Pep8 --- gandy/models/bnns.py | 23 ++++++++++++++++------- gandy/models/gans.py | 13 ++++++++++--- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/gandy/models/bnns.py b/gandy/models/bnns.py index f6b229b..714f271 100644 --- a/gandy/models/bnns.py +++ b/gandy/models/bnns.py @@ -1,21 +1,24 @@ -''' +""" +Bayes NN. + This contains the Bayes NN class, based on the KEras tutorial at https://keras.io/examples/keras_recipes/bayesian_neural_networks/ -''' +""" # imports import gandy.models.models -import tensorflow as tf +# import tensorflow as tf # typing imports -from typing import Tuple, Iterable, Any, Object, Type +from typing import Tuple, Any, Object, Type # typing +import numpy Array = Type[numpy.ndarray] class bnn(gandy.models.models.UncertaintyModel): - ''' + """ Implements a Bayesian Neural Network (BNN) BNNS place a prior on the weights of the network and apply Bayes rule. The object of the Bayesian approach for modeling neural networks is to @@ -30,7 +33,7 @@ class bnn(gandy.models.models.UncertaintyModel): Thank you to https://keras.io/examples/keras_recipes/bayesian_neural_networks/ for a guide to implementing a BNN with Keras. - ''' + """ def create_model_inputs(self, feature_names) -> Tuple[Object]: ''' @@ -48,7 +51,8 @@ def create_model_inputs(self, feature_names) -> Tuple[Object]: # inputs[feature_name] = tf.keras.layers.Input( # name=feature_name, shape=(1,), dtype=tf.float32 # ) - # return inputs + inputs = None + return inputs def prior(kernel_size, bias_size, dtype=None) -> Object: ''' @@ -73,6 +77,7 @@ def prior(kernel_size, bias_size, dtype=None) -> Object: # ) # ] # ) + prior_model = None return prior_model # Define variational posterior weight distribution as multivariate @@ -99,6 +104,7 @@ def posterior(kernel_size, bias_size, dtype=None) -> Object: # tfp.layers.MultivariateNormalTriL(n), # ] # ) + posterior_model = None return posterior_model # Since the output of the model is a distribution, rather than a @@ -198,6 +204,7 @@ def _train(self, ters or pass to nested functions. ''' # losses = self.model.fit(Xs, **kwargs) + losses = None return losses # overridden method from UncertaintyModel class @@ -224,6 +231,7 @@ def _predict(self, # mean, std = self.model.evaluate(Xs, **kwargs) # BNN model returns mean and variance as output # convert to predictions and uncertainties + predictions, uncertainties = None, None return predictions, uncertainties def _save(filename: str, **kwargs): @@ -248,4 +256,5 @@ def _load(self, filename: str, **kwargs): path of file to load """ # call Keras.load function + model = None return model diff --git a/gandy/models/gans.py b/gandy/models/gans.py index 2da7717..f8268ac 100644 --- a/gandy/models/gans.py +++ b/gandy/models/gans.py @@ -14,9 +14,10 @@ import tensorflow as tf # typing imports -from typing import Tuple, Iterable, Any, Object, Type +from typing import Tuple, Any, Object, Type # typing +import numpy Array = Type[numpy.ndarray] @@ -88,7 +89,7 @@ def get_noise_input_shape(self, **kwargs) -> Tuple[int]: ''' Returns the shape of the noise vector ''' - return noise.shape + return # noise.shape def get_data_input_shapes(self, **kwargs) -> Tuple[int]: ''' @@ -124,7 +125,7 @@ def _train(self, **kwargs - keyword arguments to assign non-default training parame- ters or pass to nested functions. ''' - + losses = None return losses # overridden method from UncertaintyModel class @@ -154,6 +155,7 @@ def _predict(self, # generated_points = gan.predict_gan_generator( # conditional_inputs=[one_hot_Ys]) # the above code generates points, but we need uncertainties as well + predictions, uncertainties = None, None return predictions, uncertainties def _save(filename: str, **kwargs): @@ -166,6 +168,8 @@ def _save(filename: str, **kwargs): name of file to save model to """ # save model aka generator and discriminator separately + # assert filename.endswith('.h5') or other extension + # self.generator.save(filename) return None def _load(self, filename: str, **kwargs): @@ -178,6 +182,9 @@ def _load(self, filename: str, **kwargs): path of file to load """ # call Keras.load function + # two filenames, one for gen and one for discrim? + # model = tf.keras.model.load_model(filename, compile=False) + model = None return model From c48b3ca45fa650346299eab328b1220f4e8dc646 Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Fri, 5 Mar 2021 17:18:10 -0800 Subject: [PATCH 17/99] Pep8 work.... --- gandy/models/gans.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gandy/models/gans.py b/gandy/models/gans.py index f8268ac..b458e38 100644 --- a/gandy/models/gans.py +++ b/gandy/models/gans.py @@ -11,7 +11,7 @@ # deep learning imports import deepchem -import tensorflow as tf +# import tensorflow as tf # typing imports from typing import Tuple, Any, Object, Type @@ -89,7 +89,7 @@ def get_noise_input_shape(self, **kwargs) -> Tuple[int]: ''' Returns the shape of the noise vector ''' - return # noise.shape + return # noise.shape def get_data_input_shapes(self, **kwargs) -> Tuple[int]: ''' From 11c0f26ec0940f7934c129c3dca5b579472691e5 Mon Sep 17 00:00:00 2001 From: evankomp Date: Fri, 5 Mar 2021 17:27:40 -0800 Subject: [PATCH 18/99] almost done with final hyperopt tests --- .../test_hypersearch/test_hypersearch.py | 52 ++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/gandy/tests/test_hypersearch/test_hypersearch.py b/gandy/tests/test_hypersearch/test_hypersearch.py index 13ddc5e..4e65784 100644 --- a/gandy/tests/test_hypersearch/test_hypersearch.py +++ b/gandy/tests/test_hypersearch/test_hypersearch.py @@ -329,5 +329,55 @@ def test___init__(self): search_space={'hyp1': (1,10), 'hyp2': ['a', 'b']}, keyword=5) - + # expected success + subject = opt.OptRoutine(subject = gandy.models.models.\ + UncertaintyModel, + Xs=numpy.array([1,2,3]), + Ys=numpy.array([1,2,3]), + search_space = {'hyp1': (1,10), + 'hyp2': ['a', 'b']}, + keyword=5) + self.assertTrue(subject.Xs is not None) + self.assertTrue(subject.Ys is not None) + self.assertTrue(self.subject == gandy.models.models.UncertaintyModel) + self.assertEqual(subject.search_space, {'hyp1': (1,10), + 'hyp2': ['a', 'b']}) + self.assertTrue('keyword' in subject.all_kwargs.keys()) + return + + @unittest.mock.patch('gandy.optimization.hypersearch.SearchableSpace') + def test__set_param_space(self, mocked_SS): + """proper parsing of dictionary into SearchableSpace objects""" + mocked_SS.side_effect = ['ss1', 'ss2'] + subject = opt.OptRoutine(subject = gandy.models.models.\ + UncertaintyModel, + Xs=numpy.array([1,2,3]), + Ys=numpy.array([1,2,3]), + search_space = {'hyp1': (1,10), + 'hyp2': ['a', 'b']}, + keyword=5) + mocked_SS.assert_called_with('hyp2', ['a', 'b']) + self.assertEqual(mocked_SS.call_count, 2) + return + + @unittest.mock.patch('gandy.optimization.hypersearch.SubjectObjective') + def test__set_objective(self, mocked_objective): + """ensure proper calling of SubjectObjective class""" + mocked_objective.return_value = 'objective' + subject = opt.OptRoutine(subject = gandy.models.models.\ + UncertaintyModel, + Xs=numpy.array([1,2,3]), + Ys=numpy.array([1,2,3]), + search_space = {'hyp1': (1,10), + 'hyp2': ['a', 'b']}, + keyword=5) + mocked__set_param = unittest.mock.MagicMock() + subject._set_param_space = mocked__set_param + # set the objective + subject._set_objective() + mocked_objective.assert_called_with(subject.subject, + subject.Xs, + subject.Ys, + **subject.all_kwargs) + self.assertEqual(subject.objective, 'objective') \ No newline at end of file From ddfdd5dfc614d20802a321ccdaa8a46037e7dc69 Mon Sep 17 00:00:00 2001 From: evankomp Date: Fri, 5 Mar 2021 17:28:46 -0800 Subject: [PATCH 19/99] see last commit --- gandy/optimization/hypersearch.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/gandy/optimization/hypersearch.py b/gandy/optimization/hypersearch.py index 9a50920..d280148 100644 --- a/gandy/optimization/hypersearch.py +++ b/gandy/optimization/hypersearch.py @@ -274,8 +274,6 @@ def _set_param_space(self, **kwargs): if specified. """ ## pseudocode - #. check self.search_space input - # raise if None #. create empty param_space #. for loop self.param_space # param_space = SearchableSpace class From 644239b8079fbb40f4e99c34af13b6bfa132bbf7 Mon Sep 17 00:00:00 2001 From: evankomp Date: Fri, 5 Mar 2021 18:48:42 -0800 Subject: [PATCH 20/99] opt tests written and flaked --- gandy/optimization/hypersearch.py | 18 +- .../test_hypersearch/test_hypersearch.py | 267 ++++++++++++------ 2 files changed, 178 insertions(+), 107 deletions(-) diff --git a/gandy/optimization/hypersearch.py b/gandy/optimization/hypersearch.py index 4e9808f..4e10804 100644 --- a/gandy/optimization/hypersearch.py +++ b/gandy/optimization/hypersearch.py @@ -276,7 +276,7 @@ def __init__(self, # . save all_kwargs return - def _set_param_space(self, **kwargs): + def _set_param_space(self): """Define the search space from user input search space according to gandy.models.hypersearch.SearchableSpace class. @@ -290,7 +290,7 @@ def _set_param_space(self, **kwargs): # set self._param_space return - def _set_objective(self, **kwargs): + def _set_objective(self): """Define the objective function for optuna to target when optimizing hyperparameters. @@ -316,7 +316,7 @@ def _set_objective(self, **kwargs): # . set self.objective return - def _set_study(self, **kwargs): + def _set_study(self): """Define the optuna study with create_study. Not meant to be interacted with directly. Creates the study to be used @@ -381,15 +381,3 @@ def train_best(self, **kwargs) -> Model: # . set self.best_model best_model = None return best_model - - @property - def search_space(self): - """dict: hyperparameter name to search space parirings""" - return self._search_space - - @search_space.setter - def search_space(self, new_search_space): - # pseudocode - # . check dict - self._search_space = new_search_space - return diff --git a/gandy/tests/test_hypersearch/test_hypersearch.py b/gandy/tests/test_hypersearch/test_hypersearch.py index 4e65784..3d12cc8 100644 --- a/gandy/tests/test_hypersearch/test_hypersearch.py +++ b/gandy/tests/test_hypersearch/test_hypersearch.py @@ -1,4 +1,5 @@ """Testing hyperparameter optimization with optuna""" + import itertools import unittest import unittest.mock @@ -9,12 +10,13 @@ import gandy.optimization.optimization as opt import gandy.models.models + class TestSearchableSpace(unittest.TestCase): - + def test_class(self): """try all possible searchable spaces""" NAME = 'hypname' - + # uniform float spaces = [(2.0, 4.0), (2.0, 4.0, 'uniform')] for space in spaces: @@ -22,22 +24,22 @@ def test_class(self): self.assertEqual(subject.name, NAME) self.assertEqual(subject.args, (2.0, 4.0)) self.assertTrue(subject.func is optuna.trial.Trial.suggest_uniform) - + # loguniform float space = (3.0, 8.0, 'loguniform') subject = opt.SearchableSpace(NAME, space) self.assertEqual(subject.name, NAME) self.assertEqual(subject.args, (3.0, 8.0)) self.assertTrue(subject.func is optuna.trial.Trial.suggest_loguniform) - + # discrete uniform space = (5.0, 10.0, 2.0) subject = opt.SearchableSpace(NAME, space) self.assertEqual(subject.name, NAME) self.assertEqual(subject.args, (5.0, 10.0, 2.0)) - self.assertTrue(subject.func is + self.assertTrue(subject.func is optuna.trial.Trial.suggest_discrete_uniform) - + # int space = (2, 10) subject = opt.SearchableSpace(NAME, space) @@ -49,7 +51,7 @@ def test_class(self): self.assertEqual(subject.name, NAME) self.assertEqual(subject.args, (2, 10, 3)) self.assertTrue(subject.func is optuna.trial.Trial.suggest_int) - + # catagorical space = ['a', 'b', 'c'] subject = opt.SearchableSpace(NAME, space) @@ -58,22 +60,23 @@ def test_class(self): self.assertTrue(subject.func is optuna.trial.Trial.suggest_catagorical) return - + class TestSubjectObjective(unittest.TestCase): - - params = [opt.SearchableSpace('hyp1', (1,10)), + + params = [opt.SearchableSpace('hyp1', (1, 10)), opt.SearchableSpace('hyp2', ['a', 'b'])] inputs = {'subject': gandy.models.models.UncertaintyModel, 'Xs': numpy.array(1), 'Ys': numpy.array(1), 'param_space': params, - } + } + def test___init__(self): """Ensure only one validation option and proper saving of parameters""" # expected success, no sessions specified, no val subject = opt.SubjectObjective(**self.inputs) self.assertTrue(subject.sessions is range(1)) - self.assertTrue(subject.param_space is params) + self.assertTrue(subject.param_space is self.params) for att in ['k', 'val_data', 'val_frac']: self.assertEqual(getattr(subject, att), None) # specify sessions @@ -85,16 +88,16 @@ def test___init__(self): # k subject = opt.SubjectObjective(**self.inputs, k=5) self.assertTrue(subject.k is not None) - subject = opt.SubjectObjective(**self.inputs, - k=[(numpy.array(1),numpy.array(1))]) + subject = opt.SubjectObjective(**self.inputs, + k=[(numpy.array(1), numpy.array(1))]) self.assertTrue(subject.k is not None) # val_data - subject = opt.SubjectObjective(**self.inputs, + subject = opt.SubjectObjective(**self.inputs, val_data=(numpy.array(1), numpy.array(1))) self.assertTrue(subject.val_data is not None) # val_frac - subject = opt.SubjectObjective(**self.inputs, + subject = opt.SubjectObjective(**self.inputs, val_frac=0.5) self.assertTrue(subject.val_frac is not None) # test failure cases - cannot have two of these options @@ -105,16 +108,16 @@ def test___init__(self): kws = dict(zip(fc, ['keywordvalue1', 'keywordvalue2'])) with self.assertRaises('ValueError'): subject = opt.SubjectObjective(**self.inputs, **kws) - + # ensure proper saving of keyword arguments subject = opt.SubjectObjective(**self.inputs, keyword1=5) self.assertTrue('keyword1' in subject.kwargs.keys()) - + # kwarg and param overlap - would cause issues later with self.assertRaises(ValueError): subject = opt.SubjectObjective(**self.inputs, hyp1=5) return - + @unittest.mock.patch('sklearn.model_selection.KFold') def test_property_k(self, mocked_kfold): """ensure proper handling of kfolds setting""" @@ -125,33 +128,33 @@ def test_property_k(self, mocked_kfold): mocked_kfold.assert_called() mocked_kfold.split.called_with(subject.Xs) self.assertTrue(mocked_kfold.split.call_count == 5) - self.assertTrue(type(subject.k) == list) + self.assertTrue(isinstance(subject.k, list)) for f in subject.k: - self.assertTrue(type(f) == tuple and len(f) == 2) - test_folds = [(1,2), (1,2)] + self.assertTrue(isinstance(f, tuple) and len(f) == 2) + test_folds = [(1, 2), (1, 2)] subject.k = test_folds self.assertEqual(test_folds, subject.k) # failure case not either with self.assertRaises(TypeError): subject.k = 'str' return - + def test_property_val_data(self): """ability to check val data before saving""" - val_data_failures = [(1, 3), + val_data_failures = [(1, 3), [numpy.array(1), numpy.array(1)], - (numpy.array([1,2]), numpy.array(1))] + (numpy.array([1, 2]), numpy.array(1))] subject = opt.SubjectObjective(**self.inputs) for val_data in val_data_failures: with self.assertRaises(TypeError): subject.val_data = val_data - + # success - tuple of arrays of the same length - val_data = (numpy.array([1,2,3]), numpy.array([1,2,3])) + val_data = (numpy.array([1, 2, 3]), numpy.array([1, 2, 3])) subject.val_data = val_data self.assertTrue(subject.val_data is not None) return - + def test_property_val_frac(self): """it should only accept float between 0 and 1""" subject = opt.SubjectObjective(**self.inputs) @@ -162,8 +165,8 @@ def test_property_val_frac(self): with self.assertRaises(TypeError): subject.val_frac = 'string' return - - @unittest.mock.patch('optuna.trial.Trial'): + + @unittest.mock.patch('optuna.trial.Trial') def test__sample_params(self, mocked_Trial): """can the sampler get parameters from optuna methods""" # prepare the mocked trial @@ -179,7 +182,7 @@ def test__sample_params(self, mocked_Trial): ) self.assertTrue(all(hyp in params.keys() for hyp in ['hyp1', 'hyp2'])) return - + @unittest.mock.patch('import gandy.models.models.UncertaintyModel') def test__execute_instance(self, mocked_UM): """does the method instantialize and call the correct model methods""" @@ -187,31 +190,31 @@ def test__execute_instance(self, mocked_UM): hyparams = {'hp1': 1, 'hp2': 2} train_data = ('Xst', 'Yst') val_data = ('Xsv', 'Ysv') - mocked_UM_in = unittest.mock.MagicMock() + mocked_UM_in = unittest.mock.MagicMock() mocked_UM.return_value = mocked_UM_in mocked_UM_in.score.return_value = 'score' # execute the instance score = subject._execute_instance(hyparams, train_data, val_data) - mocked_UM.assert_called_with(xshape=(5,), + mocked_UM.assert_called_with(xshape=(5,), keyword=5, hp1=1, hp2=2) mocked_UM_in.train.assert_called_with('Xst', 'Yst', - xshape=(5,), + xshape=(5,), keyword=5, hp1=1, hp2=2) mocked_UM_in.score.assert_called_with('Xsv', 'Ysv', - xshape=(5,), + xshape=(5,), keyword=5, hp1=1, hp2=2) - self.assertTrue(score is 'score') + self.assertTrue(score == 'score') return - + @unittest.mock.patch('optuna.trial.Trial') def test___call__(self, mocked_Trial): - """ability to identify different validation options and call the + """ability to identify different validation options and call the correct methods""" subject = opt.SubjectObjective(**self.inputs) mocked_sample = unittest.mock.MagicMock( @@ -223,15 +226,16 @@ def test___call__(self, mocked_Trial): ) subject._execute_instance = mocked_execute trial = mocked_Trial() - + # start with k specifed - folds are arrays of indexes data len = 1 - first, second = (np.array(0), np.array(0)), (np.array(0), np.array(0)) + first, second = (numpy.array(0), numpy.array(0)), \ + (numpy.array(0), numpy.array(0)) subject._k = [first, second] subject.__call__(trial) mocked_sample.assert_called_with(trial) mocked_execute.assert_called_with( - {'hp1': 1, 'hp2': 2}, - (subject.Xs[second[0]], subject.Ys[second[0]]), + {'hp1': 1, 'hp2': 2}, + (subject.Xs[second[0]], subject.Ys[second[0]]), (subject.Xs[second[0]], subject.Ys[second[0]]) ) self.assertTrue(mocked_execute.call_count == 2) @@ -240,15 +244,15 @@ def test___call__(self, mocked_Trial): mocked_execute.reset_mock() mocked_sample.reset_mock() trial.reset_mock() - + # val_data specifed val_data = ('Xsv', 'Ysv') subject._val_data = val_data subject.__call__(trial) mocked_sample.assert_called_with(trial) mocked_execute.assert_called_with( - {'hp1': 1, 'hp2': 2}, - (subject.Xs, subject.Ys), + {'hp1': 1, 'hp2': 2}, + (subject.Xs, subject.Ys), val_data ) self.assertTrue(mocked_execute.call_count == 1) @@ -257,21 +261,22 @@ def test___call__(self, mocked_Trial): mocked_execute.reset_mock() mocked_sample.reset_mock() trial.reset_mock() - + # val frac specified val_frac = 5.0 subject._val_frac = val_frac - - with unittest.mock.patch('sklearn.model_selection.train_test_split', - return_value = 'Xt', 'Xv', 'Yt', 'Yv') as - mocked_tts: + + with unittest.mock.patch( + 'sklearn.model_selection.train_test_split', + return_value=('Xt', 'Xv', 'Yt', 'Yv') + ) as mocked_tts: subject.__call__(trial) mocked_sample.assert_called_with(trial) - mocked_tts.assert_called_with(subject.Xs, subject.Ys, + mocked_tts.assert_called_with(subject.Xs, subject.Ys, test_size=val_frac) mocked_execute.assert_called_with( - {'hp1': 1, 'hp2': 2}, - ('Xt', 'Yt'), + {'hp1': 1, 'hp2': 2}, + ('Xt', 'Yt'), ('Xv', 'Yv') ) self.assertTrue(mocked_execute.call_count == 1) @@ -281,95 +286,98 @@ def test___call__(self, mocked_Trial): mocked_sample.reset_mock() trial.reset_mock() subject._val_frac = None - + # None specified - with unittest.mock.patch('sklearn.model_selection.train_test_split', - return_value = 'Xt', 'Xv', 'Yt', 'Yv') as - mocked_tts: + with unittest.mock.patch( + 'sklearn.model_selection.train_test_split', + return_value=('Xt', 'Xv', 'Yt', 'Yv') + ) as mocked_tts: subject.__call__(trial) mocked_sample.assert_called_with(trial) - mocked_tts.assert_called_with(subject.Xs, subject.Ys, + mocked_tts.assert_called_with(subject.Xs, subject.Ys, test_size=0.8) mocked_execute.assert_called_with( - {'hp1': 1, 'hp2': 2}, - ('Xt', 'Yt'), + {'hp1': 1, 'hp2': 2}, + ('Xt', 'Yt'), ('Xv', 'Yv') ) self.assertTrue(mocked_execute.call_count == 1) trial.should_prune.assert_called() return - + + class TestOptRoutine(unittest.TestCase): """User interface class""" - + def test___init__(self): """proper saving of keyword arguments and data saving""" # failure case not correct model type with self.assertRaises(TypeError): subject = opt.OptRoutine(subject=opt.SearchableSpace, - Xs=numpy.array([1,2,3]), - Ys=numpy.array([1,2,3]), - search_space={'hyp1': (1,10), - 'hyp2': ['a', 'b']}, + Xs=numpy.array([1, 2, 3]), + Ys=numpy.array([1, 2, 3]), + search_space={'hyp1': (1, 10), + 'hyp2': ['a', 'b']}, keyword=5) # failure case data not iterable with self.assertRaises(TypeError): - subject = opt.OptRoutine(subject = gandy.models.models.\ + subject = opt.OptRoutine(subject=gandy.models.models. UncertaintyModel, Xs='str', - Ys=numpy.array([1,2,3]), - search_space = {'hyp1': (1,10), - 'hyp2': ['a', 'b']}, + Ys=numpy.array([1, 2, 3]), + search_space={'hyp1': (1, 10), + 'hyp2': ['a', 'b']}, keyword=5) with self.assertRaises(TypeError): - subject = opt.OptRoutine(subject=gandy.models.models.\ + subject = opt.OptRoutine(subject=gandy.models.models. UncertaintyModel, - Xs=numpy.array([1,2,3]), + Xs=numpy.array([1, 2, 3]), Ys='str', - search_space={'hyp1': (1,10), + search_space={'hyp1': (1, 10), 'hyp2': ['a', 'b']}, keyword=5) # expected success - subject = opt.OptRoutine(subject = gandy.models.models.\ + subject = opt.OptRoutine(subject=gandy.models.models. UncertaintyModel, - Xs=numpy.array([1,2,3]), - Ys=numpy.array([1,2,3]), - search_space = {'hyp1': (1,10), - 'hyp2': ['a', 'b']}, + Xs=numpy.array([1, 2, 3]), + Ys=numpy.array([1, 2, 3]), + search_space={'hyp1': (1, 10), + 'hyp2': ['a', 'b']}, keyword=5) self.assertTrue(subject.Xs is not None) self.assertTrue(subject.Ys is not None) self.assertTrue(self.subject == gandy.models.models.UncertaintyModel) - self.assertEqual(subject.search_space, {'hyp1': (1,10), + self.assertEqual(subject.search_space, {'hyp1': (1, 10), 'hyp2': ['a', 'b']}) self.assertTrue('keyword' in subject.all_kwargs.keys()) return - + @unittest.mock.patch('gandy.optimization.hypersearch.SearchableSpace') def test__set_param_space(self, mocked_SS): """proper parsing of dictionary into SearchableSpace objects""" mocked_SS.side_effect = ['ss1', 'ss2'] - subject = opt.OptRoutine(subject = gandy.models.models.\ + subject = opt.OptRoutine(subject=gandy.models.models. UncertaintyModel, - Xs=numpy.array([1,2,3]), - Ys=numpy.array([1,2,3]), - search_space = {'hyp1': (1,10), - 'hyp2': ['a', 'b']}, + Xs=numpy.array([1, 2, 3]), + Ys=numpy.array([1, 2, 3]), + search_space={'hyp1': (1, 10), + 'hyp2': ['a', 'b']}, keyword=5) + subject._set_param_space() mocked_SS.assert_called_with('hyp2', ['a', 'b']) self.assertEqual(mocked_SS.call_count, 2) return - + @unittest.mock.patch('gandy.optimization.hypersearch.SubjectObjective') def test__set_objective(self, mocked_objective): """ensure proper calling of SubjectObjective class""" mocked_objective.return_value = 'objective' - subject = opt.OptRoutine(subject = gandy.models.models.\ + subject = opt.OptRoutine(subject=gandy.models.models. UncertaintyModel, - Xs=numpy.array([1,2,3]), - Ys=numpy.array([1,2,3]), - search_space = {'hyp1': (1,10), - 'hyp2': ['a', 'b']}, + Xs=numpy.array([1, 2, 3]), + Ys=numpy.array([1, 2, 3]), + search_space={'hyp1': (1, 10), + 'hyp2': ['a', 'b']}, keyword=5) mocked__set_param = unittest.mock.MagicMock() subject._set_param_space = mocked__set_param @@ -380,4 +388,79 @@ def test__set_objective(self, mocked_objective): subject.Ys, **subject.all_kwargs) self.assertEqual(subject.objective, 'objective') - \ No newline at end of file + mocked__set_param.assert_called() + return + + @unittest.mock.patch('optuna.create_study', return_value='study') + def test__set_study(self, mocked_cstudy): + """Can a study be correctly called and stored""" + subject = opt.OptRoutine(subject=gandy.models.models. + UncertaintyModel, + Xs=numpy.array([1, 2, 3]), + Ys=numpy.array([1, 2, 3]), + search_space={'hyp1': (1, 10), + 'hyp2': ['a', 'b']}, + keyword=5) + subject._set_study() + self.assertTrue(subject.study == 'study') + mocked_cstudy.assert_called_with(**subject.all_kwargs) + return + + def test_optimize(self): + """acceptance of kwargs and nested calls""" + subject = opt.OptRoutine(subject=gandy.models.models. + UncertaintyModel, + Xs=numpy.array([1, 2, 3]), + Ys=numpy.array([1, 2, 3]), + keyword=5) + + # failure mode no seach space specified + with self.assertRaises(AttributeError): + subject.optimize() + + # set up mocked objects + mocked_set_obj = unittest.mock.MagicMock() + mocked_set_study = unittest.mock.MagicMock() + mocked_study = unittest.mock.MagicMock() + subject._set_objective = mocked_set_obj + subject._set_study = mocked_set_study + subject.study = mocked_study + + # success case, set search space and pass new kwargs + best_score = subject.optimize(search_space={'hyp1': (1, 10), + 'hyp2': ['a', 'b']}, + keyword2=10) + mocked_set_obj.assert_called() + mocked_set_study.assert_called() + mocked_study.assert_called_with( + subject.objective, **subject.all_kwargs) + self.assertTrue(best_score is mocked_study.best_trial.value) + self.assertTrue(subject.best_params is mocked_study.best_trial.params) + self.assertTrue('keyword2' in subject.all_kwargs.keys()) + return + + @unittest.mock.patch('gandy.models.models.UncertaintyModel') + def test_train_best(self, mocked_UM): + """proper access of best params and training of a new instance""" + mocked_UMin = unittest.mock.MagicMock() + mocked_UM.return_value = mocked_UMin + subject = opt.OptRoutine(subject=gandy.models.models. + UncertaintyModel, + Xs=numpy.array([1, 2, 3]), + Ys=numpy.array([1, 2, 3]), + search_space={'hyp1': (1, 10), + 'hyp2': ['a', 'b']}, + keyword=5) + # failure no best params + with self.assertRaises(AttributeError): + subject.train_best() + # set and run + subject.best_params = {'a': 10} + model = subject.train_best(keyword2=10) + mocked_UM.assert_called_with(**subject.best_params, + **subject.all_kwargs) + mocked_UMin.fit.assert_called_with(**subject.best_params, + **subject.all_kwargs) + self.assertTrue(model is mocked_UMin) + self.asserTrue('keyword2' in subject.all_kwargs.keys()) + return From 9e28521f609cc3da38829f4c252001c0fe0763ce Mon Sep 17 00:00:00 2001 From: Kyle Moskowitz Date: Fri, 5 Mar 2021 19:18:56 -0800 Subject: [PATCH 21/99] metrics.py test functions started --- gandy/tests/test_metrics/__init__.py | 0 gandy/tests/test_metrics/test_metrics.py | 48 ++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 gandy/tests/test_metrics/__init__.py create mode 100644 gandy/tests/test_metrics/test_metrics.py diff --git a/gandy/tests/test_metrics/__init__.py b/gandy/tests/test_metrics/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gandy/tests/test_metrics/test_metrics.py b/gandy/tests/test_metrics/test_metrics.py new file mode 100644 index 0000000..f138c9e --- /dev/null +++ b/gandy/tests/test_metrics/test_metrics.py @@ -0,0 +1,48 @@ +"""Unit tests for Metrics module.""" +import unittest +import unittest.mock + +import numpy as np + +import gandy.quality_est.metrics as metrics + +class TestMetric(unittest.TestCase): + """Unit test for Metric parent class""" + + def test___init___(self): + """Test proper initialization of class with proper inputs""" + + # failure cases: data not iterable + with self.assertRaises(TypeError): + subject = metrics.Metric(predictions = "0, 1, 2", + real = np.array([0, 1, 2]), + uncertainties = np.array([0, 0.5, 1])) + + with self.assertRaises(TypeError): + subject = metrics.Metric(predictions = np.array([0, 1, 2]), + real = "0, 1, 2", + uncertainties = np.array([0, 0.5, 1])) + + with self.assertRaises(TypeError): + subject = metrics.Metric(predictions = np.array([0, 1, 2]), + real = np.array([0, 1, 2]), + uncertainties = "0, 1, 2") + + # success case + subject = metrics.Metric(predictions = np.array([0, 1, 2]), + real = np.array([0, 1, 2]), + uncertainties = np.array([0, 0.5, 1])) + + #check to make sure necessary attributes are inputted + self.assertTrue(subject.predictions is not None) + self.assertTrue(subject.real is not None) + + + def test_calculate(self): + """Test the calculate function within the parent Metric class""" + + # ensure calculate method is called using mock function + subject = metrics.Metric + subject.calculate = unittest.mock.MagicMock(name = 'calculate') + subject.calculate.assert_called_once_with(kwargs) + From 1fef71bf65ee67ea9ae5abbac5aa1d7b76415093 Mon Sep 17 00:00:00 2001 From: evankomp Date: Fri, 5 Mar 2021 19:21:38 -0800 Subject: [PATCH 22/99] pep 8 tests for models and phs --- gandy/tests/test_models/test_gps.py | 38 ++++---- gandy/tests/test_models/test_models.py | 127 +++++++++++++------------ 2 files changed, 86 insertions(+), 79 deletions(-) diff --git a/gandy/tests/test_models/test_gps.py b/gandy/tests/test_models/test_gps.py index af5d15c..1689c66 100644 --- a/gandy/tests/test_models/test_gps.py +++ b/gandy/tests/test_models/test_gps.py @@ -1,5 +1,4 @@ """Testing functions for UncertaintyModel gaussian process class""" -import numpy import unittest import unittest.mock @@ -8,11 +7,12 @@ import gandy.models.gps as gps import gandy.models.models -## ensure the class inherits +# ensure the class inherits assert issubclass(gps.ucGaussianProcess, gandy.models.models.UncertaintyModel) -def TestGaussianProcess(unittest.test_case): - + +class TestGaussianProcess(unittest.TestCase): + @unittest.mock.patch('sklearn.gaussian_process') def test__build(self, mocked_gp): """Ensure the child method creates sklearn GP""" @@ -24,40 +24,41 @@ def test__build(self, mocked_gp): # we know init kwargs get to here with self.assertRaises(ValueError): subject = gps.ucGaussianProcess((1,), (1,), - model_type = 'something') + model_type='something') subject = gps.ucGaussianProcess((1,), (1,), - model_type = 'classifer', + model_type='classifer', keyword=5) mocked_gp.GaussianProcessClassifier.called_with(keyword=5) self.assertEqual(subject.model, 'Classifer') subject = gps.ucGaussianProcess((1,), (1,), - model_type = 'regressor', + model_type='regressor', keyword=5) mocked_gp.GaussianProcessRegressor.called_with(keyword=5) self.assertEqual(subject.model, 'Regressor') return - + @unittest.mock.patch('sklearn.gaussian_process') def test__train(self, mocked_gp): """Ensure the model's fit method is called""" - Xs = 'Xs'; Ys = 'Ys' + Xs = 'Xs' + Ys = 'Ys' subject = gps.ucGaussianProcess((1,), (1,), - model_type = 'classifer') + model_type='classifer') subject._train(Xs, Ys, keyword=5) subject.model.fit.assert_called_with(Xs, Ys, keyword=5) subject = gps.ucGaussianProcess((1,), (1,), - model_type = 'regressor') + model_type='regressor') subject._train(Xs, Ys, keyword=5) subject.model.fit.assert_called_with(Xs, Ys, keyword=5) return - + @unittest.mock.patch('sklearn.gaussian_process') def test__predict(self): """Ensure the proper calls with return_std keyword""" Xs = 'Xs' # classifer subject = gps.ucGaussianProcess((1,), (1,), - model_type = 'classifer') + model_type='classifer') subject.model.predict.return_value = 'preds' subject.model.predict_proba.return_value = 'uncs' # execute the method @@ -68,7 +69,7 @@ def test__predict(self): self.assertEqual('uncs', uncs) # regressor subject = gps.ucGaussianProcess((1,), (1,), - model_type = 'regressor') + model_type='regressor') subject.model.predict.return_value = ('preds', 'uncs') # execute the method preds, uncs = subject._predict(Xs) @@ -76,22 +77,21 @@ def test__predict(self): self.assertEqual('preds', preds) self.assertEqual('uncs', uncs) return - + def test_R(self): """test direct regressor instantialization""" subject = gps.ucGaussianProcess.R((1,), (1,)) self.assertTrue( - isinstance(subject.model, + isinstance(subject.model, sklearn.gaussian_process.GaussianProcessRegressor) ) return - + def test_C(self): """test direct regressor instantialization""" subject = gps.ucGaussianProcess.C((1,), (1,)) self.assertTrue( - isinstance(subject.model, + isinstance(subject.model, sklearn.gaussian_process.GaussianProcessClassifier) ) return - \ No newline at end of file diff --git a/gandy/tests/test_models/test_models.py b/gandy/tests/test_models/test_models.py index d2757ff..19c1885 100644 --- a/gandy/tests/test_models/test_models.py +++ b/gandy/tests/test_models/test_models.py @@ -7,10 +7,10 @@ class TestUncertaintyModel(unittest.TestCase): - + def test___init__(self): """Test initialization of the UncertaintyModel class""" - ## first mock the build method + # first mock the build method with unittest.patch( 'gandy.models.models.UncertaintyModel.build' ) as mocked_build: @@ -26,33 +26,34 @@ def test___init__(self): # test that we initializzed sessions self.assertEqual(subject.sessions, {}) return - + def test_check(self): """Test the ability of the model to recognize improper data""" # prepare some data objects to check. # we only have numpy available in the dependencies # should work with other objects such as pandas dataframe # test different dimensions - XSHAPE = [(5,6,), (8,)]; YSHAPE = [(5,), (1,)] + XSHAPE = [(5, 6,), (8,)] + YSHAPE = [(5,), (1,)] for xshape, yshape in XSHAPE, YSHAPE: Xs_good = numpy.ones( - (20, *xshape), # as if it were 20 data points - dtype=int # specify int to ensure proper conversion to float + (20, *xshape), # as if it were 20 data points + dtype=int # specify int to ensure proper conversion ) Xs_bad = numpy.ones( (20, 3, 4) ) - Xs_non_numeric = XS_GOOD.astype('str') + Xs_non_numeric = Xs_good.astype('str') Ys_good = numpy.ones( - (20, *yshape) # matching 20 data points + (20, *yshape) # matching 20 data points ) Ys_bad = numpy.ones( (20, 3) ) Ys_datacount_mismatch = numpy.ones( - (10, *yshape) # not matching 20 data points + (10, *yshape) # not matching 20 data points ) - no_shape_attribute = [1,2,3] + no_shape_attribute = [1, 2, 3] # prepare the subject subject = mds.UncertaintyModel(xshape, yshape) @@ -72,9 +73,9 @@ def test_check(self): # Xs and y together # expected success Xs_out, Ys_out = subject.check(Xs_good, Ys_good) - self.assertTrue(numpy.ndarray == type(Xs_out) and \ - numpy.ndarray == type(Ys_out)) - self.assertTrue(Xs_good.shape == Xs_out.shape and \ + self.assertTrue(isinstance(Xs_out, numpy.ndarray) and + isinstance(Ys_out, numpy.ndarray)) + self.assertTrue(Xs_good.shape == Xs_out.shape and Ys_good.shape == Ys_out.shape) self.assertEqual(numpy.float64, Xs_out.dtype) # failure modes @@ -91,31 +92,34 @@ def test_check(self): with self.assertRaises(ValueError): subject.check(Xs_good, Ys_datacount_mismatch) return - + def test_build(self): - """Test the parent build method, to make sure it executes protected + """Test the parent build method, to make sure it executes protected method""" model = 'Mymodel' with unittest.mock.patch( 'gandy.models.models.UncertaintyModel._build', - return_value=model # mock the return of the model to a string + return_value=model # mock the return of the model to a string ) as mocked__build: subject = mds.UncertaintyModel((1,), (1,), keyword=5) mocked__build.assert_called_once_with(keyword=5) - self.assertTrue(subject.model is model) # ensure automatically set model + # ensure automatically set model + self.assertTrue(subject.model is model) return - + def test__build(self): """Parent _build should do nothing but raise""" with self.assertRaises(mds.NotImplimented): mds.UncertaintyModel((1,), (1,)) - # mock _build from here on out - we don;t want the init build to interfere + # mock _build from here on out - we don;t want the init build to + # interfere mds.UncertaintyModel._build = unittest.mock.MagicMock() return - + def test__get_metric(self): """test ability to retrieve the correct callables""" - with unittest.mock.patch('gandy.quality_est.metrics') as mocked_metrics: + with unittest.mock.patch('gandy.quality_est.metrics' + ) as mocked_metrics: def fake_metric(trues, predictions, uncertainties): return 5 mocked_metrics.fake_metric = fake_metric @@ -131,54 +135,55 @@ def fake_metric(trues, predictions, uncertainties): # and failure, not a class with self.assertRaises(AttributeError): subject._get_metric('not_a_class') - + return - + def test_train(self): """Proper passing of data to _train and updating of sessions""" subject = mds.UncertaintyModel((1,), (1,)) # mock the required nested calls Xs_in, Ys_in = 'Xs', 'Ys' mocked_check = unittest.mock.MagicMock( - return_value = ('Xs_checked', 'Ys_checked') + return_value=('Xs_checked', 'Ys_checked') ) subject.check = mocked_check mocked__train = unittest.mock.MagicMock( - return_value = 'losses' + return_value='losses' ) subject._train = mocked__train mocked__get_metric = unittest.mock.MagicMock( - return_value = 'some_metric' + return_value='some_metric' ) # run the train and check proper calls - with unittest.mock.patch('time.clock', return_value='thetime') as mocked_time: + with unittest.mock.patch('time.clock', return_value='thetime' + ) as mocked_time: # first specify a session name - subject.train(Xs_in, Ys_in, - metric='fake_metric', + subject.train(Xs_in, Ys_in, + metric='fake_metric', session='first_session') mocked_check.assert_called_with(Xs_in, Ys_in) mocked__get_metric.assert_called_with('fake_metric') - mocked__train.assert_called_with('Xs_checked', + mocked__train.assert_called_with('Xs_checked', 'Ys_checked', metric='some_metric') # then try without specifying session name, we want to make its own # also don't give a metric to make sure that is an allowed option subject.train(Xs_in, Ys_in) mocked_time.assert_called() - # check all of the correct storing of sessions + # check all of the correct storing of sessions self.assertEqual(2, len(subject.sessions)) self.assertTrue('first_session' in subject.sessions.keys()) self.assertEqual(subject.sessions['first_session'], 'losses') self.assertTrue('Starttime: thetime' in subject.sessions.keys()) return - + def test__train(self): """All it should do is raise an error for child to define""" subject = mds.UncertaintyModel((1,), (1,)) with self.assertRaises(mds.NotImplimented): subject._train('Xs', 'Ys') return - + def test_predict(self): """Test proper flagging of predictions""" subject = mds.UncertaintyModel((1,), (1,)) @@ -187,40 +192,41 @@ def test_predict(self): # here we set up a rotation of predictions, uncertaintains for # _predict to return, allowing us to test _predict output handling _predict_return = [ - (['length', '2'], numpy.array([5, 10], dtype=int)), # expected to work - (['length', '2'], ['wrong', 'dtype']), # failure, can't flag - (['length', '2'], 5.0), # failure, pred/unc length mismatch - ('length1', 5.0), # failure, does not match length of input - ([['array', 'is'], ['2', 'dim']], [5, 10]) # failure yshape mismatch + (['length', '2'], numpy.array([5, 10], dtype=int)), # works + (['length', '2'], ['wrong', 'dtype']), # failure, can't flag + (['length', '2'], 5.0), # failure, pred/unc length mismatch + ('length1', 5.0), # failure, does not match length of input + ([['array', 'is'], ['2', 'dim']], [5, 10]) # yshape mismatch ] # mock the check and _predict method mocked_check = unittest.mock.MagicMock( - return_value = ('Xs_checked') - ) + return_value=('Xs_checked') + ) subject.check = mocked_check mocked__predict = unittest.mock.MagicMock( - side_effect = _predict_return - ) + side_effect=_predict_return + ) subject._predict = mocked__predict # expected faulure, threshold not correct type with self.raises(TypeError): - subject.predict(Xs_in, uc_threshold = 'seven') + subject.predict(Xs_in, uc_threshold='seven') # first rotation, expected to work, check outputs and correct calls - preds, uncs, flags = subject.predict(Xs_in, - uc_threshold=7.0, + preds, uncs, flags = subject.predict(Xs_in, + uc_threshold=7.0, keyword=5) - + subject.check.assert_called_with(Xs_in) subject._predict.assert_called_with('Xs_checked', keyword=5) - - self.assertTrue(all([type(out) == numpy.ndarray for out in + + self.assertTrue(all([isinstance(out, numpy.ndarray) for out in [preds, uncs, flags]])) self.assertEqual((2, *subject.yshape), preds.shape) self.assertEqual((2, 1), uncs.shape) self.assertEqual(uncs.dtype, numpy.float64) self.assertEqual((2, 1), flags.shape) self.assertEqual(flags.dtype, bool) - self.assertTrue(numpy.array_equal(flags, np.array([[False],[True]]))) + self.assertTrue(numpy.array_equal(flags, + numpy.array([[False], [True]]))) # first failure case, can't flag strings with self.assertRaised(TypeError): subject.predict(Xs_in) @@ -233,33 +239,36 @@ def test_predict(self): # wrong dimensions with self.assertRaises(ValueError): subject.predict(Xs_in) - + return - + def test__predict(self): """Should just raise an error""" subject = mds.UncertaintyModel((1,), (1,)) with self.assertRaises(mds.NotImplimented): subject._predict('Xs') return - + def test_score(self): """Test proper handling of internal function when score is called""" subject = mds.UncertaintyModel((1,), (1,)) - Xs = 'Xs'; Ys = 'Ys' + Xs = 'Xs' + Ys = 'Ys' # mock necessary inner calls mocked_check = unittest.mock.MagicMock( - return_value = ('Xs_checked', 'Ys_checked') + return_value=('Xs_checked', 'Ys_checked') ) subject.check = mocked_check mocked_predict = unittest.mock.MagicMock( return_value=('preds', 'uncertainties') ) subject.predict = mocked_predict + def fake_metric1(true, preds, uncertainties): - return true+preds+uncertainties, [1, 1] + return true + preds + uncertainties, [1, 1] + def fake_metric2(true, preds, uncertainties): - return true+preds+uncertainties, [1, 1, 1] + return true + preds + uncertainties, [1, 1, 1] mocked__get_metric = unittest.mock.MagicMock( side_effect=[fake_metric1, fake_metric2] ) @@ -276,7 +285,7 @@ def fake_metric2(true, preds, uncertainties): with self.assertRaises(ValueError): subject.score(Xs, Ys) return - + def test_property_shapes(self): """Ensure that nonsensical shapes cannot be set""" subject = mds.UncertaintyModel((1,), (1,)) @@ -294,12 +303,10 @@ def test_property_shapes(self): subject.yshape = (5,) self.assertEqual(subject.model, None) return - + def test_property_model(self): """ensure safety of the model attribute""" subject = mds.UncertaintyModel((1,), (1,)) with self.assertRaises(RuntimeError): subject.model = 'Not None' return - - \ No newline at end of file From 9dd4fb36610997363963c6769dffe525ed5d95e6 Mon Sep 17 00:00:00 2001 From: evankomp Date: Fri, 5 Mar 2021 19:22:46 -0800 Subject: [PATCH 23/99] proper name of opt tests --- gandy/tests/{test_hypersearch => test_optimization}/__init__.py | 0 .../{test_hypersearch => test_optimization}/test_hypersearch.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename gandy/tests/{test_hypersearch => test_optimization}/__init__.py (100%) rename gandy/tests/{test_hypersearch => test_optimization}/test_hypersearch.py (100%) diff --git a/gandy/tests/test_hypersearch/__init__.py b/gandy/tests/test_optimization/__init__.py similarity index 100% rename from gandy/tests/test_hypersearch/__init__.py rename to gandy/tests/test_optimization/__init__.py diff --git a/gandy/tests/test_hypersearch/test_hypersearch.py b/gandy/tests/test_optimization/test_hypersearch.py similarity index 100% rename from gandy/tests/test_hypersearch/test_hypersearch.py rename to gandy/tests/test_optimization/test_hypersearch.py From aa35af033eff36bc1b9be332461c8f274212c5a6 Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Sat, 6 Mar 2021 15:06:10 -0800 Subject: [PATCH 24/99] Moved Deepchem GAN class to new class instead of multiple inheritence in gandy GAN model --- gandy/models/dcgan.py | 353 ++++++++++++++++++++++++++++++++++++++++++ gandy/models/gans.py | 243 +++++++++++++---------------- 2 files changed, 457 insertions(+), 139 deletions(-) create mode 100644 gandy/models/dcgan.py diff --git a/gandy/models/dcgan.py b/gandy/models/dcgan.py new file mode 100644 index 0000000..2973170 --- /dev/null +++ b/gandy/models/dcgan.py @@ -0,0 +1,353 @@ +""" +This class implements Deepchem's GAN class. + +Deepchem's tutorial on GANs (14_Conditional_Generative_Adversarial_Networks) +can be found here: +https://github.com/deepchem/deepchem/blob/master/examples/tutorials/ + 14_Conditional_Generative_Adversarial_Networks.ipynb + +""" + +# deep learning imports +import deepchem +import tensorflow as tf +from tf.keras.layers import Concatenate, Dense, Dropout, Input + +# typing imports +from typing import Tuple, Object, Type + +# more typing +import numpy as np +Array = Type[np.ndarray] + +# These should be set by the gandy model when _build is called. +XSHAPE = None +YSHAPE = None +NOISE_SHAPE = None +N_CLASSES = None + + +class DCGAN(deepchem.models.GAN): + """ + Implement Generative Adversarial Networks. + + A Generative Adversarial Network (GAN) is a type of generative model. + It consists of two parts called the "generator" and the "discriminator". + The generator takes random noise as input and transforms it into an + output that (hopefully) resembles the training data. The discriminator + takes a set of samples as input and tries to distinguish the real + training samples from the ones created by the generator. Both of them + are trained together. The discriminator tries to get better and better + at telling real from false data, while the generator tries to get better + and better at fooling the discriminator. + + Thank you to deepchem at + https://github.com/deepchem/deepchem/blob/master/deepchem/models/gan.py#L14-L442 + for the information about GANS. + + This class builds off of the deepchem GAN class found at the url above. + """ + + def create_generator(self, **kwargs): + """ + Create the generator as a keras model. + + kwargs contains the possible arguments for the generator. + See Arguments. + + Other kwargs for a Dense layer can be found at + https://keras.io/api/layers/core_layers/dense/ + + Arguments: + layer_dimensions - list of hidden dimension layers + Note: This should note include the output dimension. + Default - [128] + type == list of ndarray + activation - hidden layer activation function. + Can choose from 'relu', 'tanh', 'sigmoid', 'softmax', + 'softplus', 'softsign', 'selu', 'elu', 'exponential', + or 'linear'. See https://keras.io/api/layers/activations/ + Default - 'relu' + type == str + kernel_regularizer - regularizer of kernel/ weights + Can choose from 'l2', 'l1' + Default - 'l2' + type == str + dropout - layer dropout percetnage, + i.e., percent of weights that are randomly set to 0 + Can choose a flooat in [0.0, 1.0) + Default - 0.05 (5% dropout rate) + type == float + + Returns: + generator - the discriminator outputs a probability that + the data is real or fake + type == Keras model + + """ + # adapted from deepchem tutorial 14: + + # get hyperparameters from kwargs + layer_dimensions = kwargs.get('layer_dimensions', [128]) + activation = kwargs.get('activation', 'relu') + kernel_regularizer = kwargs.get('kernel_regularizer', 'l2') + dropout = kwargs.get('dropout', 0.05) + + # construct input + noise_in = Input(shape=self.get_noise_input_shape()) + # build first layer of network + gen = Dense(layer_dimensions[0], activation=activation, + kernel_regularizer=kernel_regularizer)(noise_in) + # adding dropout to the weights + gen = Dropout(dropout)(gen) + # build subsequent layers + for layer_dim in layer_dimensions[1:]: + gen = Dense(layer_dim, activation=activation)(gen) + gen = Dropout(dropout)(gen) + + # generator outputs + gen = Dense(XSHAPE[0], activation=activation)(gen) + gen = Dropout(dropout)(gen) + + # final construction of Keras model + generator = tf.keras.Model(inputs=[noise_in], + outputs=[gen]) + return generator + + def create_discriminator(self, **kwargs): + """ + Create the discriminator as a keras model. + + kwargs contains the possible arguments for the discriminator. + See Arguments. + + Other kwargs for a Dense layer can be found at + https://keras.io/api/layers/core_layers/dense/ + + Arguments: + layer_dimensions - list of hidden dimension layers + Default - [128] + type == list of ndarray + activation - hidden layer activation function. + Can choose from 'relu', 'tanh', 'sigmoid', 'softmax', + 'softplus', 'softsign', 'selu', 'elu', 'exponential', + or 'linear'. See https://keras.io/api/layers/activations/ + Default - 'relu' + type == str + kernel_regularizer - regularizer of kernel/ weights + Can choose from 'l2', 'l1' + Default - 'l2' + type == str + dropout - layer dropout percetnage, + i.e., percent of weights that are randomly set to 0 + Can choose a flooat in [0.0, 1.0) + Default - 0.05 (5% dropout rate) + type == float + + Returns: + discriminator - the discriminator outputs a probability that + the data is real or fake + type == Keras model + + """ + # adapted from deepchem tutorial 14: + + # get hyperparameters from kwargs + layer_dimensions = kwargs.get('layer_dimensions', [128]) + activation = kwargs.get('activation', 'relu') + kernel_regularizer = kwargs.get('kernel_regularizer', 'l2') + dropout = kwargs.get('dropout', 0.05) + + # construct input + data_in = Input(shape=XSHAPE) + # build first layer of network + discrim = Dense(layer_dimensions[0], activation=activation, + kernel_regularizer=kernel_regularizer)(data_in) + # adding dropout to the weights + discrim = Dropout(dropout)(discrim) + # build subsequent layers + for layer_dim in layer_dimensions[1:]: + discrim = Dense(layer_dim, activation=activation)(discrim) + discrim = Dropout(dropout)(discrim) + + # To maintain the interpretation of a probability, + # the final activation function is not a kwarg + discrim_prob = Dense(1, activation='sigmoid')(discrim) + + # final construction of Keras model + discriminator = tf.keras.Model(inputs=[data_in], + outputs=[discrim_prob]) + return discriminator + + def get_noise_input_shape(self) -> Tuple[int]: + """ + Return the shape of the noise vector. + + This should be set by the gandy model when an build is called. + """ + return NOISE_SHAPE + + def get_data_input_shapes(self) -> Tuple[int]: + """ + Return the shape of the data. + + This should be set by the gandy model when an build is called. + """ + return XSHAPE + + +class CondDCGAN(DCGAN): + """ + Conditional GAN subcless of deepchem's GAN class. + + This class is a subclass of the gans class and instead implements + a cgan. A Conditional GAN (cGAN) has additional inputs to the + generator and discriminator, and learns a distribution that is + conditional on the values of those inputs. They are referred + to as "conditional inputs". + """ + + def get_conditional_input_shapes(self, **kwargs) -> Array: + """ + Return the shape of the conditional input. + + This should be set by the gandy model when an build is called. + """ + return [(N_CLASSES,)] + + def create_generator(self, **kwargs) -> Object: + """ + Create the generator as a keras model. + + kwargs contains the possible arguments for the generator. + See Arguments. + + Other kwargs for a Dense layer can be found at + https://keras.io/api/layers/core_layers/dense/ + + Arguments: + layer_dimensions - list of hidden dimension layers + Note: This should note include the output dimension. + Default - [128] + type == list of ndarray + activation - hidden layer activation function. + Can choose from 'relu', 'tanh', 'sigmoid', 'softmax', + 'softplus', 'softsign', 'selu', 'elu', 'exponential', + or 'linear'. See https://keras.io/api/layers/activations/ + Default - 'relu' + type == str + kernel_regularizer - regularizer of kernel/ weights + Can choose from 'l2', 'l1' + Default - 'l2' + type == str + dropout - layer dropout percetnage, + i.e., percent of weights that are randomly set to 0 + Can choose a flooat in [0.0, 1.0) + Default - 0.05 (5% dropout rate) + type == float + + Returns: + generator - the discriminator outputs a probability that + the data is real or fake + type == Keras model + + """ + # adapted from deepchem tutorial 14: + + # get hyperparameters from kwargs + layer_dimensions = kwargs.get('layer_dimensions', [128]) + activation = kwargs.get('activation', 'relu') + kernel_regularizer = kwargs.get('kernel_regularizer', 'l2') + dropout = kwargs.get('dropout', 0.05) + + # construct input + noise_in = Input(shape=self.get_noise_input_shape()) + conditional_in = Input(shape=(N_CLASSES,)) + gen_input = Concatenate()([noise_in, conditional_in]) + + # build first layer of network + gen = Dense(layer_dimensions[0], activation=activation, + kernel_regularizer=kernel_regularizer)(gen_input) + # adding dropout to the weights + gen = Dropout(dropout)(gen) + # build subsequent layers + for layer_dim in layer_dimensions[1:]: + gen = Dense(layer_dim, activation=activation)(gen) + gen = Dropout(dropout)(gen) + + # generator outputs + gen = Dense(XSHAPE[0], activation=activation)(gen) + gen = Dropout(dropout)(gen) + + # final construction of Keras model + generator = tf.keras.Model(inputs=[noise_in, conditional_in], + outputs=[gen]) + return generator + + def create_discriminator(self, **kwargs) -> Object: + """ + Create the discriminator as a keras model. + + kwargs contains the possible arguments for the discriminator. + See Arguments. + + Other kwargs for a Dense layer can be found at + https://keras.io/api/layers/core_layers/dense/ + + Arguments: + layer_dimensions - list of hidden dimension layers + Default - [128] + type == list of ndarray + activation - hidden layer activation function. + Can choose from 'relu', 'tanh', 'sigmoid', 'softmax', + 'softplus', 'softsign', 'selu', 'elu', 'exponential', + or 'linear'. See https://keras.io/api/layers/activations/ + Default - 'relu' + type == str + kernel_regularizer - regularizer of kernel/ weights + Can choose from 'l2', 'l1' + Default - 'l2' + type == str + dropout - layer dropout percetnage, + i.e., percent of weights that are randomly set to 0 + Can choose a flooat in [0.0, 1.0) + Default - 0.05 (5% dropout rate) + type == float + + Returns: + discriminator - the discriminator outputs a probability that + the data is real or fake + type == Keras model + + """ + # adapted from deepchem tutorial 14: + + # get hyperparameters from kwargs + layer_dimensions = kwargs.get('layer_dimensions', [128]) + activation = kwargs.get('activation', 'relu') + kernel_regularizer = kwargs.get('kernel_regularizer', 'l2') + dropout = kwargs.get('dropout', 0.05) + + # construct input + data_in = Input(shape=XSHAPE) + conditional_in = Input(shape=(N_CLASSES,)) + discrim_input = Concatenate()([data_in, conditional_in]) + + # build first layer of network + discrim = Dense(layer_dimensions[0], activation=activation, + kernel_regularizer=kernel_regularizer)(discrim_input) + # adding dropout to the weights + discrim = Dropout(dropout)(discrim) + # build subsequent layers + for layer_dim in layer_dimensions[1:]: + discrim = Dense(layer_dim, activation=activation)(discrim) + discrim = Dropout(dropout)(discrim) + + # To maintain the interpretation of a probability, + # the final activation function is not a kwarg + discrim_prob = Dense(1, activation='sigmoid')(discrim) + + # final construction of Keras model + discriminator = tf.keras.Model(inputs=[data_in, conditional_in], + outputs=[discrim_prob]) + return discriminator diff --git a/gandy/models/gans.py b/gandy/models/gans.py index b458e38..3b335f1 100644 --- a/gandy/models/gans.py +++ b/gandy/models/gans.py @@ -1,29 +1,34 @@ -''' +""" +This class implements a GAN using deepchem's GAN class. + Deepchem's tutorial on GANs (14_Conditional_Generative_Adversarial_Networks) can be found here: https://github.com/deepchem/deepchem/blob/master/examples/tutorials/ 14_Conditional_Generative_Adversarial_Networks.ipynb -''' + +See dcgan for the implemented deepchem GAN and conditional GAN. +""" # gandy imports import gandy.models.models import gandy.metrics # deep learning imports -import deepchem +import gandy.models.dcgan as dcgan # import tensorflow as tf # typing imports -from typing import Tuple, Any, Object, Type +from typing import Any, Object, Type # typing -import numpy -Array = Type[numpy.ndarray] +import numpy as np +Array = Type[np.ndarray] -class gan(deepchem.models.GAN, gandy.models.models.UncertaintyModel): - ''' +class GAN(gandy.models.models.UncertaintyModel): + """ Implements Generative Adversarial Networks. + A Generative Adversarial Network (GAN) is a type of generative model. It consists of two parts called the "generator" and the "discriminator". The generator takes random noise as input and transforms it into an @@ -37,77 +42,86 @@ class gan(deepchem.models.GAN, gandy.models.models.UncertaintyModel): https://github.com/deepchem/deepchem/blob/master/deepchem/models/gan.py#L14-L442 for the information about GANS. This class builds off of the deepchem GAN class. - ''' + """ - def __init__(self, - xshape: Tuple[int], - yshape: Tuple[int], - **kwargs): - ''' - Initializes instance of a GAN - ''' - # the MRO order of init calls is deepchem.models.GAN first, - # then gandy.models.models.UncertaintyModel - super(gan, self).__init__(xshape=xshape, yshape=yshape, **kwargs) - - def create_generator(self, **kwargs): - ''' - Creates the generator (as a keras model) - Saves self.generator as this model - ''' - # adapted from deepchem tutorial 14: - # do something like: - # hyperparameters = **kwargs - # output_layer_dimension = self.xshape[0] - # noise_in = Input(shape=get_noise_input_shape()) - # gen_dense_lay_1 = Dense(layer_one_dimension, - # activation=kwargs.activation)(noise_in) - # gen_outputs = Dense(output_layer_dimension, - # activation=kwargs.activation)(gen_dense_lay_1) - # make above code for loop s.t. num_layers is changeable parameter - # self.generator = tf.keras.Model(inputs=[noise_in], - # outputs=[gen_outputs]) - return None + # overridden method from UncertaintyModel class + def _build(self, **kwargs) -> Object: + """ + Construct the model. - def create_discriminator(self, **kwargs): - ''' - Creates the discriminator (as a keras model) - Saves self.discriminator as this model - ''' - # adapted from deepchem tutorial 14: - # do something like: - # hyperparameters = **kwargs - # data_in = Input(shape=(output_layer_dimension,)) - # discrim_lay_1 = Dense(layer_one_dimension, - # activation=activation)(data_in) - # discrim_prob = Dense(1, activation=tf.sigmoid)(discrim_lay_1) - # self.discriminator = tf.keras.Model(inputs=[data_in], - # outputs=[discrim_prob]) - return None + This instantiates the deepchem gan as the model. + """ + # setting the dcgan global variables + dcgan.XSHAPE = self.xshape + dcgan.YSHAPE = self.yshape + # get noise shape from kwargs + # default noise is (10,) + dcgan.NOISE_SHAPE = kwargs.get('noise_shape', (10,)) + # determine whether to use gan or condition gan + if len(yshape) == 3: + conditional = True + else: + conditional = False + # instantiating the model as the deepchem gan + if conditional: + model = dcgan.CondDCGAN(**kwargs) + else: + model = dcgan.DCGAN(**kwargs) + return model - def get_noise_input_shape(self, **kwargs) -> Tuple[int]: - ''' - Returns the shape of the noise vector - ''' - return # noise.shape + def generate_data(Xs: Array, + Ys: Array, + batch_size: int): + """ + Generating function. - def get_data_input_shapes(self, **kwargs) -> Tuple[int]: - ''' - Returns the shape of the data, which should be xshape - ''' - return self.xshape + Creates a batch of bootstrapped data. _train helper function + + Arguments: + Xs/Ys - training examples/targets + type == ndarray + + batch_size - number of data points in a batch + type == int + + Returns: + classes - array of targets sampled from Ys + type == ndarray + + points - array of data points sampled from Xs + type == ndarray + """ + # sample with replacement a batch size num of x, y pairs + classes, points = None + return classes, points + + def iterate_batches(Xs: Array, + Ys: Array, + epcohs: int): + """ + Function that creates batches of generated data. + + The deepchem fit_gan unction reads in a dictionary for training. + This creates that dictionary for each batch. _train helper function + + Arguments: + Xs/Ys - training examples/targets + type == ndarray + + batch_size - number of data points in a batch + type == int + + Yields: + batched_data - data split into batches + type == dict + """ + # for i in range(batches): + # classes, points = generate_data(self.batch_size) + # classes = deepchem.metrics.to_one_hot(classes, n_classes) + # batched_data = {self.data_inputs[0]: points, + # self.conditional_inputs[0]: classes} + # yield batched_data - # overridden method from UncertaintyModel class - def _build(self, **kwargs) -> Object: - ''' - Construct the model - ''' - # do something like: - # self.create_generator(**kwargs) - # self.create_discriminator(**kwargs) - # self.n_classes = self.yshape - return {'generator': self.generator, - 'discriminator': self.discriminator} # overridden method from UncertaintyModel class def _train(self, @@ -115,8 +129,8 @@ def _train(self, Ys: Array, *args, **kwargs) -> Any: - ''' - Trains GAN model on data + """ + Train GAN model on data. Arguments: Xs/Ys - training examples/targets @@ -124,7 +138,11 @@ def _train(self, **kwargs - keyword arguments to assign non-default training parame- ters or pass to nested functions. - ''' + """ + # epochs and batch_size in args + # self.batch_size = batch_size + # self.fit_gan(iterbatches(Xs, Ys, epochs)) + # losses = self.model.outputs losses = None return losses @@ -133,7 +151,7 @@ def _predict(self, Xs: Array, *args, **kwargs): - ''' + """ Arguments: Xs - example data to make predictions on type == ndarray @@ -148,20 +166,21 @@ def _predict(self, uncertainties - array of prediction uncertainties of targets with the same length as Xs type == ndarray - ''' + """ # pseudocode # adapted from deepchem tutorial 14: # one_hot_Ys = deepchem.metrics.to_one_hot(Ys, self.n_classes) - # generated_points = gan.predict_gan_generator( + # generated_points = self.predict_gan_generator( # conditional_inputs=[one_hot_Ys]) # the above code generates points, but we need uncertainties as well predictions, uncertainties = None, None return predictions, uncertainties def _save(filename: str, **kwargs): - """Method defined by child to save the predictor. + """ + Method defined by child to save the predictor. - Method must save into memory the object at self.model + Method must save into memory the object at self._model Args: filename (str): @@ -173,9 +192,10 @@ def _save(filename: str, **kwargs): return None def _load(self, filename: str, **kwargs): - """Method defined by child to load a predictor into memory. + """ + Method defined by child to load a predictor into memory. - Loads the object to be assigned to self.model. + Loads the object to be assigned to self._model. Args: filename (str): @@ -186,58 +206,3 @@ def _load(self, filename: str, **kwargs): # model = tf.keras.model.load_model(filename, compile=False) model = None return model - - -class cgan(gan): - ''' - This class is a subclass of the gans class and instead implements - a cgan. A Conditional GAN (cGAN) has additional inputs to the - generator and discriminator, and learns a distribution that is - conditional on the values of those inputs. They are referred - to as "conditional inputs". - ''' - - def get_conditional_input_shapes(self, **kwargs) -> Array: - ''' - Returns the shape of the conditional input - in which the CGAN learns a distribution - ''' - # adapted from deepchem tutorial 14: - return [(self.n_classes,)] - - def create_generator(self, **kwargs) -> Object: - ''' - Creates the generator (as a keras model) - Saves self.generator as this model - ''' - # adapted from deepchem tutorial 14: - # do something like: - # hyperparameters = **kwargs - # output_layer_dimension = self.xshape[0] - # noise_in = Input(shape=get_noise_input_shape()) - # conditional_in = Input(shape=(self.n_classes,)) - # gen_input = Concatenate()([noise_in, conditional_in]) - # gen_dense_lay_1 = Dense(layer_one_dimension, - # activation=activation)(gen_input) - # gen_outputs = Dense(output_layer_dimension, - # activation=acitvation)(gen_dense_lay_1) - # self.generator = tf.keras.Model( - # inputs=[noise_in, conditional_in], outputs=[gen_outputs]) - return self.generator - - def create_discriminator(self, **kwargs) -> Object: - ''' - Creates the discriminator (as a keras model) - Saves self.discriminator as this model - ''' - # adapted from deepchem tutorial 14: - # do something like: - # data_in = Input(shape=(output_layer_dimension,)) - # conditional_in = Input(shape=(self.n_classes,)) - # discrim_in = Concatenate()([data_in, conditional_in]) - # discrim_lay_1 = Dense(layer_one_dimension, - # activation=activation)(discrim_in) - # discrim_prob = Dense(1, activation=tf.sigmoid)(discrim_lay_1) - # self.discriminator = tf.keras.Model( - # inputs=[data_in, conditional_in], outputs=[discrim_prob]) - return self.discriminator From 38fe951e9c5c56b7afaf632d7ec596ff52bb75c9 Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Sun, 7 Mar 2021 15:34:45 -0800 Subject: [PATCH 25/99] Fleshing out some code in the class functions --- gandy/models/gans.py | 121 ++++++++++++++++++++++++++++++------------- 1 file changed, 84 insertions(+), 37 deletions(-) diff --git a/gandy/models/gans.py b/gandy/models/gans.py index 3b335f1..cbde9cc 100644 --- a/gandy/models/gans.py +++ b/gandy/models/gans.py @@ -14,6 +14,7 @@ import gandy.metrics # deep learning imports +import deepchem import gandy.models.dcgan as dcgan # import tensorflow as tf @@ -38,10 +39,10 @@ class GAN(gandy.models.models.UncertaintyModel): are trained together. The discriminator tries to get better and better at telling real from false data, while the generator tries to get better and better at fooling the discriminator. + Thank you to deepchem at https://github.com/deepchem/deepchem/blob/master/deepchem/models/gan.py#L14-L442 for the information about GANS. - This class builds off of the deepchem GAN class. """ # overridden method from UncertaintyModel class @@ -50,32 +51,50 @@ def _build(self, **kwargs) -> Object: Construct the model. This instantiates the deepchem gan as the model. + + Arguments: + **kwargs - key word arguments for creating the generator + and discriminator. See dcgan.create_generator and + dcgan.create_discriminator for those kwargs. + type == dict + + Returns: + model - Deepchem GAN model found in dcgan + type == Object """ # setting the dcgan global variables dcgan.XSHAPE = self.xshape dcgan.YSHAPE = self.yshape # get noise shape from kwargs - # default noise is (10,) + # default is 10 dimensional dcgan.NOISE_SHAPE = kwargs.get('noise_shape', (10,)) - # determine whether to use gan or condition gan - if len(yshape) == 3: - conditional = True + # get n_classes from kwargs + # default is the y dimension + # e.g., regression would be == 1 + # This would also be correct for a one hot encoded y vector. + dcgan.N_CLASSES = kwargs.get('n_classes', self.yshape[0]) + + # determine whether to use gan or conditional gan + if len(self.yshape) == 3: + self.conditional = True else: - conditional = False + self.conditional = False + # instantiating the model as the deepchem gan - if conditional: + if self.conditional: model = dcgan.CondDCGAN(**kwargs) else: model = dcgan.DCGAN(**kwargs) return model - def generate_data(Xs: Array, - Ys: Array, - batch_size: int): + def generate_data(self, + Xs: Array, + Ys: Array, + batch_size: int): """ Generating function. - Creates a batch of bootstrapped data. _train helper function + Create a batch of bootstrapped data. _train helper function Arguments: Xs/Ys - training examples/targets @@ -91,13 +110,17 @@ def generate_data(Xs: Array, points - array of data points sampled from Xs type == ndarray """ - # sample with replacement a batch size num of x, y pairs - classes, points = None + # sample with replacement X, Y pairs of size batch_size + n = len(Xs) + indices = np.random.randomint(0, high=n, size=(batch_size,)) + classes = Xs[indices] + points = Ys[indices] return classes, points - def iterate_batches(Xs: Array, + def iterate_batches(self, + Xs: Array, Ys: Array, - epcohs: int): + **kwargs): """ Function that creates batches of generated data. @@ -108,20 +131,29 @@ def iterate_batches(Xs: Array, Xs/Ys - training examples/targets type == ndarray - batch_size - number of data points in a batch - type == int + **kwargs - Specify training hyperparameters + batches - number of batches to train on + type == int + batch_size - number of data points in a batch + type == int + Yields: batched_data - data split into batches type == dict """ - # for i in range(batches): - # classes, points = generate_data(self.batch_size) - # classes = deepchem.metrics.to_one_hot(classes, n_classes) - # batched_data = {self.data_inputs[0]: points, - # self.conditional_inputs[0]: classes} - # yield batched_data - + # get training hyperparamters from kwargs + batches = kwargs.get('batches', 50) + batch_size = kwargs.get('batch_size', 32) + + # training loop + for i in range(batches): + classes, points = self.generate_data(Xs, Ys, batch_size) + classes = deepchem.metrics.to_one_hot(classes, + self.model.N_CLASSES) + batched_data = {self.data_inputs[0]: points, + self.conditional_inputs[0]: classes} + yield batched_data # overridden method from UncertaintyModel class def _train(self, @@ -138,12 +170,20 @@ def _train(self, **kwargs - keyword arguments to assign non-default training parame- ters or pass to nested functions. + + Returns: + losses - array of loss for each epoch + type == ndarray """ - # epochs and batch_size in args - # self.batch_size = batch_size - # self.fit_gan(iterbatches(Xs, Ys, epochs)) - # losses = self.model.outputs - losses = None + # train GAN on data + # self.model = deepchem GAN instance + self.model.fit_gan(self.iterbatches(Xs, Ys, **kwargs)) + # The deepchem gan is a Keras model whose + # outputs are [gen_loss, disrcim_loss]. + # Thus the final losses for the generator + # and discriminator are self.model.outputs + # This is a list of 2 KerasTensors so must evaluate it. + losses = self.model.outputs return losses # overridden method from UncertaintyModel class @@ -152,6 +192,8 @@ def _predict(self, *args, **kwargs): """ + Predict on Xs. + Arguments: Xs - example data to make predictions on type == ndarray @@ -167,22 +209,25 @@ def _predict(self, the same length as Xs type == ndarray """ - # pseudocode # adapted from deepchem tutorial 14: - # one_hot_Ys = deepchem.metrics.to_one_hot(Ys, self.n_classes) - # generated_points = self.predict_gan_generator( - # conditional_inputs=[one_hot_Ys]) + if self.conditional: + Ys = kwargs.get() + one_hot_Ys = deepchem.metrics.to_one_hot(Ys, self.model.N_CLASSES) + generated_points = self.predict_gan_generator( + conditional_inputs=[one_hot_Ys]) + else: + generated_points = self.predict_gan_generator() # the above code generates points, but we need uncertainties as well - predictions, uncertainties = None, None + predictions, uncertainties = generated_points, None return predictions, uncertainties - def _save(filename: str, **kwargs): + def _save(self, filename: str, **kwargs): """ Method defined by child to save the predictor. Method must save into memory the object at self._model - Args: + Arguments: filename (str): name of file to save model to """ @@ -197,7 +242,9 @@ def _load(self, filename: str, **kwargs): Loads the object to be assigned to self._model. - Args: + Should this be a class method? + + Arguments: filename (str): path of file to load """ From 681653d4168c6cdc96e09ea6136db2e05e511b5c Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Sun, 7 Mar 2021 15:35:21 -0800 Subject: [PATCH 26/99] Fleshing out more test functions --- gandy/tests/test_models/test_gans.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/gandy/tests/test_models/test_gans.py b/gandy/tests/test_models/test_gans.py index de951fa..28bdc63 100644 --- a/gandy/tests/test_models/test_gans.py +++ b/gandy/tests/test_models/test_gans.py @@ -73,6 +73,8 @@ def test__train(self): subject = gans.gan(xshape=(4,), yshape=(2,)) kwargs = dict(option=x1) subject._train(Xs, Ys, kwargs) + + # assert generator model called fit and discriminator called predict subject.generator.fit.assert_called_with(Xs, Ys, kwargs) subject.discriminator.predict.assert_called_with(Xs, Ys, kwargs) @@ -83,10 +85,14 @@ def test__predict(self): Test predict function. The predict function returns predictions and uncertainties. - This checks predictions and uncertainties are the appropriate shape. + This checks predictions and uncertainties are the appropriate shape + and the appropriate deepchem calls are made. ''' Xs = 'Xs' subject = gans.gan(xshape=(4,), yshape=(2,)) + + subject.predict_gan_generator = mock.MagicMock(name='predict_gan_generator') + subject._predict.return_value = ('preds', 'ucs') preds, ucs = subject._predict(Xs) subject._predict.assert_called_with(Xs) From 5be8d37548586bd3d50e3bbdf671595db3697cc3 Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Mon, 8 Mar 2021 11:54:09 -0800 Subject: [PATCH 27/99] Pep8 compliance --- gandy/tests/test_models/test_gans.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/gandy/tests/test_models/test_gans.py b/gandy/tests/test_models/test_gans.py index 28bdc63..023d8b6 100644 --- a/gandy/tests/test_models/test_gans.py +++ b/gandy/tests/test_models/test_gans.py @@ -1,4 +1,7 @@ -"""Testing functions for UncertaintyModel gan class.""" +""" +Testing functions for UncertaintyModel gan class. +""" + import numpy as np import unittest import unittest.mock @@ -10,7 +13,7 @@ def TestGAN(unittest.test_case): - """ Test GAN class.""" + """Test GAN class.""" def test_inheritence(): # ensure the subclass class inherits from both parent classes From a18de08cfdec70063488b10a9e2c31b599b8b520 Mon Sep 17 00:00:00 2001 From: evankomp Date: Mon, 8 Mar 2021 12:49:36 -0800 Subject: [PATCH 28/99] UncertaintyModel written and passing tests --- gandy/models/models.py | 199 ++++++++++++++++++------- gandy/tests/test_models/test_models.py | 126 ++++++++++------ 2 files changed, 225 insertions(+), 100 deletions(-) diff --git a/gandy/models/models.py b/gandy/models/models.py index cf6d7bc..bab055c 100644 --- a/gandy/models/models.py +++ b/gandy/models/models.py @@ -11,6 +11,8 @@ """ # imports +import inspect +import time from typing import Tuple, Iterable, Any, Type, Callable, Union import numpy @@ -21,12 +23,19 @@ Array = Type[numpy.ndarray] -class NotImplimented(Warning): +class NotImplimented(Exception): """Warning to indicate that a child class has not yet implimented necessary - methods. """ - # pseudocode - # . define the exception - pass + methods. + + Args: + inst - the class instance that raises this exception + """ + + def __init__(self, inst): + self.message = """The `{}` method has not yet been implimented by + this class: `{}`.""".format(inspect.stack()[1][3], inst.__class__) + super().__init__(self.message) + return class UncertaintyModel: @@ -56,19 +65,18 @@ class UncertaintyModel: tuple is appended of (session name, losses) where losses is determined by the output of _train. """ - # to contain dictionary of callable metric classes from the metrics module metrics = gandy.quality_est.metrics - """Available metrics defined in gandy.metrics""" def __init__(self, xshape: Tuple[int], yshape: Tuple[int], **kwargs): - # pseudocode - # . set self shapes - # . assign self model by running build function - # . create empty sessions list + self._model = None + self.xshape = xshape + self.yshape = yshape + self.sessions = {} + self.build(**kwargs) return def check(self, @@ -89,15 +97,51 @@ def check(self, Xs, the formated X data Ys, the formated Y data if present """ - # pseudocode - # . assert data type has shape attribute - # . check shapes of Xs and Ys against self shapes - # . raise error if do not match - # . convert to numpy - if Ys: - return Xs, Ys + if hasattr(Xs, 'shape'): + pass + else: + raise AttributeError('Xs has no shape attribute, ensure the\ + passed data has a shape') + if Ys is not None: + if hasattr(Ys, 'shape'): + pass + else: + raise AttributeError('Ys has no shape attribute, ensure the\ + passed data has a shape') + + try: + Xs_ = numpy.array(Xs).astype(numpy.float64) + except ValueError: + raise TypeError('X data contains non numerics.') + + try: + Xs_ = Xs_.reshape(-1, *self.xshape) + if len(Xs_) != len(Xs): + raise ValueError() + except ValueError: + raise ValueError('Cannot reshape X data ({}) to the model input\ + shape ({}). ensure the correct shape of data'.format( + Xs.shape[1:], self.xshape) + ) + if Ys is not None: + Ys_ = numpy.array(Ys) + try: + Ys_ = Ys_.reshape(-1, *self.yshape) + if len(Ys_) != len(Ys): + raise ValueError() + except ValueError: + raise ValueError('Cannot reshape Y data ({}) to the model\ + input shape ({}). ensure the correct shape of data'.format( + Ys.shape[1:], self.yshape) + ) + if len(Xs_) == len(Ys_): + pass + else: + raise ValueError('X and Y data do not have the same number of\ + examples. Ensure the data are example pairs.') + return Xs_, Ys_ else: - return Xs + return Xs_ def build(self, **kwargs): """Construct and store the predictor. @@ -108,8 +152,7 @@ def build(self, **kwargs): **kwargs: keyword arguments to pass to `_build` """ - # pseudocode - # . set self model to _build + self._model = self._build(**kwargs) return def _build(self, *args, **kwargs) -> Callable: @@ -131,16 +174,14 @@ def _build(self, *args, **kwargs) -> Callable: None: children will return the predictor """ - # pseudocode - # . raise not implimented + raise NotImplimented(self) model = None return model def train(self, Xs: Iterable, - Ys: Iterable, - metric: Union[str, Callable], + metric: Union[str, Callable] = None, session: str = None, **kwargs): """Train the predictor for one session, handled by `_train`. @@ -165,11 +206,15 @@ def train(self, Keyword arguments to pass to `_train` and assign non-default \ training parameters. """ - # pseudocode - # . check data inputs with check method - conver to numpy - # . get metric method - # . execute _train with formated data and metric (?) - # . update session losses with session _train losses + if session is not None: + sname = session + else: + sname = 'Starttime: ' + str(time.clock()) + metric = self._get_metric(metric) + + Xs_, Ys_ = self.check(Xs, Ys) + losses = self._train(Xs_, Ys_, metric=metric, **kwargs) + self.sessions[sname] = losses return def _train(self, @@ -203,8 +248,7 @@ def _train(self, Desired tracking of losses during training. Not implimented here, and returns None. """ - # psudocode - # . raise not implimented + raise NotImplimented(self) losses = None return losses @@ -234,12 +278,33 @@ def predict(self, (optional) array of flags of uncertain predictions higher than threshhold of same length as Xs """ - # pseudocode - # . check X data with check function - # . run _predict to return predictions and uncertainties - # . if threshhold, return predictions, uncertainties, and flags - predictions, uncertainties, flags = None, None, None - return predictions, uncertainties, flags + Xs_ = self.check(Xs) + if uc_threshold is not None: + try: + thresh = numpy.float64(uc_threshold) + except ValueError: + raise TypeError( + 'The threshold ({}) cannot be made a float.'.format( + uc_threshold) + ) + else: + pass + + predictions, uncertainties = self._predict(Xs_, **kwargs) + predictions = numpy.array(predictions).reshape(len(Xs), *self.yshape) + uncertainties = numpy.array(uncertainties).reshape( + len(Xs), *self.yshape) + try: + uncertainties = uncertainties.astype(numpy.float64) + except ValueError: + raise TypeError('Uncertainties are not numeric. Check the return\ + of the _predict method.') + + if uc_threshold is not None: + flags = uncertainties > thresh + return predictions, uncertainties, flags + else: + return predictions, uncertainties def _predict(self, Xs: Array, @@ -265,9 +330,7 @@ def _predict(self, array of prediction uncertainties of targets withthe same length as Xs """ - # psuedocode - # . raise not implimented - # . set pred, unc to None + raise NotImplimented(self) predictions, uncertainties = None, None return predictions, uncertainties @@ -279,7 +342,19 @@ def _get_metric(self, metric_in: Union[None, Callable, str]): metric_in (str, callable, None): metric name to get or callable to use""" # if statement, None, string, callable - metric_out = None + if metric_in is None: + metric_out = None + elif callable(metric_in): + metric_out = metric_in + elif isinstance(metric_in, str): + if hasattr(self.metrics, metric_in): + metric_out = getattr(self.metrics, metric_in) + else: + raise AttributeError('gandy has no metric called {}'.format( + metric_in) + ) + else: + raise ValueError('Unable to parse metric') return metric_out def score(self, @@ -310,13 +385,15 @@ def score(self, ndarray: Score array for each prediction. """ - # pseudocode - # . if statement to get metric object from metrics or specified - # . else raise undefined metric - # . check data - # . predictions, uncertainties = execute self._predict on Xs - # . pass predictions, uncertainties to metric get back costs - metric_value, metric_values = None, None + metric = self._get_metric(metric) + Xs_, Ys_ = self.check(Xs, Ys) + + predictions, uncertainties = self.predict(Xs_, **kwargs) + + metric_value, metric_values = metric(Ys_, predictions, uncertainties) + metric_values = numpy.array(metric_values).astype(numpy.float64) + metric_values = metric_values.reshape(len(Xs), -1) + return metric_value, metric_values def save(self, @@ -333,9 +410,6 @@ def save(self, **kwargs: keyword arguments to pass to _save, child specified method """ - # pseudocode - # . execute _save with filename - # . save json with xshape, yshape, sesssions, etc. return def _save(filename: str, @@ -399,11 +473,16 @@ def model(self): @model.setter def model(self, new_model): # raise exception does not support direct setting, use build function + raise RuntimeError( + 'Do not set the model directly, execute the build method') return @model.deleter def model(self): # print message about deleting model, build needs to be ran + if self._model is not None: + print('WARNING: model no longer valid, deleting. Rerun build()') + self._model = None return @property @@ -414,7 +493,15 @@ def xshape(self): @xshape.setter def xshape(self, new_xshape): # test new shape, delete model + if isinstance(new_xshape, tuple): + if all([isinstance(dim, int) for dim in new_xshape]): + pass + else: + raise TypeError('Non-int dimension found in xshape input') + else: + raise TypeError('xshape must be a tuple (dims of an x datum)') self._xshape = new_xshape + del self.model return @property @@ -425,5 +512,13 @@ def yshape(self): @yshape.setter def yshape(self, new_yshape): # test new shape, delete model + if isinstance(new_yshape, tuple): + if all([isinstance(dim, int) for dim in new_yshape]): + pass + else: + raise TypeError('Non-int dimension found in yshape input') + else: + raise TypeError('yshape must be a tuple (dims of a y datum)') self._yshape = new_yshape + del self.model return diff --git a/gandy/tests/test_models/test_models.py b/gandy/tests/test_models/test_models.py index 19c1885..8b043f0 100644 --- a/gandy/tests/test_models/test_models.py +++ b/gandy/tests/test_models/test_models.py @@ -4,30 +4,31 @@ import unittest.mock import gandy.models.models as mds +import gandy.quality_est.metrics class TestUncertaintyModel(unittest.TestCase): - def test___init__(self): + @unittest.mock.patch('gandy.models.models.UncertaintyModel.build') + def test___init__(self, mocked_build): """Test initialization of the UncertaintyModel class""" # first mock the build method - with unittest.patch( - 'gandy.models.models.UncertaintyModel.build' - ) as mocked_build: - # initialize - subject = mds.UncertaintyModel(xshape=(6,), - yshape=(3,), - keyword=5) # keywords passed? - # test assignment of shapes - self.assertTrue(hasattr(subject, 'xshape')) - self.assertTrue(hasattr(subject, 'yshape')) - # test that build was called - mocked_build.assert_called_once_with(keyword=5) - # test that we initializzed sessions - self.assertEqual(subject.sessions, {}) + # initialize + subject = mds.UncertaintyModel(xshape=(6,), + yshape=(3,), + keyword=5) # keywords passed? + # test assignment of shapes + self.assertTrue(hasattr(subject, 'xshape')) + self.assertTrue(hasattr(subject, 'yshape')) + # test that build was called + mocked_build.assert_called_once_with(keyword=5) + # test that we initializzed sessions + self.assertEqual(subject.sessions, {}) + self.assertTrue(hasattr(subject, 'model')) return - def test_check(self): + @unittest.mock.patch('gandy.models.models.UncertaintyModel.build') + def test_check(self, mocked_build): """Test the ability of the model to recognize improper data""" # prepare some data objects to check. # we only have numpy available in the dependencies @@ -43,7 +44,7 @@ def test_check(self): Xs_bad = numpy.ones( (20, 3, 4) ) - Xs_non_numeric = Xs_good.astype('str') + Xs_non_numeric = numpy.array(['str']) Ys_good = numpy.ones( (20, *yshape) # matching 20 data points ) @@ -113,32 +114,36 @@ def test__build(self): mds.UncertaintyModel((1,), (1,)) # mock _build from here on out - we don;t want the init build to # interfere - mds.UncertaintyModel._build = unittest.mock.MagicMock() return - def test__get_metric(self): + @unittest.mock.patch( + 'gandy.models.models.UncertaintyModel._build', + return_value='Model' + ) + def test__get_metric(self, mocked__build): """test ability to retrieve the correct callables""" - with unittest.mock.patch('gandy.quality_est.metrics' - ) as mocked_metrics: - def fake_metric(trues, predictions, uncertainties): - return 5 - mocked_metrics.fake_metric = fake_metric - # initialize the subject - subject = mds.UncertaintyModel((1,), (1,)) - # try all success cases - metric_out = subject._get_metric(fake_metric) - self.assertEqual(fake_metric, metric_out) - metric_out = subject._get_metric('fake_metric') - self.assertEqual(fake_metric, metric_out) - metric_out = subject._get_metric(None) - self.assertTrue(metric_out is None) - # and failure, not a class - with self.assertRaises(AttributeError): - subject._get_metric('not_a_class') + def fake_metric(trues, predictions, uncertainties): + return 5 + # initialize the subject + subject = mds.UncertaintyModel((1,), (1,)) + # try all success cases + metric_out = subject._get_metric(fake_metric) + self.assertEqual(fake_metric, metric_out) + metric_out = subject._get_metric('Metric') + self.assertEqual(gandy.quality_est.metrics.Metric, metric_out) + metric_out = subject._get_metric(None) + self.assertTrue(metric_out is None) + # and failure, not a class + with self.assertRaises(AttributeError): + subject._get_metric('not_a_class') return - def test_train(self): + @unittest.mock.patch( + 'gandy.models.models.UncertaintyModel._build', + return_value='Model' + ) + def test_train(self, mocked__build): """Proper passing of data to _train and updating of sessions""" subject = mds.UncertaintyModel((1,), (1,)) # mock the required nested calls @@ -154,6 +159,7 @@ def test_train(self): mocked__get_metric = unittest.mock.MagicMock( return_value='some_metric' ) + subject._get_metric = mocked__get_metric # run the train and check proper calls with unittest.mock.patch('time.clock', return_value='thetime' ) as mocked_time: @@ -177,14 +183,22 @@ def test_train(self): self.assertTrue('Starttime: thetime' in subject.sessions.keys()) return - def test__train(self): + @unittest.mock.patch( + 'gandy.models.models.UncertaintyModel._build', + return_value='Model' + ) + def test__train(self, mocked__build): """All it should do is raise an error for child to define""" subject = mds.UncertaintyModel((1,), (1,)) with self.assertRaises(mds.NotImplimented): subject._train('Xs', 'Ys') return - def test_predict(self): + @unittest.mock.patch( + 'gandy.models.models.UncertaintyModel._build', + return_value='Model' + ) + def test_predict(self, mocked__build): """Test proper flagging of predictions""" subject = mds.UncertaintyModel((1,), (1,)) # prepare and mock objects @@ -192,7 +206,7 @@ def test_predict(self): # here we set up a rotation of predictions, uncertaintains for # _predict to return, allowing us to test _predict output handling _predict_return = [ - (['length', '2'], numpy.array([5, 10], dtype=int)), # works + ([5, 10], numpy.array([5, 10], dtype=int)), # works (['length', '2'], ['wrong', 'dtype']), # failure, can't flag (['length', '2'], 5.0), # failure, pred/unc length mismatch ('length1', 5.0), # failure, does not match length of input @@ -208,8 +222,8 @@ def test_predict(self): ) subject._predict = mocked__predict # expected faulure, threshold not correct type - with self.raises(TypeError): - subject.predict(Xs_in, uc_threshold='seven') + with self.assertRaises(TypeError): + subject.predict(Xs_in, uc_threshold='not_number') # first rotation, expected to work, check outputs and correct calls preds, uncs, flags = subject.predict(Xs_in, uc_threshold=7.0, @@ -228,7 +242,7 @@ def test_predict(self): self.assertTrue(numpy.array_equal(flags, numpy.array([[False], [True]]))) # first failure case, can't flag strings - with self.assertRaised(TypeError): + with self.assertRaises(TypeError): subject.predict(Xs_in) # lengths of pred/unc do not match with self.assertRaises(ValueError): @@ -242,14 +256,22 @@ def test_predict(self): return - def test__predict(self): + @unittest.mock.patch( + 'gandy.models.models.UncertaintyModel._build', + return_value='Model' + ) + def test__predict(self, mocked__build): """Should just raise an error""" subject = mds.UncertaintyModel((1,), (1,)) with self.assertRaises(mds.NotImplimented): subject._predict('Xs') return - def test_score(self): + @unittest.mock.patch( + 'gandy.models.models.UncertaintyModel._build', + return_value='Model' + ) + def test_score(self, mocked__build): """Test proper handling of internal function when score is called""" subject = mds.UncertaintyModel((1,), (1,)) Xs = 'Xs' @@ -283,10 +305,14 @@ def fake_metric2(true, preds, uncertainties): self.assertEqual((2, 1), values.shape) # check that it can find failures in metric computation with self.assertRaises(ValueError): - subject.score(Xs, Ys) + subject.score(Xs, Ys, metric='some_metric') return - def test_property_shapes(self): + @unittest.mock.patch( + 'gandy.models.models.UncertaintyModel._build', + return_value='Model' + ) + def test_property_shapes(self, mocked__build): """Ensure that nonsensical shapes cannot be set""" subject = mds.UncertaintyModel((1,), (1,)) bad_shapes_to_test = ['not tuple', ('tuple', 'of', 'not', 'int')] @@ -304,7 +330,11 @@ def test_property_shapes(self): self.assertEqual(subject.model, None) return - def test_property_model(self): + @unittest.mock.patch( + 'gandy.models.models.UncertaintyModel._build', + return_value='Model' + ) + def test_property_model(self, mocked__build): """ensure safety of the model attribute""" subject = mds.UncertaintyModel((1,), (1,)) with self.assertRaises(RuntimeError): From 22d78df306a2a842e1e857ca6ec116fdd920bdca Mon Sep 17 00:00:00 2001 From: Kyle Moskowitz Date: Mon, 8 Mar 2021 15:58:29 -0800 Subject: [PATCH 29/99] Metrics module unit tests --- gandy/tests/test_metrics/test_metrics.py | 107 ++++++++++++++++++++++- 1 file changed, 105 insertions(+), 2 deletions(-) diff --git a/gandy/tests/test_metrics/test_metrics.py b/gandy/tests/test_metrics/test_metrics.py index f138c9e..21c60df 100644 --- a/gandy/tests/test_metrics/test_metrics.py +++ b/gandy/tests/test_metrics/test_metrics.py @@ -28,10 +28,14 @@ def test___init___(self): real = np.array([0, 1, 2]), uncertainties = "0, 1, 2") + with self.assertRaises(TypeError): + subject = metrics.Metric(predictions = np.array([0, 1, 2]), + real = "0, 1, 2") + # success case subject = metrics.Metric(predictions = np.array([0, 1, 2]), - real = np.array([0, 1, 2]), - uncertainties = np.array([0, 0.5, 1])) + real = np.array([0, 1, 2]), + uncertainties = np.array([0, 0.5, 1])) #check to make sure necessary attributes are inputted self.assertTrue(subject.predictions is not None) @@ -46,3 +50,102 @@ def test_calculate(self): subject.calculate = unittest.mock.MagicMock(name = 'calculate') subject.calculate.assert_called_once_with(kwargs) + +class TestMSE(unittest.TestCase): + """Unit test for MSE subclass""" + + def test_calculate(self): + """Test the calculate function within the parent Metric class""" + + # failure case: data not iterable + with self.assertRaises(TypeError): + subject = metric.MSE(predictions = "0, 1, 2", + real = np.array([0, 1, 2])) + + with self.assertRaises(TypeError): + subject = metric.MSE(predictions = np.array([0, 1, 2]), + real = "0, 1, 2") + + # failure case: uncertainties given when None expected + with self.assertRaises(TypeError): + subject = metric.MSE(predictions = np.array([0, 1, 2]), + real = np.array([0, 1, 2]), + uncertainties = np.array([0, 1, 2])) + + #check to make sure necessary attributes are inputted + subject = metrics.MSE(predictions = np.array([0, 1, 2]), + real = np.array([0, 1, 2])) + + self.assertTrue(subject.predictions is not None) + self.assertTrue(subject.real is not None) + self.assertTrue(subject.uncertainties is None) + + #check to make sure output is correct type + self.assertTrue(isinstance(subject, tuple)) + + +class TestRMSE(unittest.TestCase): + """Unit test for RMSE subclass""" + + def test_calculate(self): + """Test the calculate function within the parent Metric class""" + + # failure case: data not iterable + with self.assertRaises(TypeError): + subject = metric.RMSE(predictions = "0, 1, 2", + real = np.array([0, 1, 2])) + + with self.assertRaises(TypeError): + subject = metric.RMSE(predictions = np.array([0, 1, 2]), + real = "0, 1, 2") + + # failure case: uncertainties given when None expected + with self.assertRaises(TypeError): + subject = metric.RMSE(predictions = np.array([0, 1, 2]), + real = np.array([0, 1, 2]), + uncertainties = np.array([0, 1, 2])) + + #check to make sure necessary attributes are inputted + subject = metrics.RMSE(predictions = np.array([0, 1, 2]), + real = np.array([0, 1, 2])) + + self.assertTrue(subject.predictions is not None) + self.assertTrue(subject.real is not None) + self.assertTrue(subject.uncertainties is None) + + #check to make sure output is correct type + self.assertTrue(isinstance(subject, tuple)) + + +class TestF1(unittest.TestCase): + """Unit test for F1 subclass""" + + def test_calculate(self): + """Test the calculate function within the parent Metric class""" + + # failure case: data not iterable + with self.assertRaises(TypeError): + subject = metric.F1(predictions = "0, 1, 2", + real = np.array([0, 1, 2])) + + with self.assertRaises(TypeError): + subject = metric.F1(predictions = np.array([0, 1, 2]), + real = "0, 1, 2") + + # failure case: uncertainties given when None expected + with self.assertRaises(TypeError): + subject = metric.F1(predictions = np.array([0, 1, 2]), + real = np.array([0, 1, 2]), + uncertainties = np.array([0, 1, 2])) + + #check to make sure necessary attributes are inputted + subject = metrics.F1(predictions = np.array([0, 1, 2]), + real = np.array([0, 1, 2])) + + self.assertTrue(subject.predictions is not None) + self.assertTrue(subject.real is not None) + self.assertTrue(subject.uncertainties is None) + + #check to make sure output is correct type + self.assertTrue(isinstance(subject, (float, int))) + From 88f9655ff8b132167da42e29391418092d099704 Mon Sep 17 00:00:00 2001 From: evankomp Date: Mon, 8 Mar 2021 16:01:28 -0800 Subject: [PATCH 30/99] gps written and passing tests --- gandy/models/gps.py | 37 +++++++++++++++++++++-------- gandy/tests/test_models/test_gps.py | 20 +++++++++------- 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/gandy/models/gps.py b/gandy/models/gps.py index e015796..3e72739 100644 --- a/gandy/models/gps.py +++ b/gandy/models/gps.py @@ -19,7 +19,7 @@ score = cfr.evaluate(Xs, Ys, metric='mse') """ # imports -from typing import Type, Tuple, Object +from typing import Type, Tuple import sklearn.gaussian_process import numpy @@ -27,9 +27,8 @@ import gandy.models.models # Typing -Model = Type[Object] Array = Type[numpy.ndarray] -Predictor = Type[sklearn.gaussian_process] +Predictor = sklearn.gaussian_process # The gaussian process uncertainty model @@ -37,8 +36,8 @@ class ucGaussianProcess(gandy.models.models.UncertaintyModel): """Gaussian Process Regressor/Classifier Uncertainty Model - Utilizes sklearn's GP objects as an Uncertainty Model, able to make predic- - tions and uncertainty predictions. + Utilizes sklearn's GP Callables as an Uncertainty Model, able to make + predictions and uncertainty predictions. Args: xshape (tuple of int): @@ -74,7 +73,14 @@ def _build(self, # instatiate scikitlearn object with kwargs # . else # raise not implimented error - model = None + if model_type == 'classifier': + modelcls = sklearn.gaussian_process.GaussianProcessClassifier + elif model_type == 'regressor': + modelcls = sklearn.gaussian_process.GaussianProcessRegressor + else: + raise ValueError( + '`model_type` should be "classifier" or "regressor"') + model = modelcls(**kwargs) return model def _train(self, @@ -100,6 +106,7 @@ def _train(self, """ # pseudocode # . fit self model with Xs, Ys + self.model.fit(Xs, Ys, **kwargs) return None def _predict(self, @@ -126,12 +133,22 @@ def _predict(self, # pseudocode # . get uncertainties and predictions by passing return_std to # sklearn object's predict - predictions = None - uncertainties = None + print(self.model.__class__) + if isinstance(self.model, + sklearn.gaussian_process.GaussianProcessRegressor): + predictions, uncertainties = self.model.predict( + Xs, return_std=True + ) + elif isinstance(self.model, + sklearn.gaussian_process.GaussianProcessClassifier): + predictions = self.model.predict(Xs) + uncertainties = self.model.predict_proba(Xs) + else: + raise TypeError('The model does not seem to be a sklearn GP') return predictions, uncertainties @classmethod - def R(cls, *args, **kwargs) -> Model: + def R(cls, *args, **kwargs): """Alternative to passing model_type as 'regressor' to object initialization. @@ -144,7 +161,7 @@ def R(cls, *args, **kwargs) -> Model: return cls(*args, model_type='regressor', **kwargs) @classmethod - def C(cls, *args, **kwargs) -> Model: + def C(cls, *args, **kwargs): """Alternative to passing model_type as 'classifier' to object initialization. diff --git a/gandy/tests/test_models/test_gps.py b/gandy/tests/test_models/test_gps.py index 1689c66..b97a67c 100644 --- a/gandy/tests/test_models/test_gps.py +++ b/gandy/tests/test_models/test_gps.py @@ -18,7 +18,7 @@ def test__build(self, mocked_gp): """Ensure the child method creates sklearn GP""" # set up mocks mocked_gp.GaussianProcessRegressor.return_value = 'Regressor' - mocked_gp.GaussianProcessClassifier.return_value = 'Classifer' + mocked_gp.GaussianProcessClassifier.return_value = 'Classifier' # run both options and test calls # init and build methods already tested in parent # we know init kwargs get to here @@ -26,10 +26,10 @@ def test__build(self, mocked_gp): subject = gps.ucGaussianProcess((1,), (1,), model_type='something') subject = gps.ucGaussianProcess((1,), (1,), - model_type='classifer', + model_type='classifier', keyword=5) mocked_gp.GaussianProcessClassifier.called_with(keyword=5) - self.assertEqual(subject.model, 'Classifer') + self.assertEqual(subject.model, 'Classifier') subject = gps.ucGaussianProcess((1,), (1,), model_type='regressor', keyword=5) @@ -43,7 +43,7 @@ def test__train(self, mocked_gp): Xs = 'Xs' Ys = 'Ys' subject = gps.ucGaussianProcess((1,), (1,), - model_type='classifer') + model_type='classifier') subject._train(Xs, Ys, keyword=5) subject.model.fit.assert_called_with(Xs, Ys, keyword=5) subject = gps.ucGaussianProcess((1,), (1,), @@ -52,15 +52,16 @@ def test__train(self, mocked_gp): subject.model.fit.assert_called_with(Xs, Ys, keyword=5) return - @unittest.mock.patch('sklearn.gaussian_process') def test__predict(self): """Ensure the proper calls with return_std keyword""" Xs = 'Xs' # classifer subject = gps.ucGaussianProcess((1,), (1,), - model_type='classifer') - subject.model.predict.return_value = 'preds' - subject.model.predict_proba.return_value = 'uncs' + model_type='classifier') + subject.model.predict = unittest.mock.MagicMock( + return_value='preds') + subject.model.predict_proba = unittest.mock.MagicMock( + return_value='uncs') # execute the method preds, uncs = subject._predict(Xs) subject.model.predict.assert_called_with(Xs) @@ -70,7 +71,8 @@ def test__predict(self): # regressor subject = gps.ucGaussianProcess((1,), (1,), model_type='regressor') - subject.model.predict.return_value = ('preds', 'uncs') + subject.model.predict = unittest.mock.MagicMock( + return_value=('preds', 'uncs')) # execute the method preds, uncs = subject._predict(Xs) subject.model.predict.assert_called_with(Xs, return_std=True) From fce93f89066ff60f85d209f36174f6d49ad42308 Mon Sep 17 00:00:00 2001 From: evankomp Date: Mon, 8 Mar 2021 16:04:42 -0800 Subject: [PATCH 31/99] removed now unneeded pseudocode --- gandy/models/gps.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/gandy/models/gps.py b/gandy/models/gps.py index 3e72739..dd0bcd8 100644 --- a/gandy/models/gps.py +++ b/gandy/models/gps.py @@ -68,11 +68,6 @@ def _build(self, instance of sklearn.gaussian_process: The built predictor. """ - # psueudocode - # . if statement classifier or regressor - # instatiate scikitlearn object with kwargs - # . else - # raise not implimented error if model_type == 'classifier': modelcls = sklearn.gaussian_process.GaussianProcessClassifier elif model_type == 'regressor': @@ -104,8 +99,6 @@ def _train(self, None: No losses to return for GP fitting. """ - # pseudocode - # . fit self model with Xs, Ys self.model.fit(Xs, Ys, **kwargs) return None @@ -130,9 +123,6 @@ def _predict(self, array of prediction uncertainties of targets withthe same length as Xs """ - # pseudocode - # . get uncertainties and predictions by passing return_std to - # sklearn object's predict print(self.model.__class__) if isinstance(self.model, sklearn.gaussian_process.GaussianProcessRegressor): From b12f5b66b93b6162eef477219f895cb21d874204 Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Mon, 8 Mar 2021 16:17:15 -0800 Subject: [PATCH 32/99] Redistributed test functions for deepchem class versus gandy model --- gandy/models/gans.py | 19 ++-- gandy/tests/test_models/test_dcgan.py | 43 +++++++++ gandy/tests/test_models/test_gans.py | 128 ++++++++++++++------------ 3 files changed, 122 insertions(+), 68 deletions(-) create mode 100644 gandy/tests/test_models/test_dcgan.py diff --git a/gandy/models/gans.py b/gandy/models/gans.py index cbde9cc..85a8c88 100644 --- a/gandy/models/gans.py +++ b/gandy/models/gans.py @@ -67,16 +67,18 @@ def _build(self, **kwargs) -> Object: dcgan.YSHAPE = self.yshape # get noise shape from kwargs # default is 10 dimensional - dcgan.NOISE_SHAPE = kwargs.get('noise_shape', (10,)) - # get n_classes from kwargs - # default is the y dimension - # e.g., regression would be == 1 - # This would also be correct for a one hot encoded y vector. - dcgan.N_CLASSES = kwargs.get('n_classes', self.yshape[0]) + self.noise_shape = kwargs.get('noise_shape', (10,)) + dcgan.NOISE_SHAPE = self.noise_shape # determine whether to use gan or conditional gan - if len(self.yshape) == 3: + if len(self.yshape) == 2: self.conditional = True + # get n_classes from kwargs + # default is the y dimension + # e.g., regression would be == 1 + # This would also be correct for a one hot encoded y vector. + self.n_classes = kwargs.get('n_classes', self.yshape[0]) + dcgan.N_CLASSES = self.n_classes else: self.conditional = False @@ -211,7 +213,8 @@ def _predict(self, """ # adapted from deepchem tutorial 14: if self.conditional: - Ys = kwargs.get() + Ys = kwargs.get('Ys', None) + assert Ys is not None, "This is a cGAN. Must specify Ys (Ys=) to call predict." one_hot_Ys = deepchem.metrics.to_one_hot(Ys, self.model.N_CLASSES) generated_points = self.predict_gan_generator( conditional_inputs=[one_hot_Ys]) diff --git a/gandy/tests/test_models/test_dcgan.py b/gandy/tests/test_models/test_dcgan.py new file mode 100644 index 0000000..bbab8c8 --- /dev/null +++ b/gandy/tests/test_models/test_dcgan.py @@ -0,0 +1,43 @@ +"""Testing functions for deepchem GAN class.""" + +import numpy as np +import unittest +import unittest.mock + +import deepchem + +import gandy.models.dcgan as dcgan + + +class TestGAN(unittest.TestCase): + """Test Deepchem GAN class.""" + + def test_create_generator(self): + """ + Test create generator function. + + The create generator function uses kwargs to create a Keras model. + This checks that the model compiles. + """ + return + + def test_create_discriminator(self): + """ + Test create discriminator function. + + The create discriminator function uses kwargs to create a Keras model. + This checks that the model compiles. + """ + return + + def test_get_noise_input_shape(self): + """Test get_noise_input_shape function.""" + return + + def test_get_data_input_shapes(self): + """Test get_data_input_shapes function.""" + return + + def test_get_conditional_input_shapes(self): + """Test get_conditional_input_shapes function.""" + return diff --git a/gandy/tests/test_models/test_gans.py b/gandy/tests/test_models/test_gans.py index 023d8b6..2c39ab6 100644 --- a/gandy/tests/test_models/test_gans.py +++ b/gandy/tests/test_models/test_gans.py @@ -1,120 +1,128 @@ -""" -Testing functions for UncertaintyModel gan class. -""" +"""Testing functions for UncertaintyModel gan class.""" -import numpy as np +# import numpy as np import unittest -import unittest.mock +import unittest.mock as mock -import deepchem +# import deepchem import gandy.models.gans as gans import gandy.models.models -def TestGAN(unittest.test_case): +class TestGAN(unittest.TestCase): """Test GAN class.""" def test_inheritence(): - # ensure the subclass class inherits from both parent classes + """Ensure the subclass class inherits from parent class.""" assert issubclass(gans.gan, gandy.models.models.UncertaintyModel) - assert issubclass(gans.gan, deepchem.models.GAN) - - def test_create_generator(self): - ''' - Test create generator function. - - The create generator function uses kwargs to create a Keras model. - This checks that the model compiles. - ''' - return - - def test_create_discriminator(self): - ''' - Test create discriminator function. - - The create discriminator function uses kwargs to create a Keras model. - This checks that the model compiles. - ''' - return def test__build(self): - ''' + """ Test build function. The build function should create a generator and discriminator. This checks both functions are called. It also checks that both generator and discriminator are attributes with type == Keras model. - ''' + """ + # CHECK (normal) GAN # create gan instance - subject = gans.gan(xshape=(4,), yshape=(2,)) - # create mock functions - subject.create_generator = mock.MagicMock(name='create_generator') - subject.create_discriminator = mock.MagicMock(name='create_discriminator') - kwargs = dict(option=x1) - subject.build(kwargs) + subject = gans.GAN(xshape=(4,), yshape=(2,)) + kwargs = dict(noise_shape=(5,)) + subject._build(kwargs) + # assert create generator function called + subject.create_generator.assert_called_once_with(kwargs) + # assert create discriminator function called + subject.create_discriminator.assert_called_once_with(kwargs) + # check attributes + self.assertTrue(hasattr('conditional', False)) + self.assertTrue(hasattr('noise_shape', (5,))) + + # CHECK Conditional GAN + # create gan instance + subject = gans.CondGAN(xshape=(4,), yshape=(2, 4)) + kwargs = dict(noise_shape=(5,), n_classes=4) + subject._build(kwargs) # assert create generator function called subject.create_generator.assert_called_once_with(kwargs) # assert create discriminator function called subject.create_discriminator.assert_called_once_with(kwargs) # check attributes - self.assertTrue(hasattr(subject, 'n_classes')) - self.assertTrue(hasattr(subject, 'generator')) - self.assertTrue(hasattr(subject, 'discriminator')) + self.assertTrue(hasattr('conditional', True)) + self.assertTrue(hasattr('noise_shape', (5,))) + self.assertTrue(hasattr('n_classes', 4)) return def test__train(self): - ''' + """ Test train function. The train function calls the fit function for the generator and the predict function for the discriminator. This checks that there is a Keras callback History object returned. - ''' + """ Xs = 'Xs' Ys = 'Ys' - subject = gans.gan(xshape=(4,), yshape=(2,)) - kwargs = dict(option=x1) + subject = gans.GAN(xshape=(4,), yshape=(2,)) + subject.iterbacthes = mock.MagicMock(name='iterbatches', + return_value="Batch1") + subject.fit_gan = mock.MagicMock(name='fit_gan') + kwargs = dict(option='x1') subject._train(Xs, Ys, kwargs) - - # assert generator model called fit and discriminator called predict - subject.generator.fit.assert_called_with(Xs, Ys, kwargs) - subject.discriminator.predict.assert_called_with(Xs, Ys, kwargs) + # assert fit_gan was called + subject.iterbacthes.assert_called_with(Xs, Ys, kwargs) + subject.fit_gan.assert_called_with("Batch1") return - def test__predict(self): - ''' + @unittest.mock.patch('gandy.models.gans.GAN._build', return_value='Model') + def test__predict(self, mocked__build): + """ Test predict function. The predict function returns predictions and uncertainties. This checks predictions and uncertainties are the appropriate shape and the appropriate deepchem calls are made. - ''' + """ Xs = 'Xs' + # CHECK (normal) GAN subject = gans.gan(xshape=(4,), yshape=(2,)) - - subject.predict_gan_generator = mock.MagicMock(name='predict_gan_generator') - - subject._predict.return_value = ('preds', 'ucs') + subject.predict_gan_generator = mock.MagicMock( + name='predict_gan_generator', return_value='generated_points') preds, ucs = subject._predict(Xs) subject._predict.assert_called_with(Xs) - self.assertEqual('preds', preds) - self.assertEqual('ucs', ucs) + subject.predict_gan_generator.assert_called_with(None) + self.assertEqual('preds', 'generated_points') + self.assertEqual('ucs', None) + + # CHECK Conditional GAN + Ys = 'Ys' + subject = gans.gan(xshape=(4,), yshape=(2, 3), n_classes=3) + subject.predict_gan_generator = mock.MagicMock( + name='predict_gan_generator', return_value='generated_points') + with mock.path('deepchem.metrics.to_one_hot', + return_value=[10]) as mocked_one_hot: + preds, ucs = subject._predict(Xs, Ys=Ys) + mocked_one_hot.assert_called_with(Ys, 3) + subject._predict.assert_called_with(Xs, Ys=Ys) + subject.predict_gan_generator.assert_called_with( + conditional_inputs=[10]) + self.assertEqual('preds', 'generated_points') + self.assertEqual('ucs', None) return def test__save(self): - ''' + """ Test save function. This checks that a file is written with the appropriate name. - ''' + """ return def test__load(self): - ''' + """ Test load function. This checks that a Keras model instance is returned. - ''' - return \ No newline at end of file + """ + return From 1bb927a9ccabd8ceb31bf8586f844fe0b07725f2 Mon Sep 17 00:00:00 2001 From: evankomp Date: Mon, 8 Mar 2021 16:22:19 -0800 Subject: [PATCH 33/99] setup now finds packages --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9fd2fd6..f4da3cc 100644 --- a/setup.py +++ b/setup.py @@ -32,7 +32,7 @@ 'BNN', 'machine_learning', ], - packages=['gandy'], + packages=find_packages(exclude="tests"), python_requires='>=3.6', install_requires=[ 'numpy>=1.19.1' From 2e09e0ef457a3cfa6e35a86ae812ff1a8bdaee72 Mon Sep 17 00:00:00 2001 From: evankomp Date: Mon, 8 Mar 2021 17:29:26 -0800 Subject: [PATCH 34/99] GPs working, example notebook written --- examples/GPs_Showcase.ipynb | 327 ++++++++++++++++++++++++++++++++++++ gandy/models/gps.py | 4 +- 2 files changed, 329 insertions(+), 2 deletions(-) create mode 100644 examples/GPs_Showcase.ipynb diff --git a/examples/GPs_Showcase.ipynb b/examples/GPs_Showcase.ipynb new file mode 100644 index 0000000..11b31ee --- /dev/null +++ b/examples/GPs_Showcase.ipynb @@ -0,0 +1,327 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "adverse-monday", + "metadata": {}, + "source": [ + "# Example of using Gaussian Processes as an uncertainty model" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "requested-industry", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy\n", + "import sklearn.datasets\n", + "import sklearn.model_selection\n", + "import sklearn.preprocessing\n", + "\n", + "import gandy.models.gps" + ] + }, + { + "cell_type": "markdown", + "id": "waiting-springfield", + "metadata": {}, + "source": [ + "## A regression task, using the boston dataset" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "applied-renewal", + "metadata": {}, + "outputs": [], + "source": [ + "## loading the data\n", + "Xs, Ys = sklearn.datasets.load_boston(return_X_y=True)\n", + "Xsr, Xst, Ysr, Yst = sklearn.model_selection.train_test_split(Xs, Ys, train_size = 0.8)\n", + "## normalizing it\n", + "norm = sklearn.preprocessing.Normalizer()\n", + "Xsr = norm.fit_transform(Xsr)\n", + "Xst = norm.transform(Xst)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "simplified-benjamin", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X data: (506, 13) float64\n", + "Y data: (506,) float64\n" + ] + } + ], + "source": [ + "print('X data: ', Xs.shape, Xs.dtype)\n", + "print('Y data: ', Ys.shape, Ys.dtype)" + ] + }, + { + "cell_type": "markdown", + "id": "joint-lunch", + "metadata": {}, + "source": [ + "We have 13 features and 1 target." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "packed-active", + "metadata": {}, + "outputs": [], + "source": [ + "## instantialize our uncertainty model\n", + "## give it the shapes that we need\n", + "gpr = gandy.models.gps.ucGaussianProcess.R(xshape=(13,), yshape=(1,))" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "progressive-camcorder", + "metadata": {}, + "outputs": [], + "source": [ + "## fit the model\n", + "gpr.train(Xsr, Ysr)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "looking-feedback", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/ek/miniconda3/envs/gandy_env/lib/python3.6/site-packages/sklearn/gaussian_process/_gpr.py:370: UserWarning: Predicted variances smaller than 0. Setting those variances to 0.\n", + " warnings.warn(\"Predicted variances smaller than 0. \"\n" + ] + } + ], + "source": [ + "## make predictions\n", + "preds, uncs, flags = gpr.predict(Xst, uc_threshold = 0.01)" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "transparent-institute", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0.5, 1.0, 'Certain and uncertain predictions, boston data')" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots()\n", + "ax.scatter(Yst.reshape(-1,1)[flags == False],\n", + " preds[flags == False],\n", + " c='b', label='std <= 0.01')\n", + "ax.scatter(Yst.reshape(-1,1)[flags == True],\n", + " preds[flags == True],\n", + " c='r', label='std > 0.01')\n", + "ax.set_xlabel('True target')\n", + "ax.set_ylabel('Predicted target')\n", + "plt.legend()\n", + "ax.set_title('Certain and uncertain predictions, boston data', size=20)" + ] + }, + { + "cell_type": "markdown", + "id": "affected-lending", + "metadata": {}, + "source": [ + "## A classification task, using the boston dataset" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "fifty-deadline", + "metadata": {}, + "outputs": [], + "source": [ + "## loading the data\n", + "Xs, Ys = sklearn.datasets.load_iris(return_X_y=True)\n", + "Xsr, Xst, Ysr, Yst = sklearn.model_selection.train_test_split(Xs, Ys, train_size = 0.8)\n", + "## normalizing it\n", + "norm = sklearn.preprocessing.Normalizer()\n", + "Xsr = norm.fit_transform(Xsr)\n", + "Xst = norm.transform(Xst)" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "excess-trading", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X data: (150, 4) float64\n", + "Y data: (150,) int64\n" + ] + } + ], + "source": [ + "print('X data: ', Xs.shape, Xs.dtype)\n", + "print('Y data: ', Ys.shape, Ys.dtype)" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "bigger-binding", + "metadata": {}, + "outputs": [], + "source": [ + "## instantialize our uncertainty model\n", + "## give it the shapes that we need\n", + "gpc = gandy.models.gps.ucGaussianProcess.C(xshape=(4,), yshape=(1,))" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "violent-vacation", + "metadata": {}, + "outputs": [], + "source": [ + "## fit the model\n", + "gpr.train(Xsr, Ysr)" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "sensitive-uncertainty", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/ek/miniconda3/envs/gandy_env/lib/python3.6/site-packages/sklearn/gaussian_process/_gpr.py:370: UserWarning: Predicted variances smaller than 0. Setting those variances to 0.\n", + " warnings.warn(\"Predicted variances smaller than 0. \"\n" + ] + } + ], + "source": [ + "## make predictions\n", + "preds, uncs, flags = gpr.predict(Xst, uc_threshold = 0.001)" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "id": "orange-horse", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + ":6: RuntimeWarning: Converting input from bool to for compatibility.\n" + ] + } + ], + "source": [ + "counts, edges = numpy.histogram(flags, bins=2, density=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "id": "presidential-drawing", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "([,\n", + " ],\n", + " [Text(-1.0759623636622138, 0.22870284646768646, 'precise'),\n", + " Text(1.0759623690153899, -0.22870282128297006, 'uncertain')])" + ] + }, + "execution_count": 71, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots()\n", + "ax.pie(counts, labels=['precise', 'uncertain'])" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/gandy/models/gps.py b/gandy/models/gps.py index dd0bcd8..3c363ca 100644 --- a/gandy/models/gps.py +++ b/gandy/models/gps.py @@ -19,7 +19,7 @@ score = cfr.evaluate(Xs, Ys, metric='mse') """ # imports -from typing import Type, Tuple +from typing import Type, Tuple, Callable import sklearn.gaussian_process import numpy @@ -81,6 +81,7 @@ def _build(self, def _train(self, Xs: Array, Ys: Array, + metric: Callable = None, **kwargs): """Trains the gaussian process on training data via covariance kernel. @@ -123,7 +124,6 @@ def _predict(self, array of prediction uncertainties of targets withthe same length as Xs """ - print(self.model.__class__) if isinstance(self.model, sklearn.gaussian_process.GaussianProcessRegressor): predictions, uncertainties = self.model.predict( From 46d0067ddb7adb33fa14789e42349f763975d167 Mon Sep 17 00:00:00 2001 From: evankomp Date: Mon, 8 Mar 2021 17:59:44 -0800 Subject: [PATCH 35/99] removed _load and _save, which are redundant. save and load should be overloaded by child directly --- gandy/models/models.py | 47 +++----------------------- gandy/tests/test_models/test_models.py | 17 ++++++++++ 2 files changed, 22 insertions(+), 42 deletions(-) diff --git a/gandy/models/models.py b/gandy/models/models.py index bab055c..08170ea 100644 --- a/gandy/models/models.py +++ b/gandy/models/models.py @@ -399,30 +399,14 @@ def score(self, def save(self, filename: str, **kwargs): - """Save the model out of memory to the hard drive by specified format. - - Save to model to hardrive as two files, "`filename`.json" and - "`filename`.XX" where the XX is determined by the predictor type + """Save the model out of memory to the hard drive. Must be overloaded + by child. Args: filename (str): path to save model to, no extension - **kwargs: - keyword arguments to pass to _save, child specified method """ - return - - def _save(filename: str, - **kwargs): - """Method defined by child to save the predictor. - - Method must save into memory the object at self.model - - Args: - filename (str): - name of file to save model to - """ - # raise not implimented + raise NotImplimented(self) return @classmethod @@ -431,40 +415,19 @@ def load(cls, **kwargs): """Load a model from hardrive at filename. - From two files, "`filename`.json" and "`filename`.XX" where the XX is - determined by the predictor type, load the model into memory. + Must be overloaded by child. Args: filename (str): path of file to load - **kwargs: - keyword arguments to pass to _load Returns: instance of class: the loaded UncertaintyModel """ - # pseudocode - # . load the json and run cls(args) - # . predictor = _load - # . instance._model = predictor + raise NotImplimented(cls) instance = None return instance - def _load(self, - filename: str, - **kwargs): - """Method defined by child to load a predictor into memory. - - Loads the object to be assigned to self.model. - - Args: - filename (str): - path of file to load - """ - # raise not implimented - model = None - return model - @property def model(self): """predictor: the overall predictor model""" diff --git a/gandy/tests/test_models/test_models.py b/gandy/tests/test_models/test_models.py index 8b043f0..608b589 100644 --- a/gandy/tests/test_models/test_models.py +++ b/gandy/tests/test_models/test_models.py @@ -340,3 +340,20 @@ def test_property_model(self, mocked__build): with self.assertRaises(RuntimeError): subject.model = 'Not None' return + + @unittest.mock.patch( + 'gandy.models.models.UncertaintyModel._build', + return_value='Model' + ) + def test_save(self, mocked__build): + """Not implimented, we don't want to save parent""" + subject = mds.UncertaintyModel((1,), (1,)) + with self.assertRaises(mds.NotImplimented): + subject.save('filename') + return + + def test_load(self): + """Not implimented, don't want to load parent""" + with self.assertRaises(mds.NotImplimented): + mds.UncertaintyModel.load('filename') + return From 95f65a9716763ac36925339d7087f75717ae3786 Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Mon, 8 Mar 2021 18:12:45 -0800 Subject: [PATCH 36/99] Addjusted class attributes from global --- gandy/models/dcgan.py | 34 ++++++++------ gandy/models/gans.py | 106 +++++++++++++++++++++++++++--------------- 2 files changed, 87 insertions(+), 53 deletions(-) diff --git a/gandy/models/dcgan.py b/gandy/models/dcgan.py index 2973170..2746553 100644 --- a/gandy/models/dcgan.py +++ b/gandy/models/dcgan.py @@ -20,12 +20,6 @@ import numpy as np Array = Type[np.ndarray] -# These should be set by the gandy model when _build is called. -XSHAPE = None -YSHAPE = None -NOISE_SHAPE = None -N_CLASSES = None - class DCGAN(deepchem.models.GAN): """ @@ -48,6 +42,15 @@ class DCGAN(deepchem.models.GAN): This class builds off of the deepchem GAN class found at the url above. """ + def __init__(self, xshape, yshape, noise_shape, n_classes=None, **kwargs): + """Deepchem init function + class atributes.""" + super(DCGAN, self).__init__(**kwargs) + # These should be set by the gandy model when _build is called. + self.xshape = xshape + self.yshape = yshape + self.noise_shape = noise_shape + self.n_classes = n_classes + def create_generator(self, **kwargs): """ Create the generator as a keras model. @@ -106,7 +109,8 @@ def create_generator(self, **kwargs): gen = Dropout(dropout)(gen) # generator outputs - gen = Dense(XSHAPE[0], activation=activation)(gen) + # is xhape[0] really what we want, or batch size? + gen = Dense(self.xshape[0], activation=activation)(gen) gen = Dropout(dropout)(gen) # final construction of Keras model @@ -159,7 +163,7 @@ def create_discriminator(self, **kwargs): dropout = kwargs.get('dropout', 0.05) # construct input - data_in = Input(shape=XSHAPE) + data_in = Input(shape=self.xshape) # build first layer of network discrim = Dense(layer_dimensions[0], activation=activation, kernel_regularizer=kernel_regularizer)(data_in) @@ -185,7 +189,7 @@ def get_noise_input_shape(self) -> Tuple[int]: This should be set by the gandy model when an build is called. """ - return NOISE_SHAPE + return self.noise_shape def get_data_input_shapes(self) -> Tuple[int]: """ @@ -193,7 +197,7 @@ def get_data_input_shapes(self) -> Tuple[int]: This should be set by the gandy model when an build is called. """ - return XSHAPE + return self.xshape class CondDCGAN(DCGAN): @@ -213,7 +217,7 @@ def get_conditional_input_shapes(self, **kwargs) -> Array: This should be set by the gandy model when an build is called. """ - return [(N_CLASSES,)] + return [(self.n_classes,)] def create_generator(self, **kwargs) -> Object: """ @@ -262,7 +266,7 @@ def create_generator(self, **kwargs) -> Object: # construct input noise_in = Input(shape=self.get_noise_input_shape()) - conditional_in = Input(shape=(N_CLASSES,)) + conditional_in = Input(shape=(self.n_classes,)) gen_input = Concatenate()([noise_in, conditional_in]) # build first layer of network @@ -276,7 +280,7 @@ def create_generator(self, **kwargs) -> Object: gen = Dropout(dropout)(gen) # generator outputs - gen = Dense(XSHAPE[0], activation=activation)(gen) + gen = Dense(self.xshape[0], activation=activation)(gen) gen = Dropout(dropout)(gen) # final construction of Keras model @@ -329,8 +333,8 @@ def create_discriminator(self, **kwargs) -> Object: dropout = kwargs.get('dropout', 0.05) # construct input - data_in = Input(shape=XSHAPE) - conditional_in = Input(shape=(N_CLASSES,)) + data_in = Input(shape=self.xshape) + conditional_in = Input(shape=(self.n_classes,)) discrim_input = Concatenate()([data_in, conditional_in]) # build first layer of network diff --git a/gandy/models/gans.py b/gandy/models/gans.py index 85a8c88..fcc4f1e 100644 --- a/gandy/models/gans.py +++ b/gandy/models/gans.py @@ -16,7 +16,7 @@ # deep learning imports import deepchem import gandy.models.dcgan as dcgan -# import tensorflow as tf +import tensorflow as tf # typing imports from typing import Any, Object, Type @@ -62,31 +62,38 @@ def _build(self, **kwargs) -> Object: model - Deepchem GAN model found in dcgan type == Object """ - # setting the dcgan global variables - dcgan.XSHAPE = self.xshape - dcgan.YSHAPE = self.yshape # get noise shape from kwargs # default is 10 dimensional - self.noise_shape = kwargs.get('noise_shape', (10,)) - dcgan.NOISE_SHAPE = self.noise_shape + noise_shape = kwargs.get('noise_shape', (10,)) + # get n_classes from kwargs + # default is the y dimension + # e.g., regression would be == 1 + # This would also be correct for a one hot encoded y vector. + n_classes = kwargs.get('n_classes', None) # determine whether to use gan or conditional gan - if len(self.yshape) == 2: + if n_classes is not None: + # if number of classes is specified, assumes conditional GAN self.conditional = True - # get n_classes from kwargs - # default is the y dimension - # e.g., regression would be == 1 - # This would also be correct for a one hot encoded y vector. - self.n_classes = kwargs.get('n_classes', self.yshape[0]) - dcgan.N_CLASSES = self.n_classes + if len(self.yshape) == 2: + # Ys are already one hot encoded + n_classes = kwargs.get('n_classes', self.yshape[1]) + else: + # Ys are NOT one hot encoded + n_classes = kwargs.get('n_classes', self.yshape[0]) else: self.conditional = False + # if no n_classes specified, assumed to be regression + # and no need for conditional inputs + n_classes = kwargs.get('n_classes', self.yshape[0]) # instantiating the model as the deepchem gan if self.conditional: - model = dcgan.CondDCGAN(**kwargs) + model = dcgan.CondDCGAN(self.xshape, self.yshape, noise_shape, + n_classes=n_classes, **kwargs) else: - model = dcgan.DCGAN(**kwargs) + model = dcgan.DCGAN(self.xshape, self.yshape, + noise_shape, **kwargs) return model def generate_data(self, @@ -96,7 +103,8 @@ def generate_data(self, """ Generating function. - Create a batch of bootstrapped data. _train helper function + Create a batch of bootstrapped data. _train helper function. + From deepchem tutorial 14. Arguments: Xs/Ys - training examples/targets @@ -119,15 +127,16 @@ def generate_data(self, points = Ys[indices] return classes, points - def iterate_batches(self, - Xs: Array, - Ys: Array, - **kwargs): + def iterbatches(self, + Xs: Array, + Ys: Array, + **kwargs): """ Function that creates batches of generated data. The deepchem fit_gan unction reads in a dictionary for training. - This creates that dictionary for each batch. _train helper function + This creates that dictionary for each batch. _train helper function. + From deepchem tutorial 14. Arguments: Xs/Ys - training examples/targets @@ -139,7 +148,6 @@ def iterate_batches(self, batch_size - number of data points in a batch type == int - Yields: batched_data - data split into batches type == dict @@ -151,8 +159,13 @@ def iterate_batches(self, # training loop for i in range(batches): classes, points = self.generate_data(Xs, Ys, batch_size) - classes = deepchem.metrics.to_one_hot(classes, - self.model.N_CLASSES) + if len(Ys.shape) == 2: + # Ys already one hot encoded + pass + else: + # must one hot encode Ys + classes = deepchem.metrics.to_one_hot(classes, + self.model.n_classes) batched_data = {self.data_inputs[0]: points, self.conditional_inputs[0]: classes} yield batched_data @@ -179,7 +192,7 @@ def _train(self, """ # train GAN on data # self.model = deepchem GAN instance - self.model.fit_gan(self.iterbatches(Xs, Ys, **kwargs)) + self._model.fit_gan(self.iterbatches(Xs, Ys, **kwargs)) # The deepchem gan is a Keras model whose # outputs are [gen_loss, disrcim_loss]. # Thus the final losses for the generator @@ -214,45 +227,62 @@ def _predict(self, # adapted from deepchem tutorial 14: if self.conditional: Ys = kwargs.get('Ys', None) - assert Ys is not None, "This is a cGAN. Must specify Ys (Ys=) to call predict." - one_hot_Ys = deepchem.metrics.to_one_hot(Ys, self.model.N_CLASSES) - generated_points = self.predict_gan_generator( + assert Ys is not None, "This is a cGAN.\ + Must specify Ys (Ys=) to call predict." + if len(Ys.shape) == 2: + # assumes data is in bacthed form + # Ys already one hot encoded + one_hot_Ys = Ys + else: + # must one hot encode Ys + one_hot_Ys = deepchem.metrics.to_one_hot(Ys, + self.model.n_classes) + # generate data with conditional inputs + generated_points = self._model.predict_gan_generator( conditional_inputs=[one_hot_Ys]) else: - generated_points = self.predict_gan_generator() + generated_points = self._model.predict_gan_generator() # the above code generates points, but we need uncertainties as well predictions, uncertainties = generated_points, None return predictions, uncertainties - def _save(self, filename: str, **kwargs): + def save(self, filename: str, **kwargs): """ Method defined by child to save the predictor. Method must save into memory the object at self._model + For other functionalities to add, see + https://www.tensorflow.org/guide/keras/save_and_serialize Arguments: filename (str): name of file to save model to """ # save model aka generator and discriminator separately - # assert filename.endswith('.h5') or other extension - # self.generator.save(filename) + if filename.endswith('.h5'): + self._model.save(filename) + else: + path_to_model = filename + self._model.save(path_to_model) return None - def _load(self, filename: str, **kwargs): + @classmethod + def load(cls, filename: str, **kwargs): """ Method defined by child to load a predictor into memory. Loads the object to be assigned to self._model. - - Should this be a class method? + For other functionalities to add, see + https://www.tensorflow.org/guide/keras/save_and_serialize Arguments: filename (str): path of file to load """ # call Keras.load function - # two filenames, one for gen and one for discrim? - # model = tf.keras.model.load_model(filename, compile=False) - model = None + if filename.endswith('.h5'): + model = tf.keras.model.load_model(filename, compile=False) + else: + path_to_model = filename + model = tf.keras.model.load_model(path_to_model) return model From bfd4289cb8ab55cd976e260486f82a2aa8ce7451 Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Mon, 8 Mar 2021 18:13:07 -0800 Subject: [PATCH 37/99] finished first draft of gan tests --- gandy/tests/test_models/test_gans.py | 92 +++++++++++++++++++++++++--- 1 file changed, 82 insertions(+), 10 deletions(-) diff --git a/gandy/tests/test_models/test_gans.py b/gandy/tests/test_models/test_gans.py index 2c39ab6..2af4ac5 100644 --- a/gandy/tests/test_models/test_gans.py +++ b/gandy/tests/test_models/test_gans.py @@ -35,12 +35,13 @@ def test__build(self): # assert create discriminator function called subject.create_discriminator.assert_called_once_with(kwargs) # check attributes - self.assertTrue(hasattr('conditional', False)) - self.assertTrue(hasattr('noise_shape', (5,))) + self.assertTrue(subject.conditional, False) + self.assertTrue(subject.noise_shape, (5,)) # CHECK Conditional GAN # create gan instance subject = gans.CondGAN(xshape=(4,), yshape=(2, 4)) + assert issubclass(gans.CondGAN, gans.GAN) kwargs = dict(noise_shape=(5,), n_classes=4) subject._build(kwargs) # assert create generator function called @@ -48,9 +49,9 @@ def test__build(self): # assert create discriminator function called subject.create_discriminator.assert_called_once_with(kwargs) # check attributes - self.assertTrue(hasattr('conditional', True)) - self.assertTrue(hasattr('noise_shape', (5,))) - self.assertTrue(hasattr('n_classes', 4)) + self.assertTrue(subject.conditional, True) + self.assertTrue(subject.noise_shape, (5,)) + self.assertTrue(subject.n_classes, 4) return def test__train(self): @@ -86,7 +87,7 @@ def test__predict(self, mocked__build): """ Xs = 'Xs' # CHECK (normal) GAN - subject = gans.gan(xshape=(4,), yshape=(2,)) + subject = gans.GAN(xshape=(4,), yshape=(2,)) subject.predict_gan_generator = mock.MagicMock( name='predict_gan_generator', return_value='generated_points') preds, ucs = subject._predict(Xs) @@ -97,11 +98,11 @@ def test__predict(self, mocked__build): # CHECK Conditional GAN Ys = 'Ys' - subject = gans.gan(xshape=(4,), yshape=(2, 3), n_classes=3) + subject = gans.GAN(xshape=(4,), yshape=(2, 3), n_classes=3) subject.predict_gan_generator = mock.MagicMock( name='predict_gan_generator', return_value='generated_points') - with mock.path('deepchem.metrics.to_one_hot', - return_value=[10]) as mocked_one_hot: + with mock.patch('deepchem.metrics.to_one_hot', + return_value=[10]) as mocked_one_hot: preds, ucs = subject._predict(Xs, Ys=Ys) mocked_one_hot.assert_called_with(Ys, 3) subject._predict.assert_called_with(Xs, Ys=Ys) @@ -111,12 +112,77 @@ def test__predict(self, mocked__build): self.assertEqual('ucs', None) return - def test__save(self): + @unittest.mock.patch('gandy.models.gans.GAN._build', return_value='Model') + def test_iterbacthes(self, mocked__build): + """ + Test iterbacthes function. + + The iterbacthes function calls the generate_data function to + create batches of boostrapped data. + """ + # check NOT one hot encoded Ys + Xs = 'Xs' + Ys = 'Ys' + subject = gans.GAN(xshape=(4,), yshape=(2,), n_classes=10) + subject.generate_data = mock.MagicMock( + name='generate_data', return_value=('classes', 'points')) + kwargs = dict(bacthes=1, batch_size=5) + with mock.patch('deepchem.metrics.to_one_hot', + return_value='one_hot_classes') as mocked_one_hot: + result = list(subject.iterbacthes(Xs, Ys, kwargs)) + subject.generate_data.assert_called_with(Xs, Ys, 5) + expected_result = {subject._model.data_inputs[0]: 'points', + subject._model.conditional_inputs[0]: + 'classes'} + self.assertEqual(expected_result, result) + # check one hot encoded Ys + Xs = 'Xs' + Ys = [[0, 1], [1, 0], [1, 0]] + subject = gans.GAN(xshape=(4,), yshape=(2,), n_classes=10) + subject.generate_data = mock.MagicMock( + name='generate_data', return_value=('classes', 'points')) + kwargs = dict(bacthes=1, batch_size=5) + with mock.patch('deepchem.metrics.to_one_hot', + return_value='one_hot_classes') as mocked_one_hot: + result = list(subject.iterbacthes(Xs, Ys, kwargs)) + subject.generate_data.assert_called_with(Xs, Ys, 5) + mocked_one_hot.assert_called_with('classes', 10) + expected_result = {subject._model.data_inputs[0]: 'points', + subject._model.conditional_inputs[0]: + 'one_hot_classes'} + self.assertEqual(expected_result, result) + return + + @unittest.mock.patch('gandy.models.gans.GAN._build', return_value='Model') + def test_generate_data(self, mocked__build): + """ + Test generate_data function. + + The generate_data function creates batches of boostrapped data. + """ + Xs = ['x1', 'x2', 'x3'] + Ys = ['y1', 'y2', 'y3'] + subject = gans.GAN(xshape=(4,), yshape=(2,), n_classes=1) + classes, points = subject.generate_data(Xs, Ys, 5) + self.assertEqual(len(classes), 5) + self.assertEqual(len(points), 5) + return + + @unittest.mock.patch('gandy.models.gans.GAN._build', return_value='Model') + def test__save(self, mocked__build): """ Test save function. This checks that a file is written with the appropriate name. """ + # test path save + subject = gans.GAN(xshape=(4,), yshape=(2,), n_classes=10) + subject._model.save = mock.MagicMock('save') + subject.save('path') + subject._model.save.assert_called_with('path') + # test h5 save + subject.save('test_model.h5') + subject._model.save.assert_called_with('test_model.h5') return def test__load(self): @@ -125,4 +191,10 @@ def test__load(self): This checks that a Keras model instance is returned. """ + # test path save + subject = gans.GAN(xshape=(4,), yshape=(2,), n_classes=10) + subject.save() + # test h5 save + subject = gans.GAN(xshape=(4,), yshape=(2,), n_classes=10) + subject.save('test_model.h5') return From adfe77a2346f6870e30b7b5b16d942ed5a85877d Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Mon, 8 Mar 2021 18:28:07 -0800 Subject: [PATCH 38/99] Added self._model in front of deepchem function calls --- gandy/tests/test_models/test_gans.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/gandy/tests/test_models/test_gans.py b/gandy/tests/test_models/test_gans.py index 2af4ac5..f38fa50 100644 --- a/gandy/tests/test_models/test_gans.py +++ b/gandy/tests/test_models/test_gans.py @@ -31,9 +31,9 @@ def test__build(self): kwargs = dict(noise_shape=(5,)) subject._build(kwargs) # assert create generator function called - subject.create_generator.assert_called_once_with(kwargs) + subject._model.create_generator.assert_called_once_with(kwargs) # assert create discriminator function called - subject.create_discriminator.assert_called_once_with(kwargs) + subject._model.create_discriminator.assert_called_once_with(kwargs) # check attributes self.assertTrue(subject.conditional, False) self.assertTrue(subject.noise_shape, (5,)) @@ -45,9 +45,9 @@ def test__build(self): kwargs = dict(noise_shape=(5,), n_classes=4) subject._build(kwargs) # assert create generator function called - subject.create_generator.assert_called_once_with(kwargs) + subject._model.create_generator.assert_called_once_with(kwargs) # assert create discriminator function called - subject.create_discriminator.assert_called_once_with(kwargs) + subject._model.create_discriminator.assert_called_once_with(kwargs) # check attributes self.assertTrue(subject.conditional, True) self.assertTrue(subject.noise_shape, (5,)) @@ -67,13 +67,13 @@ def test__train(self): subject = gans.GAN(xshape=(4,), yshape=(2,)) subject.iterbacthes = mock.MagicMock(name='iterbatches', return_value="Batch1") - subject.fit_gan = mock.MagicMock(name='fit_gan') + subject._model.fit_gan = mock.MagicMock(name='fit_gan') kwargs = dict(option='x1') subject._train(Xs, Ys, kwargs) # assert fit_gan was called subject.iterbacthes.assert_called_with(Xs, Ys, kwargs) - subject.fit_gan.assert_called_with("Batch1") + subject._model.fit_gan.assert_called_with("Batch1") return @unittest.mock.patch('gandy.models.gans.GAN._build', return_value='Model') @@ -92,21 +92,21 @@ def test__predict(self, mocked__build): name='predict_gan_generator', return_value='generated_points') preds, ucs = subject._predict(Xs) subject._predict.assert_called_with(Xs) - subject.predict_gan_generator.assert_called_with(None) + subject._model.predict_gan_generator.assert_called_with(None) self.assertEqual('preds', 'generated_points') self.assertEqual('ucs', None) # CHECK Conditional GAN Ys = 'Ys' subject = gans.GAN(xshape=(4,), yshape=(2, 3), n_classes=3) - subject.predict_gan_generator = mock.MagicMock( + subject._model.predict_gan_generator = mock.MagicMock( name='predict_gan_generator', return_value='generated_points') with mock.patch('deepchem.metrics.to_one_hot', return_value=[10]) as mocked_one_hot: preds, ucs = subject._predict(Xs, Ys=Ys) mocked_one_hot.assert_called_with(Ys, 3) subject._predict.assert_called_with(Xs, Ys=Ys) - subject.predict_gan_generator.assert_called_with( + subject._model.predict_gan_generator.assert_called_with( conditional_inputs=[10]) self.assertEqual('preds', 'generated_points') self.assertEqual('ucs', None) From 91345999eda6f1efb5aa13e58a01d5041e2bca96 Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Mon, 8 Mar 2021 18:34:46 -0800 Subject: [PATCH 39/99] Added mutiple predictions to _predict to get uncertainties --- gandy/models/gans.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/gandy/models/gans.py b/gandy/models/gans.py index fcc4f1e..c6cb01f 100644 --- a/gandy/models/gans.py +++ b/gandy/models/gans.py @@ -225,6 +225,8 @@ def _predict(self, type == ndarray """ # adapted from deepchem tutorial 14: + num_predictions = kwargs.get('num_predictions', 100) + predictions = [] if self.conditional: Ys = kwargs.get('Ys', None) assert Ys is not None, "This is a cGAN.\ @@ -237,13 +239,18 @@ def _predict(self, # must one hot encode Ys one_hot_Ys = deepchem.metrics.to_one_hot(Ys, self.model.n_classes) - # generate data with conditional inputs - generated_points = self._model.predict_gan_generator( - conditional_inputs=[one_hot_Ys]) + for i in range(num_predictions): + # generate data with conditional inputs + generated_points = self._model.predict_gan_generator( + conditional_inputs=[one_hot_Ys]) + predictions.append(generated_points) else: - generated_points = self._model.predict_gan_generator() + for i in range(num_predictions): + generated_points = self._model.predict_gan_generator() + predictions.append(generated_points) # the above code generates points, but we need uncertainties as well - predictions, uncertainties = generated_points, None + predictions = np.average(predictions, axis=1) + uncertainties = np.std(predictions, axis=1) return predictions, uncertainties def save(self, filename: str, **kwargs): From 8fa079cae6649f971c536c6031617e06ad0a3fa7 Mon Sep 17 00:00:00 2001 From: evankomp Date: Tue, 9 Mar 2021 13:43:37 -0800 Subject: [PATCH 40/99] bnn tests started, helper functions tested, working on UM methods --- gandy/models/bnns.py | 26 +-------- gandy/tests/test_models/test_bnns.py | 82 ++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 23 deletions(-) create mode 100644 gandy/tests/test_models/test_bnns.py diff --git a/gandy/models/bnns.py b/gandy/models/bnns.py index 714f271..26e22c9 100644 --- a/gandy/models/bnns.py +++ b/gandy/models/bnns.py @@ -17,7 +17,7 @@ Array = Type[numpy.ndarray] -class bnn(gandy.models.models.UncertaintyModel): +class BNN(gandy.models.models.UncertaintyModel): """ Implements a Bayesian Neural Network (BNN) BNNS place a prior on the weights of the network and apply Bayes rule. @@ -35,25 +35,6 @@ class bnn(gandy.models.models.UncertaintyModel): for a guide to implementing a BNN with Keras. """ - def create_model_inputs(self, feature_names) -> Tuple[Object]: - ''' - Arguments: - feature_names - example data to make predictions on - type == ndarray, list, or dictionary - Returns: - inputs - type == dictionary of Keras Input layers - ''' - # do something like: - # https://keras.io/examples/keras_recipes/bayesian_neural_networks/ - # inputs = {} - # for feature_name in feature_names: - # inputs[feature_name] = tf.keras.layers.Input( - # name=feature_name, shape=(1,), dtype=tf.float32 - # ) - inputs = None - return inputs - def prior(kernel_size, bias_size, dtype=None) -> Object: ''' Arguments: @@ -158,7 +139,7 @@ def _build(self, *args, **kwargs) -> Object: # loss = negative_loglikelihood(targets, estimated_distribution) # get train_size, i.e., train_size = xshape[0] - # inputs = create_model_inputs() + # inputs = keras.Input(self.xshape) # input_values = list(inputs.values()) # features = tf.keras.layers.concatenate(input_values) # features = tf.keras.layers.BatchNormalization()(features) @@ -184,8 +165,7 @@ def _build(self, *args, **kwargs) -> Object: # model = keras.Model(inputs=inputs, outputs=outputs) # model.compile(**kwargs) - # self.model = model - return self.model + return model # overridden method from UncertaintyModel class def _train(self, diff --git a/gandy/tests/test_models/test_bnns.py b/gandy/tests/test_models/test_bnns.py new file mode 100644 index 0000000..770dc7a --- /dev/null +++ b/gandy/tests/test_models/test_bnns.py @@ -0,0 +1,82 @@ +import unittest +import unittest.mock + +import tensorflow + +import gandy.models.bnns + +class TestBNN(unittest.TestCase): + + @unittest.mock.patch( + 'gandy.models.bnns.BNN._build') + def test_prior(self, mocked__build): + """Ensure handling of bias and kernel size. + + Function MUST pass tf.size(kernel), tf.size(bias) + """ + kernel_size = 5; bias_size = 5 + subject = gandy.models.bnns.BNN((1,), (1,)) + # expected success must return a model + prior = subject.prior(kernel_size, bias_size) + self.assertTrue(isinstance(prior, tf.keras.Model)) + # failure cannot parse inputs + with self.assertRaises(TypeError): + subject.prior('kernel_size', 'bias_size') + return + + @unittest.mock.patch( + 'gandy.models.bnns.BNN._build') + def test_posterior(self, mocked__build): + """Ensure handling of bias and kernel size. + + Function MUST pass tf.size(kernel), tf.size(bias) + """ + kernel_size = 5; bias_size = 5 + subject = gandy.models.bnns.BNN((1,), (1,)) + # expected success must return a model + prior = subject.posterior(kernel_size, bias_size) + self.assertTrue(isinstance(prior, tf.keras.Model)) + # failure cannot parse inputs + with self.assertRaises(TypeError): + subject.prior('kernel_size', 'bias_size') + return + + @unittest.mock.patch( + 'gandy.models.bnns.BNN._build') + def test_negative_loglikelihood(self, mocked_build): + """Input predictions are distributions instead of deterministic. + + Distribution should impliment log_prob method + """ + subject = gandy.models.bnns.BNN((1,), (1,)) + # failure mode, does not have method + def callable_wo_log_prob(): + return + with self.assertRaises(TypeError): + subject.negative_loglikelihood(numpy.array([1,2]), + callable_wo_log_prob) + # expected success + mocked_dist = unittest.mock.MagicMock() + val = subject.negative_loglikelihood('targets', + mocked_dist) + mocked_dist.log_prob.assert_called_with('targets') + + # ability to catch non float + mocked_dist.return_value = 'string' + with self.assertRaises(ValueError): + subject.negative_loglikelihood('targets', + mocked_dist) + return + + def test__build(self): + """""" + # start with default initialization + subject = gandy.models.bnns.BNN((5,), (1,)) + self.assertTrue(isinstance(subject.model, tf.keras.Model)) + self.assertTrue(subject.model._compile_was_called) + self.assertTrue(subject.model.built) + self.assertEqual(tuple(subject.model.input.shapes.as_list()), + subject.xshape) + self.assertEqual + + \ No newline at end of file From 2c02072a9495c7b898e71c702270b94cd19e174d Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Tue, 9 Mar 2021 14:34:15 -0800 Subject: [PATCH 41/99] Added network hyperparameters as dictionary attribute --- gandy/models/dcgan.py | 194 ++++++++++++++++++-------- gandy/models/gans.py | 23 +-- gandy/tests/test_models/test_dcgan.py | 12 +- 3 files changed, 159 insertions(+), 70 deletions(-) diff --git a/gandy/models/dcgan.py b/gandy/models/dcgan.py index 2746553..62d5f98 100644 --- a/gandy/models/dcgan.py +++ b/gandy/models/dcgan.py @@ -8,6 +8,9 @@ """ +# warnings +import warnings + # deep learning imports import deepchem import tensorflow as tf @@ -45,13 +48,54 @@ class DCGAN(deepchem.models.GAN): def __init__(self, xshape, yshape, noise_shape, n_classes=None, **kwargs): """Deepchem init function + class atributes.""" super(DCGAN, self).__init__(**kwargs) + # These should be set by the gandy model when _build is called. self.xshape = xshape self.yshape = yshape self.noise_shape = noise_shape self.n_classes = n_classes - def create_generator(self, **kwargs): + Base_hyperparams = {layer_dimensions: [128], + dropout: 0.05, + activation: 'relu', + use_bias: True, + kernel_initializer: "glorot_uniform", + bias_initializer: "zeros", + kernel_regularizer: 'l2', + bias_regularizer: None, + activity_regularizer: None, + kernel_constraint: None, + bias_constraint: None} + + # Create separate hyperparam dictionaries for the generator + # and discriminator + self.generator_hyperparameters = Base_hyperparams.copy() + self.discriminator_hyperparameters = Base_hyperparams.copy() + + # get network hyperparameters from kwargs + for key in kwargs.keys(): + if key.startswith('generator_'): + # generator param + param = key.replace('generator_', '') + # check if the key is a valid hyperparamter + if param in generator_hyperparameter.keys(): + generator_hyperparameters[param] = kwargs[key] + else: + warnings.warn(f"Incorrect key {key}. Must be in\ + {Base_hyperparams.keys()}") + elif key.startswith('discriminator_'): + # discriminator param + param = key.replace('discriminator_', '') + if param in discriminator_hyperparameter.keys(): + discriminator_hyperparameters[param] = kwargs[key] + else: + warnings.warn(f"Incorrect key {key}. Must be in\ + {Base_hyperparams.keys()}") + else: + warnings.warn(f"Incorrect key {key}.\ + Must start with generator_ or discriminator_") + + def create_generator(self): """ Create the generator as a keras model. @@ -62,10 +106,21 @@ def create_generator(self, **kwargs): https://keras.io/api/layers/core_layers/dense/ Arguments: + Kwargs for the model architecture: + layer_dimensions - list of hidden dimension layers Note: This should note include the output dimension. Default - [128] type == list of ndarray + dropout - layer dropout percetnage, + i.e., percent of weights that are randomly set to 0 + Can choose a flooat in [0.0, 1.0) + Default - 0.05 (5% dropout rate) + type == float + + The kwargs for each layer that are different than the Keras + default are: + activation - hidden layer activation function. Can choose from 'relu', 'tanh', 'sigmoid', 'softmax', 'softplus', 'softsign', 'selu', 'elu', 'exponential', @@ -73,44 +128,40 @@ def create_generator(self, **kwargs): Default - 'relu' type == str kernel_regularizer - regularizer of kernel/ weights - Can choose from 'l2', 'l1' + Can choose from 'l2', 'l1', etc. Default - 'l2' type == str - dropout - layer dropout percetnage, - i.e., percent of weights that are randomly set to 0 - Can choose a flooat in [0.0, 1.0) - Default - 0.05 (5% dropout rate) - type == float Returns: - generator - the discriminator outputs a probability that - the data is real or fake + generator - creates data from random noise type == Keras model """ # adapted from deepchem tutorial 14: + kwargs = self.generator_hyperparameters + # get hyperparameters from kwargs layer_dimensions = kwargs.get('layer_dimensions', [128]) - activation = kwargs.get('activation', 'relu') - kernel_regularizer = kwargs.get('kernel_regularizer', 'l2') dropout = kwargs.get('dropout', 0.05) + # every other kwarg is for the layers + layer_kwargs = {key: kwargs[key] for key in kwargs.keys() + - {'layer_dimensions', 'dropout'}} # construct input noise_in = Input(shape=self.get_noise_input_shape()) # build first layer of network - gen = Dense(layer_dimensions[0], activation=activation, - kernel_regularizer=kernel_regularizer)(noise_in) + gen = Dense(layer_dimensions[0], layer_kwargs)(noise_in) # adding dropout to the weights gen = Dropout(dropout)(gen) # build subsequent layers for layer_dim in layer_dimensions[1:]: - gen = Dense(layer_dim, activation=activation)(gen) + gen = Dense(layer_dim, layer_kwargs)(gen) gen = Dropout(dropout)(gen) # generator outputs # is xhape[0] really what we want, or batch size? - gen = Dense(self.xshape[0], activation=activation)(gen) + gen = Dense(self.xshape[0], layer_kwargs)(gen) gen = Dropout(dropout)(gen) # final construction of Keras model @@ -118,7 +169,7 @@ def create_generator(self, **kwargs): outputs=[gen]) return generator - def create_discriminator(self, **kwargs): + def create_discriminator(self): """ Create the discriminator as a keras model. @@ -129,9 +180,21 @@ def create_discriminator(self, **kwargs): https://keras.io/api/layers/core_layers/dense/ Arguments: + Kwargs for the model architecture: + layer_dimensions - list of hidden dimension layers + Note: This should note include the output dimension. Default - [128] type == list of ndarray + dropout - layer dropout percetnage, + i.e., percent of weights that are randomly set to 0 + Can choose a flooat in [0.0, 1.0) + Default - 0.05 (5% dropout rate) + type == float + + The kwargs for each layer that are different than the Keras + default are: + activation - hidden layer activation function. Can choose from 'relu', 'tanh', 'sigmoid', 'softmax', 'softplus', 'softsign', 'selu', 'elu', 'exponential', @@ -139,14 +202,9 @@ def create_discriminator(self, **kwargs): Default - 'relu' type == str kernel_regularizer - regularizer of kernel/ weights - Can choose from 'l2', 'l1' + Can choose from 'l2', 'l1', etc. Default - 'l2' type == str - dropout - layer dropout percetnage, - i.e., percent of weights that are randomly set to 0 - Can choose a flooat in [0.0, 1.0) - Default - 0.05 (5% dropout rate) - type == float Returns: discriminator - the discriminator outputs a probability that @@ -156,27 +214,31 @@ def create_discriminator(self, **kwargs): """ # adapted from deepchem tutorial 14: + kwargs = self.discriminator_hyperparameters + # get hyperparameters from kwargs layer_dimensions = kwargs.get('layer_dimensions', [128]) - activation = kwargs.get('activation', 'relu') - kernel_regularizer = kwargs.get('kernel_regularizer', 'l2') dropout = kwargs.get('dropout', 0.05) + # every other kwarg is for the layers + layer_kwargs = {key: kwargs[key] for key in kwargs.keys() + - {'layer_dimensions', 'dropout'}} # construct input data_in = Input(shape=self.xshape) # build first layer of network - discrim = Dense(layer_dimensions[0], activation=activation, - kernel_regularizer=kernel_regularizer)(data_in) + discrim = Dense(layer_dimensions[0], layer_kwargs)(data_in) # adding dropout to the weights discrim = Dropout(dropout)(discrim) # build subsequent layers for layer_dim in layer_dimensions[1:]: - discrim = Dense(layer_dim, activation=activation)(discrim) + discrim = Dense(layer_dim, layer_kwargs)(discrim) discrim = Dropout(dropout)(discrim) # To maintain the interpretation of a probability, # the final activation function is not a kwarg - discrim_prob = Dense(1, activation='sigmoid')(discrim) + final_layer_kwargs = layer_kwargs.copy() + final_layer_kwargs[activation] = 'sigmoid' + discrim_prob = Dense(1, final_layer_kwargs)(discrim) # final construction of Keras model discriminator = tf.keras.Model(inputs=[data_in], @@ -211,7 +273,7 @@ class CondDCGAN(DCGAN): to as "conditional inputs". """ - def get_conditional_input_shapes(self, **kwargs) -> Array: + def get_conditional_input_shapes(self) -> Array: """ Return the shape of the conditional input. @@ -219,7 +281,7 @@ def get_conditional_input_shapes(self, **kwargs) -> Array: """ return [(self.n_classes,)] - def create_generator(self, **kwargs) -> Object: + def create_generator(self) -> Object: """ Create the generator as a keras model. @@ -230,10 +292,21 @@ def create_generator(self, **kwargs) -> Object: https://keras.io/api/layers/core_layers/dense/ Arguments: + Kwargs for the model architecture: + layer_dimensions - list of hidden dimension layers Note: This should note include the output dimension. Default - [128] type == list of ndarray + dropout - layer dropout percetnage, + i.e., percent of weights that are randomly set to 0 + Can choose a flooat in [0.0, 1.0) + Default - 0.05 (5% dropout rate) + type == float + + The kwargs for each layer that are different than the Keras + default are: + activation - hidden layer activation function. Can choose from 'relu', 'tanh', 'sigmoid', 'softmax', 'softplus', 'softsign', 'selu', 'elu', 'exponential', @@ -241,28 +314,25 @@ def create_generator(self, **kwargs) -> Object: Default - 'relu' type == str kernel_regularizer - regularizer of kernel/ weights - Can choose from 'l2', 'l1' + Can choose from 'l2', 'l1', etc. Default - 'l2' type == str - dropout - layer dropout percetnage, - i.e., percent of weights that are randomly set to 0 - Can choose a flooat in [0.0, 1.0) - Default - 0.05 (5% dropout rate) - type == float Returns: - generator - the discriminator outputs a probability that - the data is real or fake + generator - creates data from random noise type == Keras model """ # adapted from deepchem tutorial 14: + kwargs = self.generator_hyperparameters + # get hyperparameters from kwargs layer_dimensions = kwargs.get('layer_dimensions', [128]) - activation = kwargs.get('activation', 'relu') - kernel_regularizer = kwargs.get('kernel_regularizer', 'l2') dropout = kwargs.get('dropout', 0.05) + # every other kwarg is for the layers + layer_kwargs = {key: kwargs[key] for key in kwargs.keys() + - {'layer_dimensions', 'dropout'}} # construct input noise_in = Input(shape=self.get_noise_input_shape()) @@ -270,17 +340,16 @@ def create_generator(self, **kwargs) -> Object: gen_input = Concatenate()([noise_in, conditional_in]) # build first layer of network - gen = Dense(layer_dimensions[0], activation=activation, - kernel_regularizer=kernel_regularizer)(gen_input) + gen = Dense(layer_dimensions[0], layer_kwargs)(gen_input) # adding dropout to the weights gen = Dropout(dropout)(gen) # build subsequent layers for layer_dim in layer_dimensions[1:]: - gen = Dense(layer_dim, activation=activation)(gen) + gen = Dense(layer_dim, layer_kwargs)(gen) gen = Dropout(dropout)(gen) # generator outputs - gen = Dense(self.xshape[0], activation=activation)(gen) + gen = Dense(self.xshape[0], layer_kwargs)(gen) gen = Dropout(dropout)(gen) # final construction of Keras model @@ -288,7 +357,7 @@ def create_generator(self, **kwargs) -> Object: outputs=[gen]) return generator - def create_discriminator(self, **kwargs) -> Object: + def create_discriminator(self) -> Object: """ Create the discriminator as a keras model. @@ -299,9 +368,21 @@ def create_discriminator(self, **kwargs) -> Object: https://keras.io/api/layers/core_layers/dense/ Arguments: + Kwargs for the model architecture: + layer_dimensions - list of hidden dimension layers + Note: This should note include the output dimension. Default - [128] type == list of ndarray + dropout - layer dropout percetnage, + i.e., percent of weights that are randomly set to 0 + Can choose a flooat in [0.0, 1.0) + Default - 0.05 (5% dropout rate) + type == float + + The kwargs for each layer that are different than the Keras + default are: + activation - hidden layer activation function. Can choose from 'relu', 'tanh', 'sigmoid', 'softmax', 'softplus', 'softsign', 'selu', 'elu', 'exponential', @@ -309,14 +390,9 @@ def create_discriminator(self, **kwargs) -> Object: Default - 'relu' type == str kernel_regularizer - regularizer of kernel/ weights - Can choose from 'l2', 'l1' + Can choose from 'l2', 'l1', etc. Default - 'l2' type == str - dropout - layer dropout percetnage, - i.e., percent of weights that are randomly set to 0 - Can choose a flooat in [0.0, 1.0) - Default - 0.05 (5% dropout rate) - type == float Returns: discriminator - the discriminator outputs a probability that @@ -326,11 +402,14 @@ def create_discriminator(self, **kwargs) -> Object: """ # adapted from deepchem tutorial 14: + kwargs = self.discriminator_hyperparameters + # get hyperparameters from kwargs layer_dimensions = kwargs.get('layer_dimensions', [128]) - activation = kwargs.get('activation', 'relu') - kernel_regularizer = kwargs.get('kernel_regularizer', 'l2') dropout = kwargs.get('dropout', 0.05) + # every other kwarg is for the layers + layer_kwargs = {key: kwargs[key] for key in kwargs.keys() + - {'layer_dimensions', 'dropout'}} # construct input data_in = Input(shape=self.xshape) @@ -338,18 +417,19 @@ def create_discriminator(self, **kwargs) -> Object: discrim_input = Concatenate()([data_in, conditional_in]) # build first layer of network - discrim = Dense(layer_dimensions[0], activation=activation, - kernel_regularizer=kernel_regularizer)(discrim_input) + discrim = Dense(layer_dimensions[0], layer_kwargs)(discrim_input) # adding dropout to the weights discrim = Dropout(dropout)(discrim) # build subsequent layers for layer_dim in layer_dimensions[1:]: - discrim = Dense(layer_dim, activation=activation)(discrim) + discrim = Dense(layer_dim, layer_kwargs)(discrim) discrim = Dropout(dropout)(discrim) # To maintain the interpretation of a probability, # the final activation function is not a kwarg - discrim_prob = Dense(1, activation='sigmoid')(discrim) + final_layer_kwargs = layer_kwargs.copy() + final_layer_kwargs[activation] = 'sigmoid' + discrim_prob = Dense(1, final_layer_kwargs)(discrim) # final construction of Keras model discriminator = tf.keras.Model(inputs=[data_in, conditional_in], diff --git a/gandy/models/gans.py b/gandy/models/gans.py index c6cb01f..896946e 100644 --- a/gandy/models/gans.py +++ b/gandy/models/gans.py @@ -19,7 +19,7 @@ import tensorflow as tf # typing imports -from typing import Any, Object, Type +from typing import Any, Object, Type, Callable # typing import numpy as np @@ -66,34 +66,37 @@ def _build(self, **kwargs) -> Object: # default is 10 dimensional noise_shape = kwargs.get('noise_shape', (10,)) # get n_classes from kwargs - # default is the y dimension - # e.g., regression would be == 1 - # This would also be correct for a one hot encoded y vector. n_classes = kwargs.get('n_classes', None) # determine whether to use gan or conditional gan if n_classes is not None: # if number of classes is specified, assumes conditional GAN self.conditional = True - if len(self.yshape) == 2: + if self.yshape[0] > 1: # Ys are already one hot encoded - n_classes = kwargs.get('n_classes', self.yshape[1]) + n_classes = kwargs.get('n_classes', self.yshape[0]) else: # Ys are NOT one hot encoded + # this should be flagged somewhere... n_classes = kwargs.get('n_classes', self.yshape[0]) else: self.conditional = False # if no n_classes specified, assumed to be regression # and no need for conditional inputs + # e.g., regression would be == 1 n_classes = kwargs.get('n_classes', self.yshape[0]) + # get other kwargs as hyperparameters + hyperparams = {key: kwargs[key] for key in kwargs.keys() - + {'n_classes', 'noise_shape'}} + # instantiating the model as the deepchem gan if self.conditional: model = dcgan.CondDCGAN(self.xshape, self.yshape, noise_shape, - n_classes=n_classes, **kwargs) + hyperparams, n_classes=n_classes) else: model = dcgan.DCGAN(self.xshape, self.yshape, - noise_shape, **kwargs) + noise_shape, hyperparams) return model def generate_data(self, @@ -174,7 +177,8 @@ def iterbatches(self, def _train(self, Xs: Array, Ys: Array, - *args, + *args, # use args thoughtfully? + metric: Callable = None, **kwargs) -> Any: """ Train GAN model on data. @@ -199,6 +203,7 @@ def _train(self, # and discriminator are self.model.outputs # This is a list of 2 KerasTensors so must evaluate it. losses = self.model.outputs + # compute metric return losses # overridden method from UncertaintyModel class diff --git a/gandy/tests/test_models/test_dcgan.py b/gandy/tests/test_models/test_dcgan.py index bbab8c8..29f347d 100644 --- a/gandy/tests/test_models/test_dcgan.py +++ b/gandy/tests/test_models/test_dcgan.py @@ -1,12 +1,12 @@ """Testing functions for deepchem GAN class.""" -import numpy as np +# import numpy as np import unittest -import unittest.mock +# import unittest.mock -import deepchem +# import deepchem -import gandy.models.dcgan as dcgan +# import gandy.models.dcgan as dcgan class TestGAN(unittest.TestCase): @@ -19,6 +19,10 @@ def test_create_generator(self): The create generator function uses kwargs to create a Keras model. This checks that the model compiles. """ + # conditional_model = dcgan.CondDCGAN(xshape, yshape, noise_shape, + # n_classes=n_classes, **kwargs) + + # model = dcgan.DCGAN(self.xshape, self.yshape, noise_shape, **kwargs) return def test_create_discriminator(self): From ebf823f9bbddee242b19013190a3c591fc7fb41b Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Tue, 9 Mar 2021 15:51:09 -0800 Subject: [PATCH 42/99] Fixed test functions syntax errors re: EAK's comments --- gandy/tests/test_models/test_gans.py | 48 +++++++++++++++------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/gandy/tests/test_models/test_gans.py b/gandy/tests/test_models/test_gans.py index f38fa50..6573052 100644 --- a/gandy/tests/test_models/test_gans.py +++ b/gandy/tests/test_models/test_gans.py @@ -13,9 +13,10 @@ class TestGAN(unittest.TestCase): """Test GAN class.""" - def test_inheritence(): + def test_inheritence(self): """Ensure the subclass class inherits from parent class.""" - assert issubclass(gans.gan, gandy.models.models.UncertaintyModel) + self.assertTrue(issubclass(gans.gan, + gandy.models.models.UncertaintyModel)) def test__build(self): """ @@ -29,7 +30,12 @@ def test__build(self): # create gan instance subject = gans.GAN(xshape=(4,), yshape=(2,)) kwargs = dict(noise_shape=(5,)) - subject._build(kwargs) + subject._build(**kwargs) + # created mocked functions + subject._model.create_generator = unittest.mock.MagicMock( + name='create_generator') + subject._model.create_discriminator = unittest.mock.MagicMock( + name='create_discriminator') # assert create generator function called subject._model.create_generator.assert_called_once_with(kwargs) # assert create discriminator function called @@ -41,9 +47,9 @@ def test__build(self): # CHECK Conditional GAN # create gan instance subject = gans.CondGAN(xshape=(4,), yshape=(2, 4)) - assert issubclass(gans.CondGAN, gans.GAN) + self.assertTrue(issubclass(gans.CondGAN, gans.GAN)) kwargs = dict(noise_shape=(5,), n_classes=4) - subject._build(kwargs) + subject._build(**kwargs) # assert create generator function called subject._model.create_generator.assert_called_once_with(kwargs) # assert create discriminator function called @@ -69,10 +75,10 @@ def test__train(self): return_value="Batch1") subject._model.fit_gan = mock.MagicMock(name='fit_gan') kwargs = dict(option='x1') - subject._train(Xs, Ys, kwargs) + subject._train(Xs, Ys, **kwargs) # assert fit_gan was called - subject.iterbacthes.assert_called_with(Xs, Ys, kwargs) + subject.iterbacthes.assert_called_with(Xs, Ys, **kwargs) subject._model.fit_gan.assert_called_with("Batch1") return @@ -91,10 +97,9 @@ def test__predict(self, mocked__build): subject.predict_gan_generator = mock.MagicMock( name='predict_gan_generator', return_value='generated_points') preds, ucs = subject._predict(Xs) - subject._predict.assert_called_with(Xs) subject._model.predict_gan_generator.assert_called_with(None) - self.assertEqual('preds', 'generated_points') - self.assertEqual('ucs', None) + self.assertEqual(preds, 'generated_points') + self.assertEqual(ucs, None) # CHECK Conditional GAN Ys = 'Ys' @@ -105,11 +110,10 @@ def test__predict(self, mocked__build): return_value=[10]) as mocked_one_hot: preds, ucs = subject._predict(Xs, Ys=Ys) mocked_one_hot.assert_called_with(Ys, 3) - subject._predict.assert_called_with(Xs, Ys=Ys) subject._model.predict_gan_generator.assert_called_with( conditional_inputs=[10]) - self.assertEqual('preds', 'generated_points') - self.assertEqual('ucs', None) + self.assertEqual(preds, 'generated_points') + self.assertEqual(ucs, None) return @unittest.mock.patch('gandy.models.gans.GAN._build', return_value='Model') @@ -129,7 +133,7 @@ def test_iterbacthes(self, mocked__build): kwargs = dict(bacthes=1, batch_size=5) with mock.patch('deepchem.metrics.to_one_hot', return_value='one_hot_classes') as mocked_one_hot: - result = list(subject.iterbacthes(Xs, Ys, kwargs)) + result = list(subject.iterbacthes(Xs, Ys, **kwargs)) subject.generate_data.assert_called_with(Xs, Ys, 5) expected_result = {subject._model.data_inputs[0]: 'points', subject._model.conditional_inputs[0]: @@ -144,7 +148,7 @@ def test_iterbacthes(self, mocked__build): kwargs = dict(bacthes=1, batch_size=5) with mock.patch('deepchem.metrics.to_one_hot', return_value='one_hot_classes') as mocked_one_hot: - result = list(subject.iterbacthes(Xs, Ys, kwargs)) + result = list(subject.iterbacthes(Xs, Ys, **kwargs)) subject.generate_data.assert_called_with(Xs, Ys, 5) mocked_one_hot.assert_called_with('classes', 10) expected_result = {subject._model.data_inputs[0]: 'points', @@ -169,7 +173,7 @@ def test_generate_data(self, mocked__build): return @unittest.mock.patch('gandy.models.gans.GAN._build', return_value='Model') - def test__save(self, mocked__build): + def test_save(self, mocked__build): """ Test save function. @@ -185,16 +189,14 @@ def test__save(self, mocked__build): subject._model.save.assert_called_with('test_model.h5') return - def test__load(self): + @unittest.mock.patch('tf.keras.models.load_model', return_value='Model') + def test_load(self, mocked_load): """ Test load function. This checks that a Keras model instance is returned. """ - # test path save - subject = gans.GAN(xshape=(4,), yshape=(2,), n_classes=10) - subject.save() - # test h5 save - subject = gans.GAN(xshape=(4,), yshape=(2,), n_classes=10) - subject.save('test_model.h5') + # test load + subject = gans.GAN.load('test_model.h5') + self.assertEqaul(subject, 'Model') return From f0a46fb86b41c5179cc04036c3ca880587875947 Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Tue, 9 Mar 2021 15:52:17 -0800 Subject: [PATCH 43/99] Fixing misc bugs in load and _build --- gandy/models/dcgan.py | 10 +++++----- gandy/models/gans.py | 15 +++++++-------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/gandy/models/dcgan.py b/gandy/models/dcgan.py index 62d5f98..ca62eba 100644 --- a/gandy/models/dcgan.py +++ b/gandy/models/dcgan.py @@ -45,7 +45,7 @@ class DCGAN(deepchem.models.GAN): This class builds off of the deepchem GAN class found at the url above. """ - def __init__(self, xshape, yshape, noise_shape, n_classes=None, **kwargs): + def __init__(self, xshape, yshape, noise_shape, n_classes, **kwargs): """Deepchem init function + class atributes.""" super(DCGAN, self).__init__(**kwargs) @@ -78,16 +78,16 @@ def __init__(self, xshape, yshape, noise_shape, n_classes=None, **kwargs): # generator param param = key.replace('generator_', '') # check if the key is a valid hyperparamter - if param in generator_hyperparameter.keys(): - generator_hyperparameters[param] = kwargs[key] + if param in self.generator_hyperparameters.keys(): + self.generator_hyperparameters[param] = kwargs[key] else: warnings.warn(f"Incorrect key {key}. Must be in\ {Base_hyperparams.keys()}") elif key.startswith('discriminator_'): # discriminator param param = key.replace('discriminator_', '') - if param in discriminator_hyperparameter.keys(): - discriminator_hyperparameters[param] = kwargs[key] + if param in self.discriminator_hyperparameters.keys(): + self.discriminator_hyperparameters[param] = kwargs[key] else: warnings.warn(f"Incorrect key {key}. Must be in\ {Base_hyperparams.keys()}") diff --git a/gandy/models/gans.py b/gandy/models/gans.py index 896946e..956b8f6 100644 --- a/gandy/models/gans.py +++ b/gandy/models/gans.py @@ -65,26 +65,25 @@ def _build(self, **kwargs) -> Object: # get noise shape from kwargs # default is 10 dimensional noise_shape = kwargs.get('noise_shape', (10,)) - # get n_classes from kwargs + # get n_classes from kwargs, default None n_classes = kwargs.get('n_classes', None) # determine whether to use gan or conditional gan if n_classes is not None: # if number of classes is specified, assumes conditional GAN self.conditional = True + # Should this be flagged somewhere?... if self.yshape[0] > 1: # Ys are already one hot encoded n_classes = kwargs.get('n_classes', self.yshape[0]) else: # Ys are NOT one hot encoded - # this should be flagged somewhere... + # Or this is regression, which would be == 1 n_classes = kwargs.get('n_classes', self.yshape[0]) else: - self.conditional = False # if no n_classes specified, assumed to be regression # and no need for conditional inputs - # e.g., regression would be == 1 - n_classes = kwargs.get('n_classes', self.yshape[0]) + self.conditional = False # get other kwargs as hyperparameters hyperparams = {key: kwargs[key] for key in kwargs.keys() - @@ -93,10 +92,10 @@ def _build(self, **kwargs) -> Object: # instantiating the model as the deepchem gan if self.conditional: model = dcgan.CondDCGAN(self.xshape, self.yshape, noise_shape, - hyperparams, n_classes=n_classes) + n_classes, hyperparams) else: - model = dcgan.DCGAN(self.xshape, self.yshape, - noise_shape, hyperparams) + model = dcgan.DCGAN(self.xshape, self.yshape, noise_shape, + n_classes, hyperparams) return model def generate_data(self, From 3b7e3753dbd8b936f7e75297fd34d447592aaf65 Mon Sep 17 00:00:00 2001 From: evankomp Date: Tue, 9 Mar 2021 16:02:34 -0800 Subject: [PATCH 44/99] BNN tests initially written --- gandy/models/bnns.py | 41 +++++----- gandy/tests/test_models/test_bnns.py | 107 ++++++++++++++++++++++----- 2 files changed, 109 insertions(+), 39 deletions(-) diff --git a/gandy/models/bnns.py b/gandy/models/bnns.py index 26e22c9..ef8cbe1 100644 --- a/gandy/models/bnns.py +++ b/gandy/models/bnns.py @@ -10,7 +10,7 @@ # import tensorflow as tf # typing imports -from typing import Tuple, Any, Object, Type +from typing import Any, Object, Type # typing import numpy @@ -165,6 +165,7 @@ def _build(self, *args, **kwargs) -> Object: # model = keras.Model(inputs=inputs, outputs=outputs) # model.compile(**kwargs) + model = None return model # overridden method from UncertaintyModel class @@ -214,27 +215,27 @@ def _predict(self, predictions, uncertainties = None, None return predictions, uncertainties - def _save(filename: str, **kwargs): - """Method defined by child to save the predictor. + def save(filename: str, **kwargs): + """Method defined by child to save the predictor. - Method must save into memory the object at self.model + Method must save into memory the object at self.model - Args: - filename (str): - name of file to save model to - """ - # call Keras save function - return None + Args: + filename (str): + name of file to save model to + """ + # call Keras save function + return None - def _load(self, filename: str, **kwargs): - """Method defined by child to load a predictor into memory. + def load(self, filename: str, **kwargs): + """Method defined by child to load a predictor into memory. - Loads the object to be assigned to self.model. + Loads the object to be assigned to self.model. - Args: - filename (str): - path of file to load - """ - # call Keras.load function - model = None - return model + Args: + filename (str): + path of file to load + """ + # call Keras.load function + model = None + return model diff --git a/gandy/tests/test_models/test_bnns.py b/gandy/tests/test_models/test_bnns.py index 770dc7a..03eeac6 100644 --- a/gandy/tests/test_models/test_bnns.py +++ b/gandy/tests/test_models/test_bnns.py @@ -1,20 +1,23 @@ import unittest import unittest.mock -import tensorflow +import numpy +import tensorflow as tf import gandy.models.bnns + class TestBNN(unittest.TestCase): - + @unittest.mock.patch( 'gandy.models.bnns.BNN._build') def test_prior(self, mocked__build): """Ensure handling of bias and kernel size. - + Function MUST pass tf.size(kernel), tf.size(bias) """ - kernel_size = 5; bias_size = 5 + kernel_size = 5 + bias_size = 5 subject = gandy.models.bnns.BNN((1,), (1,)) # expected success must return a model prior = subject.prior(kernel_size, bias_size) @@ -23,15 +26,16 @@ def test_prior(self, mocked__build): with self.assertRaises(TypeError): subject.prior('kernel_size', 'bias_size') return - + @unittest.mock.patch( 'gandy.models.bnns.BNN._build') def test_posterior(self, mocked__build): """Ensure handling of bias and kernel size. - + Function MUST pass tf.size(kernel), tf.size(bias) """ - kernel_size = 5; bias_size = 5 + kernel_size = 5 + bias_size = 5 subject = gandy.models.bnns.BNN((1,), (1,)) # expected success must return a model prior = subject.posterior(kernel_size, bias_size) @@ -40,36 +44,41 @@ def test_posterior(self, mocked__build): with self.assertRaises(TypeError): subject.prior('kernel_size', 'bias_size') return - + @unittest.mock.patch( 'gandy.models.bnns.BNN._build') def test_negative_loglikelihood(self, mocked_build): """Input predictions are distributions instead of deterministic. - + Distribution should impliment log_prob method """ subject = gandy.models.bnns.BNN((1,), (1,)) # failure mode, does not have method + def callable_wo_log_prob(): return with self.assertRaises(TypeError): - subject.negative_loglikelihood(numpy.array([1,2]), + subject.negative_loglikelihood(numpy.array([1, 2]), callable_wo_log_prob) # expected success mocked_dist = unittest.mock.MagicMock() - val = subject.negative_loglikelihood('targets', - mocked_dist) + subject.negative_loglikelihood('targets', + mocked_dist) mocked_dist.log_prob.assert_called_with('targets') - + # ability to catch non float mocked_dist.return_value = 'string' with self.assertRaises(ValueError): subject.negative_loglikelihood('targets', - mocked_dist) + mocked_dist) return - + def test__build(self): - """""" + """The build should pass kwargs to the correct place. + + We need to ensure the returned keras model is both compiled + and built. + """ # start with default initialization subject = gandy.models.bnns.BNN((5,), (1,)) self.assertTrue(isinstance(subject.model, tf.keras.Model)) @@ -77,6 +86,66 @@ def test__build(self): self.assertTrue(subject.model.built) self.assertEqual(tuple(subject.model.input.shapes.as_list()), subject.xshape) - self.assertEqual - - \ No newline at end of file + self.assertEqual(tuple(subject.model.output.shapes.as_list()), + subject.yshape) + + # test keyword assignment + with unittest.mock.patch( + 'tensorflow.keras.Sequential.compile' + ) as mocked_compile: + subject = gandy.models.bnns.BNN((5,), (1,), + optimizer='rms_prop', + metrics=['MSE']) + mocked_compile.assert_called_with(optimizer='rms_prop', + metrics=['MSE']) + return + + def test__train(self): + """We just want to call the host fit method""" + Xs = 'Xs' + Ys = 'Ys' + with unittest.mock.patch( + 'tensorflow.keras.Sequential.fit' + ) as mocked_fit: + subject = gandy.models.bnns.BNN((5,), (1,)) + subject._train(Xs, Ys, epochs=10) + mocked_fit.assert_called_with(Xs, Ys, epochs=10) + return + + def test__predict(self): + """Predict for a probabilistic BNN is just letting the tensors + flow, make sure it is passed to input. + """ + subject = gandy.models.bnns.BNN((5,), (1,)) + subject.model = unittest.mock.MagicMock() + subject._predict('Xs') + subject.model.assert_called_with('Xs') + return + + def test_save(self): + """Save should just call keras save""" + with unittest.mock.patch( + 'tensorflow.keras.Sequential.save' + ) as mocked_save: + subject = gandy.models.bnns.BNN((5,), (1,)) + subject.save('filename') + mocked_save.assert_called_with('filename') + return + + def test_load(self): + """load needs to use keras load, but then also stick it into a gandy + model with the correct shape + """ + model_mocked = unittest.mock.MagicMock() + model_mocked.input.shape.to_list.return_value = [5, ] + model_mocked.output.shape.to_list.return_value = [3, ] + with unittest.mock.patch( + 'tensorflow.keras.models.load', + return_value=model_mocked + ) as mocked_load: + subject = gandy.models.bnns.BNN.load('filename') + self.assertTrue(isinstance(subject), gandy.models.bnns.BNN) + self.assertEqual(subject.xhsape, (5,)) + self.assertEqual(subject.xhsape, (3,)) + mocked_load.assert_called_with('filename') + return From e48c6b05f1ab6969ef43322b41f5ddb4e93dfc34 Mon Sep 17 00:00:00 2001 From: Kyle Moskowitz Date: Tue, 9 Mar 2021 16:19:04 -0800 Subject: [PATCH 45/99] PEP8 style changes --- gandy/tests/test_metrics/test_metrics.py | 204 +++++++++++------------ 1 file changed, 102 insertions(+), 102 deletions(-) diff --git a/gandy/tests/test_metrics/test_metrics.py b/gandy/tests/test_metrics/test_metrics.py index 21c60df..bf5b421 100644 --- a/gandy/tests/test_metrics/test_metrics.py +++ b/gandy/tests/test_metrics/test_metrics.py @@ -1,4 +1,5 @@ -"""Unit tests for Metrics module.""" +"""Unit tests for Metrics module""" + import unittest import unittest.mock @@ -6,146 +7,145 @@ import gandy.quality_est.metrics as metrics + class TestMetric(unittest.TestCase): - """Unit test for Metric parent class""" + """Unit test for Metric parent class""" - def test___init___(self): - """Test proper initialization of class with proper inputs""" + def test___init___(self): + """Test proper initialization of class with proper inputs""" - # failure cases: data not iterable - with self.assertRaises(TypeError): - subject = metrics.Metric(predictions = "0, 1, 2", - real = np.array([0, 1, 2]), - uncertainties = np.array([0, 0.5, 1])) + # failure cases: data not iterable + with self.assertRaises(TypeError): + subject = metrics.Metric(predictions="0, 1, 2", + real=np.array([0, 1, 2]), + uncertainties=np.array([0, 0.5, 1])) - with self.assertRaises(TypeError): - subject = metrics.Metric(predictions = np.array([0, 1, 2]), - real = "0, 1, 2", - uncertainties = np.array([0, 0.5, 1])) + with self.assertRaises(TypeError): + subject = metrics.Metric(predictions=np.array([0, 1, 2]), + real="0, 1, 2", + uncertainties=np.array([0, 0.5, 1])) - with self.assertRaises(TypeError): - subject = metrics.Metric(predictions = np.array([0, 1, 2]), - real = np.array([0, 1, 2]), - uncertainties = "0, 1, 2") + with self.assertRaises(TypeError): + subject = metrics.Metric(predictions=np.array([0, 1, 2]), + real=np.array([0, 1, 2]), + uncertainties="0, 1, 2") - with self.assertRaises(TypeError): - subject = metrics.Metric(predictions = np.array([0, 1, 2]), - real = "0, 1, 2") + with self.assertRaises(TypeError): + subject = metrics.Metric(predictions=np.array([0, 1, 2]), + real="0, 1, 2") - # success case - subject = metrics.Metric(predictions = np.array([0, 1, 2]), - real = np.array([0, 1, 2]), - uncertainties = np.array([0, 0.5, 1])) + # success case + subject = metrics.Metric(predictions=np.array([0, 1, 2]), + real=np.array([0, 1, 2]), + uncertainties=np.array([0, 0.5, 1])) - #check to make sure necessary attributes are inputted - self.assertTrue(subject.predictions is not None) - self.assertTrue(subject.real is not None) - + # check to make sure necessary attributes are inputted + self.assertTrue(subject.predictions is not None) + self.assertTrue(subject.real is not None) - def test_calculate(self): - """Test the calculate function within the parent Metric class""" + def test_calculate(self): + """Test the calculate function within the parent Metric class""" - # ensure calculate method is called using mock function - subject = metrics.Metric - subject.calculate = unittest.mock.MagicMock(name = 'calculate') - subject.calculate.assert_called_once_with(kwargs) + # ensure calculate method is called using mock function + subject = metrics.Metric + subject.calculate = unittest.mock.MagicMock(name='calculate') + subject.calculate.assert_called_once_with(kwargs) class TestMSE(unittest.TestCase): """Unit test for MSE subclass""" def test_calculate(self): - """Test the calculate function within the parent Metric class""" - - # failure case: data not iterable - with self.assertRaises(TypeError): - subject = metric.MSE(predictions = "0, 1, 2", - real = np.array([0, 1, 2])) - - with self.assertRaises(TypeError): - subject = metric.MSE(predictions = np.array([0, 1, 2]), - real = "0, 1, 2") - - # failure case: uncertainties given when None expected + """Test the calculate function within the parent Metric class""" + + # failure case: data not iterable + with self.assertRaises(TypeError): + subject = metric.MSE(predictions="0, 1, 2", + real=np.array([0, 1, 2])) + with self.assertRaises(TypeError): - subject = metric.MSE(predictions = np.array([0, 1, 2]), - real = np.array([0, 1, 2]), - uncertainties = np.array([0, 1, 2])) + subject = metric.MSE(predictions=np.array([0, 1, 2]), + real="0, 1, 2") - #check to make sure necessary attributes are inputted - subject = metrics.MSE(predictions = np.array([0, 1, 2]), - real = np.array([0, 1, 2])) + # failure case: uncertainties given when None expected + with self.assertRaises(TypeError): + subject = metric.MSE(predictions=np.array([0, 1, 2]), + real=np.array([0, 1, 2]), + uncertainties=np.array([0, 1, 2])) + + # check to make sure necessary attributes are inputted + subject = metrics.MSE(predictions=np.array([0, 1, 2]), + real=np.array([0, 1, 2])) - self.assertTrue(subject.predictions is not None) - self.assertTrue(subject.real is not None) - self.assertTrue(subject.uncertainties is None) + self.assertTrue(subject.predictions is not None) + self.assertTrue(subject.real is not None) + self.assertTrue(subject.uncertainties is None) - #check to make sure output is correct type - self.assertTrue(isinstance(subject, tuple)) + # check to make sure output is correct type + self.assertTrue(isinstance(subject, tuple)) class TestRMSE(unittest.TestCase): """Unit test for RMSE subclass""" def test_calculate(self): - """Test the calculate function within the parent Metric class""" - - # failure case: data not iterable - with self.assertRaises(TypeError): - subject = metric.RMSE(predictions = "0, 1, 2", - real = np.array([0, 1, 2])) + """Test the calculate function within the parent Metric class""" - with self.assertRaises(TypeError): - subject = metric.RMSE(predictions = np.array([0, 1, 2]), - real = "0, 1, 2") + # failure case: data not iterable + with self.assertRaises(TypeError): + subject = metric.RMSE(predictions="0, 1, 2", + real=np.array([0, 1, 2])) - # failure case: uncertainties given when None expected with self.assertRaises(TypeError): - subject = metric.RMSE(predictions = np.array([0, 1, 2]), - real = np.array([0, 1, 2]), - uncertainties = np.array([0, 1, 2])) + subject = metric.RMSE(predictions=np.array([0, 1, 2]), + real="0, 1, 2") + + # failure case: uncertainties given when None expected + with self.assertRaises(TypeError): + subject = metric.RMSE(predictions=np.array([0, 1, 2]), + real=np.array([0, 1, 2]), + uncertainties=np.array([0, 1, 2])) - #check to make sure necessary attributes are inputted - subject = metrics.RMSE(predictions = np.array([0, 1, 2]), - real = np.array([0, 1, 2])) + # check to make sure necessary attributes are inputted + subject = metrics.RMSE(predictions=np.array([0, 1, 2]), + real=np.array([0, 1, 2])) - self.assertTrue(subject.predictions is not None) - self.assertTrue(subject.real is not None) - self.assertTrue(subject.uncertainties is None) + self.assertTrue(subject.predictions is not None) + self.assertTrue(subject.real is not None) + self.assertTrue(subject.uncertainties is None) - #check to make sure output is correct type - self.assertTrue(isinstance(subject, tuple)) + # check to make sure output is correct type + self.assertTrue(isinstance(subject, tuple)) class TestF1(unittest.TestCase): """Unit test for F1 subclass""" def test_calculate(self): - """Test the calculate function within the parent Metric class""" + """Test the calculate function within the parent Metric class""" - # failure case: data not iterable - with self.assertRaises(TypeError): - subject = metric.F1(predictions = "0, 1, 2", - real = np.array([0, 1, 2])) + # failure case: data not iterable + with self.assertRaises(TypeError): + subject = metric.F1(predictions="0, 1, 2", + real=np.array([0, 1, 2])) - with self.assertRaises(TypeError): - subject = metric.F1(predictions = np.array([0, 1, 2]), - real = "0, 1, 2") - + with self.assertRaises(TypeError): + subject = metric.F1(predictions=np.array([0, 1, 2]), + real="0, 1, 2") + # failure case: uncertainties given when None expected with self.assertRaises(TypeError): - subject = metric.F1(predictions = np.array([0, 1, 2]), - real = np.array([0, 1, 2]), - uncertainties = np.array([0, 1, 2])) - - #check to make sure necessary attributes are inputted - subject = metrics.F1(predictions = np.array([0, 1, 2]), - real = np.array([0, 1, 2])) - - self.assertTrue(subject.predictions is not None) - self.assertTrue(subject.real is not None) - self.assertTrue(subject.uncertainties is None) - - #check to make sure output is correct type - self.assertTrue(isinstance(subject, (float, int))) - + subject = metric.F1(predictions=np.array([0, 1, 2]), + real=np.array([0, 1, 2]), + uncertainties=np.array([0, 1, 2])) + + # check to make sure necessary attributes are inputted + subject = metrics.F1(predictions=np.array([0, 1, 2]), + real=np.array([0, 1, 2])) + + self.assertTrue(subject.predictions is not None) + self.assertTrue(subject.real is not None) + self.assertTrue(subject.uncertainties is None) + + # check to make sure output is correct type + self.assertTrue(isinstance(subject, (float, int))) From 9ea5313ca20aecd112a82c3c7fa8677687b45272 Mon Sep 17 00:00:00 2001 From: Kyle Moskowitz Date: Tue, 9 Mar 2021 16:56:55 -0800 Subject: [PATCH 46/99] started writing metrics.py code --- gandy/quality_est/metrics.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/gandy/quality_est/metrics.py b/gandy/quality_est/metrics.py index 7360a5c..407fdb8 100644 --- a/gandy/quality_est/metrics.py +++ b/gandy/quality_est/metrics.py @@ -49,11 +49,11 @@ def __init__(self, predictions: Array, real: Array, uncertainties=None): Optional argument which contains array of uncertainty values generated from the uncertainty module ''' - # psuedocode - # set self.predictions - # set self.real - # set self.uncertainties - # call calculate function within init: self.calculate() + self.predictions = predictions + self.real = real + self.uncertainties = uncertainties + self.calculate() + def calculate(self, **kwargs): ''' Empty calculate function @@ -89,11 +89,12 @@ def calculate(self, **kwargs) -> Tuple[float, Array]: An array of MSE scores for each prediction ''' - # pseudocode - # define mathematical formula for MSE calculation using self.args - # iteration over arrays likely, then plug into defined formula - MSE_value = None - MSE_values = None + MSE_value = np.square(np.subtract(self.real, self.predictions)).mean() + MSE_values = [] + + for i in range(len(self.predictions)): + MSE_values.append((self.real[i] - self.predictions[i])**2) + return MSE_value, MSE_values From 5f889b0c9b5877fb03bebe19a25f4ddc0d880d4a Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Tue, 9 Mar 2021 21:14:03 -0800 Subject: [PATCH 47/99] Fixed base hyperparam dict --- gandy/models/dcgan.py | 46 +++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/gandy/models/dcgan.py b/gandy/models/dcgan.py index ca62eba..07b6f59 100644 --- a/gandy/models/dcgan.py +++ b/gandy/models/dcgan.py @@ -55,17 +55,17 @@ def __init__(self, xshape, yshape, noise_shape, n_classes, **kwargs): self.noise_shape = noise_shape self.n_classes = n_classes - Base_hyperparams = {layer_dimensions: [128], - dropout: 0.05, - activation: 'relu', - use_bias: True, - kernel_initializer: "glorot_uniform", - bias_initializer: "zeros", - kernel_regularizer: 'l2', - bias_regularizer: None, - activity_regularizer: None, - kernel_constraint: None, - bias_constraint: None} + Base_hyperparams = dict(layer_dimensions = [128], + dropout = 0.05, + activation = 'relu', + use_bias = True, + kernel_initializer = "glorot_uniform", + bias_initializer = "zeros", + kernel_regularizer = 'l2', + bias_regularizer = None, + activity_regularizer = None, + kernel_constraint = None, + bias_constraint = None) # Create separate hyperparam dictionaries for the generator # and discriminator @@ -151,17 +151,17 @@ def create_generator(self): # construct input noise_in = Input(shape=self.get_noise_input_shape()) # build first layer of network - gen = Dense(layer_dimensions[0], layer_kwargs)(noise_in) + gen = Dense(layer_dimensions[0], **layer_kwargs)(noise_in) # adding dropout to the weights gen = Dropout(dropout)(gen) # build subsequent layers for layer_dim in layer_dimensions[1:]: - gen = Dense(layer_dim, layer_kwargs)(gen) + gen = Dense(layer_dim, **layer_kwargs)(gen) gen = Dropout(dropout)(gen) # generator outputs # is xhape[0] really what we want, or batch size? - gen = Dense(self.xshape[0], layer_kwargs)(gen) + gen = Dense(self.xshape[0], **layer_kwargs)(gen) gen = Dropout(dropout)(gen) # final construction of Keras model @@ -226,19 +226,19 @@ def create_discriminator(self): # construct input data_in = Input(shape=self.xshape) # build first layer of network - discrim = Dense(layer_dimensions[0], layer_kwargs)(data_in) + discrim = Dense(layer_dimensions[0], **layer_kwargs)(data_in) # adding dropout to the weights discrim = Dropout(dropout)(discrim) # build subsequent layers for layer_dim in layer_dimensions[1:]: - discrim = Dense(layer_dim, layer_kwargs)(discrim) + discrim = Dense(layer_dim, **layer_kwargs)(discrim) discrim = Dropout(dropout)(discrim) # To maintain the interpretation of a probability, # the final activation function is not a kwarg final_layer_kwargs = layer_kwargs.copy() final_layer_kwargs[activation] = 'sigmoid' - discrim_prob = Dense(1, final_layer_kwargs)(discrim) + discrim_prob = Dense(1, **final_layer_kwargs)(discrim) # final construction of Keras model discriminator = tf.keras.Model(inputs=[data_in], @@ -340,16 +340,16 @@ def create_generator(self) -> Object: gen_input = Concatenate()([noise_in, conditional_in]) # build first layer of network - gen = Dense(layer_dimensions[0], layer_kwargs)(gen_input) + gen = Dense(layer_dimensions[0], **layer_kwargs)(gen_input) # adding dropout to the weights gen = Dropout(dropout)(gen) # build subsequent layers for layer_dim in layer_dimensions[1:]: - gen = Dense(layer_dim, layer_kwargs)(gen) + gen = Dense(layer_dim, **layer_kwargs)(gen) gen = Dropout(dropout)(gen) # generator outputs - gen = Dense(self.xshape[0], layer_kwargs)(gen) + gen = Dense(self.xshape[0], **layer_kwargs)(gen) gen = Dropout(dropout)(gen) # final construction of Keras model @@ -417,19 +417,19 @@ def create_discriminator(self) -> Object: discrim_input = Concatenate()([data_in, conditional_in]) # build first layer of network - discrim = Dense(layer_dimensions[0], layer_kwargs)(discrim_input) + discrim = Dense(layer_dimensions[0], **layer_kwargs)(discrim_input) # adding dropout to the weights discrim = Dropout(dropout)(discrim) # build subsequent layers for layer_dim in layer_dimensions[1:]: - discrim = Dense(layer_dim, layer_kwargs)(discrim) + discrim = Dense(layer_dim, **layer_kwargs)(discrim) discrim = Dropout(dropout)(discrim) # To maintain the interpretation of a probability, # the final activation function is not a kwarg final_layer_kwargs = layer_kwargs.copy() final_layer_kwargs[activation] = 'sigmoid' - discrim_prob = Dense(1, final_layer_kwargs)(discrim) + discrim_prob = Dense(1, **final_layer_kwargs)(discrim) # final construction of Keras model discriminator = tf.keras.Model(inputs=[data_in, conditional_in], From 2d10cc8001d11e83c4dbb374fae3c0175c7a97ed Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Tue, 9 Mar 2021 21:15:41 -0800 Subject: [PATCH 48/99] Testing travis --- gandy/models/dcgan.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gandy/models/dcgan.py b/gandy/models/dcgan.py index 07b6f59..55db2d5 100644 --- a/gandy/models/dcgan.py +++ b/gandy/models/dcgan.py @@ -55,6 +55,7 @@ def __init__(self, xshape, yshape, noise_shape, n_classes, **kwargs): self.noise_shape = noise_shape self.n_classes = n_classes + # base hyperparameters for generator and discirminator Base_hyperparams = dict(layer_dimensions = [128], dropout = 0.05, activation = 'relu', From 8e0eb84d99f6497e8407d09e36e1b9b9d7ccfd8a Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Tue, 9 Mar 2021 21:22:34 -0800 Subject: [PATCH 49/99] Pep8 compliant --- gandy/models/dcgan.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/gandy/models/dcgan.py b/gandy/models/dcgan.py index 55db2d5..2565129 100644 --- a/gandy/models/dcgan.py +++ b/gandy/models/dcgan.py @@ -56,17 +56,17 @@ def __init__(self, xshape, yshape, noise_shape, n_classes, **kwargs): self.n_classes = n_classes # base hyperparameters for generator and discirminator - Base_hyperparams = dict(layer_dimensions = [128], - dropout = 0.05, - activation = 'relu', - use_bias = True, - kernel_initializer = "glorot_uniform", - bias_initializer = "zeros", - kernel_regularizer = 'l2', - bias_regularizer = None, - activity_regularizer = None, - kernel_constraint = None, - bias_constraint = None) + Base_hyperparams = dict(layer_dimensions=[128], + dropout=0.05, + activation='relu', + use_bias=True, + kernel_initializer="glorot_uniform", + bias_initializer="zeros", + kernel_regularizer='l2', + bias_regularizer=None, + activity_regularizer=None, + kernel_constraint=None, + bias_constraint=None) # Create separate hyperparam dictionaries for the generator # and discriminator @@ -238,7 +238,7 @@ def create_discriminator(self): # To maintain the interpretation of a probability, # the final activation function is not a kwarg final_layer_kwargs = layer_kwargs.copy() - final_layer_kwargs[activation] = 'sigmoid' + final_layer_kwargs.update(activation='sigmoid') discrim_prob = Dense(1, **final_layer_kwargs)(discrim) # final construction of Keras model @@ -429,7 +429,7 @@ def create_discriminator(self) -> Object: # To maintain the interpretation of a probability, # the final activation function is not a kwarg final_layer_kwargs = layer_kwargs.copy() - final_layer_kwargs[activation] = 'sigmoid' + final_layer_kwargs.update(activation='sigmoid') discrim_prob = Dense(1, **final_layer_kwargs)(discrim) # final construction of Keras model From 59fc3db53a544c850f32301c98ee5fa8d30f29d1 Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Tue, 9 Mar 2021 21:37:54 -0800 Subject: [PATCH 50/99] Fixed errors from unit tests --- gandy/models/gans.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gandy/models/gans.py b/gandy/models/gans.py index 956b8f6..7193813 100644 --- a/gandy/models/gans.py +++ b/gandy/models/gans.py @@ -11,7 +11,7 @@ # gandy imports import gandy.models.models -import gandy.metrics +import gandy.quality_est.metrics # deep learning imports import deepchem From 2807d290d828b5a791aa5fcfbcbef9d8295f859c Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Tue, 9 Mar 2021 21:54:36 -0800 Subject: [PATCH 51/99] Adding GAN example --- examples/GANs_Showcase.ipynb | 69 ++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 examples/GANs_Showcase.ipynb diff --git a/examples/GANs_Showcase.ipynb b/examples/GANs_Showcase.ipynb new file mode 100644 index 0000000..9008d40 --- /dev/null +++ b/examples/GANs_Showcase.ipynb @@ -0,0 +1,69 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Demo of GANs" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy\n", + "import sklearn.datasets\n", + "import sklearn.model_selection\n", + "import sklearn.preprocessing\n", + "\n", + "# import gandy.models.gans" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "## loading the data\n", + "Xs, Ys = sklearn.datasets.load_boston(return_X_y=True)\n", + "Xsr, Xst, Ysr, Yst = sklearn.model_selection.train_test_split(Xs, Ys, train_size = 0.8)\n", + "## normalizing it\n", + "norm = sklearn.preprocessing.Normalizer()\n", + "Xsr = norm.fit_transform(Xsr)\n", + "Xst = norm.transform(Xst)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 63217f106f9226b77aa79545c4f8aa0e40c60fa0 Mon Sep 17 00:00:00 2001 From: evankomp Date: Tue, 9 Mar 2021 21:55:17 -0800 Subject: [PATCH 52/99] removed python 3.7+ from travis --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index adf3b63..dab01f9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,8 +6,6 @@ language: python matrix: include: - python: 3.6 - - python: 3.7 - - python: 3.8 # what branches should be evaluated branches: From 86b5b8d7570408be5191859aa5ca9d10cccd12ec Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Tue, 9 Mar 2021 21:55:46 -0800 Subject: [PATCH 53/99] Fixing more unit test errors --- gandy/models/dcgan.py | 2 +- gandy/tests/test_models/test_gans.py | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/gandy/models/dcgan.py b/gandy/models/dcgan.py index 2565129..dee5af3 100644 --- a/gandy/models/dcgan.py +++ b/gandy/models/dcgan.py @@ -14,7 +14,7 @@ # deep learning imports import deepchem import tensorflow as tf -from tf.keras.layers import Concatenate, Dense, Dropout, Input +from tensorflow.keras.layers import Concatenate, Dense, Dropout, Input # typing imports from typing import Tuple, Object, Type diff --git a/gandy/tests/test_models/test_gans.py b/gandy/tests/test_models/test_gans.py index 6573052..d95aba0 100644 --- a/gandy/tests/test_models/test_gans.py +++ b/gandy/tests/test_models/test_gans.py @@ -1,11 +1,8 @@ """Testing functions for UncertaintyModel gan class.""" -# import numpy as np import unittest import unittest.mock as mock -# import deepchem - import gandy.models.gans as gans import gandy.models.models From c6af05cceaeaaf99dd061ee73070e3198dc365ca Mon Sep 17 00:00:00 2001 From: evankomp Date: Tue, 9 Mar 2021 22:03:04 -0800 Subject: [PATCH 54/99] adding tensorflow core to environment, maybe that fixes the import error --- environment.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/environment.yml b/environment.yml index 12142b9..7624ec0 100644 --- a/environment.yml +++ b/environment.yml @@ -11,4 +11,5 @@ dependencies: - scikit-learn=0.23.2 - scipy=1.5.2 - tensorflow=2.0.0 + - tensorflow_core - optuna=2.5.0 From f39d09d0e369e20a2fb807d06b21b0c529aac55c Mon Sep 17 00:00:00 2001 From: evankomp Date: Tue, 9 Mar 2021 22:05:31 -0800 Subject: [PATCH 55/99] jk that module doesn't exist --- environment.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/environment.yml b/environment.yml index 7624ec0..12142b9 100644 --- a/environment.yml +++ b/environment.yml @@ -11,5 +11,4 @@ dependencies: - scikit-learn=0.23.2 - scipy=1.5.2 - tensorflow=2.0.0 - - tensorflow_core - optuna=2.5.0 From a3746eb94b66d6ec11e72c09bfbb29f4fceb3e7c Mon Sep 17 00:00:00 2001 From: evankomp Date: Tue, 9 Mar 2021 22:13:38 -0800 Subject: [PATCH 56/99] removed stack, don't want to deal with it --- gandy/models/models.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gandy/models/models.py b/gandy/models/models.py index 08170ea..b99d554 100644 --- a/gandy/models/models.py +++ b/gandy/models/models.py @@ -11,7 +11,6 @@ """ # imports -import inspect import time from typing import Tuple, Iterable, Any, Type, Callable, Union @@ -32,8 +31,8 @@ class NotImplimented(Exception): """ def __init__(self, inst): - self.message = """The `{}` method has not yet been implimented by - this class: `{}`.""".format(inspect.stack()[1][3], inst.__class__) + self.message = """This method has not yet been implimented by + this class: `{}`.""".format(inst.__class__) super().__init__(self.message) return From 207cf8e1a9632eb20e98a42ef86dbadd2044b828 Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Tue, 9 Mar 2021 22:26:26 -0800 Subject: [PATCH 57/99] Travis pls work --- gandy/models/bnns.py | 2 +- gandy/models/gans.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gandy/models/bnns.py b/gandy/models/bnns.py index ef8cbe1..0a6e0a0 100644 --- a/gandy/models/bnns.py +++ b/gandy/models/bnns.py @@ -10,7 +10,7 @@ # import tensorflow as tf # typing imports -from typing import Any, Object, Type +from typing import Any, Type # typing import numpy diff --git a/gandy/models/gans.py b/gandy/models/gans.py index 7193813..940ab75 100644 --- a/gandy/models/gans.py +++ b/gandy/models/gans.py @@ -19,7 +19,7 @@ import tensorflow as tf # typing imports -from typing import Any, Object, Type, Callable +from typing import Any, Type, Callable # typing import numpy as np @@ -46,7 +46,7 @@ class GAN(gandy.models.models.UncertaintyModel): """ # overridden method from UncertaintyModel class - def _build(self, **kwargs) -> Object: + def _build(self, **kwargs): """ Construct the model. @@ -60,7 +60,7 @@ def _build(self, **kwargs) -> Object: Returns: model - Deepchem GAN model found in dcgan - type == Object + type == Keras model """ # get noise shape from kwargs # default is 10 dimensional From 8e4977f28c267f77617139e0f3351b4d83b335b8 Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Tue, 9 Mar 2021 22:36:46 -0800 Subject: [PATCH 58/99] My dependencies are :( --- GANs_Showcase.ipynb | 76 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 GANs_Showcase.ipynb diff --git a/GANs_Showcase.ipynb b/GANs_Showcase.ipynb new file mode 100644 index 0000000..c2a089d --- /dev/null +++ b/GANs_Showcase.ipynb @@ -0,0 +1,76 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Demo of GANS" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy\n", + "import sklearn.datasets\n", + "import sklearn.model_selection\n", + "import sklearn.preprocessing\n", + "\n", + "from GANdy import gandy" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "ename": "ModuleNotFoundError", + "evalue": "No module named 'gandy'", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[1;32mfrom\u001b[0m \u001b[0mGANdy\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mgandy\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mmodels\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mgans\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[1;32m~\\Desktop\\DSClass\\GANdy\\gandy\\models\\gans.py\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 11\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 12\u001b[0m \u001b[1;31m# gandy imports\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 13\u001b[1;33m \u001b[1;32mimport\u001b[0m \u001b[0mgandy\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mmodels\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mmodels\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 14\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mgandy\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mquality_est\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mmetrics\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 15\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;31mModuleNotFoundError\u001b[0m: No module named 'gandy'" + ] + } + ], + "source": [ + "from GANdy.gandy.models import gans" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 1580ab592fe36f973504e94b6f1a2158846e2052 Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Tue, 9 Mar 2021 22:38:53 -0800 Subject: [PATCH 59/99] Pep8 --- gandy/models/bnns.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gandy/models/bnns.py b/gandy/models/bnns.py index 0a6e0a0..59ee993 100644 --- a/gandy/models/bnns.py +++ b/gandy/models/bnns.py @@ -35,7 +35,7 @@ class BNN(gandy.models.models.UncertaintyModel): for a guide to implementing a BNN with Keras. """ - def prior(kernel_size, bias_size, dtype=None) -> Object: + def prior(kernel_size, bias_size, dtype=None): ''' Arguments: kernel_size @@ -64,7 +64,7 @@ def prior(kernel_size, bias_size, dtype=None) -> Object: # Define variational posterior weight distribution as multivariate # Gaussian. Note that the learnable parameters for this # distribution are the means, variances, and covariances. - def posterior(kernel_size, bias_size, dtype=None) -> Object: + def posterior(kernel_size, bias_size, dtype=None): ''' Arguments: kernel_size @@ -108,7 +108,7 @@ def negative_loglikelihood(targets, estimated_distribution) -> Array: # return -estimated_distribution.log_prob(targets) # overridden method from UncertaintyModel class - def _build(self, *args, **kwargs) -> Object: + def _build(self, *args, **kwargs): ''' Construct the model. User has the option to specify: From eb60db1e14e168fec1bbbdab3cc2c96181fcffd7 Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Tue, 9 Mar 2021 23:25:37 -0800 Subject: [PATCH 60/99] removing Object typing --- gandy/models/dcgan.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gandy/models/dcgan.py b/gandy/models/dcgan.py index dee5af3..dba17cc 100644 --- a/gandy/models/dcgan.py +++ b/gandy/models/dcgan.py @@ -17,7 +17,7 @@ from tensorflow.keras.layers import Concatenate, Dense, Dropout, Input # typing imports -from typing import Tuple, Object, Type +from typing import Tuple, Type # more typing import numpy as np @@ -282,7 +282,7 @@ def get_conditional_input_shapes(self) -> Array: """ return [(self.n_classes,)] - def create_generator(self) -> Object: + def create_generator(self): """ Create the generator as a keras model. @@ -358,7 +358,7 @@ def create_generator(self) -> Object: outputs=[gen]) return generator - def create_discriminator(self) -> Object: + def create_discriminator(self): """ Create the discriminator as a keras model. From dd60c0dc2cb89113628e5bfcefa14de182808a59 Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Tue, 9 Mar 2021 23:43:02 -0800 Subject: [PATCH 61/99] Fixing testing errors --- gandy/models/gans.py | 10 +++++----- gandy/tests/test_models/test_gans.py | 8 +++++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/gandy/models/gans.py b/gandy/models/gans.py index 940ab75..f0ee7e3 100644 --- a/gandy/models/gans.py +++ b/gandy/models/gans.py @@ -46,7 +46,7 @@ class GAN(gandy.models.models.UncertaintyModel): """ # overridden method from UncertaintyModel class - def _build(self, **kwargs): + def _build(self, *args, **kwargs): """ Construct the model. @@ -71,7 +71,7 @@ def _build(self, **kwargs): # determine whether to use gan or conditional gan if n_classes is not None: # if number of classes is specified, assumes conditional GAN - self.conditional = True + conditional = True # Should this be flagged somewhere?... if self.yshape[0] > 1: # Ys are already one hot encoded @@ -83,14 +83,14 @@ def _build(self, **kwargs): else: # if no n_classes specified, assumed to be regression # and no need for conditional inputs - self.conditional = False + conditional = False # get other kwargs as hyperparameters hyperparams = {key: kwargs[key] for key in kwargs.keys() - {'n_classes', 'noise_shape'}} # instantiating the model as the deepchem gan - if self.conditional: + if conditional: model = dcgan.CondDCGAN(self.xshape, self.yshape, noise_shape, n_classes, hyperparams) else: @@ -124,7 +124,7 @@ def generate_data(self, """ # sample with replacement X, Y pairs of size batch_size n = len(Xs) - indices = np.random.randomint(0, high=n, size=(batch_size,)) + indices = np.random.randint(0, high=n, size=(batch_size,)) classes = Xs[indices] points = Ys[indices] return classes, points diff --git a/gandy/tests/test_models/test_gans.py b/gandy/tests/test_models/test_gans.py index d95aba0..5056b4f 100644 --- a/gandy/tests/test_models/test_gans.py +++ b/gandy/tests/test_models/test_gans.py @@ -6,13 +6,15 @@ import gandy.models.gans as gans import gandy.models.models +import tensorflow as tf + class TestGAN(unittest.TestCase): """Test GAN class.""" def test_inheritence(self): """Ensure the subclass class inherits from parent class.""" - self.assertTrue(issubclass(gans.gan, + self.assertTrue(issubclass(gans.GAN, gandy.models.models.UncertaintyModel)) def test__build(self): @@ -130,7 +132,7 @@ def test_iterbacthes(self, mocked__build): kwargs = dict(bacthes=1, batch_size=5) with mock.patch('deepchem.metrics.to_one_hot', return_value='one_hot_classes') as mocked_one_hot: - result = list(subject.iterbacthes(Xs, Ys, **kwargs)) + result = list(subject.iterbatches(Xs, Ys, **kwargs)) subject.generate_data.assert_called_with(Xs, Ys, 5) expected_result = {subject._model.data_inputs[0]: 'points', subject._model.conditional_inputs[0]: @@ -178,7 +180,7 @@ def test_save(self, mocked__build): """ # test path save subject = gans.GAN(xshape=(4,), yshape=(2,), n_classes=10) - subject._model.save = mock.MagicMock('save') + subject._model.save = mock.MagicMock(name='save') subject.save('path') subject._model.save.assert_called_with('path') # test h5 save From 584f69672064041a8e8202947ff8ff5d0fca3d4a Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Tue, 9 Mar 2021 23:47:24 -0800 Subject: [PATCH 62/99] pep8 --- gandy/tests/test_models/test_gans.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/gandy/tests/test_models/test_gans.py b/gandy/tests/test_models/test_gans.py index 5056b4f..980f86f 100644 --- a/gandy/tests/test_models/test_gans.py +++ b/gandy/tests/test_models/test_gans.py @@ -6,8 +6,6 @@ import gandy.models.gans as gans import gandy.models.models -import tensorflow as tf - class TestGAN(unittest.TestCase): """Test GAN class.""" From d38f9943e15495c4342238539f1ea053d6ec623a Mon Sep 17 00:00:00 2001 From: evankomp Date: Wed, 10 Mar 2021 09:33:49 -0800 Subject: [PATCH 63/99] Object not a real type, changed to callable --- gandy/models/bnns.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gandy/models/bnns.py b/gandy/models/bnns.py index ef8cbe1..ff676f6 100644 --- a/gandy/models/bnns.py +++ b/gandy/models/bnns.py @@ -10,7 +10,7 @@ # import tensorflow as tf # typing imports -from typing import Any, Object, Type +from typing import Any, Callable, Type # typing import numpy @@ -35,7 +35,7 @@ class BNN(gandy.models.models.UncertaintyModel): for a guide to implementing a BNN with Keras. """ - def prior(kernel_size, bias_size, dtype=None) -> Object: + def prior(kernel_size, bias_size, dtype=None) -> Callable: ''' Arguments: kernel_size @@ -64,7 +64,7 @@ def prior(kernel_size, bias_size, dtype=None) -> Object: # Define variational posterior weight distribution as multivariate # Gaussian. Note that the learnable parameters for this # distribution are the means, variances, and covariances. - def posterior(kernel_size, bias_size, dtype=None) -> Object: + def posterior(kernel_size, bias_size, dtype=None) -> Callable: ''' Arguments: kernel_size @@ -108,7 +108,7 @@ def negative_loglikelihood(targets, estimated_distribution) -> Array: # return -estimated_distribution.log_prob(targets) # overridden method from UncertaintyModel class - def _build(self, *args, **kwargs) -> Object: + def _build(self, *args, **kwargs) -> Callable: ''' Construct the model. User has the option to specify: From 68bd4729effdf07c76a1e77c9b6c5a3468283e2e Mon Sep 17 00:00:00 2001 From: evankomp Date: Wed, 10 Mar 2021 13:20:14 -0800 Subject: [PATCH 64/99] BNNS written and passing tests --- gandy/models/bnns.py | 162 +++++++++++++++++++++++---- gandy/tests/test_models/test_bnns.py | 86 ++++++++------ 2 files changed, 189 insertions(+), 59 deletions(-) diff --git a/gandy/models/bnns.py b/gandy/models/bnns.py index 0a15070..45a518d 100644 --- a/gandy/models/bnns.py +++ b/gandy/models/bnns.py @@ -4,16 +4,18 @@ This contains the Bayes NN class, based on the KEras tutorial at https://keras.io/examples/keras_recipes/bayesian_neural_networks/ """ +# typing imports +from typing import Any, Callable, Type, Union, Tuple -# imports -import gandy.models.models -# import tensorflow as tf +# 3rd part imports +import numpy +import tensorflow as tf +import tensorflow_probability as tfp -# typing imports -from typing import Any, Callable, Type +# gandy +import gandy.models.models # typing -import numpy Array = Type[numpy.ndarray] @@ -34,8 +36,8 @@ class BNN(gandy.models.models.UncertaintyModel): https://keras.io/examples/keras_recipes/bayesian_neural_networks/ for a guide to implementing a BNN with Keras. """ - - def prior(kernel_size, bias_size, dtype=None) -> Callable: + + def prior(self, kernel_size, bias_size, dtype=None) -> Callable: ''' Arguments: kernel_size @@ -58,13 +60,27 @@ def prior(kernel_size, bias_size, dtype=None) -> Callable: # ) # ] # ) - prior_model = None + try: + kernel_size = int(kernel_size) + bias_size = int(bias_size) + except BaseException: + raise TypeError('Cannot convert kernel and bias to int.') + n = kernel_size + bias_size + prior_model = tf.keras.Sequential( + [ + tfp.layers.DistributionLambda( + lambda t: tfp.distributions.MultivariateNormalDiag( + loc=tf.zeros(n), scale_diag=tf.ones(n) + ) + ) + ] + ) return prior_model # Define variational posterior weight distribution as multivariate # Gaussian. Note that the learnable parameters for this # distribution are the means, variances, and covariances. - def posterior(kernel_size, bias_size, dtype=None) -> Callable: + def posterior(self, kernel_size, bias_size, dtype=None) -> Callable: ''' Arguments: kernel_size @@ -85,14 +101,28 @@ def posterior(kernel_size, bias_size, dtype=None) -> Callable: # tfp.layers.MultivariateNormalTriL(n), # ] # ) - posterior_model = None + try: + kernel_size = int(kernel_size) + bias_size = int(bias_size) + except BaseException: + raise TypeError('Cannot convert kernel and bias to int.') + n = kernel_size + bias_size + posterior_model = tf.keras.Sequential( + [ + tfp.layers.VariableLayer( + tfp.layers.MultivariateNormalTriL.params_size(n), + dtype=dtype + ), + tfp.layers.MultivariateNormalTriL(n) + ] + ) return posterior_model # Since the output of the model is a distribution, rather than a # point estimate, we use the negative loglikelihood as our loss function # to compute how likely to see the true data (targets) from the # estimated distribution produced by the model. - def negative_loglikelihood(targets, estimated_distribution) -> Array: + def negative_loglikelihood(self, targets, estimated_distribution) -> Array: ''' Arguments: targets - training targets @@ -106,9 +136,27 @@ def negative_loglikelihood(targets, estimated_distribution) -> Array: # do something like: # https://keras.io/examples/keras_recipes/bayesian_neural_networks/ # return -estimated_distribution.log_prob(targets) + try: + nll = estimated_distribution.log_prob(targets) + except AttributeError: + raise AttributeError('Passed distribution does not have the\ + log_prob method') + if not isinstance(nll, float): + raise ValueError( + 'the returned value should be a float, not {}'.format(type(nll) + ) + ) + return -nll # overridden method from UncertaintyModel class - def _build(self, *args, **kwargs) -> Callable: + def _build(self, + train_size: int, + task_type: str = 'regression', + activation: Union[Callable, str] = 'relu', + optimizer: Union[Callable, str] = 'adam', + neurons: Tuple[int] = (12, 12, 12), + metrics=['MSE'], + **kwargs) -> Callable: ''' Construct the model. User has the option to specify: @@ -165,7 +213,69 @@ def _build(self, *args, **kwargs) -> Callable: # model = keras.Model(inputs=inputs, outputs=outputs) # model.compile(**kwargs) - model = None + + # parse kwargs + layer_kwargs = {} + optimizer_kwargs = {} + output_kwargs = {} + + for k, v in kwargs.items(): + if k.startswith('optimizer_'): + optimizer_kwargs[k[10:]] = v + elif k.startswith('layer_'): + layer_kwargs[k[7:]] = v + elif k.startswith('output_'): + output_kwargs[k[8:]] = v + else: + print(k + ' is not a valid hyperparamter, ignoring') + pass + + inputs = tf.keras.Input(self.xshape) + f = tf.keras.layers.BatchNormalization()(inputs) + + # loop through each neuron + for n in neurons: + f = tfp.layers.DenseVariational( + units=n, + make_prior_fn=self.prior, + make_posterior_fn=self.posterior, + kl_weight=1 / train_size, + activation=activation, + **layer_kwargs + )(f) + + # determine output type + if task_type == 'regression': + outl = tfp.layers.IndependentNormal + distr = tf.keras.layers.Dense( + outl.params_size(self.yshape), + **output_kwargs + )(f) + outputs = outl(self.yshape)(distr) + elif task_type == 'classification': + outl = tfp.layers.CategoricalMixtureOfOneHotCategorical + distr = tf.keras.layers.Dense( + outl.params_size(self.yshape, 2), + **output_kwargs + )(f) + outputs = outl(self.yshape, 2)(distr) + else: + raise ValueError('Unknown task typle {}'.format(task_type)) + + if not callable(optimizer): + if isinstance(optimizer, str): + optimizer = tf.keras.optimizers.get(optimizer, + **optimizer_kwargs) + else: + pass + else: + optimizer = optimizer(**optimizer_kwargs) + + model = tf.keras.Model(inputs=inputs, outputs=outputs) + model.compile(optimizer=optimizer, + loss=self.negative_loglikelihood, + metrics=metrics) + return model # overridden method from UncertaintyModel class @@ -185,13 +295,12 @@ def _train(self, ters or pass to nested functions. ''' # losses = self.model.fit(Xs, **kwargs) - losses = None + losses = self.model.fit(Xs, Ys, **kwargs) return losses # overridden method from UncertaintyModel class def _predict(self, Xs: Array, - *args, **kwargs): ''' Arguments: @@ -212,10 +321,12 @@ def _predict(self, # mean, std = self.model.evaluate(Xs, **kwargs) # BNN model returns mean and variance as output # convert to predictions and uncertainties - predictions, uncertainties = None, None + dists = self.model(Xs, **kwargs) + predictions = dists.mean().numpy() + uncertainties = dists.stddev().numpy() return predictions, uncertainties - def save(filename: str, **kwargs): + def save(self, filename: str, **kwargs): """Method defined by child to save the predictor. Method must save into memory the object at self.model @@ -225,9 +336,11 @@ def save(filename: str, **kwargs): name of file to save model to """ # call Keras save function + self.model.save(filename) return None - def load(self, filename: str, **kwargs): + @classmethod + def load(cls, filename: str, **kwargs): """Method defined by child to load a predictor into memory. Loads the object to be assigned to self.model. @@ -236,6 +349,11 @@ def load(self, filename: str, **kwargs): filename (str): path of file to load """ - # call Keras.load function - model = None - return model + model = tf.keras.models.load_model(filename) + xshape = model.input_shape[1:] + yshape = model.layers[-1].get_config()['event_shape'] + inst = cls.__new__(cls) + inst._xshape = xshape + inst._yshape = yshape + inst._model = model + return inst diff --git a/gandy/tests/test_models/test_bnns.py b/gandy/tests/test_models/test_bnns.py index 03eeac6..82e28ad 100644 --- a/gandy/tests/test_models/test_bnns.py +++ b/gandy/tests/test_models/test_bnns.py @@ -3,6 +3,7 @@ import numpy import tensorflow as tf +import tensorflow_probability as tfp import gandy.models.bnns @@ -57,17 +58,18 @@ def test_negative_loglikelihood(self, mocked_build): def callable_wo_log_prob(): return - with self.assertRaises(TypeError): + with self.assertRaises(AttributeError): subject.negative_loglikelihood(numpy.array([1, 2]), callable_wo_log_prob) # expected success mocked_dist = unittest.mock.MagicMock() + mocked_dist.log_prob.return_value = 5.0 subject.negative_loglikelihood('targets', mocked_dist) mocked_dist.log_prob.assert_called_with('targets') # ability to catch non float - mocked_dist.return_value = 'string' + mocked_dist.log_prob.return_value = 'string' with self.assertRaises(ValueError): subject.negative_loglikelihood('targets', mocked_dist) @@ -79,73 +81,83 @@ def test__build(self): We need to ensure the returned keras model is both compiled and built. """ + x = numpy.array([[1, 2], + [3, 4], + [5, 6]]) # start with default initialization - subject = gandy.models.bnns.BNN((5,), (1,)) + subject = gandy.models.bnns.BNN((2,), (4,), train_size=len(x)) self.assertTrue(isinstance(subject.model, tf.keras.Model)) self.assertTrue(subject.model._compile_was_called) self.assertTrue(subject.model.built) - self.assertEqual(tuple(subject.model.input.shapes.as_list()), + self.assertEqual(tuple(subject.model.input.shape.as_list())[1:], subject.xshape) - self.assertEqual(tuple(subject.model.output.shapes.as_list()), - subject.yshape) + predict = subject.model.predict(x) + self.assertTrue(predict.shape == (3, 4)) + out = subject.model(x) + self.assertTrue(isinstance(out, tfp.distributions.Distribution)) # test keyword assignment - with unittest.mock.patch( - 'tensorflow.keras.Sequential.compile' - ) as mocked_compile: - subject = gandy.models.bnns.BNN((5,), (1,), - optimizer='rms_prop', - metrics=['MSE']) - mocked_compile.assert_called_with(optimizer='rms_prop', - metrics=['MSE']) + subject = gandy.models.bnns.BNN((2,), (4,), + train_size=len(x), + optimizer='RMSProp') + self.assertTrue(isinstance(subject.model.optimizer, + tf.keras.optimizers.RMSprop)) + subject = gandy.models.bnns.BNN((2,), (4,), + train_size=len(x), + optimizer=tf.keras.optimizers.RMSprop) + self.assertTrue(isinstance(subject.model.optimizer, + tf.keras.optimizers.RMSprop)) + opt = tf.keras.optimizers.RMSprop() + subject = gandy.models.bnns.BNN( + (2,), (4,), + train_size=len(x), + optimizer=opt + ) + self.assertTrue(subject.model.optimizer is opt) return def test__train(self): """We just want to call the host fit method""" Xs = 'Xs' Ys = 'Ys' - with unittest.mock.patch( - 'tensorflow.keras.Sequential.fit' - ) as mocked_fit: - subject = gandy.models.bnns.BNN((5,), (1,)) - subject._train(Xs, Ys, epochs=10) - mocked_fit.assert_called_with(Xs, Ys, epochs=10) - return + mocked_fit = unittest.mock.MagicMock(return_value='loss') + subject = gandy.models.bnns.BNN((5,), (1,), train_size=2) + subject.model.fit = mocked_fit + losses = subject._train(Xs, Ys, epochs=10) + mocked_fit.assert_called_with(Xs, Ys, epochs=10) + self.assertEqual(losses, 'loss') + return def test__predict(self): """Predict for a probabilistic BNN is just letting the tensors flow, make sure it is passed to input. """ - subject = gandy.models.bnns.BNN((5,), (1,)) - subject.model = unittest.mock.MagicMock() + subject = gandy.models.bnns.BNN((5,), (1,), train_size=2) + dists = unittest.mock.MagicMock() + subject._model = unittest.mock.MagicMock(return_value=dists) subject._predict('Xs') subject.model.assert_called_with('Xs') + dists.mean.assert_called() + dists.stddev.assert_called() return def test_save(self): """Save should just call keras save""" - with unittest.mock.patch( - 'tensorflow.keras.Sequential.save' - ) as mocked_save: - subject = gandy.models.bnns.BNN((5,), (1,)) - subject.save('filename') - mocked_save.assert_called_with('filename') + mocked_save = unittest.mock.MagicMock() + subject = gandy.models.bnns.BNN((5,), (1,), train_size=2) + subject.model.save = mocked_save + subject.save('filename') + mocked_save.assert_called_with('filename') return def test_load(self): """load needs to use keras load, but then also stick it into a gandy model with the correct shape """ - model_mocked = unittest.mock.MagicMock() - model_mocked.input.shape.to_list.return_value = [5, ] - model_mocked.output.shape.to_list.return_value = [3, ] with unittest.mock.patch( - 'tensorflow.keras.models.load', - return_value=model_mocked + 'tensorflow.keras.models.load_model' ) as mocked_load: subject = gandy.models.bnns.BNN.load('filename') - self.assertTrue(isinstance(subject), gandy.models.bnns.BNN) - self.assertEqual(subject.xhsape, (5,)) - self.assertEqual(subject.xhsape, (3,)) + self.assertTrue(isinstance(subject, gandy.models.bnns.BNN)) mocked_load.assert_called_with('filename') return From c5ca8fb82ca6258adc93dd5fd62161a0de51892d Mon Sep 17 00:00:00 2001 From: evankomp Date: Wed, 10 Mar 2021 13:21:44 -0800 Subject: [PATCH 65/99] env now contains tf probability for bnns --- environment.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/environment.yml b/environment.yml index 12142b9..d09ed89 100644 --- a/environment.yml +++ b/environment.yml @@ -7,8 +7,8 @@ dependencies: - numpy=1.19.2 - pandas=1.1.3 - pip=20.3.3 - - python=3.6.* - scikit-learn=0.23.2 - scipy=1.5.2 - - tensorflow=2.0.0 + - tensorflow - optuna=2.5.0 + - tensorflow_probability From 70dd35d6f966493b3fff4b230922694d20d3a2a3 Mon Sep 17 00:00:00 2001 From: EvanKomp Date: Wed, 10 Mar 2021 14:54:24 -0800 Subject: [PATCH 66/99] Better README documentation fill in checkboxes and demos as we create them --- README.md | 55 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index de6861c..a270dd4 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,14 @@ # GANdy -This repository estimates uncertainty using GANs and other machine learning models such as GPs and BNNs. +Automatically creating and comparing supervised machine learning models capable of returning uncertainty estimates in addition to predictions. + +__Current Functionality__: +- [ ] Instantialize, train, and use uncertainty models +- - [ ] Gaussian Processes +- - [ ] Bayesian Neural Networks +- - [ ] uncertainty GANs +- [ ] Judge the quality of produced uncertainties with uncertainty metrics +- [ ] Model optimization to uncertainty metrics +- [ ] Comparison of model structures ## Installation In console, execute the following command where package_path is the path to the folder containing this Readme (GANdy): @@ -7,14 +16,48 @@ In console, execute the following command where package_path is the > It can then be imported on the installed environment as gandy. +## Repo structure +``` +GANdy +----- +setup.py # package installation +environment.yml # development environment +examples/ +|-GPs_Showcase.ipynb # demo of gaussian processes as an uncertainty model +gandy/ +|-tests/ +|-models/ +| |-models.py # package parent model class +| |-bnns.py # bayesian neural nets as an uncertainty model +| |-dcgan.py # helper functions for GANs +| |-gans.py # GANs as an uncertainty model +| |-gps.py # gaussian processes as an uncertainty model +|-quality_est/ +| |-metrics.py # tools for evaluating returned uncertainties and predictions + +``` + +## Justification +For a supervised machine learning task, one generally obtains deterministic predictions of a target variable based on a learned relationship between that target and a set of features. Such models make predictions on new quantities idependant of known variability or lack of knowledge, and there is no idication of the quality of a prediction. For many tasks, where the target variable is sensative to small changes, it is important not only to have a prediction but also the uncertainty associated with the prediction, in order to inform prediction costs. + +Some models already exist that can approximate the uncertainty of a prediction, such as Gaussian Processes or Bayesian models, which have their own downsides including training cost. Recently (2020), it has been shown by Lee and Seok \[1\] that the relatively new architecture Generative Adversarial Networks (GAN) can formatted to produce distributions of a target conditions on features. Here, they invert the input and output of a traditional conditional GAN (cGAN) in order to make target predictions with uncertainty. + +It is desirable to have a tool in python that allows for the formulation of such uncertainty GANs, but also a comparison with other tools capable of predicting uncertainty. GANdy aims to incorporate these different tools and allow for automated optimization and comparison such that a model ideal for a task's cost to quality ratio can be identified. + +\[1\] M. Lee and J. Seok, “Estimation with Uncertainty via Conditional Generative Adversarial Networks.” ArXiv 2007.00334v1 + +## Examples +See for demonstrations on predicting uncertainties with the available tools. + ## For developers -To install the development environment conda env create --file devenv.yml. -If any new installed development dependancies, add them to the devenv.yml environment file by Manually adding the dependency, eg. +### Installation +To install the development environment conda env create --file environment.yml. +If any new installed development dependancies, add them to the environment.yml environment file by Manually adding the dependency, eg. > \- python=3.6.* -To update dev environment with new dependencies in the .yml file, conda env update --file devenv.yml +To update dev environment with new dependencies in the .yml file, conda env update --file environment.yml ./working/ is a workspace for notebooks/testing. It will be ignored by git by default, and will be removed upon release. To specifically "save" your files to git or to share work with other developers, use git add --force working. -## Testing -Tests located at gandy/tests +### Testing +Tests located at From 910ae74d2d3411b7c62f82e745b5b48655aa67a4 Mon Sep 17 00:00:00 2001 From: EvanKomp Date: Wed, 10 Mar 2021 14:58:28 -0800 Subject: [PATCH 67/99] Added build badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a270dd4..0a56fd1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# GANdy +# GANdy [![Build Status](https://travis-ci.org/GANdy-team/GANdy.svg?branch=main)](https://travis-ci.org/GANdy-team/GANdy) Automatically creating and comparing supervised machine learning models capable of returning uncertainty estimates in addition to predictions. __Current Functionality__: From c2c3acff3432abf5dc97ce938009a11f1e105557 Mon Sep 17 00:00:00 2001 From: evankomp Date: Wed, 10 Mar 2021 15:42:30 -0800 Subject: [PATCH 68/99] Searchable space written and passing --- gandy/optimization/hypersearch.py | 80 +++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 4 deletions(-) diff --git a/gandy/optimization/hypersearch.py b/gandy/optimization/hypersearch.py index 4e10804..657c0c3 100644 --- a/gandy/optimization/hypersearch.py +++ b/gandy/optimization/hypersearch.py @@ -38,10 +38,7 @@ # Typing Model = Type[gandy.models.models.UncertaintyModel] Array = Type[numpy.ndarray] -Trial = Type[optuna.trials.Trial] - -# class to specify optuna search space from python readable inputs - +Trial = Type[optuna.trial.Trial] class SearchableSpace: """Wrapper to convert user specified search space into Optuna readable @@ -67,6 +64,81 @@ def __init__(self, hypname, space): # pseudocode # . if statement format of space # set self.func, self.args, and self.hypname + self.name = hypname + + # categorical + if type(space) == list: + self.args = (space) + self.func = optuna.trial.Trial.suggest_categorical + # others + elif type(space) == tuple: + # check if we need to add a parameter to the end (len =2) + if len(space) == 2: + if all(type(i) == int for i in space): + space_ = list(space) + space_.append(1) + space_ = tuple(space_) + print( + 'Assuming uniform integer sampling for hyperparameter\ + {} with search space specified as Tuple[int] with len 2'.format(hypname) + ) + elif all(type(i) == float for i in space): + space_ = list(space) + space_.append('uniform') + space_ = tuple(space_) + print( + 'Assuming uniform continuous sampling for\ + hyperparameter {} with search space specified as Tuple[float] with\ + len 2'.format(hypname) + ) + else: + raise ValueError('hyperparameter space as tuple must have\ + the first two arguments be both float or integer') + + elif len(space) == 3: + space_ = space + else: + raise ValueError( + 'space as a tuple indicates (min, max, step/type) and\ + should have 2 or 3 contents, not {}'.format(len(space))) + + if type(space_[0]) != type(space_[1]): + raise ValueError('hyperparameter space as tuple must have\ + the first two arguments be both float or integer') + # integer choice + elif type(space_[0]) == int: + if type(space_[2]) != int: + raise ValueError('First two values in space are int,\ + indicating integer selection, but the third (step size) is not an int') + else: + pass + self.args = space_ + self.func = optuna.trial.Trial.suggest_int + elif type(space_[0]) == float: + if space_[2] == 'uniform': + self.args = space_[:2] + self.func = optuna.trial.Trial.suggest_uniform + elif space_[2] == 'loguniform': + self.args = space_[:2] + self.func = optuna.trial.Trial.suggest_loguniform + elif type(space_[2]) == float: + self.args = space_ + self.func = optuna.trial.Trial.suggest_discrete_uniform + else: + raise ValueError( + 'Unknown specification for float suggestion {}, should\ + be "uniform" or "loguniform" indicating the distribution, or a float,\ + indicating a discrete spep' + ) + + else: + raise ValueError('hyperparameter space as tuple must have\ + the first two arguments be both float or integer') + + else: + raise TypeError( + 'space must be a list or tuple, not {}'.format(type(space)) + ) return From 73023ea59fe2860372076323d646fc7b42d58f2c Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Wed, 10 Mar 2021 16:02:28 -0800 Subject: [PATCH 69/99] deleted unnecessry jupyter notebooks --- GANs_Showcase.ipynb | 76 ------------------------------------ examples/GANs_Showcase.ipynb | 69 -------------------------------- 2 files changed, 145 deletions(-) delete mode 100644 GANs_Showcase.ipynb delete mode 100644 examples/GANs_Showcase.ipynb diff --git a/GANs_Showcase.ipynb b/GANs_Showcase.ipynb deleted file mode 100644 index c2a089d..0000000 --- a/GANs_Showcase.ipynb +++ /dev/null @@ -1,76 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Demo of GANS" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt\n", - "import numpy\n", - "import sklearn.datasets\n", - "import sklearn.model_selection\n", - "import sklearn.preprocessing\n", - "\n", - "from GANdy import gandy" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "ename": "ModuleNotFoundError", - "evalue": "No module named 'gandy'", - "output_type": "error", - "traceback": [ - "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[1;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)", - "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[1;32mfrom\u001b[0m \u001b[0mGANdy\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mgandy\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mmodels\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mgans\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[1;32m~\\Desktop\\DSClass\\GANdy\\gandy\\models\\gans.py\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 11\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 12\u001b[0m \u001b[1;31m# gandy imports\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 13\u001b[1;33m \u001b[1;32mimport\u001b[0m \u001b[0mgandy\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mmodels\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mmodels\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 14\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mgandy\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mquality_est\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mmetrics\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 15\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n", - "\u001b[1;31mModuleNotFoundError\u001b[0m: No module named 'gandy'" - ] - } - ], - "source": [ - "from GANdy.gandy.models import gans" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/GANs_Showcase.ipynb b/examples/GANs_Showcase.ipynb deleted file mode 100644 index 9008d40..0000000 --- a/examples/GANs_Showcase.ipynb +++ /dev/null @@ -1,69 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Demo of GANs" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt\n", - "import numpy\n", - "import sklearn.datasets\n", - "import sklearn.model_selection\n", - "import sklearn.preprocessing\n", - "\n", - "# import gandy.models.gans" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "## loading the data\n", - "Xs, Ys = sklearn.datasets.load_boston(return_X_y=True)\n", - "Xsr, Xst, Ysr, Yst = sklearn.model_selection.train_test_split(Xs, Ys, train_size = 0.8)\n", - "## normalizing it\n", - "norm = sklearn.preprocessing.Normalizer()\n", - "Xsr = norm.fit_transform(Xsr)\n", - "Xst = norm.transform(Xst)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} From a9d595bab4f5c95d14e3b7a1fa528af25a1ce0cf Mon Sep 17 00:00:00 2001 From: evankomp Date: Wed, 10 Mar 2021 20:42:15 -0800 Subject: [PATCH 70/99] Subject objective written and passing, only opt routine left --- gandy/optimization/hypersearch.py | 201 +++++++- .../test_optimization/test_hypersearch.py | 465 +++++++++--------- 2 files changed, 412 insertions(+), 254 deletions(-) diff --git a/gandy/optimization/hypersearch.py b/gandy/optimization/hypersearch.py index 657c0c3..db5c63f 100644 --- a/gandy/optimization/hypersearch.py +++ b/gandy/optimization/hypersearch.py @@ -28,10 +28,12 @@ """ # imports -from typing import Tuple, Iterable, Type, List, Union +from typing import Tuple, Iterable, Type, List, Union, Callable -import optuna import numpy +import optuna.trial +import optuna +import sklearn.model_selection import gandy.models.models @@ -40,6 +42,7 @@ Array = Type[numpy.ndarray] Trial = Type[optuna.trial.Trial] + class SearchableSpace: """Wrapper to convert user specified search space into Optuna readable function. @@ -65,16 +68,16 @@ def __init__(self, hypname, space): # . if statement format of space # set self.func, self.args, and self.hypname self.name = hypname - + # categorical - if type(space) == list: - self.args = (space) + if isinstance(space, list): + self.args = (space,) self.func = optuna.trial.Trial.suggest_categorical # others - elif type(space) == tuple: + elif isinstance(space, tuple): # check if we need to add a parameter to the end (len =2) if len(space) == 2: - if all(type(i) == int for i in space): + if all(isinstance(i, int) for i in space): space_ = list(space) space_.append(1) space_ = tuple(space_) @@ -82,7 +85,7 @@ def __init__(self, hypname, space): 'Assuming uniform integer sampling for hyperparameter\ {} with search space specified as Tuple[int] with len 2'.format(hypname) ) - elif all(type(i) == float for i in space): + elif all(isinstance(i, float) for i in space): space_ = list(space) space_.append('uniform') space_ = tuple(space_) @@ -94,34 +97,34 @@ def __init__(self, hypname, space): else: raise ValueError('hyperparameter space as tuple must have\ the first two arguments be both float or integer') - + elif len(space) == 3: space_ = space else: raise ValueError( 'space as a tuple indicates (min, max, step/type) and\ should have 2 or 3 contents, not {}'.format(len(space))) - - if type(space_[0]) != type(space_[1]): + + if not isinstance(space_[0], type(space_[1])): raise ValueError('hyperparameter space as tuple must have\ the first two arguments be both float or integer') # integer choice - elif type(space_[0]) == int: - if type(space_[2]) != int: + elif isinstance(space_[0], int): + if not isinstance(space_[2], int): raise ValueError('First two values in space are int,\ indicating integer selection, but the third (step size) is not an int') else: pass self.args = space_ self.func = optuna.trial.Trial.suggest_int - elif type(space_[0]) == float: + elif isinstance(space_[0], float): if space_[2] == 'uniform': self.args = space_[:2] self.func = optuna.trial.Trial.suggest_uniform elif space_[2] == 'loguniform': self.args = space_[:2] self.func = optuna.trial.Trial.suggest_loguniform - elif type(space_[2]) == float: + elif isinstance(space_[2], float): self.args = space_ self.func = optuna.trial.Trial.suggest_discrete_uniform else: @@ -130,11 +133,11 @@ def __init__(self, hypname, space): be "uniform" or "loguniform" indicating the distribution, or a float,\ indicating a discrete spep' ) - + else: raise ValueError('hyperparameter space as tuple must have\ the first two arguments be both float or integer') - + else: raise TypeError( 'space must be a list or tuple, not {}'.format(type(space)) @@ -183,7 +186,7 @@ def __init__(self, param_space: list, sessions: Union[int, List[str]] = None, k: Union[int, tuple] = None, - val_data: Tuple[Array] = None, + val_data: Iterable[Array] = None, val_frac: float = None, **kwargs): # pseudocode @@ -192,6 +195,60 @@ def __init__(self, # . make sure only one of k, val_data, val_frac # . test input type # . set self attributes in proper form + for param in param_space: + if param.name in kwargs.keys(): + raise ValueError( + 'Whoa! A searchable parameter {} is also passed as a\ + keyword argument. A parameter cannot be both searched and stationary.\ + '.format(param.name) + ) + else: + pass + + self.kwargs = kwargs + self.param_space = param_space + self.subject = subject + + # store data + if len(Xs) != len(Ys): + raise ValueError('Data should be the same length') + self.Xs = Xs + self.Ys = Ys + + # check only one argument passed + passed = [i is not None for i in [k, val_data, val_frac]] + if numpy.sum(passed) > 1: + raise ValueError('Only one k, val_data, val_frac acceptable') + elif numpy.sum(passed) == 0: + val_frac = 0.2 + else: + pass + + if k is not None: + self.k = k + else: + self._k = None + if val_data is not None: + self.val_data = val_data + else: + self._val_data = None + if val_frac is not None: + self.val_frac = val_frac + else: + self._val_frac = None + + if isinstance(sessions, int): + self.sessions = range(sessions) + elif isinstance(sessions, list): + self.sessions = sessions + elif sessions is None: + self.sessions = range(1) + else: + raise TypeError( + 'sessions should be a list of names or an integer number,\ + not {}'.format(type(sessions)) + ) + return def _sample_params(self, trial: Trial) -> dict: @@ -210,10 +267,15 @@ def _sample_params(self, trial: Trial) -> dict: """ # pseudocode # . hyparams = dict loop self.param_space trial.method(args) - hyparams = None + hyparams = {} + for param in self.param_space: + print(param.func) + hyparams[param.name] = param.func(trial, param.name, *param.args) + print(hyparams) return hyparams def _execute_instance(self, + instance: Callable, hyparams: dict, train_data: Tuple[Array], val_data: Tuple[Array]) -> float: @@ -235,7 +297,8 @@ def _execute_instance(self, # . construct model with hyparms and self kwargs # . train model with hyparms and self kwargs # . score model with self kwargs - single_loss = None + instance.train(*train_data, **self.kwargs, **hyparams) + single_loss = instance.score(*val_data, **self.kwargs, **hyparams) return single_loss def __call__(self, trial: Trial) -> float: @@ -256,8 +319,63 @@ def __call__(self, trial: Trial) -> float: # . split data based on above # . for each session, execute instances # . check for prune - loss = None - return loss + hyparams = self._sample_params(trial) + + if self.k is not None: + # need k instances + instances = [ + self.subject(**self.kwargs, **hyparams) for i in self.k + ] + for session in self.sessions: + losses = [] + for fold, instance in enumerate(instances): + train_data = (self.Xs[self.k[fold][0]], + self.Ys[self.k[fold][0]]) + val_data = (self.Xs[self.k[fold][1]], + self.Ys[self.k[fold][1]]) + single_loss = self._execute_instance(instance, + hyparams, + train_data, + val_data) + losses.append(single_loss) + loss = numpy.mean(losses) + + trial.report(loss, session) + + if trial.should_prune(): + raise optuna.exceptions.TrialPruned() + return loss + + if self.val_data is not None: + instance = self.subject(**self.kwargs, **hyparams) + for session in self.sessions: + loss = self._execute_instance(instance, + hyparams, + (self.Xs, self.Ys), + self.val_data) + trial.report(loss, session) + + if trial.should_prune(): + raise optuna.exceptions.TrialPruned() + return loss + + if self.val_frac is not None: + instance = self.subject(**self.kwargs, **hyparams) + Xt, Xv, Yt, Yv = sklearn.model_selection.train_test_split( + self.Xs, self.Ys, test_size=self.val_frac + ) + train_data = (Xt, Yt) + val_data = (Xv, Yv) + for session in self.sessions: + loss = self._execute_instance(instance, + hyparams, + train_data, + val_data) + trial.report(loss, session) + + if trial.should_prune(): + raise optuna.exceptions.TrialPruned() + return loss @property def k(self): @@ -268,13 +386,28 @@ def k(self): def k(self, new_k): # if int convert to indexes # otherwise check proper form + if not isinstance(new_k, int): + for inds in new_k: + if len(inds) != 2: + raise ValueError( + 'k if not an integer of no. folds, should be an\ + iterable of fold idexes (train ind, test ind). {} does not have the correct\ + format of Iterable of len(2) iterables'.format(new_k) + ) + else: + kfold = sklearn.model_selection.KFold(new_k, shuffle=True) + new_k = kfold.split(self.Xs) + new_k = list(new_k) self._k = new_k + del self.val_data + del self.val_frac return @k.deleter def k(self): self._k = None # print message + print('Cannot have more than one k, val_data, val_frac. Deleting k') return @property @@ -285,13 +418,27 @@ def val_data(self): @val_data.setter def val_data(self, new_val_data): # check tuple of array + if len(new_val_data) != 2: + raise ValueError( + 'val_data should be an iterable of len 2, x and y data arrays' + ) + else: + for d in new_val_data: + if not hasattr(d, "__len__"): + raise ValueError('One object passed in val_data not iter') + if len(new_val_data[0]) != len(new_val_data[1]): + raise ValueError('val_data x, y different lengths') self._val_data = new_val_data + del self.k + del self.val_frac return @val_data.deleter def val_data(self): self._val_data = None # print message + print('Cannot have more than one k, val_data, val_frac. \ + Deleting val_data') return @property @@ -303,12 +450,22 @@ def val_frac(self): @val_frac.setter def val_frac(self, new_val_frac): # check float + try: + new_val_frac = float(new_val_frac) + except BaseException: + raise TypeError('Cannot convert {} to float'.format(new_val_frac)) + if not 0.0 < new_val_frac < 1.0: + raise ValueError('val_frac must be between 0 and 1') self._val_frac = new_val_frac + del self.k + del self.val_data return @val_frac.deleter def val_frac(self): self._val_frac = None + print('Cannot have more than one k, val_data, val_frac.\ + Deleting val_frac') return diff --git a/gandy/tests/test_optimization/test_hypersearch.py b/gandy/tests/test_optimization/test_hypersearch.py index 3d12cc8..177d57f 100644 --- a/gandy/tests/test_optimization/test_hypersearch.py +++ b/gandy/tests/test_optimization/test_hypersearch.py @@ -7,7 +7,7 @@ import numpy import optuna.trial -import gandy.optimization.optimization as opt +import gandy.optimization.hypersearch as opt import gandy.models.models @@ -52,12 +52,12 @@ def test_class(self): self.assertEqual(subject.args, (2, 10, 3)) self.assertTrue(subject.func is optuna.trial.Trial.suggest_int) - # catagorical + # categorical space = ['a', 'b', 'c'] subject = opt.SearchableSpace(NAME, space) self.assertEqual(subject.name, NAME) - self.assertEqual(subject.args, space) - self.assertTrue(subject.func is optuna.trial.Trial.suggest_catagorical) + self.assertEqual(subject.args, (space,)) + self.assertTrue(subject.func is optuna.trial.Trial.suggest_categorical) return @@ -66,8 +66,8 @@ class TestSubjectObjective(unittest.TestCase): params = [opt.SearchableSpace('hyp1', (1, 10)), opt.SearchableSpace('hyp2', ['a', 'b'])] inputs = {'subject': gandy.models.models.UncertaintyModel, - 'Xs': numpy.array(1), - 'Ys': numpy.array(1), + 'Xs': numpy.array([1, 2, 3]), + 'Ys': numpy.array([1, 2, 3]), 'param_space': params, } @@ -75,26 +75,26 @@ def test___init__(self): """Ensure only one validation option and proper saving of parameters""" # expected success, no sessions specified, no val subject = opt.SubjectObjective(**self.inputs) - self.assertTrue(subject.sessions is range(1)) + self.assertEqual(subject.sessions, range(1)) self.assertTrue(subject.param_space is self.params) - for att in ['k', 'val_data', 'val_frac']: + for att in ['k', 'val_data']: self.assertEqual(getattr(subject, att), None) # specify sessions subject = opt.SubjectObjective(**self.inputs, sessions=5) - self.assertTrue(subject.sessions is range(5)) + self.assertEqual(subject.sessions, range(5)) subject = opt.SubjectObjective(**self.inputs, sessions=['a', 'b']) self.assertEqual(subject.sessions, ['a', 'b']) # test proper validation handling # k - subject = opt.SubjectObjective(**self.inputs, k=5) + subject = opt.SubjectObjective(**self.inputs, k=2) self.assertTrue(subject.k is not None) subject = opt.SubjectObjective(**self.inputs, k=[(numpy.array(1), numpy.array(1))]) self.assertTrue(subject.k is not None) # val_data subject = opt.SubjectObjective(**self.inputs, - val_data=(numpy.array(1), - numpy.array(1))) + val_data=(numpy.array([1]), + numpy.array([1]))) self.assertTrue(subject.val_data is not None) # val_frac subject = opt.SubjectObjective(**self.inputs, @@ -106,7 +106,7 @@ def test___init__(self): ) for fc in failure_cases: kws = dict(zip(fc, ['keywordvalue1', 'keywordvalue2'])) - with self.assertRaises('ValueError'): + with self.assertRaises(ValueError): subject = opt.SubjectObjective(**self.inputs, **kws) # ensure proper saving of keyword arguments @@ -121,13 +121,15 @@ def test___init__(self): @unittest.mock.patch('sklearn.model_selection.KFold') def test_property_k(self, mocked_kfold): """ensure proper handling of kfolds setting""" - mocked_kfold.split.return_value = ('train', 'test') + mocked_kfold_inst = unittest.mock.MagicMock( + return_value=('train', 'test') + ) + mocked_kfold.return_value = mocked_kfold_inst subject = opt.SubjectObjective(**self.inputs) # int or iterable of tuple works subject.k = 5 mocked_kfold.assert_called() - mocked_kfold.split.called_with(subject.Xs) - self.assertTrue(mocked_kfold.split.call_count == 5) + mocked_kfold_inst.split.assert_called_with(subject.Xs) self.assertTrue(isinstance(subject.k, list)) for f in subject.k: self.assertTrue(isinstance(f, tuple) and len(f) == 2) @@ -135,18 +137,17 @@ def test_property_k(self, mocked_kfold): subject.k = test_folds self.assertEqual(test_folds, subject.k) # failure case not either - with self.assertRaises(TypeError): + with self.assertRaises(ValueError): subject.k = 'str' return def test_property_val_data(self): """ability to check val data before saving""" val_data_failures = [(1, 3), - [numpy.array(1), numpy.array(1)], - (numpy.array([1, 2]), numpy.array(1))] + (numpy.array([1, 2]), numpy.array([1]))] subject = opt.SubjectObjective(**self.inputs) for val_data in val_data_failures: - with self.assertRaises(TypeError): + with self.assertRaises(ValueError): subject.val_data = val_data # success - tuple of arrays of the same length @@ -174,46 +175,36 @@ def test__sample_params(self, mocked_Trial): subject = opt.SubjectObjective(**self.inputs) # run the sample and test correct calls params = subject._sample_params(trial) - mocked_Trial.suggest_int.assert_called_with( - trial, 'hyp1', 1, 10, 1 - ) - mocked_Trial.suggest_catagorical.assert_called_with( - trial, 'hyp2', ['a', 'b'] - ) + self.assertEqual(trial._suggest.call_count, 2) self.assertTrue(all(hyp in params.keys() for hyp in ['hyp1', 'hyp2'])) return - @unittest.mock.patch('import gandy.models.models.UncertaintyModel') - def test__execute_instance(self, mocked_UM): + def test__execute_instance(self): """does the method instantialize and call the correct model methods""" subject = opt.SubjectObjective(**self.inputs, xshape=(5,), keyword=5) + mocked_inst = unittest.mock.MagicMock() + mocked_inst.score.return_value = 'score' hyparams = {'hp1': 1, 'hp2': 2} train_data = ('Xst', 'Yst') val_data = ('Xsv', 'Ysv') - mocked_UM_in = unittest.mock.MagicMock() - mocked_UM.return_value = mocked_UM_in - mocked_UM_in.score.return_value = 'score' # execute the instance - score = subject._execute_instance(hyparams, train_data, val_data) - mocked_UM.assert_called_with(xshape=(5,), - keyword=5, - hp1=1, - hp2=2) - mocked_UM_in.train.assert_called_with('Xst', 'Yst', - xshape=(5,), - keyword=5, - hp1=1, - hp2=2) - mocked_UM_in.score.assert_called_with('Xsv', 'Ysv', - xshape=(5,), - keyword=5, - hp1=1, - hp2=2) + score = subject._execute_instance(mocked_inst, hyparams, + train_data, val_data) + + mocked_inst.train.assert_called_with('Xst', 'Yst', + xshape=(5,), + keyword=5, + hp1=1, + hp2=2) + mocked_inst.score.assert_called_with('Xsv', 'Ysv', + xshape=(5,), + keyword=5, + hp1=1, + hp2=2) self.assertTrue(score == 'score') return - @unittest.mock.patch('optuna.trial.Trial') - def test___call__(self, mocked_Trial): + def test___call__(self): """ability to identify different validation options and call the correct methods""" subject = opt.SubjectObjective(**self.inputs) @@ -222,10 +213,37 @@ def test___call__(self, mocked_Trial): ) subject._sample_params = mocked_sample mocked_execute = unittest.mock.MagicMock( - return_value='loss' + return_value=5 ) subject._execute_instance = mocked_execute - trial = mocked_Trial() + trial = unittest.mock.MagicMock() + mocked_inst = unittest.mock.MagicMock() + mocked_UM = unittest.mock.MagicMock(return_value=mocked_inst) + subject.subject = mocked_UM + + # None specified + with unittest.mock.patch( + 'sklearn.model_selection.train_test_split', + return_value=('Xt', 'Xv', 'Yt', 'Yv') + ) as mocked_tts: + subject.__call__(trial) + mocked_sample.assert_called_with(trial) + mocked_tts.assert_called() + mocked_execute.assert_called_with( + mocked_inst, + {'hp1': 1, 'hp2': 2}, + ('Xt', 'Yt'), + ('Xv', 'Yv') + ) + self.assertTrue(mocked_execute.call_count == 1) + + trial.should_prune.assert_called() + + # reset calls + mocked_execute.reset_mock() + mocked_sample.reset_mock() + trial.reset_mock() + subject._val_frac = None # start with k specifed - folds are arrays of indexes data len = 1 first, second = (numpy.array(0), numpy.array(0)), \ @@ -234,6 +252,7 @@ def test___call__(self, mocked_Trial): subject.__call__(trial) mocked_sample.assert_called_with(trial) mocked_execute.assert_called_with( + mocked_inst, {'hp1': 1, 'hp2': 2}, (subject.Xs[second[0]], subject.Ys[second[0]]), (subject.Xs[second[0]], subject.Ys[second[0]]) @@ -247,10 +266,11 @@ def test___call__(self, mocked_Trial): # val_data specifed val_data = ('Xsv', 'Ysv') - subject._val_data = val_data + subject.val_data = val_data subject.__call__(trial) mocked_sample.assert_called_with(trial) mocked_execute.assert_called_with( + mocked_inst, {'hp1': 1, 'hp2': 2}, (subject.Xs, subject.Ys), val_data @@ -263,8 +283,8 @@ def test___call__(self, mocked_Trial): trial.reset_mock() # val frac specified - val_frac = 5.0 - subject._val_frac = val_frac + val_frac = .5 + subject.val_frac = val_frac with unittest.mock.patch( 'sklearn.model_selection.train_test_split', @@ -275,192 +295,173 @@ def test___call__(self, mocked_Trial): mocked_tts.assert_called_with(subject.Xs, subject.Ys, test_size=val_frac) mocked_execute.assert_called_with( + mocked_inst, {'hp1': 1, 'hp2': 2}, ('Xt', 'Yt'), ('Xv', 'Yv') ) self.assertTrue(mocked_execute.call_count == 1) trial.should_prune.assert_called() - # reset calls - mocked_execute.reset_mock() - mocked_sample.reset_mock() - trial.reset_mock() - subject._val_frac = None - - # None specified - with unittest.mock.patch( - 'sklearn.model_selection.train_test_split', - return_value=('Xt', 'Xv', 'Yt', 'Yv') - ) as mocked_tts: - subject.__call__(trial) - mocked_sample.assert_called_with(trial) - mocked_tts.assert_called_with(subject.Xs, subject.Ys, - test_size=0.8) - mocked_execute.assert_called_with( - {'hp1': 1, 'hp2': 2}, - ('Xt', 'Yt'), - ('Xv', 'Yv') - ) - self.assertTrue(mocked_execute.call_count == 1) - trial.should_prune.assert_called() - return - - -class TestOptRoutine(unittest.TestCase): - """User interface class""" - def test___init__(self): - """proper saving of keyword arguments and data saving""" - # failure case not correct model type - with self.assertRaises(TypeError): - subject = opt.OptRoutine(subject=opt.SearchableSpace, - Xs=numpy.array([1, 2, 3]), - Ys=numpy.array([1, 2, 3]), - search_space={'hyp1': (1, 10), - 'hyp2': ['a', 'b']}, - keyword=5) - # failure case data not iterable - with self.assertRaises(TypeError): - subject = opt.OptRoutine(subject=gandy.models.models. - UncertaintyModel, - Xs='str', - Ys=numpy.array([1, 2, 3]), - search_space={'hyp1': (1, 10), - 'hyp2': ['a', 'b']}, - keyword=5) - with self.assertRaises(TypeError): - subject = opt.OptRoutine(subject=gandy.models.models. - UncertaintyModel, - Xs=numpy.array([1, 2, 3]), - Ys='str', - search_space={'hyp1': (1, 10), - 'hyp2': ['a', 'b']}, - keyword=5) - # expected success - subject = opt.OptRoutine(subject=gandy.models.models. - UncertaintyModel, - Xs=numpy.array([1, 2, 3]), - Ys=numpy.array([1, 2, 3]), - search_space={'hyp1': (1, 10), - 'hyp2': ['a', 'b']}, - keyword=5) - self.assertTrue(subject.Xs is not None) - self.assertTrue(subject.Ys is not None) - self.assertTrue(self.subject == gandy.models.models.UncertaintyModel) - self.assertEqual(subject.search_space, {'hyp1': (1, 10), - 'hyp2': ['a', 'b']}) - self.assertTrue('keyword' in subject.all_kwargs.keys()) return - @unittest.mock.patch('gandy.optimization.hypersearch.SearchableSpace') - def test__set_param_space(self, mocked_SS): - """proper parsing of dictionary into SearchableSpace objects""" - mocked_SS.side_effect = ['ss1', 'ss2'] - subject = opt.OptRoutine(subject=gandy.models.models. - UncertaintyModel, - Xs=numpy.array([1, 2, 3]), - Ys=numpy.array([1, 2, 3]), - search_space={'hyp1': (1, 10), - 'hyp2': ['a', 'b']}, - keyword=5) - subject._set_param_space() - mocked_SS.assert_called_with('hyp2', ['a', 'b']) - self.assertEqual(mocked_SS.call_count, 2) - return - - @unittest.mock.patch('gandy.optimization.hypersearch.SubjectObjective') - def test__set_objective(self, mocked_objective): - """ensure proper calling of SubjectObjective class""" - mocked_objective.return_value = 'objective' - subject = opt.OptRoutine(subject=gandy.models.models. - UncertaintyModel, - Xs=numpy.array([1, 2, 3]), - Ys=numpy.array([1, 2, 3]), - search_space={'hyp1': (1, 10), - 'hyp2': ['a', 'b']}, - keyword=5) - mocked__set_param = unittest.mock.MagicMock() - subject._set_param_space = mocked__set_param - # set the objective - subject._set_objective() - mocked_objective.assert_called_with(subject.subject, - subject.Xs, - subject.Ys, - **subject.all_kwargs) - self.assertEqual(subject.objective, 'objective') - mocked__set_param.assert_called() - return - - @unittest.mock.patch('optuna.create_study', return_value='study') - def test__set_study(self, mocked_cstudy): - """Can a study be correctly called and stored""" - subject = opt.OptRoutine(subject=gandy.models.models. - UncertaintyModel, - Xs=numpy.array([1, 2, 3]), - Ys=numpy.array([1, 2, 3]), - search_space={'hyp1': (1, 10), - 'hyp2': ['a', 'b']}, - keyword=5) - subject._set_study() - self.assertTrue(subject.study == 'study') - mocked_cstudy.assert_called_with(**subject.all_kwargs) - return - - def test_optimize(self): - """acceptance of kwargs and nested calls""" - subject = opt.OptRoutine(subject=gandy.models.models. - UncertaintyModel, - Xs=numpy.array([1, 2, 3]), - Ys=numpy.array([1, 2, 3]), - keyword=5) - - # failure mode no seach space specified - with self.assertRaises(AttributeError): - subject.optimize() - - # set up mocked objects - mocked_set_obj = unittest.mock.MagicMock() - mocked_set_study = unittest.mock.MagicMock() - mocked_study = unittest.mock.MagicMock() - subject._set_objective = mocked_set_obj - subject._set_study = mocked_set_study - subject.study = mocked_study - - # success case, set search space and pass new kwargs - best_score = subject.optimize(search_space={'hyp1': (1, 10), - 'hyp2': ['a', 'b']}, - keyword2=10) - mocked_set_obj.assert_called() - mocked_set_study.assert_called() - mocked_study.assert_called_with( - subject.objective, **subject.all_kwargs) - self.assertTrue(best_score is mocked_study.best_trial.value) - self.assertTrue(subject.best_params is mocked_study.best_trial.params) - self.assertTrue('keyword2' in subject.all_kwargs.keys()) - return - @unittest.mock.patch('gandy.models.models.UncertaintyModel') - def test_train_best(self, mocked_UM): - """proper access of best params and training of a new instance""" - mocked_UMin = unittest.mock.MagicMock() - mocked_UM.return_value = mocked_UMin - subject = opt.OptRoutine(subject=gandy.models.models. - UncertaintyModel, - Xs=numpy.array([1, 2, 3]), - Ys=numpy.array([1, 2, 3]), - search_space={'hyp1': (1, 10), - 'hyp2': ['a', 'b']}, - keyword=5) - # failure no best params - with self.assertRaises(AttributeError): - subject.train_best() - # set and run - subject.best_params = {'a': 10} - model = subject.train_best(keyword2=10) - mocked_UM.assert_called_with(**subject.best_params, - **subject.all_kwargs) - mocked_UMin.fit.assert_called_with(**subject.best_params, - **subject.all_kwargs) - self.assertTrue(model is mocked_UMin) - self.asserTrue('keyword2' in subject.all_kwargs.keys()) - return +# class TestOptRoutine(unittest.TestCase): +# """User interface class""" + +# def test___init__(self): +# """proper saving of keyword arguments and data saving""" +# # failure case not correct model type +# with self.assertRaises(TypeError): +# subject = opt.OptRoutine(subject=opt.SearchableSpace, +# Xs=numpy.array([1, 2, 3]), +# Ys=numpy.array([1, 2, 3]), +# search_space={'hyp1': (1, 10), +# 'hyp2': ['a', 'b']}, +# keyword=5) +# # failure case data not iterable +# with self.assertRaises(TypeError): +# subject = opt.OptRoutine(subject=gandy.models.models. +# UncertaintyModel, +# Xs='str', +# Ys=numpy.array([1, 2, 3]), +# search_space={'hyp1': (1, 10), +# 'hyp2': ['a', 'b']}, +# keyword=5) +# with self.assertRaises(TypeError): +# subject = opt.OptRoutine(subject=gandy.models.models. +# UncertaintyModel, +# Xs=numpy.array([1, 2, 3]), +# Ys='str', +# search_space={'hyp1': (1, 10), +# 'hyp2': ['a', 'b']}, +# keyword=5) +# # expected success +# subject = opt.OptRoutine(subject=gandy.models.models. +# UncertaintyModel, +# Xs=numpy.array([1, 2, 3]), +# Ys=numpy.array([1, 2, 3]), +# search_space={'hyp1': (1, 10), +# 'hyp2': ['a', 'b']}, +# keyword=5) +# self.assertTrue(subject.Xs is not None) +# self.assertTrue(subject.Ys is not None) +# self.assertTrue(self.subject == gandy.models.models.UncertaintyModel) +# self.assertEqual(subject.search_space, {'hyp1': (1, 10), +# 'hyp2': ['a', 'b']}) +# self.assertTrue('keyword' in subject.all_kwargs.keys()) +# return + +# @unittest.mock.patch('gandy.optimization.hypersearch.SearchableSpace') +# def test__set_param_space(self, mocked_SS): +# """proper parsing of dictionary into SearchableSpace objects""" +# mocked_SS.side_effect = ['ss1', 'ss2'] +# subject = opt.OptRoutine(subject=gandy.models.models. +# UncertaintyModel, +# Xs=numpy.array([1, 2, 3]), +# Ys=numpy.array([1, 2, 3]), +# search_space={'hyp1': (1, 10), +# 'hyp2': ['a', 'b']}, +# keyword=5) +# subject._set_param_space() +# mocked_SS.assert_called_with('hyp2', ['a', 'b']) +# self.assertEqual(mocked_SS.call_count, 2) +# return + +# @unittest.mock.patch('gandy.optimization.hypersearch.SubjectObjective') +# def test__set_objective(self, mocked_objective): +# """ensure proper calling of SubjectObjective class""" +# mocked_objective.return_value = 'objective' +# subject = opt.OptRoutine(subject=gandy.models.models. +# UncertaintyModel, +# Xs=numpy.array([1, 2, 3]), +# Ys=numpy.array([1, 2, 3]), +# search_space={'hyp1': (1, 10), +# 'hyp2': ['a', 'b']}, +# keyword=5) +# mocked__set_param = unittest.mock.MagicMock() +# subject._set_param_space = mocked__set_param +# # set the objective +# subject._set_objective() +# mocked_objective.assert_called_with(subject.subject, +# subject.Xs, +# subject.Ys, +# **subject.all_kwargs) +# self.assertEqual(subject.objective, 'objective') +# mocked__set_param.assert_called() +# return + +# @unittest.mock.patch('optuna.create_study', return_value='study') +# def test__set_study(self, mocked_cstudy): +# """Can a study be correctly called and stored""" +# subject = opt.OptRoutine(subject=gandy.models.models. +# UncertaintyModel, +# Xs=numpy.array([1, 2, 3]), +# Ys=numpy.array([1, 2, 3]), +# search_space={'hyp1': (1, 10), +# 'hyp2': ['a', 'b']}, +# keyword=5) +# subject._set_study() +# self.assertTrue(subject.study == 'study') +# mocked_cstudy.assert_called_with(**subject.all_kwargs) +# return + +# def test_optimize(self): +# """acceptance of kwargs and nested calls""" +# subject = opt.OptRoutine(subject=gandy.models.models. +# UncertaintyModel, +# Xs=numpy.array([1, 2, 3]), +# Ys=numpy.array([1, 2, 3]), +# keyword=5) + +# # failure mode no seach space specified +# with self.assertRaises(AttributeError): +# subject.optimize() + +# # set up mocked objects +# mocked_set_obj = unittest.mock.MagicMock() +# mocked_set_study = unittest.mock.MagicMock() +# mocked_study = unittest.mock.MagicMock() +# subject._set_objective = mocked_set_obj +# subject._set_study = mocked_set_study +# subject.study = mocked_study + +# # success case, set search space and pass new kwargs +# best_score = subject.optimize(search_space={'hyp1': (1, 10), +# 'hyp2': ['a', 'b']}, +# keyword2=10) +# mocked_set_obj.assert_called() +# mocked_set_study.assert_called() +# mocked_study.assert_called_with( +# subject.objective, **subject.all_kwargs) +# self.assertTrue(best_score is mocked_study.best_trial.value) +# self.assertTrue(subject.best_params is mocked_study.\ +# best_trial.params) +# self.assertTrue('keyword2' in subject.all_kwargs.keys()) +# return + +# @unittest.mock.patch('gandy.models.models.UncertaintyModel') +# def test_train_best(self, mocked_UM): +# """proper access of best params and training of a new instance""" +# mocked_UMin = unittest.mock.MagicMock() +# mocked_UM.return_value = mocked_UMin +# subject = opt.OptRoutine(subject=gandy.models.models. +# UncertaintyModel, +# Xs=numpy.array([1, 2, 3]), +# Ys=numpy.array([1, 2, 3]), +# search_space={'hyp1': (1, 10), +# 'hyp2': ['a', 'b']}, +# keyword=5) +# # failure no best params +# with self.assertRaises(AttributeError): +# subject.train_best() +# # set and run +# subject.best_params = {'a': 10} +# model = subject.train_best(keyword2=10) +# mocked_UM.assert_called_with(**subject.best_params, +# **subject.all_kwargs) +# mocked_UMin.fit.assert_called_with(**subject.best_params, +# **subject.all_kwargs) +# self.assertTrue(model is mocked_UMin) +# self.asserTrue('keyword2' in subject.all_kwargs.keys()) +# return From 04b2b9262efbc1e6addf70e68755731aba551a45 Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Wed, 10 Mar 2021 23:22:01 -0800 Subject: [PATCH 71/99] Debugging the code --- gandy/models/dcgan.py | 24 ++++++------ gandy/models/gans.py | 90 ++++++++++++++++++++----------------------- 2 files changed, 55 insertions(+), 59 deletions(-) diff --git a/gandy/models/dcgan.py b/gandy/models/dcgan.py index dba17cc..d0a20a4 100644 --- a/gandy/models/dcgan.py +++ b/gandy/models/dcgan.py @@ -46,8 +46,7 @@ class DCGAN(deepchem.models.GAN): """ def __init__(self, xshape, yshape, noise_shape, n_classes, **kwargs): - """Deepchem init function + class atributes.""" - super(DCGAN, self).__init__(**kwargs) + """Override deepchem init function.""" # These should be set by the gandy model when _build is called. self.xshape = xshape @@ -96,6 +95,9 @@ def __init__(self, xshape, yshape, noise_shape, n_classes, **kwargs): warnings.warn(f"Incorrect key {key}.\ Must start with generator_ or discriminator_") + # Deepchem init function + class atributes. + super(DCGAN, self).__init__(**kwargs) + def create_generator(self): """ Create the generator as a keras model. @@ -260,7 +262,15 @@ def get_data_input_shapes(self) -> Tuple[int]: This should be set by the gandy model when an build is called. """ - return self.xshape + return [self.xshape] + + def get_conditional_input_shapes(self) -> Array: + """ + Return the shape of the conditional input. + + This should be set by the gandy model when an build is called. + """ + return [(self.n_classes,)] class CondDCGAN(DCGAN): @@ -274,14 +284,6 @@ class CondDCGAN(DCGAN): to as "conditional inputs". """ - def get_conditional_input_shapes(self) -> Array: - """ - Return the shape of the conditional input. - - This should be set by the gandy model when an build is called. - """ - return [(self.n_classes,)] - def create_generator(self): """ Create the generator as a keras model. diff --git a/gandy/models/gans.py b/gandy/models/gans.py index f0ee7e3..4ab9c8e 100644 --- a/gandy/models/gans.py +++ b/gandy/models/gans.py @@ -71,31 +71,37 @@ def _build(self, *args, **kwargs): # determine whether to use gan or conditional gan if n_classes is not None: # if number of classes is specified, assumes conditional GAN - conditional = True + self.conditional = True # Should this be flagged somewhere?... - if self.yshape[0] > 1: + if self.yshape[0] == n_classes: # Ys are already one hot encoded - n_classes = kwargs.get('n_classes', self.yshape[0]) + self.one_hot = False else: # Ys are NOT one hot encoded # Or this is regression, which would be == 1 - n_classes = kwargs.get('n_classes', self.yshape[0]) + if n_classes == 1: + # this is regression! + self.one_hot = False + else: + # Ys are NOT one hot encoded, so we must convert them later + self.one_hot = True else: # if no n_classes specified, assumed to be regression # and no need for conditional inputs - conditional = False + self.conditional = False + n_classes = kwargs.get('n_classes', self.yshape[0]) # get other kwargs as hyperparameters hyperparams = {key: kwargs[key] for key in kwargs.keys() - {'n_classes', 'noise_shape'}} # instantiating the model as the deepchem gan - if conditional: + if self.conditional: model = dcgan.CondDCGAN(self.xshape, self.yshape, noise_shape, - n_classes, hyperparams) + n_classes, **hyperparams) else: model = dcgan.DCGAN(self.xshape, self.yshape, noise_shape, - n_classes, hyperparams) + n_classes, **hyperparams) return model def generate_data(self, @@ -125,14 +131,14 @@ def generate_data(self, # sample with replacement X, Y pairs of size batch_size n = len(Xs) indices = np.random.randint(0, high=n, size=(batch_size,)) - classes = Xs[indices] - points = Ys[indices] + points = Xs[indices] + classes = Ys[indices] return classes, points def iterbatches(self, Xs: Array, Ys: Array, - **kwargs): + batches: int): """ Function that creates batches of generated data. @@ -144,39 +150,28 @@ def iterbatches(self, Xs/Ys - training examples/targets type == ndarray - **kwargs - Specify training hyperparameters - batches - number of batches to train on - type == int - batch_size - number of data points in a batch - type == int + batches - number of batches to train on + type == int Yields: batched_data - data split into batches type == dict """ - # get training hyperparamters from kwargs - batches = kwargs.get('batches', 50) - batch_size = kwargs.get('batch_size', 32) - # training loop for i in range(batches): - classes, points = self.generate_data(Xs, Ys, batch_size) - if len(Ys.shape) == 2: - # Ys already one hot encoded - pass - else: - # must one hot encode Ys + classes, points = self.generate_data(Xs, Ys, self.model.batch_size) + if self.one_hot: classes = deepchem.metrics.to_one_hot(classes, self.model.n_classes) - batched_data = {self.data_inputs[0]: points, - self.conditional_inputs[0]: classes} + batched_data = {self._model.data_inputs[0]: points, + self._model.conditional_inputs[0]: classes} yield batched_data # overridden method from UncertaintyModel class def _train(self, Xs: Array, Ys: Array, - *args, # use args thoughtfully? + batches: int = 50, metric: Callable = None, **kwargs) -> Any: """ @@ -195,20 +190,21 @@ def _train(self, """ # train GAN on data # self.model = deepchem GAN instance - self._model.fit_gan(self.iterbatches(Xs, Ys, **kwargs)) + self._model.fit_gan(self.iterbatches(Xs, Ys, batches)) # The deepchem gan is a Keras model whose # outputs are [gen_loss, disrcim_loss]. # Thus the final losses for the generator # and discriminator are self.model.outputs # This is a list of 2 KerasTensors so must evaluate it. - losses = self.model.outputs + # losses = self._model.outputs + losses = None # compute metric return losses # overridden method from UncertaintyModel class def _predict(self, Xs: Array, - *args, + Ys: Array = None, **kwargs): """ Predict on Xs. @@ -218,6 +214,10 @@ def _predict(self, type == ndarray **kwargs - keyword arguments for predicting + num_predictions - number of predictions to make + to sample uncertainties + type == int + deafult == 100 Returns: predictions - array of predictions of targets with the same length @@ -232,30 +232,24 @@ def _predict(self, num_predictions = kwargs.get('num_predictions', 100) predictions = [] if self.conditional: - Ys = kwargs.get('Ys', None) assert Ys is not None, "This is a cGAN.\ Must specify Ys (Ys=) to call predict." - if len(Ys.shape) == 2: - # assumes data is in bacthed form - # Ys already one hot encoded - one_hot_Ys = Ys - else: + if self.one_hot: # must one hot encode Ys - one_hot_Ys = deepchem.metrics.to_one_hot(Ys, - self.model.n_classes) - for i in range(num_predictions): - # generate data with conditional inputs - generated_points = self._model.predict_gan_generator( - conditional_inputs=[one_hot_Ys]) - predictions.append(generated_points) + Ys = deepchem.metrics.to_one_hot(Ys, self.model.n_classes) + for i in range(num_predictions): + # generate data with conditional inputs + generated_points = self._model.predict_gan_generator( + conditional_inputs=[Ys]) + predictions.append(generated_points) else: for i in range(num_predictions): generated_points = self._model.predict_gan_generator() predictions.append(generated_points) # the above code generates points, but we need uncertainties as well - predictions = np.average(predictions, axis=1) - uncertainties = np.std(predictions, axis=1) - return predictions, uncertainties + preds = np.average(predictions, axis=0) + uncertainties = np.std(predictions, axis=0) + return preds, uncertainties def save(self, filename: str, **kwargs): """ From 32c63ae84e5e60279f86bc1dd42a8ffde0ec3db9 Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Thu, 11 Mar 2021 00:03:09 -0800 Subject: [PATCH 72/99] First working GAN demo :) --- examples/gan_demo.ipynb | 437 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 437 insertions(+) create mode 100644 examples/gan_demo.ipynb diff --git a/examples/gan_demo.ipynb b/examples/gan_demo.ipynb new file mode 100644 index 0000000..72bbe8f --- /dev/null +++ b/examples/gan_demo.ipynb @@ -0,0 +1,437 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#

Demo of GANs

" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:This caffe2 python run does not have GPU support. Will run in CPU only mode.\n" + ] + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import sklearn.datasets\n", + "import sklearn.model_selection\n", + "import sklearn.preprocessing\n", + "import deepchem\n", + "\n", + "from gandy.models import gans" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'2.5.0.dev'" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "deepchem.__version__" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#

A regression task, using the boston dataset

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##

Get Data

" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "# load data\n", + "Xs, Ys = sklearn.datasets.load_boston(return_X_y=True)\n", + "Xs_train, Xs_test, Ys_train, Ys_test = sklearn.model_selection.train_test_split(Xs, Ys, train_size = 0.8)\n", + "\n", + "# normalize\n", + "x_norm = sklearn.preprocessing.Normalizer()\n", + "Xs_train = x_norm.fit_transform(Xs_train)\n", + "Xs_test = x_norm.transform(Xs_test)\n", + "\n", + "# scale the ys\n", + "SCALING_FACTOR = np.max(Ys_train)\n", + "Ys_train = Ys_train/SCALING_FACTOR\n", + "Ys_test = Ys_test/SCALING_FACTOR" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(13,)\n" + ] + } + ], + "source": [ + "xshape = (Xs_train.shape[1],) # remove zero dimension\n", + "print(xshape)\n", + "yshape = (1,) # regression" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##

Initialize Model

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "###

Hyperparam options!

\n", + "\n", + "### Below are the defaults for\n", + "\n", + "#### GAN stuff:\n", + "- ```n_classes=yshape[0]```\n", + "- ```noise_shape=(10,)```\n", + "\n", + "\n", + "### *ALL HYPERPARMS BELOW MUST START WITH*\n", + "```generator_``` or ```discriminator_```\n", + "\n", + "#### Network architecture:\n", + "\n", + "- ```layer_dimensions=[128]```\n", + "- ```dropout=0.05```\n", + "\n", + "\n", + "#### Layer kwargs:\n", + "\n", + "- ```activation='relu'```\n", + "- ```use_bias=True```\n", + "- ```kernel_initializer=\"glorot_uniform\"```\n", + "- ```bias_initializer=\"zeros\"```\n", + "- ```kernel_regularizer='l2'```\n", + "- ```bias_regularizer=None```\n", + "- ```activity_regularizer=None```\n", + "- ```kernel_constraint=None```\n", + "- ```bias_constraint=None```\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Users\\saman\\Downloads\\Anaconda\\lib\\site-packages\\gandy\\models\\dcgan.py:96: UserWarning: Incorrect key learning_rate. Must start with generator_ or discriminator_\n", + " Must start with generator_ or discriminator_\")\n" + ] + } + ], + "source": [ + "\"\"\"\n", + "Specifying n_classes yields a cGAN (contional GAN),\n", + "whereas a normal GAN is instanciated if n_classes is not specified.\n", + "For our uncertainty estimator, we always want a cGAN, so always set n_classes.\n", + "\"\"\"\n", + "# According the Lee and Seok, flip x and y:\n", + "# todo why specifying learning rate make non nan loss?\n", + "GAN = gans.GAN(xshape=yshape, yshape=xshape, n_classes=13, noise_shape=(5,), learning_rate=1e-4)\n", + "\n", + "# A normal data generation call:\n", + "# GAN = gans.GAN(xshape=xshape, yshape=yshape, n_classes=1, noise_shape=(5,), learning_rate=1e-4)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##

Train!

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "###

Hyperparam options

\n", + "\n", + "#### Training:\n", + "- ```batches=50```\n", + "\n", + " (Number of batches of bootstrapped data, e.g., epochs, to train on)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Ending global_step 999: generator average loss 0.69536, discriminator average loss 1.38287\n", + "Ending global_step 1999: generator average loss 0.696301, discriminator average loss 1.38107\n", + "Ending global_step 2999: generator average loss 0.700762, discriminator average loss 1.37573\n", + "Ending global_step 3999: generator average loss 0.70827, discriminator average loss 1.37029\n", + "Ending global_step 4999: generator average loss 0.718148, discriminator average loss 1.36587\n", + "Ending global_step 5999: generator average loss 0.731029, discriminator average loss 1.36072\n", + "Ending global_step 6999: generator average loss 0.744675, discriminator average loss 1.35707\n", + "Ending global_step 7999: generator average loss 0.760214, discriminator average loss 1.35337\n", + "Ending global_step 8999: generator average loss 0.779749, discriminator average loss 1.35111\n", + "Ending global_step 9999: generator average loss 0.792294, discriminator average loss 1.34851\n", + "Ending global_step 10999: generator average loss 0.80483, discriminator average loss 1.34579\n", + "Ending global_step 11999: generator average loss 0.827481, discriminator average loss 1.3447\n", + "Ending global_step 12999: generator average loss 0.83564, discriminator average loss 1.34294\n", + "Ending global_step 13999: generator average loss 0.856997, discriminator average loss 1.3411\n", + "Ending global_step 14999: generator average loss 0.875456, discriminator average loss 1.34052\n", + "Ending global_step 15999: generator average loss 0.891027, discriminator average loss 1.33849\n", + "Ending global_step 16999: generator average loss 0.906972, discriminator average loss 1.33875\n", + "Ending global_step 17999: generator average loss 0.925623, discriminator average loss 1.33755\n", + "Ending global_step 18999: generator average loss 0.93856, discriminator average loss 1.33633\n", + "Ending global_step 19999: generator average loss 0.959721, discriminator average loss 1.33743\n", + "Ending global_step 20999: generator average loss 0.972299, discriminator average loss 1.3351\n", + "Ending global_step 21999: generator average loss 0.995185, discriminator average loss 1.33613\n", + "Ending global_step 22999: generator average loss 1.01666, discriminator average loss 1.33549\n", + "Ending global_step 23999: generator average loss 1.0242, discriminator average loss 1.33608\n", + "Ending global_step 24999: generator average loss 1.02922, discriminator average loss 1.33904\n", + "Ending global_step 25999: generator average loss 1.04247, discriminator average loss 1.34015\n", + "Ending global_step 26999: generator average loss 1.06492, discriminator average loss 1.34133\n", + "Ending global_step 27999: generator average loss 1.06534, discriminator average loss 1.34221\n", + "Ending global_step 28999: generator average loss 1.08166, discriminator average loss 1.34247\n", + "Ending global_step 29999: generator average loss 1.0789, discriminator average loss 1.34523\n", + "Ending global_step 30999: generator average loss 1.09527, discriminator average loss 1.34513\n", + "Ending global_step 31999: generator average loss 1.11206, discriminator average loss 1.34491\n", + "Ending global_step 32999: generator average loss 1.10704, discriminator average loss 1.34602\n", + "Ending global_step 33999: generator average loss 1.13349, discriminator average loss 1.34522\n", + "Ending global_step 34999: generator average loss 1.13885, discriminator average loss 1.34669\n", + "Ending global_step 35999: generator average loss 1.15736, discriminator average loss 1.34602\n", + "Ending global_step 36999: generator average loss 1.1358, discriminator average loss 1.34578\n", + "Ending global_step 37999: generator average loss 1.14593, discriminator average loss 1.34663\n", + "Ending global_step 38999: generator average loss 1.16952, discriminator average loss 1.34695\n", + "Ending global_step 39999: generator average loss 1.17907, discriminator average loss 1.34748\n", + "Ending global_step 40999: generator average loss 1.15456, discriminator average loss 1.34795\n", + "Ending global_step 41999: generator average loss 1.17172, discriminator average loss 1.34837\n", + "Ending global_step 42999: generator average loss 1.19077, discriminator average loss 1.34858\n", + "Ending global_step 43999: generator average loss 1.18499, discriminator average loss 1.34921\n", + "Ending global_step 44999: generator average loss 1.19698, discriminator average loss 1.34782\n", + "Ending global_step 45999: generator average loss 1.19372, discriminator average loss 1.34842\n", + "Ending global_step 46999: generator average loss 1.20151, discriminator average loss 1.34818\n", + "Ending global_step 47999: generator average loss 1.21182, discriminator average loss 1.34853\n", + "Ending global_step 48999: generator average loss 1.23156, discriminator average loss 1.34689\n", + "Ending global_step 49999: generator average loss 1.22769, discriminator average loss 1.34798\n", + "TIMING: model fitting took 475.032 s\n" + ] + } + ], + "source": [ + "# A normal data generation call:\n", + "# GAN.train(Xs_train, Ys_train, batches=50000)\n", + "\n", + "# Flipping x and y:\n", + "GAN.train(Ys_train, Xs_train, batches=50000)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "100" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "GAN._model.batch_size # this is automaticaally baked into deepchem" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##

Predict

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "###

Hyperparam options

\n", + "\n", + "#### Prediction:\n", + "- ```num_predictions=100```\n", + "\n", + " (Number of predictions to make in order to sample uncertainties.)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "# Because this is a conditional GAN, MUST specify Ys or a warning will be thrown\n", + "\n", + "# A normal data generation call:\n", + "# preds, uncs, flags = GAN.predict(Xs_test, uc_threshold = 0.01, Ys=Ys_test)\n", + "\n", + "# Flipping x and y:\n", + "# preds, uncs, flags = GAN.predict(Ys_test, uc_threshold = 0.01, Ys=Xs_test)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##

Results

" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.015745554\n" + ] + } + ], + "source": [ + "preds, uncs = GAN._predict(Ys_test, Xs_test, num_predictions=500)\n", + "thresh = 0.02\n", + "flags = uncs > thresh\n", + "print(np.average(uncs))" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0.5, 1.0, 'Certain and uncertain predictions, boston data')" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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Tz6Zs846ixkzZnD++efn+RUiVIf/+q//yvkFQURERMqX0lqkQPfffz8NGzbk6KOP5pRTTmHOnDlceumlvPrqq4BvSV+4cCG/+tWvaNasGdu2bct5HHnkkXTt2pW33347X7k33XRTnsA8np07d/L3v/+dSy+9lNTU1DzbaNmyJSeccELONrKzs3n++edp3749N9xwQ76yqlQp/mlfv379nHSRcAsWLKBdu3Z07tw5Tx0PHDjA+eefz0cffcTevXsBqFOnDlu2bOGjjz4qdn2Kqk6dOnz55Zd88cUX5VYHERERyaWW8xJSEi3UyWrYsNF9UZcAACAASURBVGH07dsXM+PII4+kTZs2eW7W3Lp1K9u3b+ftt9+O2eVhtIC4TZs2ha7DunXryM7OZvbs2cyePTvqMscffzwA27ZtIzMzkwsvvLDUhplv1aoVVatWzTd9zZo17N27N27Xj9u2bePYY4/lwQcf5Le//S09evSgadOm9OrVi4svvpgrrriClJSUUql3pClTpjBo0CA6duzI8ccfzznnnEPv3r3p3bt3iXyJERERkcQoOJcCtW7dmvPOOy/mfOccAOeddx533313ocstbKt5+DYGDhzIkCFDoi5zxBFH5Fm2tAJziF135xwdO3bk0UcfjbluKHA/66yz+Pbbb3nrrbdYvHgxixcvZtGiRTzwwAN89NFHBfZWc+jQoaK/gMBll13Gxo0beeONN1iyZAnvvvsus2fPpkePHrz77rtl9iVBREREPAXnUmwNGzakbt267Nq1K24QXxwnnHACZsaBAwcK3EbDhg2pV69eoe4BKOkAvnXr1mzdupVzzz23UC3PNWvW5PLLL+fyyy8H/MioI0eOZPbs2Tk9ptSvX5+MjIx860b2ghNLQa+xfv36DBw4kIEDB+Kc4/e//z1//vOfee211+jbt2+htiEiIiIlQ79bS7FVqVKFAQMGsGzZMl588cWoy0Tr6jARRx11FBdddBEvv/wyS5cuzTffOcfWrVtz6tOvXz+++uqrqCkwoZZ18MExEDX4LYrBgwfz448/xmw5/89//pPz/7Zt2/LNP+200/LVp02bNnzyySfs2bMnZ1pmZmZOl5MFifUas7Ky2LFjR55pZsapp54adXkREREpfWo5lxIxadIk/vd//5crr7ySK6+8kq5du5KSksKmTZt444036Ny5c4H9hxfkqaeeonv37vzqV79i8ODBnHrqqWRnZ7N+/Xpee+01Bg8enNNbywMPPMD777/P9ddfz9tvv0337t1xzrFq1SoOHTqU03Vi165dmTZtGiNGjODiiy+mevXqnHnmmRx33HFFquNtt93GO++8w1133cX777/PueeeS+3atdm8eTPvvfceqampLF68GID27dvTtWtXzjzzTJo2bZrT5WFKSgpXX311Tpk333wzAwcO5Nxzz2XQoEHs2LGDmTNn0qJFi5wBm+I5/fTTqVKlCpMmTSIzM5MjjzyS4447jrZt29KkSRMuvfRSTj31VI4++mg2bNjAU089Rb169ejdu3eR9oGIiIgUg3NOj+DRuXNnV5CvvvqqwGUqisWLFzvAPfTQQ4Va/pdffnETJ050HTp0cKmpqa5mzZquXbt27vrrr3dLly7NWW7u3LkOcIsXL45azpAhQ5w/NfPbunWru/POO13r1q1djRo1XJ06dVyHDh3crbfe6r788ss8y2ZmZrq77rrLtWrVylWvXt3Vr1/fde/e3b3wwgs5y2RlZbk77rjDNWvWzFWpUsUBbu7cuXFfZ4sWLVzPnj1jzj948KB77LHHXJcuXVxaWppLS0tzJ5xwguvfv7976623cpb74x//6Hr06OEaNmzoUlJS3DHHHOOuuOIKt3Llynxl/vnPf3bNmzd3KSkprl27dm727NlR92OsfTtv3jzXvn17V716dQe4IUOGuP3797vf//737vTTT3f169d3KSkprkWLFu6aa65xX3/9ddx94Fzlei+IiEgFs2CBczVqOAfOtWjhn5cxYIWLEo+aC/uJv7Lr0qWLW7FiRdxl1qxZkzOQi0hlpveCiIgclhYuhGHDICxdlLQ0mDEDBgwos2qY2UrnXJfI6co5FxEREZHKY+zYvIE5+Odjx5ZPfSIoOBcRERGRymPz5sSmlzEF5yIiIiJSeTRvntj0MqbgXEREREQqj0mTfI55uLQ0Pz0JKDgXERERkcpjwAB/82eNGv55ixZlfjNoPOrnXEREREQqlwEDYOZM/396erlWJZJazkVEREREkoSCcxERERGRJKHgXEREREQkSSg4FxERERFJEgrOk0y/l16g30svlHc1RERERKQcKDgXEREREUkSCs5FRERERJJEUgbnZnaPmf3VzNabmTOzjUUsZ7CZrTKzvWb2HzObZWYNS7i6Ugays7OZPHky7dq1IzU1lWOPPZY77riDX375pVTK+Prrrxk3bhxdu3alYcOG1KpVi06dOjFp0qSEtikiIiJJKj096fo4hyQNzoEHgXOBb4HMohRgZqOB+cBO4DbgaeBqIN3MjiyhekoZGT16NLfffjsnnngijz/+OH379mXq1Kn07t2b7OzsEi9jzpw5TJ48mVatWjFu3Dgeeugh2rZty7333ku3bt3Yu3dvabxMERERqeSSdYTQVs659QBm9gVQM5GVzawB8ACwHPi1cy4rmL4ceB0frD9YojWWItmzZw/vv/8+3bt3p27dulGX+fLLL3n88cfp06cPL730Us704447jltvvZXnn3+e/v37x91OomVcccUV3HPPPdSpUydn2vDhw2ndujWTJk1i9uzZ3HzzzUV92SIiIiJRJWXLeSgwL4bfAmnA46HAPCj3b8B6YGAxyy8Vr61bw6off+DTLd/Rfe4MXlu3pryrlGPJkiVccsklNGzYkKpVq2JmeR49evQodFlff/01U6ZM4YILLqB+/fr07t2bbdu2xVz+ueeewznHqFGj8ky/4YYbSEtLY8GCBQVuM9EyunTpkicwD7nqqqsA+OKLLwrcpoiIiEiikrXlvLhOD/5+EmXeUqCfmdV0zv0cPmPlypWYWcxCnXMlV8MIr61bw5j33uZAlv8u8f3u3Yx5720ALmvbvtS2Wxjz58/n2muvpUmTJowcOZKjjjqKl19+mfT0dOrWrUvPnj05//zzY66/d+9eFi9ezD//+U/eeOMN1q/3371OOukkbr31Vi666CKOO+64mOsvX76cKlWqcMYZZ+SZnpqaSqdOnVi+fHmBr6EkygD47rvvAGjUqFGhlhcREZHktGPHDp577jmGDx8eN/4raxU1OG8a/N0SZd4WwIJlvi6zGhXgoY8/ZO+hQ3mm7T10iIc+/rBcg/P169czfPhw2rVrx0cffUS9evUAn+Jx0kknsWnTJhYtWkRaWlqe9fbt28fMmTN54403SE9PZ9++fRx55JGce+653HXXXVx88cUce+yxharD999/T4MGDahRo0a+ec2aNePjjz/mwIEDpKSklGoZWVlZTJw4kWrVqhWYRiMiIiLJKTs7m2eeeYbf/e53bN++ne7du9OxY8fyrlaOpExrKQGhSHF/lHn7IpbJ0blzZ5xzMR+l6YfduxOaXlYmT56cE2iHAnOA6tWr06tXLw4cOMCmTZvyrffjjz9y66238uabb5Kdnc2dd97J999/z+uvv87w4cMLHZiDz0uPFlSDb/kOLVPaZYwaNYqlS5cyceJE2rZtW1C1RUREJMmsWrWK7t27c80119C6dWtWrlyZVIE5VNzgPBRlRYvGUiOWSQpNatVKaHpZef311znhhBPo1q1bvnn79/vvPjVr5r9ft3Hjxjz22GNccMEFVKlShYcffpgmTZrQu3dvnnzySTZs2FDoOqSlpeVsK9K+fftylinNMu677z6mTZvGsGHDuOeeewpTbREREUkSO3bs4JZbbqFLly588803zJ07lw8//JBOnTqVd9XyqajB+ffB32ZR5jUDXNgySeGubj04olreLKMjqlXjrm6Fv9GypO3YsYPNmzdzyimnRJ2/bNkyGjduHLUVPDU1NaflPCMjg7///e9cc801fPXVV4wcOZLjjz+edu3aMXr0aN5++20ORaT0hGvatCnbtm2LGlxv2bKFBg0axE1HKW4Z48eP54EHHuCaa65h+vTpcbcjIiIiySM7O5u5c+fSpk0bnnzySUaMGMHXX3/N0KFDqVIlOcPg5KxV8YXu7jsryrwzgXWRN4OWt8vatufBX/+GlKpVAWhaqxYP/vo35ZpvvmvXLoCoQeuyZctYu3YtV155ZYHlHHHEEVx88cVMmzaNb7/9lrVr1/Loo4/SvHlznnrqKS644AI2btwYc/3TTz+d7Oxsli1blmf6vn37WL16NV26dCmwDkUtY8KECUyYMIHBgwcza9aspLphRERERGILpbBce+21OSksjz/+eMyum5NGvBzrZHgAXwAb48xvDrQDqodNa4hPW/kUqBo2vTe+1fzeaGV17tzZFeSrr74qcJniuPrF593VLz5fqtsorP3797vU1FTXtGlTt2fPnpzpGRkZrmPHjq527druu+++K9Y2fv75Z/f666+7zMzMmMv861//cmbm+vTpk2f61KlTHeCeffbZnGkHDhxwa9ascZs2bSpyGSETJkxwgBs0aJDLysoqysur0Er7vSAiIlIUGRkZbuTIka5KlSquYcOGbu7cudE/x3v29I9yAqxwUeLRpOytxcwGAS2Cpw2BFDO7N3i+yTn3bNjizwA9geOAjQDOua1mdh/wMPCumT2HT2e5A1gLTCn1F1EBpKSkcOONN/LYY49xzjnn0L9/fzIyMpg9ezaZmZm88sorNGsWLXPIp8RMmVL43Xz22WfHnNexY0dGjhzJtGnT6NOnDxdddBFr1qxh6tSp9OzZM0/PKVu2bKF9+/b07NmT9LAheRMpA+CJJ57g/vvvp3nz5px33nksWrQoz/xGjRrF7T5SREREylZ2djbz58/n7rvvZvv27YwYMYI//OEPyd9SHilaxF7eDyAd38Id7ZEeY9mWUcoZCnyO76HlJ2AOcHSs7arlPL8DBw64MWPGuObNm7vq1au7xo0bu8GDB7t169bFXW/Dhg2xjl/Ux7///e+45R06dMg9/PDDrk2bNi4lJcU1bdrUjR492u3evTvqdntG+SZc2DKcc27IkCFx6xut/MpGLeciIpIsVq5c6bp27eoA161bN7dq1aqCV0rSlnNzpdxF4OGkS5cubsWKFXGXWbNmDe3bl14eeL+XXgDgucuvKrVtiJSE0n4viIiIFCQzM5P77ruPp556iqOOOoqHHnqIQYMGFe5mz1CL+o4dpVvJGMxspXMu301vSZnWIiIiIiISS4VJYYlCwXmSUYu5iIiISGyrVq1ixIgRLF26lG7duvH2228nZX/lRVVRu1IUERERkQokMzOTm2++mS5duvDtt98yb968pB1IqDjUci4iIiIiSStaCsvEiROpV69eeVetVCg4FxEREZGktGrVKkaOHMknn3xSIVNYolFai4iIiIgklfAUlm+++Ya5c+dWyBSWaBSci4iIiEhSyM7OZu7cubRt25annnqKESNG8PXXXzN06NDCdY9YWAsXwq5dsHMntGzpnycJpbWIiIiISLkrsxSWhQth2DAIjfWzaZN/DjBgQMlvL0FqORcRERGRchOZwlLqvbCMHQt79uSdtmePn54E1HIuIiIiImUusheWkSNHMnHixNIfSGjTpsSmlzG1nCebXr38Q0RERKSC+uyzzzj77LO59tpradOmDStXrmTq1KllM8Jn1aqJTS9jCs5FREREpExkZmYycuRITj/9dNavX8/8+fPLvheWrKzEppcxBeciIiIiUqpCvbC0adOG6dOnc/PNN7Nu3ToGDx6MmZVtZVq0SGx6GVNwLoeF7OxsJk+eTLt27UhNTeXYY4/ljjvu4JdffimVMr7++mvGjRtH165dadiwIbVq1aJTp05MmjQpoW2KiIhUduEpLG3btuWzzz7jscceK5sUlmgmTYK0tLzT0tL89CSg4FwOC6NHj+b222/nxBNP5PHHH6dv375MnTqV3r17k52dXeJlzJkzh8mTJ9OqVSvGjRvHQw89RNu2bbn33nvp1q0be/fuLY2XKSIiUmFEprCEemE55ZRTyrdiAwbAjBkQarFv0cI/T4JuFAFwzukRPDp37uwK8tVXXxW4TLH07Okfh7mff/7ZrVu3rkTK+uKLL5yZuT59+uSZPnXqVAe4hQsXlngZy5cvdzt27MhXztixYx3gHn/88SK8koql1N8LIiJyWMrKynKzZ892DRo0cFWqVHG33nqry8zMLO9q5Venjn+UE2CFixKPquU8mSxcCEuXwpIlSTda1ZIlS7jkkkto2LAhVatWxczyPHr06JFn+a1bt9K2bVvOOOMMpk6dyk8//VTkbT/33HM45xg1alSe6TfccANpaWksWLCgxMvo0qULderUyVfOVVddBcAXX3yR6MsQERGp8EIpLNddd11ypLAchhScJ4vQaFX79/vnodGqkiBAnz9/Pueeey6rV69m5MiRTJkyhV5Bd49169blsssu4+qrr86zTpMmTZg6dSpVq1bltttuo1mzZlx00UU899xz7Ins+L8Ay5cvp0qVKpxxxhl5pqemptKpUyeWL19eJmUAfPfddwA0atSokLUXERGp+EIpLF26dMlJYfnggw/KP4XlcBStOb2yPso1raVFC+f8QLJ5Hy1alM72Cunbb791qamp7sQTT3QZGRk50w8cOOBat27tUlJS3C+//BK3jG+++cZNnDjRtWvXzgGuZs2abvDgwe7tt992WVlZBdahQ4cO7uijj446r2/fvg5w+/fvL/UyDh065Lp27eqqVavm1q5dW2C9KzqltYiIyGGTwhKN0lokrs2bE5teRiZPnsy+ffuYOXMm9erVy5levXp1evXqxYEDB9hUwIharVq14r777mPNmjWsXLmSYcOG8d577/Gb3/yGY445psBeV/bs2UONGjWizktNTc1ZJp6SKGPUqFEsXbqUiRMn0rZt27jLioiIVHRKYSkdCs6TRfPmiU0vI6+//jonnHAC3bp1yzdvf5CCU7NmzUKXd9ppp/HII4+wdOlSLrnkEn744QceffRRtm7dGnOdtLS0nG1F2rdvX84y8RS3jPvuu49p06YxbNgw7rnnnrjbEhERqcgiU1hCAwkphaVkKDhPFknY5+aOHTvYvHlzzDfbsmXLaNy4Mccee2yhy5s9eza//vWvadGiBW+++SYXXnghCxcu5Jhjjom5XtOmTdm2bVvU4HrLli00aNCAlJSUuNsuThnjx4/ngQce4JprrmH69OkFvEoREZGKKTs7mzlz5uQMJHTLLbeU30BCJaFTJ/9IMgrOk0Woz81Q6kUS9Lm5a9cugKhB67Jly1i7di1XXnll3DL27dvHiy++SJ8+fWjcuDHXX389O3fu5NFHH2XLli3885//pH///lSrVi1mGaeffjrZ2dksW7YsX9mrV6+mS5cuBb6WopYxYcIEJkyYwODBg5k1a9bhefEREREpJqWwlB0F58lkwADo2hV69oSNG8u9M/zGjRuTmprKkiVL8gy6k5mZyfXXX0/t2rX53e9+F3Xd3bt3M3ToUBo1akTfvn1ZtWoVd9xxB2vWrGHFihXcdtttHH300YWqx1VXXYWZMWXKlDzTZ86cyZ49exgQtp8OHjzI2rVr2RyRq59IGSETJ05k/PjxDBo0iLlz51Klit4uIiJSuSiFpezFbq6USi8lJYUbb7yRxx57jHPOOYf+/fuTkZHB7NmzyczM5JVXXqFZs2ZR192+fTuvvfYaV111FQMHDqRHjx5FbnXu2LEjI0eOZNq0afTp04eLLrqINWvWMHXqVHr27En//v1zlt2yZQvt27enZ8+epKenF6kMgCeeeIL777+f5s2bc95557Fo0aI88xs1asT5559fpNcjIiKS7LKzs5k3bx533303GRkZ3HLLLUyYMKFitZSHxQnJRMG5xPXQQw9x5JFHsmDBAu68806OOuoofvOb3zB27FjatGkTc71mzZrx448/xuwhJVFTpkyhZcuWzJgxg3/84x80aNCAW265hYkTJxa6RTuRMkL9nm/evJkhQ4bkK6tnz54KzkVEpEL67LPPGDFiBJ9++ilnn302TzzxhFrKy5D5bhYFoEuXLm7FihVxl1mzZg3t27cvvUoEg/sk67c5kZBSfy+IiEiZysjI4N5772X69Ok0bNiQP//5z4fvzZ6HATNb6ZzLd9ObkmhFREREKrFQLyxt27bl6aefzumFZciQIQrMy4HSWpKNWsxFRESkjCiFJfmo5VxERESkksnIyGDEiBF06dKFDRs28Mwzz6gXliSh4FxERESkkoiVwjJo0CClsCQJBeciIpJcevXKvTleRErM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8S7ES5TJjxUJ2l1TQ8fVa9WFJUBN5aJlRXdERCRfqDiXvJUok9+vrJzrTqjAbd2Kcw42b+bK0o4seOD/1IUlQE3homVFd0REJJ8o1pIHFLeILRybGPHKi9TU1VEamjE/+4gjufPOOxk3bhzbdu6k/ILzefvhR+POlOvnm77KHj0ZOfelBtGWYruoVtEdERHJJyrOJafCcYKaujpOeWhK/ax4WHQmf968eRx/0Q9Yvnw55557Lpx1Jm2/8Q1FWLIk3hekYipam0p0R0RECoOKc8mZeHECoEHxN63/paxZs4YBAwYwffp0Dj/8cObMmcN5552Xk3E3NcV+0XK8i4uLKbojIiKFQ5lzyZlkvcoBdu3axZ133knXrl3ru7AsW7ZMhbkERusNiIhIPtHMueRMsjjBq6++ytChQ6mqquK8885jwoQJHHHEEY05RGkCmkJ0R0RECoeKc0nZgID7hceLE3y9dRsuu+wyZsyYoQhLAkH/fSRSjHGWSMUe3RERkcKhWIs0EK/neDbEihM0d44PHn6UZ555RhEWERERaXI0cy450yBOUFsLW7aw9uk59GpXysRly9SvXERERJocFedSL1lbw2yoaLs/26c9yScLF3H44YczbeJEzZSnIBd/V8VOcRYREckHirUIkHyVxKCXN4/swrL6b2/zzUt+oAhLirSipQSpMaNsIiKSnIpzARK3NQy6GJw3bx7HH388lZWV9OrViw9WrGDpjCe1kFCKUmlBKSIiIoVJxbkAidsaBlUMhhcSOv3009m+fTtz5szh2WefVXtEn7SipQQl6N+IiYhI5lScCxB/NcT2JSUZF4OREZZnnnmGm266SRGWDCT6uxJJleJRIiL5ScW5AIlXScykGHz11Vc57rjjqKyspHfv3ixbtoyxY8cqwpIBrWgpQVA8SkQkP6k4F8Brazi+T19aNmsGQGlJCeP79KVfWXlaxeCaNWu47LLL6NOnDzt37uTZZ59lzpw5irAEINHflUiqFI8SEclPaqUo9eKtkuhnefNdu3YxceJExo0bR21tLePGjWP48OHsu+++jfMmmgitaCmZirdCr+JRIiK5Zc65XI8hb1RUVLjFixfnehh5K9ly8a+++ipDhw6lqqqK888/nwkTJnD44Yc35hBFJEXhzHlktKV18+b6LYyISCMxs3eccxXR2xVrkYzFirDMnj1bhblIHlM8SkQkP+VlcW5m+5jZr8xshZntMLNPzewuM9svhWOPNrObzexNM9tkZtVm9q6ZjUrleEldTU0Nd9xxB2VlZcyePbu+C8u5556b0Xm1KIpI4+hXVs4J7dpzcodDeX3QYBXmIiJ5IF8z53cD1wJPA3cB5aHHJ5jZGc653QmO/TEwBJgDTAV2Ab2B3wKXmFl359z2bA6+WEXGWebOncvQoUNZsWJFgwhLsuiLiIiIiMSXd8W5mR0LXAPMcs71j9j+EXAPcBnweIJTPAX8zjn3VcS2+83sA2AUcDUwOfCBNxFr1qxh2LBhzJgxgyOOOILnnnuOc845J9fDEpE06Yu0iEh+ycdYywDAgAlR2x8AtgGXJzrYObc4qjAPC+ckvpnxCJug6AjLuHHjWLZsWYPCPIjVBrVioYiIiDRleTdzDpwI7Ab+FrnRObfDzN4NPZ+OQ0P3GzIYW5MUL8ISKd5qg0DKOdYgziEiIiJSyPJx5rwU+Mw5tzPGc2uAQ8yspZ8TmlkzYAxQS4JIzDvvvIOZxb01NatXr+bSSy/ljDPOoKamhueeey5uF5YgVhvUioUiIiLS1OVjcd4GiFWYA+yI2MePCUB3YIxzbmW6A2sqwhGWrl27MmfOnJgRlmhBrDaoFQtFRESkqcvH4nwb0CrOc/tG7JMSM/sNMBSY4pz7XaJ9u3XrhnMu7q0pmDt3LscddxzDhw+nT58+LF++nDFjxiRd4TPeqkmFH5UAABKZSURBVIJ+VhsM4hwiIiIihSwfi/O1eNGVWAV6B7zIS00qJzKzm4AbgYeAnwU2wiIUHWHxu5BQZY+etG7e8BKG1s2bU9mjZ8pjCOIcIiIiIoUsH4vzt/HGdVLkRjPbFzgeWJzKScxsLDAWeAT4iWsqU98+xYuw+F1IKIjVBrVioYiIiDR1+ditZQYwEvglEHkl4E/xsuZTwxvMrAvQwjm3IvIEZjYGuAl4FBiUZNGiJuvVV19l6NChVFVVxe3C4ke/snKmv78ESL93chDnEBERESlUeVecO+eWmtm9wFAzmwX8mT0rhL5Gw24rc4FOeH3RATCzIcA44BPgFeCHUZ1WNjjnXs7qm8hzq1evZtiwYTzxxBOBLySkglpEREQkfXlXnIf8ElgFDAbOAT4DJuF1W0k2Cx7ug94ReDjG868BTbI4r6mpYcKECdx8883U1dUxbtw4hg8fnvRiz8amAl9ERESaKlMUe4+Kigq3eHFKkfaCk8pCQiIiIiLSOMzsHedcRfT2fLwgVALkZyEhEREREcktFedFqqamhttvv93XQkIiIiIiklv5mjmXDLzyyitcc801irCIiIiIFBjNnBeRcITlzDPPVIRFREREpACpOC8C0RGWm2++WREWERERkQKkWEuBU4RFREREpHho5rxArV69mksuuUQRFhEREZEiouK8wNTU1HDbbbfRtWtXnn32WUVYRERERIqIYi0F5JVXXmHo0KGsXLmS8847j4kTJ2qmXERERKSIaOa8AER2Ydm1axfPPfccc+bMUWEuIiIiUmRUnOcxLSQkIiIi0rQo1pKn5s6dy9ChQ9WFRURERKQJ0cx5ngl3YTnjjDPUhUVERESkiVFxnifUhUVEREREFGvJAy+//DLXXHMNK1eupF+/ftx9992aKRcRERFpglSc59iSJUvo27cvXbp04fnnn+fss8/O9ZBEREREJEdUnOfYt7/9bZ544gnOO+889t1331wPR0RERERySMV5Hrj44otzPQQRERERyQO6IFREREREJE+oOBcRERERyRMqzkVERERE8oSKcxERERGRPKHiXEREREQkT6g4FxERERHJEyrORURERETyhIpzEREREZE8oeJcRERERCRPqDiXhMwMM8v1MKQA6bMj6dDnRtKhz42kI18/NyrORURERETyhIpzEREREZE8oeJcRERERCRPqDgXEREREckTKs5FRERERPKEinMRERERkTyh4lxEREREJE+Ycy7XY8gbZrYJ+DjX4xARERGRotfJOfe16I0qzkVERERE8oRiLSIiIiIieULFuYiIiIhInlBxLiIiIiKSJ1Sci4iIiIjkCRXnTZCZ7WNmvzKzFWa2w8w+NbO7zGy/FI492sxuNrM3zWyTmVWb2btmNiqV46WwZfLZiXGuNmb2kZk5M5ucjfFKfgjic2NmB5vZnWb2r9A5NpnZPDPrmc2xS+5k+rkxs7ZmNtLMlob+X/WZmS0ys6vMzLI9fskNM7vBzJ40sw9D/39ZleZ5rjCzf5jZdjPbYGZ/NLO9OqtkQ/PGeBHJO3cD1wJPA3cB5aHHJ5jZGc653QmO/TEwBJgDTAV2Ab2B3wKXmFl359z2bA5eciqTz060m4FDgh+i5KGMPjdm1gmYD7QF/g/4J3AA8G2gQ/aGLTmW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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# figure\n", + "fig, ax = plt.subplots(figsize=(12,8))\n", + "\n", + "# plotting predicted vs true\n", + "# scatter\n", + "ax.errorbar(Ys_test.reshape(-1,1)[flags == False],\n", + " preds[flags == False], yerr = uncs[flags == False], ls='none',\n", + " c=plt.cm.viridis(0.5), marker='o', label=f'$\\sigma \\leq {thresh}$')\n", + "ax.errorbar(Ys_test.reshape(-1,1)[flags == True],\n", + " preds[flags == True], yerr = uncs[flags == True], ls='none',\n", + " c='r', marker='o', label=f'$\\sigma > {thresh}$')\n", + "min_y, max_y = np.min(Ys_test.reshape(-1,1)), np.max(Ys_test.reshape(-1,1))\n", + "# perfect results\n", + "x = np.linspace(min_y, max_y, 100)\n", + "ax.plot(x, x, 'k-', label= \"Perfect results\")\n", + "\n", + "# axes labels formatting\n", + "ax.set_xlabel('True target', fontsize=24)\n", + "ax.set_ylabel('Predicted target', fontsize=24)\n", + "\n", + "# tick formatting\n", + "plt.setp(ax.get_xticklabels(), fontsize=18)\n", + "plt.setp(ax.get_yticklabels(), fontsize=18)\n", + "ax.tick_params(direction='in', width=2, length=8)\n", + "\n", + "# legend & title\n", + "plt.legend(fontsize=18)\n", + "plt.title('Certain and uncertain predictions, boston data', size=24)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From b02c3b825e9e7abbbf6e1b99f825c5483839b456 Mon Sep 17 00:00:00 2001 From: evankomp Date: Thu, 11 Mar 2021 10:24:33 -0800 Subject: [PATCH 73/99] optimization written and passing tests --- gandy/optimization/hypersearch.py | 73 +++- .../test_optimization/test_hypersearch.py | 321 +++++++++--------- 2 files changed, 224 insertions(+), 170 deletions(-) diff --git a/gandy/optimization/hypersearch.py b/gandy/optimization/hypersearch.py index db5c63f..7c6adea 100644 --- a/gandy/optimization/hypersearch.py +++ b/gandy/optimization/hypersearch.py @@ -342,7 +342,7 @@ def __call__(self, trial: Trial) -> float: trial.report(loss, session) - if trial.should_prune(): + if trial.should_prune() is True: raise optuna.exceptions.TrialPruned() return loss @@ -355,7 +355,7 @@ def __call__(self, trial: Trial) -> float: self.val_data) trial.report(loss, session) - if trial.should_prune(): + if trial.should_prune() is True: raise optuna.exceptions.TrialPruned() return loss @@ -373,7 +373,7 @@ def __call__(self, trial: Trial) -> float: val_data) trial.report(loss, session) - if trial.should_prune(): + if trial.should_prune() is True: raise optuna.exceptions.TrialPruned() return loss @@ -503,6 +503,23 @@ def __init__(self, # . set the class to self.subject # . set self the Xs and Ys data after taking values # . save all_kwargs + if not issubclass(subject, gandy.models.models.UncertaintyModel): + raise TypeError('subject must be an UncertaintyModel class') + else: + self.subject = subject + + Xs = numpy.array(Xs) + Ys = numpy.array(Ys) + if not hasattr(Xs, '__len__') and hasattr(Ys, '__len__'): + raise TypeError('Passed data are not arrays (iterable)') + if len(Xs) != len(Ys): + raise ValueError('Passed data does not have the same length; \ +len(Xs) = {}, len(Ys) = {}'.format(len(Xs), len(Ys))) + self.Xs = Xs + self.Ys = Ys + + self.search_space = search_space + self.all_kwargs = kwargs return def _set_param_space(self): @@ -517,6 +534,12 @@ def _set_param_space(self): # . for loop self.param_space # param_space = SearchableSpace class # set self._param_space + param_space = [] + for k, v in self.search_space.items(): + param_space.append( + SearchableSpace(k, v) + ) + self.param_space = param_space return def _set_objective(self): @@ -543,6 +566,19 @@ def _set_objective(self): # **kwargs) # . set self.objective + if self.search_space is None: + raise AttributeError('Set search space, the desired hyperparameter\ + space to search over according to SearchableSpace class') + else: + self._set_param_space() + + objective = SubjectObjective( + self.subject, + self.Xs, + self.Ys, + **self.all_kwargs + ) + self.objective = objective return def _set_study(self): @@ -557,6 +593,8 @@ def _set_study(self): # pseudocode # . create study optuna.create_study # . set to self.study + study = optuna.create_study(**self.all_kwargs) + self.study = study return def optimize(self, @@ -583,20 +621,28 @@ def optimize(self, # . set optimizer, study with all kwargs # . set self.best_params # . get best_score - best_score = None + if search_space is not None: + self.search_space = search_space + else: + pass + + self.all_kwargs.update(kwargs) + + self._set_objective() + self._set_study() + + self.study.optimize(self.objective, **self.all_kwargs) + best_score = self.study.best_value + self.best_params = self.study.best_params return best_score - def train_best(self, **kwargs) -> Model: + def train_best(self) -> Model: """Train the subject on the entire dataset with the best found parameters. Requires self.optimize to have been executed or best_params to have been specified. - Args: - **kwargs: - Keyword arguments to pass to the constructor and trainer - Returns: best_model (UncertaintyModel): Instance of subject with specified static and best searched @@ -604,9 +650,14 @@ def train_best(self, **kwargs) -> Model: """ # pseudocode # . check best_params exist - # . update all kwargs # . Initiate model with and best_params and kwargs # . train model with best_params training and kwargs # . set self.best_model - best_model = None + if not hasattr(self, 'best_params'): + raise AttributeError('No best parameters found, run the optimizer') + else: + pass + + best_model = self.subject(**self.best_params, **self.all_kwargs) + best_model.fit(**self.best_params, **self.all_kwargs) return best_model diff --git a/gandy/tests/test_optimization/test_hypersearch.py b/gandy/tests/test_optimization/test_hypersearch.py index 177d57f..a992612 100644 --- a/gandy/tests/test_optimization/test_hypersearch.py +++ b/gandy/tests/test_optimization/test_hypersearch.py @@ -306,162 +306,165 @@ def test___call__(self): return -# class TestOptRoutine(unittest.TestCase): -# """User interface class""" - -# def test___init__(self): -# """proper saving of keyword arguments and data saving""" -# # failure case not correct model type -# with self.assertRaises(TypeError): -# subject = opt.OptRoutine(subject=opt.SearchableSpace, -# Xs=numpy.array([1, 2, 3]), -# Ys=numpy.array([1, 2, 3]), -# search_space={'hyp1': (1, 10), -# 'hyp2': ['a', 'b']}, -# keyword=5) -# # failure case data not iterable -# with self.assertRaises(TypeError): -# subject = opt.OptRoutine(subject=gandy.models.models. -# UncertaintyModel, -# Xs='str', -# Ys=numpy.array([1, 2, 3]), -# search_space={'hyp1': (1, 10), -# 'hyp2': ['a', 'b']}, -# keyword=5) -# with self.assertRaises(TypeError): -# subject = opt.OptRoutine(subject=gandy.models.models. -# UncertaintyModel, -# Xs=numpy.array([1, 2, 3]), -# Ys='str', -# search_space={'hyp1': (1, 10), -# 'hyp2': ['a', 'b']}, -# keyword=5) -# # expected success -# subject = opt.OptRoutine(subject=gandy.models.models. -# UncertaintyModel, -# Xs=numpy.array([1, 2, 3]), -# Ys=numpy.array([1, 2, 3]), -# search_space={'hyp1': (1, 10), -# 'hyp2': ['a', 'b']}, -# keyword=5) -# self.assertTrue(subject.Xs is not None) -# self.assertTrue(subject.Ys is not None) -# self.assertTrue(self.subject == gandy.models.models.UncertaintyModel) -# self.assertEqual(subject.search_space, {'hyp1': (1, 10), -# 'hyp2': ['a', 'b']}) -# self.assertTrue('keyword' in subject.all_kwargs.keys()) -# return - -# @unittest.mock.patch('gandy.optimization.hypersearch.SearchableSpace') -# def test__set_param_space(self, mocked_SS): -# """proper parsing of dictionary into SearchableSpace objects""" -# mocked_SS.side_effect = ['ss1', 'ss2'] -# subject = opt.OptRoutine(subject=gandy.models.models. -# UncertaintyModel, -# Xs=numpy.array([1, 2, 3]), -# Ys=numpy.array([1, 2, 3]), -# search_space={'hyp1': (1, 10), -# 'hyp2': ['a', 'b']}, -# keyword=5) -# subject._set_param_space() -# mocked_SS.assert_called_with('hyp2', ['a', 'b']) -# self.assertEqual(mocked_SS.call_count, 2) -# return - -# @unittest.mock.patch('gandy.optimization.hypersearch.SubjectObjective') -# def test__set_objective(self, mocked_objective): -# """ensure proper calling of SubjectObjective class""" -# mocked_objective.return_value = 'objective' -# subject = opt.OptRoutine(subject=gandy.models.models. -# UncertaintyModel, -# Xs=numpy.array([1, 2, 3]), -# Ys=numpy.array([1, 2, 3]), -# search_space={'hyp1': (1, 10), -# 'hyp2': ['a', 'b']}, -# keyword=5) -# mocked__set_param = unittest.mock.MagicMock() -# subject._set_param_space = mocked__set_param -# # set the objective -# subject._set_objective() -# mocked_objective.assert_called_with(subject.subject, -# subject.Xs, -# subject.Ys, -# **subject.all_kwargs) -# self.assertEqual(subject.objective, 'objective') -# mocked__set_param.assert_called() -# return - -# @unittest.mock.patch('optuna.create_study', return_value='study') -# def test__set_study(self, mocked_cstudy): -# """Can a study be correctly called and stored""" -# subject = opt.OptRoutine(subject=gandy.models.models. -# UncertaintyModel, -# Xs=numpy.array([1, 2, 3]), -# Ys=numpy.array([1, 2, 3]), -# search_space={'hyp1': (1, 10), -# 'hyp2': ['a', 'b']}, -# keyword=5) -# subject._set_study() -# self.assertTrue(subject.study == 'study') -# mocked_cstudy.assert_called_with(**subject.all_kwargs) -# return - -# def test_optimize(self): -# """acceptance of kwargs and nested calls""" -# subject = opt.OptRoutine(subject=gandy.models.models. -# UncertaintyModel, -# Xs=numpy.array([1, 2, 3]), -# Ys=numpy.array([1, 2, 3]), -# keyword=5) - -# # failure mode no seach space specified -# with self.assertRaises(AttributeError): -# subject.optimize() - -# # set up mocked objects -# mocked_set_obj = unittest.mock.MagicMock() -# mocked_set_study = unittest.mock.MagicMock() -# mocked_study = unittest.mock.MagicMock() -# subject._set_objective = mocked_set_obj -# subject._set_study = mocked_set_study -# subject.study = mocked_study - -# # success case, set search space and pass new kwargs -# best_score = subject.optimize(search_space={'hyp1': (1, 10), -# 'hyp2': ['a', 'b']}, -# keyword2=10) -# mocked_set_obj.assert_called() -# mocked_set_study.assert_called() -# mocked_study.assert_called_with( -# subject.objective, **subject.all_kwargs) -# self.assertTrue(best_score is mocked_study.best_trial.value) -# self.assertTrue(subject.best_params is mocked_study.\ -# best_trial.params) -# self.assertTrue('keyword2' in subject.all_kwargs.keys()) -# return - -# @unittest.mock.patch('gandy.models.models.UncertaintyModel') -# def test_train_best(self, mocked_UM): -# """proper access of best params and training of a new instance""" -# mocked_UMin = unittest.mock.MagicMock() -# mocked_UM.return_value = mocked_UMin -# subject = opt.OptRoutine(subject=gandy.models.models. -# UncertaintyModel, -# Xs=numpy.array([1, 2, 3]), -# Ys=numpy.array([1, 2, 3]), -# search_space={'hyp1': (1, 10), -# 'hyp2': ['a', 'b']}, -# keyword=5) -# # failure no best params -# with self.assertRaises(AttributeError): -# subject.train_best() -# # set and run -# subject.best_params = {'a': 10} -# model = subject.train_best(keyword2=10) -# mocked_UM.assert_called_with(**subject.best_params, -# **subject.all_kwargs) -# mocked_UMin.fit.assert_called_with(**subject.best_params, -# **subject.all_kwargs) -# self.assertTrue(model is mocked_UMin) -# self.asserTrue('keyword2' in subject.all_kwargs.keys()) -# return +class TestOptRoutine(unittest.TestCase): + """User interface class""" + + def test___init__(self): + """proper saving of keyword arguments and data saving""" + # failure case not correct model type + with self.assertRaises(TypeError): + subject = opt.OptRoutine(subject=opt.SearchableSpace, + Xs=numpy.array([1, 2, 3]), + Ys=numpy.array([1, 2, 3]), + search_space={'hyp1': (1, 10), + 'hyp2': ['a', 'b']}, + keyword=5) + # failure case data not iterable + with self.assertRaises(TypeError): + subject = opt.OptRoutine(subject=gandy.models.models. + UncertaintyModel, + Xs='str', + Ys=numpy.array([1, 2, 3]), + search_space={'hyp1': (1, 10), + 'hyp2': ['a', 'b']}, + keyword=5) + with self.assertRaises(TypeError): + subject = opt.OptRoutine(subject=gandy.models.models. + UncertaintyModel, + Xs=numpy.array([1, 2, 3]), + Ys='str', + search_space={'hyp1': (1, 10), + 'hyp2': ['a', 'b']}, + keyword=5) + # expected success + subject = opt.OptRoutine(subject=gandy.models.models. + UncertaintyModel, + Xs=numpy.array([1, 2, 3]), + Ys=numpy.array([1, 2, 3]), + search_space={'hyp1': (1, 10), + 'hyp2': ['a', 'b']}, + keyword=5) + self.assertTrue(subject.Xs is not None) + self.assertTrue(subject.Ys is not None) + self.assertTrue(subject.subject == + gandy.models.models.UncertaintyModel) + self.assertEqual(subject.search_space, {'hyp1': (1, 10), + 'hyp2': ['a', 'b']}) + self.assertTrue('keyword' in subject.all_kwargs.keys()) + return + + @unittest.mock.patch('gandy.optimization.hypersearch.SearchableSpace') + def test__set_param_space(self, mocked_SS): + """proper parsing of dictionary into SearchableSpace objects""" + mocked_SS.side_effect = ['ss1', 'ss2'] + subject = opt.OptRoutine(subject=gandy.models.models. + UncertaintyModel, + Xs=numpy.array([1, 2, 3]), + Ys=numpy.array([1, 2, 3]), + search_space={'hyp1': (1, 10), + 'hyp2': ['a', 'b']}, + keyword=5) + subject._set_param_space() + mocked_SS.assert_called_with('hyp2', ['a', 'b']) + self.assertEqual(mocked_SS.call_count, 2) + return + + @unittest.mock.patch('gandy.optimization.hypersearch.SubjectObjective') + def test__set_objective(self, mocked_objective): + """ensure proper calling of SubjectObjective class""" + mocked_objective.return_value = 'objective' + subject = opt.OptRoutine(subject=gandy.models.models. + UncertaintyModel, + Xs=numpy.array([1, 2, 3]), + Ys=numpy.array([1, 2, 3]), + search_space={'hyp1': (1, 10), + 'hyp2': ['a', 'b']}, + keyword=5) + mocked__set_param = unittest.mock.MagicMock() + subject._set_param_space = mocked__set_param + # set the objective + subject._set_objective() + mocked_objective.assert_called_with(subject.subject, + subject.Xs, + subject.Ys, + **subject.all_kwargs) + self.assertEqual(subject.objective, 'objective') + mocked__set_param.assert_called() + return + + @unittest.mock.patch('optuna.create_study', return_value='study') + def test__set_study(self, mocked_cstudy): + """Can a study be correctly called and stored""" + subject = opt.OptRoutine(subject=gandy.models.models. + UncertaintyModel, + Xs=numpy.array([1, 2, 3]), + Ys=numpy.array([1, 2, 3]), + search_space={'hyp1': (1, 10), + 'hyp2': ['a', 'b']}, + keyword=5) + subject._set_study() + self.assertTrue(subject.study == 'study') + mocked_cstudy.assert_called_with(**subject.all_kwargs) + return + + def test_optimize(self): + """acceptance of kwargs and nested calls""" + subject = opt.OptRoutine(subject=gandy.models.models. + UncertaintyModel, + Xs=numpy.array([1, 2, 3]), + Ys=numpy.array([1, 2, 3]), + keyword=5) + + # failure mode no seach space specified + with self.assertRaises(AttributeError): + subject.optimize() + + # set up mocked objects + mocked_set_obj = unittest.mock.MagicMock() + mocked_obj = unittest.mock.MagicMock() + mocked_set_study = unittest.mock.MagicMock() + mocked_study = unittest.mock.MagicMock() + subject._set_objective = mocked_set_obj + subject.objective = mocked_obj + subject._set_study = mocked_set_study + subject.study = mocked_study + + # success case, set search space and pass new kwargs + best_score = subject.optimize(search_space={'hyp1': (1, 10), + 'hyp2': ['a', 'b']}, + keyword2=10) + mocked_set_obj.assert_called() + mocked_set_study.assert_called() + mocked_study.optimize.assert_called_with( + subject.objective, **subject.all_kwargs) + self.assertTrue(best_score is mocked_study.best_value) + self.assertTrue(subject.best_params is mocked_study. + best_params) + self.assertTrue('keyword2' in subject.all_kwargs.keys()) + return + + def test_train_best(self): + """proper access of best params and training of a new instance""" + mocked_UM = unittest.mock.MagicMock() + mocked_UMin = unittest.mock.MagicMock() + mocked_UM.return_value = mocked_UMin + subject = opt.OptRoutine(subject=gandy.models.models. + UncertaintyModel, + Xs=numpy.array([1, 2, 3]), + Ys=numpy.array([1, 2, 3]), + search_space={'hyp1': (1, 10), + 'hyp2': ['a', 'b']}, + keyword=5) + subject.subject = mocked_UM + # failure no best params + with self.assertRaises(AttributeError): + subject.train_best() + # set and run + subject.best_params = {'a': 10} + model = subject.train_best() + mocked_UM.assert_called_with(**subject.best_params, + **subject.all_kwargs) + mocked_UMin.fit.assert_called_with(**subject.best_params, + **subject.all_kwargs) + self.assertTrue(model is mocked_UMin) + return From b97023bfd21c12c3af2c189c2781a3e6ea2887fe Mon Sep 17 00:00:00 2001 From: evankomp Date: Thu, 11 Mar 2021 16:01:13 -0800 Subject: [PATCH 74/99] first pass bnn demo --- examples/BNN_demo.ipynb | 2287 ++++++++++++++++++++++++++ gandy/models/bnns.py | 8 +- gandy/tests/test_models/test_bnns.py | 5 - 3 files changed, 2289 insertions(+), 11 deletions(-) create mode 100644 examples/BNN_demo.ipynb diff --git a/examples/BNN_demo.ipynb b/examples/BNN_demo.ipynb new file mode 100644 index 0000000..34a64fb --- /dev/null +++ b/examples/BNN_demo.ipynb @@ -0,0 +1,2287 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "hybrid-postage", + "metadata": {}, + "source": [ + "# Demonstration of BNNs as uncertainty models" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "short-connecticut", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import sklearn.datasets\n", + "import sklearn.model_selection\n", + "import sklearn.preprocessing\n", + "\n", + "import tensorflow as tf\n", + "import tensorflow_probability as tfp\n", + "import tensorflow_datasets\n", + "\n", + "import gandy.models.bnns" + ] + }, + { + "cell_type": "code", + "execution_count": 133, + "id": "waiting-result", + "metadata": {}, + "outputs": [], + "source": [ + "## loading the data\n", + "Xs, Ys = sklearn.datasets.load_boston(return_X_y=True)\n", + "Xsr, Xst, Ysr, Yst = sklearn.model_selection.train_test_split(Xs, Ys, train_size = 0.8)\n", + "Ysr = Ysr.reshape(-1,1); Yst = Yst.reshape(-1,1)\n", + "## normalizing the features and scaling the target\n", + "norm = sklearn.preprocessing.Normalizer()\n", + "scale = sklearn.preprocessing.StandardScaler()\n", + "Xsr = norm.fit_transform(Xsr)\n", + "Xst = norm.transform(Xst)\n", + "Ysr = scale.fit_transform(Ysr)\n", + "Yst = scale.transform(Yst)" + ] + }, + { + "cell_type": "code", + "execution_count": 134, + "id": "complete-expense", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X data: (404, 13) float64\n", + "Y data: (404, 1) float64\n" + ] + } + ], + "source": [ + "print('X data: ', Xsr.shape, Xsr.dtype)\n", + "print('Y data: ', Ysr.shape, Ysr.dtype)" + ] + }, + { + "cell_type": "markdown", + "id": "fifth-defensive", + "metadata": {}, + "source": [ + "Instantiate our model - using two hidden layers with 3 neurons each" + ] + }, + { + "cell_type": "code", + "execution_count": 135, + "id": "accessible-ready", + "metadata": {}, + "outputs": [], + "source": [ + "bnn = gandy.models.bnns.BNN(Xs.shape[1:], (1,), \n", + " train_size=len(Xsr), \n", + " metrics=['mae'],\n", + " neurons=(3,3))" + ] + }, + { + "cell_type": "markdown", + "id": "informal-outside", + "metadata": {}, + "source": [ + "Train it for 1000 epochs, default batch size." + ] + }, + { + "cell_type": "code", + "execution_count": 136, + "id": "natural-couple", + "metadata": { + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 1/1000\n", + "13/13 [==============================] - 1s 847us/step - loss: 2.8041 - mae: 1.2213\n", + "Epoch 2/1000\n", + "13/13 [==============================] - 0s 770us/step - loss: 2.6672 - mae: 1.1379\n", + "Epoch 3/1000\n", + "13/13 [==============================] - 0s 757us/step - loss: 2.5625 - mae: 1.1922\n", + "Epoch 4/1000\n", + "13/13 [==============================] - 0s 764us/step - loss: 2.2123 - mae: 1.0164\n", + "Epoch 5/1000\n", + "13/13 [==============================] - 0s 759us/step - loss: 2.0716 - mae: 1.0783\n", + "Epoch 6/1000\n", + "13/13 [==============================] - 0s 754us/step - loss: 2.0409 - mae: 1.0826\n", + "Epoch 7/1000\n", + "13/13 [==============================] - 0s 775us/step - loss: 1.7491 - mae: 0.9929\n", + "Epoch 8/1000\n", + "13/13 [==============================] - 0s 785us/step - loss: 2.1113 - mae: 1.0331\n", + "Epoch 9/1000\n", + "13/13 [==============================] - 0s 785us/step - loss: 1.8911 - mae: 1.0953\n", + "Epoch 10/1000\n", + "13/13 [==============================] - 0s 761us/step - loss: 2.0516 - mae: 1.1019\n", + "Epoch 11/1000\n", + "13/13 [==============================] - 0s 769us/step - loss: 1.8686 - mae: 1.0198\n", + "Epoch 12/1000\n", + "13/13 [==============================] - 0s 778us/step - loss: 1.7792 - mae: 1.0496\n", + "Epoch 13/1000\n", + "13/13 [==============================] - 0s 760us/step - loss: 1.8546 - mae: 1.0699\n", + "Epoch 14/1000\n", + "13/13 [==============================] - 0s 780us/step - loss: 1.8324 - mae: 1.1628\n", + "Epoch 15/1000\n", + "13/13 [==============================] - 0s 771us/step - loss: 1.6721 - mae: 1.1144\n", + "Epoch 16/1000\n", + "13/13 [==============================] - 0s 792us/step - loss: 1.8686 - mae: 0.9695\n", + "Epoch 17/1000\n", + "13/13 [==============================] - 0s 799us/step - loss: 1.6657 - mae: 1.0399\n", + "Epoch 18/1000\n", + "13/13 [==============================] - 0s 811us/step - loss: 1.5542 - mae: 0.9580\n", + "Epoch 19/1000\n", + "13/13 [==============================] - 0s 799us/step - loss: 1.6353 - mae: 1.1670\n", + "Epoch 20/1000\n", + "13/13 [==============================] - 0s 802us/step - loss: 1.8236 - mae: 1.0519\n", + "Epoch 21/1000\n", + "13/13 [==============================] - 0s 794us/step - loss: 1.6438 - mae: 0.9863\n", + "Epoch 22/1000\n", + "13/13 [==============================] - 0s 795us/step - loss: 1.7089 - mae: 0.9593\n", + "Epoch 23/1000\n", + "13/13 [==============================] - 0s 796us/step - loss: 1.6273 - mae: 1.0677\n", + "Epoch 24/1000\n", + "13/13 [==============================] - 0s 813us/step - loss: 1.5083 - mae: 1.0911\n", + "Epoch 25/1000\n", + "13/13 [==============================] - 0s 848us/step - loss: 1.6686 - mae: 1.0366\n", + "Epoch 26/1000\n", + "13/13 [==============================] - 0s 830us/step - loss: 1.4822 - mae: 1.0088\n", + "Epoch 27/1000\n", + "13/13 [==============================] - 0s 847us/step - loss: 1.5268 - mae: 1.0164\n", + "Epoch 28/1000\n", + "13/13 [==============================] - 0s 822us/step - loss: 1.4263 - mae: 1.0035\n", + "Epoch 29/1000\n", + "13/13 [==============================] - 0s 810us/step - loss: 1.6339 - mae: 1.1277\n", + "Epoch 30/1000\n", + "13/13 [==============================] - 0s 821us/step - loss: 1.6162 - mae: 1.0669\n", + "Epoch 31/1000\n", + "13/13 [==============================] - 0s 822us/step - loss: 1.5061 - mae: 1.0290\n", + "Epoch 32/1000\n", + "13/13 [==============================] - 0s 810us/step - loss: 1.5248 - mae: 1.0701\n", + "Epoch 33/1000\n", + "13/13 [==============================] - 0s 818us/step - loss: 1.5036 - mae: 1.0835\n", + "Epoch 34/1000\n", + "13/13 [==============================] - 0s 809us/step - loss: 1.5248 - mae: 1.0530\n", + "Epoch 35/1000\n", + "13/13 [==============================] - 0s 803us/step - loss: 1.4409 - mae: 1.0619\n", + "Epoch 36/1000\n", + "13/13 [==============================] - 0s 827us/step - loss: 1.4561 - mae: 1.0495\n", + "Epoch 37/1000\n", + "13/13 [==============================] - 0s 812us/step - loss: 1.5414 - mae: 1.1210\n", + "Epoch 38/1000\n", + "13/13 [==============================] - 0s 843us/step - loss: 1.4531 - mae: 1.0074\n", + "Epoch 39/1000\n", + "13/13 [==============================] - 0s 852us/step - loss: 1.3954 - mae: 1.1062\n", + "Epoch 40/1000\n", + "13/13 [==============================] - 0s 848us/step - loss: 1.5333 - mae: 1.1108\n", + "Epoch 41/1000\n", + "13/13 [==============================] - 0s 864us/step - loss: 1.4864 - mae: 1.0744\n", + "Epoch 42/1000\n", + "13/13 [==============================] - 0s 850us/step - loss: 1.4114 - mae: 1.0213\n", + "Epoch 43/1000\n", + "13/13 [==============================] - 0s 838us/step - loss: 1.5156 - mae: 1.0719\n", + "Epoch 44/1000\n", + "13/13 [==============================] - 0s 847us/step - loss: 1.4592 - mae: 1.0409\n", + "Epoch 45/1000\n", + "13/13 [==============================] - 0s 854us/step - loss: 1.4734 - mae: 1.0406\n", + "Epoch 46/1000\n", + "13/13 [==============================] - 0s 840us/step - loss: 1.3832 - mae: 1.0677\n", + "Epoch 47/1000\n", + "13/13 [==============================] - 0s 831us/step - loss: 1.4911 - mae: 1.0674\n", + "Epoch 48/1000\n", + "13/13 [==============================] - 0s 900us/step - loss: 1.5189 - mae: 1.1181\n", + "Epoch 49/1000\n", + "13/13 [==============================] - 0s 895us/step - loss: 1.5473 - mae: 1.1260\n", + "Epoch 50/1000\n", + "13/13 [==============================] - 0s 899us/step - loss: 1.4789 - mae: 1.0493\n", + "Epoch 51/1000\n", + "13/13 [==============================] - 0s 869us/step - loss: 1.4645 - mae: 1.0138\n", + "Epoch 52/1000\n", + "13/13 [==============================] - 0s 873us/step - loss: 1.4312 - mae: 0.9815\n", + "Epoch 53/1000\n", + "13/13 [==============================] - 0s 895us/step - loss: 1.4594 - mae: 1.0390\n", + "Epoch 54/1000\n", + "13/13 [==============================] - 0s 882us/step - loss: 1.5313 - mae: 1.1021\n", + "Epoch 55/1000\n", + "13/13 [==============================] - 0s 873us/step - loss: 1.4747 - mae: 1.0939\n", + "Epoch 56/1000\n", + "13/13 [==============================] - 0s 863us/step - loss: 1.4639 - mae: 1.1567\n", + "Epoch 57/1000\n", + "13/13 [==============================] - 0s 877us/step - loss: 1.5190 - mae: 1.1255\n", + "Epoch 58/1000\n", + "13/13 [==============================] - 0s 872us/step - loss: 1.5896 - mae: 1.1388\n", + "Epoch 59/1000\n", + "13/13 [==============================] - 0s 872us/step - loss: 1.4557 - mae: 1.0124\n", + "Epoch 60/1000\n", + "13/13 [==============================] - 0s 857us/step - loss: 1.4523 - mae: 1.0818\n", + "Epoch 61/1000\n", + "13/13 [==============================] - 0s 866us/step - loss: 1.4284 - mae: 1.0534\n", + "Epoch 62/1000\n", + "13/13 [==============================] - 0s 877us/step - loss: 1.4086 - mae: 1.0728\n", + "Epoch 63/1000\n", + "13/13 [==============================] - 0s 880us/step - loss: 1.4584 - mae: 1.1574\n", + "Epoch 64/1000\n", + "13/13 [==============================] - 0s 895us/step - loss: 1.5075 - mae: 1.1098\n", + "Epoch 65/1000\n", + "13/13 [==============================] - 0s 902us/step - loss: 1.4098 - mae: 1.0823\n", + "Epoch 66/1000\n", + "13/13 [==============================] - 0s 925us/step - loss: 1.4535 - mae: 1.1136\n", + "Epoch 67/1000\n", + "13/13 [==============================] - 0s 905us/step - loss: 1.4222 - mae: 1.0069\n", + "Epoch 68/1000\n", + "13/13 [==============================] - 0s 898us/step - loss: 1.5527 - mae: 1.1695\n", + "Epoch 69/1000\n", + "13/13 [==============================] - 0s 902us/step - loss: 1.4791 - mae: 1.0986\n", + "Epoch 70/1000\n", + "13/13 [==============================] - 0s 907us/step - loss: 1.4573 - mae: 1.1400\n", + "Epoch 71/1000\n", + "13/13 [==============================] - 0s 917us/step - loss: 1.4321 - mae: 1.1190\n", + "Epoch 72/1000\n", + "13/13 [==============================] - 0s 888us/step - loss: 1.4167 - mae: 1.1090\n", + "Epoch 73/1000\n", + "13/13 [==============================] - 0s 900us/step - loss: 1.3810 - mae: 1.0624\n", + "Epoch 74/1000\n", + "13/13 [==============================] - 0s 928us/step - loss: 1.4078 - mae: 1.1006\n", + "Epoch 75/1000\n", + "13/13 [==============================] - 0s 957us/step - loss: 1.3842 - mae: 1.1005\n", + "Epoch 76/1000\n", + "13/13 [==============================] - 0s 958us/step - loss: 1.3951 - mae: 1.0928\n", + "Epoch 77/1000\n", + "13/13 [==============================] - 0s 942us/step - loss: 1.4130 - mae: 1.0825\n", + "Epoch 78/1000\n", + "13/13 [==============================] - 0s 957us/step - loss: 1.3785 - mae: 1.0648\n", + "Epoch 79/1000\n", + "13/13 [==============================] - 0s 933us/step - loss: 1.4826 - mae: 1.1419\n", + "Epoch 80/1000\n", + "13/13 [==============================] - 0s 939us/step - loss: 1.4074 - mae: 1.0122\n", + "Epoch 81/1000\n", + "13/13 [==============================] - 0s 945us/step - loss: 1.4325 - mae: 1.1177\n", + "Epoch 82/1000\n", + "13/13 [==============================] - 0s 935us/step - loss: 1.3885 - mae: 1.0421\n", + "Epoch 83/1000\n", + "13/13 [==============================] - 0s 943us/step - loss: 1.4094 - mae: 1.0313\n", + "Epoch 84/1000\n", + "13/13 [==============================] - 0s 950us/step - loss: 1.4906 - mae: 1.1246\n", + "Epoch 85/1000\n", + "13/13 [==============================] - 0s 935us/step - loss: 1.4798 - mae: 1.1306\n", + "Epoch 86/1000\n", + "13/13 [==============================] - 0s 945us/step - loss: 1.4694 - mae: 1.1967\n", + "Epoch 87/1000\n", + "13/13 [==============================] - 0s 920us/step - loss: 1.4313 - mae: 1.1507\n", + "Epoch 88/1000\n", + "13/13 [==============================] - 0s 923us/step - loss: 1.4427 - mae: 1.0576\n", + "Epoch 89/1000\n", + "13/13 [==============================] - 0s 931us/step - loss: 1.4085 - mae: 1.0421\n", + "Epoch 90/1000\n", + "13/13 [==============================] - 0s 943us/step - loss: 1.3677 - mae: 1.1546\n", + "Epoch 91/1000\n", + "13/13 [==============================] - 0s 938us/step - loss: 1.4941 - mae: 1.1076\n", + "Epoch 92/1000\n", + "13/13 [==============================] - 0s 953us/step - loss: 1.4996 - mae: 1.1621\n", + "Epoch 93/1000\n", + "13/13 [==============================] - 0s 944us/step - loss: 1.4746 - mae: 1.1574\n", + "Epoch 94/1000\n", + "13/13 [==============================] - 0s 975us/step - loss: 1.3825 - mae: 1.1321\n", + "Epoch 95/1000\n", + "13/13 [==============================] - 0s 972us/step - loss: 1.4842 - mae: 1.1361\n", + "Epoch 96/1000\n", + "13/13 [==============================] - 0s 958us/step - loss: 1.4512 - mae: 1.1130\n", + "Epoch 97/1000\n", + "13/13 [==============================] - 0s 972us/step - loss: 1.4580 - mae: 1.0604\n", + "Epoch 98/1000\n", + "13/13 [==============================] - 0s 956us/step - loss: 1.4712 - mae: 1.0486\n", + "Epoch 99/1000\n", + "13/13 [==============================] - 0s 981us/step - loss: 1.4129 - mae: 1.0610\n", + "Epoch 100/1000\n", + "13/13 [==============================] - 0s 958us/step - loss: 1.4546 - mae: 1.2158\n", + "Epoch 101/1000\n", + "13/13 [==============================] - 0s 986us/step - loss: 1.3667 - mae: 1.0784\n", + "Epoch 102/1000\n", + "13/13 [==============================] - 0s 998us/step - loss: 1.4008 - mae: 1.0758\n", + "Epoch 103/1000\n", + "13/13 [==============================] - 0s 969us/step - loss: 1.4155 - mae: 1.1386\n", + "Epoch 104/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.5099 - mae: 1.1587\n", + "Epoch 105/1000\n", + "13/13 [==============================] - 0s 998us/step - loss: 1.5019 - mae: 1.2244\n", + "Epoch 106/1000\n", + "13/13 [==============================] - 0s 974us/step - loss: 1.4925 - mae: 1.1256\n", + "Epoch 107/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4489 - mae: 1.0856\n", + "Epoch 108/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4478 - mae: 1.0526\n", + "Epoch 109/1000\n", + "13/13 [==============================] - 0s 994us/step - loss: 1.4070 - mae: 1.0286\n", + "Epoch 110/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4812 - mae: 1.1863\n", + "Epoch 111/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3567 - mae: 1.0501\n", + "Epoch 112/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4505 - mae: 1.1223\n", + "Epoch 113/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.5051 - mae: 1.1636\n", + "Epoch 114/1000\n", + "13/13 [==============================] - 0s 999us/step - loss: 1.4662 - mae: 1.1563\n", + "Epoch 115/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3864 - mae: 1.2185\n", + "Epoch 116/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3915 - mae: 1.0704\n", + "Epoch 117/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4927 - mae: 1.2071\n", + "Epoch 118/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4515 - mae: 1.0965\n", + "Epoch 119/1000\n", + "13/13 [==============================] - 0s 990us/step - loss: 1.4482 - mae: 1.0812\n", + "Epoch 120/1000\n", + "13/13 [==============================] - 0s 999us/step - loss: 1.4458 - mae: 1.0661\n", + "Epoch 121/1000\n", + "13/13 [==============================] - 0s 997us/step - loss: 1.3957 - mae: 1.0994\n", + "Epoch 122/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4234 - mae: 1.1830\n", + "Epoch 123/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4279 - mae: 1.1275\n", + "Epoch 124/1000\n", + "13/13 [==============================] - 0s 998us/step - loss: 1.4155 - mae: 1.2371\n", + "Epoch 125/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4152 - mae: 1.1662\n", + "Epoch 126/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4436 - mae: 1.1533\n", + "Epoch 127/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4548 - mae: 1.1175\n", + "Epoch 128/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4318 - mae: 1.1338\n", + "Epoch 129/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.5312 - mae: 1.1411\n", + "Epoch 130/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4388 - mae: 1.0560\n", + "Epoch 131/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.5117 - mae: 1.1848\n", + "Epoch 132/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4703 - mae: 1.0648\n", + "Epoch 133/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4517 - mae: 1.1471\n", + "Epoch 134/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4161 - mae: 1.1008\n", + "Epoch 135/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4105 - mae: 1.1198\n", + "Epoch 136/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4175 - mae: 1.1254\n", + "Epoch 137/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4433 - mae: 1.1344\n", + "Epoch 138/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4042 - mae: 1.0484\n", + "Epoch 139/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4717 - mae: 1.1784\n", + "Epoch 140/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4704 - mae: 1.2275\n", + "Epoch 141/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4378 - mae: 1.0697\n", + "Epoch 142/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4532 - mae: 1.1211\n", + "Epoch 143/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.5064 - mae: 1.1429\n", + "Epoch 144/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4500 - mae: 1.1237\n", + "Epoch 145/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4458 - mae: 1.0803\n", + "Epoch 146/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4293 - mae: 1.0545\n", + "Epoch 147/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.5060 - mae: 1.1389\n", + "Epoch 148/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4679 - mae: 1.1980\n", + "Epoch 149/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3984 - mae: 1.1468\n", + "Epoch 150/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4316 - mae: 1.0610\n", + "Epoch 151/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4072 - mae: 1.1534\n", + "Epoch 152/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4424 - mae: 1.1180\n", + "Epoch 153/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4519 - mae: 1.1100\n", + "Epoch 154/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4555 - mae: 1.0739\n", + "Epoch 155/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4339 - mae: 1.0568\n", + "Epoch 156/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3793 - mae: 1.1191\n", + "Epoch 157/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4543 - mae: 1.1675\n", + "Epoch 158/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4539 - mae: 1.0906\n", + "Epoch 159/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4183 - mae: 1.1206\n", + "Epoch 160/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3898 - mae: 1.0607\n", + "Epoch 161/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4136 - mae: 1.1150\n", + "Epoch 162/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3454 - mae: 1.0815\n", + "Epoch 163/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3876 - mae: 1.0861\n", + "Epoch 164/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4491 - mae: 1.1364\n", + "Epoch 165/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4617 - mae: 1.1473\n", + "Epoch 166/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4929 - mae: 1.1003\n", + "Epoch 167/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4696 - mae: 1.1653\n", + "Epoch 168/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4618 - mae: 1.1927\n", + "Epoch 169/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4022 - mae: 1.1112\n", + "Epoch 170/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3900 - mae: 1.0553\n", + "Epoch 171/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3635 - mae: 1.0454\n", + "Epoch 172/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4193 - mae: 1.0488\n", + "Epoch 173/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4260 - mae: 1.1317\n", + "Epoch 174/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4509 - mae: 1.0852\n", + "Epoch 175/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4026 - mae: 1.1447\n", + "Epoch 176/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4139 - mae: 1.1094\n", + "Epoch 177/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4403 - mae: 1.0539\n", + "Epoch 178/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4273 - mae: 1.1456\n", + "Epoch 179/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4206 - mae: 1.0858\n", + "Epoch 180/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4507 - mae: 1.1371\n", + "Epoch 181/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3969 - mae: 1.1018\n", + "Epoch 182/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4687 - mae: 1.1706\n", + "Epoch 183/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3763 - mae: 1.0392\n", + "Epoch 184/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4257 - mae: 1.1660\n", + "Epoch 185/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3890 - mae: 1.1016\n", + "Epoch 186/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.5051 - mae: 1.1150\n", + "Epoch 187/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4265 - mae: 1.0968\n", + "Epoch 188/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4344 - mae: 1.0635\n", + "Epoch 189/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4134 - mae: 1.0309\n", + "Epoch 190/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4469 - mae: 1.1526\n", + "Epoch 191/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4014 - mae: 1.0707\n", + "Epoch 192/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3780 - mae: 1.0792\n", + "Epoch 193/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4298 - mae: 1.0989\n", + "Epoch 194/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4207 - mae: 1.1027\n", + "Epoch 195/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4494 - mae: 1.0948\n", + "Epoch 196/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4553 - mae: 1.0744\n", + "Epoch 197/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4252 - mae: 1.0903\n", + "Epoch 198/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.5019 - mae: 1.1662\n", + "Epoch 199/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3536 - mae: 1.0670\n", + "Epoch 200/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4674 - mae: 1.1114\n", + "Epoch 201/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4358 - mae: 1.1330\n", + "Epoch 202/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4712 - mae: 1.0960\n", + "Epoch 203/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4580 - mae: 1.0123\n", + "Epoch 204/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4451 - mae: 1.0419\n", + "Epoch 205/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4194 - mae: 1.1035\n", + "Epoch 206/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4119 - mae: 1.0680\n", + "Epoch 207/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3898 - mae: 1.1389\n", + "Epoch 208/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3646 - mae: 1.0445\n", + "Epoch 209/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4179 - mae: 1.0710\n", + "Epoch 210/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4066 - mae: 1.1556\n", + "Epoch 211/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4642 - mae: 1.0534\n", + "Epoch 212/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3396 - mae: 1.0155\n", + "Epoch 213/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3908 - mae: 1.0178\n", + "Epoch 214/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4165 - mae: 1.1241\n", + "Epoch 215/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3601 - mae: 1.1163\n", + "Epoch 216/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3955 - mae: 1.0983\n", + "Epoch 217/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3550 - mae: 1.1349\n", + "Epoch 218/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4429 - mae: 1.0960\n", + "Epoch 219/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4126 - mae: 1.0760\n", + "Epoch 220/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4488 - mae: 1.0348\n", + "Epoch 221/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3783 - mae: 1.0768\n", + "Epoch 222/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4126 - mae: 1.1821\n", + "Epoch 223/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4493 - mae: 1.0870\n", + "Epoch 224/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4316 - mae: 1.0866\n", + "Epoch 225/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3617 - mae: 1.0272\n", + "Epoch 226/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4311 - mae: 1.0801\n", + "Epoch 227/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3766 - mae: 1.0237\n", + "Epoch 228/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3768 - mae: 1.0927\n", + "Epoch 229/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3702 - mae: 1.1019\n", + "Epoch 230/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4597 - mae: 1.0844\n", + "Epoch 231/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3559 - mae: 1.1091\n", + "Epoch 232/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3307 - mae: 0.9692\n", + "Epoch 233/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3811 - mae: 1.0638\n", + "Epoch 234/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3365 - mae: 1.0219\n", + "Epoch 235/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4375 - mae: 1.1433\n", + "Epoch 236/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3709 - mae: 1.0442\n", + "Epoch 237/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4716 - mae: 1.0361\n", + "Epoch 238/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3150 - mae: 1.0131\n", + "Epoch 239/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3716 - mae: 1.0514\n", + "Epoch 240/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4206 - mae: 1.0263\n", + "Epoch 241/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3698 - mae: 1.0536\n", + "Epoch 242/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3475 - mae: 1.0447\n", + "Epoch 243/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3520 - mae: 1.0334\n", + "Epoch 244/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3906 - mae: 1.0037\n", + "Epoch 245/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4072 - mae: 1.0776\n", + "Epoch 246/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4641 - mae: 1.0672\n", + "Epoch 247/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4054 - mae: 1.0361\n", + "Epoch 248/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4063 - mae: 1.0778\n", + "Epoch 249/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4060 - mae: 0.9787\n", + "Epoch 250/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4357 - mae: 1.0881\n", + "Epoch 251/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3815 - mae: 1.1110\n", + "Epoch 252/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4169 - mae: 1.1697\n", + "Epoch 253/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4173 - mae: 1.0704\n", + "Epoch 254/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4199 - mae: 1.0168\n", + "Epoch 255/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3377 - mae: 0.9985\n", + "Epoch 256/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4374 - mae: 1.0961\n", + "Epoch 257/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3814 - mae: 1.1579\n", + "Epoch 258/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3711 - mae: 1.1116\n", + "Epoch 259/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3700 - mae: 1.0208\n", + "Epoch 260/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3743 - mae: 1.0677\n", + "Epoch 261/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4519 - mae: 1.0735\n", + "Epoch 262/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3609 - mae: 1.0508\n", + "Epoch 263/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3751 - mae: 0.9989\n", + "Epoch 264/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3971 - mae: 1.1097\n", + "Epoch 265/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4257 - mae: 1.1199\n", + "Epoch 266/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4009 - mae: 1.1247\n", + "Epoch 267/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3495 - mae: 1.0148\n", + "Epoch 268/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3731 - mae: 1.0270\n", + "Epoch 269/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4216 - mae: 1.1150\n", + "Epoch 270/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3853 - mae: 1.0409\n", + "Epoch 271/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3627 - mae: 1.0601\n", + "Epoch 272/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2881 - mae: 0.8983\n", + "Epoch 273/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3816 - mae: 1.0624\n", + "Epoch 274/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4065 - mae: 1.0191\n", + "Epoch 275/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3922 - mae: 1.0573\n", + "Epoch 276/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3804 - mae: 1.0753\n", + "Epoch 277/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3318 - mae: 0.9744\n", + "Epoch 278/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3151 - mae: 1.0394\n", + "Epoch 279/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4031 - mae: 1.0457\n", + "Epoch 280/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3545 - mae: 0.9898\n", + "Epoch 281/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4046 - mae: 1.0330\n", + "Epoch 282/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3846 - mae: 1.0994\n", + "Epoch 283/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3291 - mae: 0.9781\n", + "Epoch 284/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3181 - mae: 1.0067\n", + "Epoch 285/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4034 - mae: 1.0580\n", + "Epoch 286/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3650 - mae: 1.0047\n", + "Epoch 287/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3452 - mae: 0.9806\n", + "Epoch 288/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3122 - mae: 1.0002\n", + "Epoch 289/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3106 - mae: 0.9986\n", + "Epoch 290/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3102 - mae: 0.9707\n", + "Epoch 291/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2999 - mae: 0.9772\n", + "Epoch 292/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3821 - mae: 1.0038\n", + "Epoch 293/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2979 - mae: 0.9733\n", + "Epoch 294/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3538 - mae: 0.9642\n", + "Epoch 295/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3757 - mae: 1.0256\n", + "Epoch 296/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3683 - mae: 0.9878\n", + "Epoch 297/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3551 - mae: 1.0032\n", + "Epoch 298/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2970 - mae: 0.9042\n", + "Epoch 299/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2925 - mae: 0.8828\n", + "Epoch 300/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3228 - mae: 1.0090\n", + "Epoch 301/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3804 - mae: 1.0105\n", + "Epoch 302/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4004 - mae: 1.0395\n", + "Epoch 303/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2707 - mae: 0.9581\n", + "Epoch 304/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3450 - mae: 0.9836\n", + "Epoch 305/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3481 - mae: 0.9992\n", + "Epoch 306/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3580 - mae: 0.9691\n", + "Epoch 307/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2759 - mae: 0.9563\n", + "Epoch 308/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2966 - mae: 0.9460\n", + "Epoch 309/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3679 - mae: 1.0041\n", + "Epoch 310/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3029 - mae: 0.9814\n", + "Epoch 311/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3397 - mae: 1.0416\n", + "Epoch 312/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2394 - mae: 0.9486\n", + "Epoch 313/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2849 - mae: 0.9956\n", + "Epoch 314/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3054 - mae: 0.9717\n", + "Epoch 315/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3355 - mae: 0.9764\n", + "Epoch 316/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.4716 - mae: 0.9585\n", + "Epoch 317/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3308 - mae: 0.9959\n", + "Epoch 318/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3799 - mae: 0.9705\n", + "Epoch 319/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3515 - mae: 0.9779\n", + "Epoch 320/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3561 - mae: 1.0423\n", + "Epoch 321/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2573 - mae: 0.9230\n", + "Epoch 322/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2579 - mae: 0.9112\n", + "Epoch 323/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3880 - mae: 1.0073\n", + "Epoch 324/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3188 - mae: 0.9909\n", + "Epoch 325/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3340 - mae: 1.0011\n", + "Epoch 326/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3093 - mae: 0.8827\n", + "Epoch 327/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3167 - mae: 0.9909\n", + "Epoch 328/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3192 - mae: 0.8914\n", + "Epoch 329/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3188 - mae: 0.9601\n", + "Epoch 330/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2191 - mae: 0.9426\n", + "Epoch 331/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2706 - mae: 0.9724\n", + "Epoch 332/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3553 - mae: 0.9878\n", + "Epoch 333/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2976 - mae: 0.9449\n", + "Epoch 334/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2408 - mae: 0.8863\n", + "Epoch 335/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2350 - mae: 0.8682\n", + "Epoch 336/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3005 - mae: 0.9586\n", + "Epoch 337/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3313 - mae: 0.9738\n", + "Epoch 338/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3260 - mae: 0.9836\n", + "Epoch 339/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3030 - mae: 1.0137\n", + "Epoch 340/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3649 - mae: 0.9831\n", + "Epoch 341/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2762 - mae: 0.8921\n", + "Epoch 342/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3749 - mae: 0.9424\n", + "Epoch 343/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3569 - mae: 0.9381\n", + "Epoch 344/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2987 - mae: 0.9401\n", + "Epoch 345/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3114 - mae: 0.9312\n", + "Epoch 346/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2179 - mae: 0.9180\n", + "Epoch 347/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.2580 - mae: 0.8628\n", + "Epoch 348/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2506 - mae: 0.9207\n", + "Epoch 349/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3042 - mae: 0.9366\n", + "Epoch 350/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2449 - mae: 0.9246\n", + "Epoch 351/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2686 - mae: 0.9312\n", + "Epoch 352/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2654 - mae: 0.9023\n", + "Epoch 353/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2542 - mae: 0.9143\n", + "Epoch 354/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2062 - mae: 0.8260\n", + "Epoch 355/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2481 - mae: 0.8962\n", + "Epoch 356/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1724 - mae: 0.8837\n", + "Epoch 357/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.3018 - mae: 0.8667\n", + "Epoch 358/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2597 - mae: 0.9563\n", + "Epoch 359/1000\n", + "13/13 [==============================] - 0s 1ms/step - loss: 1.2375 - mae: 0.8965\n", + "Epoch 360/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.2523 - mae: 0.8764\n", + "Epoch 361/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.2523 - mae: 0.8820\n", + "Epoch 362/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1654 - mae: 0.8510\n", + "Epoch 363/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.2615 - mae: 0.9494\n", + "Epoch 364/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.2091 - mae: 0.8526\n", + "Epoch 365/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.2567 - mae: 0.8699\n", + "Epoch 366/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.2400 - mae: 0.7986\n", + "Epoch 367/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.2199 - mae: 0.8998\n", + "Epoch 368/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.2047 - mae: 0.8307\n", + "Epoch 369/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.2536 - mae: 0.8524\n", + "Epoch 370/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.2153 - mae: 0.8543\n", + "Epoch 371/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1903 - mae: 0.8496\n", + "Epoch 372/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1050 - mae: 0.7483\n", + "Epoch 373/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1939 - mae: 0.8472\n", + "Epoch 374/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.2777 - mae: 0.8579\n", + "Epoch 375/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.2180 - mae: 0.8837\n", + "Epoch 376/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1828 - mae: 0.8337\n", + "Epoch 377/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1469 - mae: 0.8331\n", + "Epoch 378/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1648 - mae: 0.8029\n", + "Epoch 379/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.2378 - mae: 0.8549\n", + "Epoch 380/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1562 - mae: 0.8353\n", + "Epoch 381/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1793 - mae: 0.8119\n", + "Epoch 382/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.2375 - mae: 0.9082\n", + "Epoch 383/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1672 - mae: 0.8202\n", + "Epoch 384/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1737 - mae: 0.8478\n", + "Epoch 385/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1915 - mae: 0.7800\n", + "Epoch 386/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.2353 - mae: 0.8952\n", + "Epoch 387/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1744 - mae: 0.8278\n", + "Epoch 388/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.2533 - mae: 0.8196\n", + "Epoch 389/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1627 - mae: 0.8103\n", + "Epoch 390/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.2364 - mae: 0.9476\n", + "Epoch 391/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1521 - mae: 0.7725\n", + "Epoch 392/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1804 - mae: 0.8209\n", + "Epoch 393/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1876 - mae: 0.8413\n", + "Epoch 394/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1976 - mae: 0.8075\n", + "Epoch 395/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1492 - mae: 0.7516\n", + "Epoch 396/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1908 - mae: 0.8421\n", + "Epoch 397/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.2111 - mae: 0.8548\n", + "Epoch 398/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1247 - mae: 0.7436\n", + "Epoch 399/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1391 - mae: 0.8428\n", + "Epoch 400/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0591 - mae: 0.7827\n", + "Epoch 401/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1312 - mae: 0.8123\n", + "Epoch 402/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1807 - mae: 0.8410\n", + "Epoch 403/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1785 - mae: 0.8390\n", + "Epoch 404/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0862 - mae: 0.7550\n", + "Epoch 405/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1053 - mae: 0.8037\n", + "Epoch 406/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1036 - mae: 0.7469\n", + "Epoch 407/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1193 - mae: 0.7569\n", + "Epoch 408/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1342 - mae: 0.7912\n", + "Epoch 409/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0842 - mae: 0.6955\n", + "Epoch 410/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1213 - mae: 0.8091\n", + "Epoch 411/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1367 - mae: 0.7890\n", + "Epoch 412/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1624 - mae: 0.7712\n", + "Epoch 413/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1338 - mae: 0.7917\n", + "Epoch 414/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0648 - mae: 0.7577\n", + "Epoch 415/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0779 - mae: 0.7155\n", + "Epoch 416/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.2355 - mae: 0.8060\n", + "Epoch 417/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1009 - mae: 0.7640\n", + "Epoch 418/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1067 - mae: 0.7614\n", + "Epoch 419/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0416 - mae: 0.7663\n", + "Epoch 420/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0207 - mae: 0.7772\n", + "Epoch 421/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0101 - mae: 0.7161\n", + "Epoch 422/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1530 - mae: 0.8079\n", + "Epoch 423/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1539 - mae: 0.8004\n", + "Epoch 424/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1353 - mae: 0.8147\n", + "Epoch 425/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1474 - mae: 0.8064\n", + "Epoch 426/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0758 - mae: 0.7396\n", + "Epoch 427/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0998 - mae: 0.7893\n", + "Epoch 428/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1141 - mae: 0.7933\n", + "Epoch 429/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1287 - mae: 0.7507\n", + "Epoch 430/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1291 - mae: 0.8470\n", + "Epoch 431/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0642 - mae: 0.7205\n", + "Epoch 432/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0449 - mae: 0.7432\n", + "Epoch 433/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0809 - mae: 0.7647\n", + "Epoch 434/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1058 - mae: 0.7925\n", + "Epoch 435/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0410 - mae: 0.7574\n", + "Epoch 436/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1070 - mae: 0.8321\n", + "Epoch 437/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0045 - mae: 0.7238\n", + "Epoch 438/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0762 - mae: 0.7573\n", + "Epoch 439/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0049 - mae: 0.6891\n", + "Epoch 440/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0113 - mae: 0.7949\n", + "Epoch 441/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0224 - mae: 0.7225\n", + "Epoch 442/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0270 - mae: 0.7590\n", + "Epoch 443/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0455 - mae: 0.7325\n", + "Epoch 444/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1146 - mae: 0.7376\n", + "Epoch 445/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0765 - mae: 0.7308\n", + "Epoch 446/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0845 - mae: 0.7239\n", + "Epoch 447/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0830 - mae: 0.7073\n", + "Epoch 448/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0544 - mae: 0.7463\n", + "Epoch 449/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9880 - mae: 0.6733\n", + "Epoch 450/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0729 - mae: 0.7659\n", + "Epoch 451/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0104 - mae: 0.6597\n", + "Epoch 452/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9973 - mae: 0.7342\n", + "Epoch 453/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0431 - mae: 0.7639\n", + "Epoch 454/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9905 - mae: 0.6933\n", + "Epoch 455/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0183 - mae: 0.7226\n", + "Epoch 456/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0221 - mae: 0.6665\n", + "Epoch 457/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0425 - mae: 0.7150\n", + "Epoch 458/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0335 - mae: 0.8074\n", + "Epoch 459/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0531 - mae: 0.6937\n", + "Epoch 460/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9994 - mae: 0.7127\n", + "Epoch 461/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0666 - mae: 0.6969\n", + "Epoch 462/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0737 - mae: 0.7531\n", + "Epoch 463/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1061 - mae: 0.7882\n", + "Epoch 464/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1530 - mae: 0.7317\n", + "Epoch 465/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9757 - mae: 0.7364\n", + "Epoch 466/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9979 - mae: 0.7472\n", + "Epoch 467/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0501 - mae: 0.7099\n", + "Epoch 468/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1220 - mae: 0.7865\n", + "Epoch 469/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0627 - mae: 0.7668\n", + "Epoch 470/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9729 - mae: 0.7477\n", + "Epoch 471/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0815 - mae: 0.7196\n", + "Epoch 472/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0283 - mae: 0.6645\n", + "Epoch 473/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0420 - mae: 0.7726\n", + "Epoch 474/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0440 - mae: 0.7474\n", + "Epoch 475/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9326 - mae: 0.6692\n", + "Epoch 476/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1927 - mae: 0.8322\n", + "Epoch 477/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0293 - mae: 0.7161\n", + "Epoch 478/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0220 - mae: 0.6862\n", + "Epoch 479/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0299 - mae: 0.6844\n", + "Epoch 480/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9837 - mae: 0.7416\n", + "Epoch 481/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0260 - mae: 0.7712\n", + "Epoch 482/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0674 - mae: 0.7280\n", + "Epoch 483/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9487 - mae: 0.7073\n", + "Epoch 484/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0442 - mae: 0.7065\n", + "Epoch 485/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0375 - mae: 0.7709\n", + "Epoch 486/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0373 - mae: 0.6770\n", + "Epoch 487/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0419 - mae: 0.7704\n", + "Epoch 488/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9805 - mae: 0.6623\n", + "Epoch 489/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9987 - mae: 0.6935\n", + "Epoch 490/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0146 - mae: 0.7805\n", + "Epoch 491/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0035 - mae: 0.7163\n", + "Epoch 492/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0197 - mae: 0.7562\n", + "Epoch 493/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9680 - mae: 0.7125\n", + "Epoch 494/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9976 - mae: 0.7223\n", + "Epoch 495/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9890 - mae: 0.7102\n", + "Epoch 496/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0462 - mae: 0.7120\n", + "Epoch 497/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.1398 - mae: 0.7285\n", + "Epoch 498/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0073 - mae: 0.6793\n", + "Epoch 499/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9787 - mae: 0.6892\n", + "Epoch 500/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9933 - mae: 0.7069\n", + "Epoch 501/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9947 - mae: 0.6394\n", + "Epoch 502/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0193 - mae: 0.7283\n", + "Epoch 503/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0114 - mae: 0.7200\n", + "Epoch 504/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0481 - mae: 0.7057\n", + "Epoch 505/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0068 - mae: 0.6846\n", + "Epoch 506/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0019 - mae: 0.6956\n", + "Epoch 507/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9632 - mae: 0.6675\n", + "Epoch 508/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0915 - mae: 0.7144\n", + "Epoch 509/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9666 - mae: 0.7038\n", + "Epoch 510/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9761 - mae: 0.6764\n", + "Epoch 511/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0235 - mae: 0.6803\n", + "Epoch 512/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0028 - mae: 0.6805\n", + "Epoch 513/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9491 - mae: 0.6851\n", + "Epoch 514/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9345 - mae: 0.7146\n", + "Epoch 515/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9499 - mae: 0.6754\n", + "Epoch 516/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.8865 - mae: 0.6329\n", + "Epoch 517/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9468 - mae: 0.6663\n", + "Epoch 518/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9348 - mae: 0.7288\n", + "Epoch 519/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9925 - mae: 0.7702\n", + "Epoch 520/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0558 - mae: 0.6812\n", + "Epoch 521/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9872 - mae: 0.6579\n", + "Epoch 522/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9837 - mae: 0.6923\n", + "Epoch 523/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9819 - mae: 0.7159\n", + "Epoch 524/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9355 - mae: 0.6829\n", + "Epoch 525/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0993 - mae: 0.7169\n", + "Epoch 526/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0079 - mae: 0.7160\n", + "Epoch 527/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0137 - mae: 0.6863\n", + "Epoch 528/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9374 - mae: 0.6758\n", + "Epoch 529/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0669 - mae: 0.7774\n", + "Epoch 530/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0262 - mae: 0.6905\n", + "Epoch 531/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9662 - mae: 0.6891\n", + "Epoch 532/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0688 - mae: 0.6822\n", + "Epoch 533/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9341 - mae: 0.6710\n", + "Epoch 534/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9274 - mae: 0.6571\n", + "Epoch 535/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9730 - mae: 0.7311\n", + "Epoch 536/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9876 - mae: 0.6757\n", + "Epoch 537/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9977 - mae: 0.6728\n", + "Epoch 538/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9813 - mae: 0.7262\n", + "Epoch 539/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9276 - mae: 0.6685\n", + "Epoch 540/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9856 - mae: 0.7533\n", + "Epoch 541/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9876 - mae: 0.6583\n", + "Epoch 542/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9366 - mae: 0.6826\n", + "Epoch 543/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0193 - mae: 0.7318\n", + "Epoch 544/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0361 - mae: 0.7375\n", + "Epoch 545/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9746 - mae: 0.7431\n", + "Epoch 546/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9808 - mae: 0.6623\n", + "Epoch 547/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.8979 - mae: 0.6730\n", + "Epoch 548/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9269 - mae: 0.6322\n", + "Epoch 549/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9848 - mae: 0.7138\n", + "Epoch 550/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9809 - mae: 0.6604\n", + "Epoch 551/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0266 - mae: 0.6987\n", + "Epoch 552/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0103 - mae: 0.6949\n", + "Epoch 553/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9666 - mae: 0.7182\n", + "Epoch 554/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0044 - mae: 0.7149\n", + "Epoch 555/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9957 - mae: 0.6571\n", + "Epoch 556/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9750 - mae: 0.7169\n", + "Epoch 557/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9770 - mae: 0.7086\n", + "Epoch 558/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9775 - mae: 0.7113\n", + "Epoch 559/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9991 - mae: 0.7117\n", + "Epoch 560/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9727 - mae: 0.7136\n", + "Epoch 561/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9534 - mae: 0.6586\n", + "Epoch 562/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9043 - mae: 0.6668\n", + "Epoch 563/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9450 - mae: 0.6822\n", + "Epoch 564/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0149 - mae: 0.6654\n", + "Epoch 565/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9401 - mae: 0.7412\n", + "Epoch 566/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0031 - mae: 0.7679\n", + "Epoch 567/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9743 - mae: 0.7240\n", + "Epoch 568/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9939 - mae: 0.6685\n", + "Epoch 569/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9406 - mae: 0.6359\n", + "Epoch 570/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9726 - mae: 0.6978\n", + "Epoch 571/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9392 - mae: 0.6764\n", + "Epoch 572/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9638 - mae: 0.6435\n", + "Epoch 573/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0292 - mae: 0.7358\n", + "Epoch 574/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0044 - mae: 0.7013\n", + "Epoch 575/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.8938 - mae: 0.6308\n", + "Epoch 576/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9505 - mae: 0.6570\n", + "Epoch 577/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0897 - mae: 0.7329\n", + "Epoch 578/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9876 - mae: 0.7095\n", + "Epoch 579/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9337 - mae: 0.7114\n", + "Epoch 580/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0306 - mae: 0.6997\n", + "Epoch 581/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0232 - mae: 0.6967\n", + "Epoch 582/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.8562 - mae: 0.6840\n", + "Epoch 583/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9489 - mae: 0.6562\n", + "Epoch 584/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9789 - mae: 0.7250\n", + "Epoch 585/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9213 - mae: 0.6204\n", + "Epoch 586/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0197 - mae: 0.6918\n", + "Epoch 587/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0650 - mae: 0.7133\n", + "Epoch 588/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9591 - mae: 0.6350\n", + "Epoch 589/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9732 - mae: 0.6696\n", + "Epoch 590/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9665 - mae: 0.6170\n", + "Epoch 591/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9270 - mae: 0.6916\n", + "Epoch 592/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9719 - mae: 0.6948\n", + "Epoch 593/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9269 - mae: 0.7624\n", + "Epoch 594/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9312 - mae: 0.7360\n", + "Epoch 595/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9339 - mae: 0.6128\n", + "Epoch 596/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9246 - mae: 0.7412\n", + "Epoch 597/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9162 - mae: 0.6608\n", + "Epoch 598/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9372 - mae: 0.6584\n", + "Epoch 599/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9577 - mae: 0.6330\n", + "Epoch 600/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9852 - mae: 0.6806\n", + "Epoch 601/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9778 - mae: 0.6780\n", + "Epoch 602/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.8745 - mae: 0.7223\n", + "Epoch 603/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9533 - mae: 0.6927\n", + "Epoch 604/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0004 - mae: 0.6122\n", + "Epoch 605/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9659 - mae: 0.6663\n", + "Epoch 606/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9373 - mae: 0.6434\n", + "Epoch 607/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0905 - mae: 0.7298\n", + "Epoch 608/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9710 - mae: 0.6670\n", + "Epoch 609/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9560 - mae: 0.7128\n", + "Epoch 610/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9750 - mae: 0.6696\n", + "Epoch 611/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0666 - mae: 0.7303\n", + "Epoch 612/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9732 - mae: 0.6789\n", + "Epoch 613/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9511 - mae: 0.6860\n", + "Epoch 614/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9889 - mae: 0.7495\n", + "Epoch 615/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9462 - mae: 0.7184\n", + "Epoch 616/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.8943 - mae: 0.6675\n", + "Epoch 617/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9909 - mae: 0.7063\n", + "Epoch 618/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9148 - mae: 0.6035\n", + "Epoch 619/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9146 - mae: 0.6885\n", + "Epoch 620/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0144 - mae: 0.6983\n", + "Epoch 621/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9903 - mae: 0.6452\n", + "Epoch 622/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9180 - mae: 0.6920\n", + "Epoch 623/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9455 - mae: 0.6332\n", + "Epoch 624/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9227 - mae: 0.6808\n", + "Epoch 625/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9559 - mae: 0.6649\n", + "Epoch 626/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.8585 - mae: 0.6163\n", + "Epoch 627/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9367 - mae: 0.6608\n", + "Epoch 628/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9570 - mae: 0.6693\n", + "Epoch 629/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0781 - mae: 0.7018\n", + "Epoch 630/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9390 - mae: 0.6874\n", + "Epoch 631/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9813 - mae: 0.6707\n", + "Epoch 632/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9447 - mae: 0.6739\n", + "Epoch 633/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9656 - mae: 0.7142\n", + "Epoch 634/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9500 - mae: 0.7504\n", + "Epoch 635/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.8984 - mae: 0.6757\n", + "Epoch 636/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9544 - mae: 0.6650\n", + "Epoch 637/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9152 - mae: 0.6983\n", + "Epoch 638/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0307 - mae: 0.6761\n", + "Epoch 639/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0771 - mae: 0.6549\n", + "Epoch 640/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9853 - mae: 0.7302\n", + "Epoch 641/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9435 - mae: 0.6950\n", + "Epoch 642/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9499 - mae: 0.6684\n", + "Epoch 643/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0300 - mae: 0.6751\n", + "Epoch 644/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9876 - mae: 0.6668\n", + "Epoch 645/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9487 - mae: 0.6707\n", + "Epoch 646/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0138 - mae: 0.7292\n", + "Epoch 647/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9796 - mae: 0.6854\n", + "Epoch 648/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9104 - mae: 0.6351\n", + "Epoch 649/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9268 - mae: 0.6530\n", + "Epoch 650/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.8668 - mae: 0.6372\n", + "Epoch 651/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9242 - mae: 0.6461\n", + "Epoch 652/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0430 - mae: 0.7228\n", + "Epoch 653/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9002 - mae: 0.6815\n", + "Epoch 654/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9330 - mae: 0.7201\n", + "Epoch 655/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9396 - mae: 0.6944\n", + "Epoch 656/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9561 - mae: 0.7073\n", + "Epoch 657/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9303 - mae: 0.6976\n", + "Epoch 658/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9081 - mae: 0.6670\n", + "Epoch 659/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.8791 - mae: 0.6433\n", + "Epoch 660/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9248 - mae: 0.6678\n", + "Epoch 661/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9460 - mae: 0.6702\n", + "Epoch 662/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.8670 - mae: 0.6600\n", + "Epoch 663/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9054 - mae: 0.6614\n", + "Epoch 664/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9638 - mae: 0.6502\n", + "Epoch 665/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0127 - mae: 0.6519\n", + "Epoch 666/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9006 - mae: 0.6122\n", + "Epoch 667/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 1.0285 - mae: 0.7438\n", + "Epoch 668/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9254 - mae: 0.7385\n", + "Epoch 669/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9375 - mae: 0.6586\n", + "Epoch 670/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9071 - mae: 0.6411\n", + "Epoch 671/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9711 - mae: 0.7181\n", + "Epoch 672/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9213 - mae: 0.6585\n", + "Epoch 673/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9522 - mae: 0.6837\n", + "Epoch 674/1000\n", + "13/13 [==============================] - 0s 2ms/step - loss: 0.9390 - mae: 0.6395\n", + "Epoch 675/1000\n", + "13/13 [==============================] - 6s 486ms/step - loss: 0.9395 - mae: 0.6616\n", + "Epoch 676/1000\n", + "13/13 [==============================] - 0s 773us/step - loss: 0.9404 - mae: 0.6579\n", + "Epoch 677/1000\n", + "13/13 [==============================] - 0s 711us/step - loss: 0.9536 - mae: 0.6595\n", + "Epoch 678/1000\n", + "13/13 [==============================] - 0s 650us/step - loss: 0.9002 - mae: 0.6330\n", + "Epoch 679/1000\n", + "13/13 [==============================] - 0s 654us/step - loss: 0.9020 - mae: 0.6923\n", + "Epoch 680/1000\n", + "13/13 [==============================] - 0s 642us/step - loss: 0.9729 - mae: 0.6938\n", + "Epoch 681/1000\n", + "13/13 [==============================] - 0s 637us/step - loss: 0.9532 - mae: 0.7114\n", + "Epoch 682/1000\n", + "13/13 [==============================] - 0s 675us/step - loss: 0.9242 - mae: 0.6162\n", + "Epoch 683/1000\n", + "13/13 [==============================] - 0s 664us/step - loss: 0.9785 - mae: 0.6953\n", + "Epoch 684/1000\n", + "13/13 [==============================] - 0s 745us/step - loss: 0.9290 - mae: 0.6540\n", + "Epoch 685/1000\n", + "13/13 [==============================] - 0s 690us/step - loss: 0.9727 - mae: 0.7079\n", + "Epoch 686/1000\n", + "13/13 [==============================] - 0s 682us/step - loss: 0.8937 - mae: 0.6736\n", + "Epoch 687/1000\n", + "13/13 [==============================] - 0s 706us/step - loss: 0.8786 - mae: 0.5720\n", + "Epoch 688/1000\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.9142 - mae: 0.6149\n", + "Epoch 689/1000\n", + "13/13 [==============================] - 0s 641us/step - loss: 0.8481 - mae: 0.6182\n", + "Epoch 690/1000\n", + "13/13 [==============================] - 0s 655us/step - loss: 0.9556 - mae: 0.6492\n", + "Epoch 691/1000\n", + "13/13 [==============================] - 0s 648us/step - loss: 0.9248 - mae: 0.6467\n", + "Epoch 692/1000\n", + "13/13 [==============================] - 0s 662us/step - loss: 1.0155 - mae: 0.6936\n", + "Epoch 693/1000\n", + "13/13 [==============================] - 0s 661us/step - loss: 0.9536 - mae: 0.6806\n", + "Epoch 694/1000\n", + "13/13 [==============================] - 0s 670us/step - loss: 0.9494 - mae: 0.6066\n", + "Epoch 695/1000\n", + "13/13 [==============================] - 0s 638us/step - loss: 0.8689 - mae: 0.6513\n", + "Epoch 696/1000\n", + "13/13 [==============================] - 0s 705us/step - loss: 1.0367 - mae: 0.6678\n", + "Epoch 697/1000\n", + "13/13 [==============================] - 0s 666us/step - loss: 0.8563 - mae: 0.5858\n", + "Epoch 698/1000\n", + "13/13 [==============================] - 0s 670us/step - loss: 0.9306 - mae: 0.6681\n", + "Epoch 699/1000\n", + "13/13 [==============================] - 0s 655us/step - loss: 0.9484 - mae: 0.6776\n", + "Epoch 700/1000\n", + "13/13 [==============================] - 0s 667us/step - loss: 0.9206 - mae: 0.6694\n", + "Epoch 701/1000\n", + "13/13 [==============================] - 0s 674us/step - loss: 0.9859 - mae: 0.6836\n", + "Epoch 702/1000\n", + "13/13 [==============================] - 0s 667us/step - loss: 0.9226 - mae: 0.6461\n", + "Epoch 703/1000\n", + "13/13 [==============================] - 0s 678us/step - loss: 0.9429 - mae: 0.6929\n", + "Epoch 704/1000\n", + "13/13 [==============================] - 0s 696us/step - loss: 0.9397 - mae: 0.6893\n", + "Epoch 705/1000\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.9507 - mae: 0.6694\n", + "Epoch 706/1000\n", + "13/13 [==============================] - 0s 683us/step - loss: 0.9593 - mae: 0.6445\n", + "Epoch 707/1000\n", + "13/13 [==============================] - 0s 682us/step - loss: 0.8954 - mae: 0.6565\n", + "Epoch 708/1000\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.8906 - mae: 0.6630\n", + "Epoch 709/1000\n", + "13/13 [==============================] - 0s 672us/step - loss: 0.8717 - mae: 0.6719\n", + "Epoch 710/1000\n", + "13/13 [==============================] - 0s 675us/step - loss: 0.8877 - mae: 0.6110\n", + "Epoch 711/1000\n", + "13/13 [==============================] - 0s 647us/step - loss: 0.9397 - mae: 0.6178\n", + "Epoch 712/1000\n", + "13/13 [==============================] - 0s 674us/step - loss: 0.9319 - mae: 0.6265\n", + "Epoch 713/1000\n", + "13/13 [==============================] - 0s 667us/step - loss: 1.0016 - mae: 0.6740\n", + "Epoch 714/1000\n", + "13/13 [==============================] - 0s 679us/step - loss: 0.9758 - mae: 0.6951\n", + "Epoch 715/1000\n", + "13/13 [==============================] - 0s 688us/step - loss: 0.9185 - mae: 0.5984\n", + "Epoch 716/1000\n", + "13/13 [==============================] - 0s 678us/step - loss: 0.9343 - mae: 0.6118\n", + "Epoch 717/1000\n", + "13/13 [==============================] - 0s 664us/step - loss: 0.9254 - mae: 0.6560\n", + "Epoch 718/1000\n", + "13/13 [==============================] - 0s 676us/step - loss: 0.9446 - mae: 0.6687\n", + "Epoch 719/1000\n", + "13/13 [==============================] - 0s 679us/step - loss: 0.9086 - mae: 0.6782\n", + "Epoch 720/1000\n", + "13/13 [==============================] - 0s 673us/step - loss: 0.9473 - mae: 0.6471\n", + "Epoch 721/1000\n", + "13/13 [==============================] - 0s 660us/step - loss: 0.9186 - mae: 0.6327\n", + "Epoch 722/1000\n", + "13/13 [==============================] - 0s 666us/step - loss: 0.9395 - mae: 0.5917\n", + "Epoch 723/1000\n", + "13/13 [==============================] - 0s 657us/step - loss: 0.9441 - mae: 0.6698\n", + "Epoch 724/1000\n", + "13/13 [==============================] - 0s 662us/step - loss: 0.9252 - mae: 0.5886\n", + "Epoch 725/1000\n", + "13/13 [==============================] - 0s 681us/step - loss: 0.8959 - mae: 0.6606\n", + "Epoch 726/1000\n", + "13/13 [==============================] - 0s 691us/step - loss: 0.9295 - mae: 0.7067\n", + "Epoch 727/1000\n", + "13/13 [==============================] - 0s 674us/step - loss: 0.9559 - mae: 0.6335\n", + "Epoch 728/1000\n", + "13/13 [==============================] - 0s 680us/step - loss: 0.8978 - mae: 0.6123\n", + "Epoch 729/1000\n", + "13/13 [==============================] - 0s 669us/step - loss: 0.9529 - mae: 0.6525\n", + "Epoch 730/1000\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.9286 - mae: 0.6361\n", + "Epoch 731/1000\n", + "13/13 [==============================] - 0s 667us/step - loss: 0.9408 - mae: 0.6527\n", + "Epoch 732/1000\n", + "13/13 [==============================] - 0s 686us/step - loss: 1.0436 - mae: 0.6497\n", + "Epoch 733/1000\n", + "13/13 [==============================] - 0s 685us/step - loss: 0.8778 - mae: 0.6822\n", + "Epoch 734/1000\n", + "13/13 [==============================] - 0s 684us/step - loss: 0.8845 - mae: 0.5915\n", + "Epoch 735/1000\n", + "13/13 [==============================] - 0s 683us/step - loss: 0.9345 - mae: 0.6229\n", + "Epoch 736/1000\n", + "13/13 [==============================] - 0s 679us/step - loss: 0.9239 - mae: 0.6617\n", + "Epoch 737/1000\n", + "13/13 [==============================] - 0s 676us/step - loss: 0.8933 - mae: 0.6105\n", + "Epoch 738/1000\n", + "13/13 [==============================] - 0s 741us/step - loss: 0.9027 - mae: 0.6738\n", + "Epoch 739/1000\n", + "13/13 [==============================] - 0s 676us/step - loss: 0.8509 - mae: 0.6187\n", + "Epoch 740/1000\n", + "13/13 [==============================] - 0s 670us/step - loss: 1.0304 - mae: 0.6125\n", + "Epoch 741/1000\n", + "13/13 [==============================] - 0s 676us/step - loss: 0.8714 - mae: 0.6493\n", + "Epoch 742/1000\n", + "13/13 [==============================] - 0s 673us/step - loss: 0.9233 - mae: 0.6406\n", + "Epoch 743/1000\n", + "13/13 [==============================] - 0s 671us/step - loss: 0.8425 - mae: 0.6198\n", + "Epoch 744/1000\n", + "13/13 [==============================] - 0s 668us/step - loss: 0.9021 - mae: 0.6752\n", + "Epoch 745/1000\n", + "13/13 [==============================] - 0s 668us/step - loss: 0.9291 - mae: 0.6491\n", + "Epoch 746/1000\n", + "13/13 [==============================] - 0s 662us/step - loss: 0.9153 - mae: 0.6835\n", + "Epoch 747/1000\n", + "13/13 [==============================] - 0s 656us/step - loss: 0.9909 - mae: 0.6985\n", + "Epoch 748/1000\n", + "13/13 [==============================] - 0s 669us/step - loss: 0.9020 - mae: 0.6599\n", + "Epoch 749/1000\n", + "13/13 [==============================] - 0s 666us/step - loss: 0.9416 - mae: 0.7135\n", + "Epoch 750/1000\n", + "13/13 [==============================] - 0s 673us/step - loss: 0.9239 - mae: 0.6534\n", + "Epoch 751/1000\n", + "13/13 [==============================] - 0s 688us/step - loss: 0.9028 - mae: 0.6799\n", + "Epoch 752/1000\n", + "13/13 [==============================] - 0s 668us/step - loss: 0.9319 - mae: 0.7216\n", + "Epoch 753/1000\n", + "13/13 [==============================] - 0s 683us/step - loss: 0.9617 - mae: 0.6958\n", + "Epoch 754/1000\n", + "13/13 [==============================] - 0s 675us/step - loss: 0.8968 - mae: 0.6227\n", + "Epoch 755/1000\n", + "13/13 [==============================] - 0s 683us/step - loss: 0.8889 - mae: 0.6964\n", + "Epoch 756/1000\n", + "13/13 [==============================] - 0s 666us/step - loss: 0.8900 - mae: 0.6236\n", + "Epoch 757/1000\n", + "13/13 [==============================] - 0s 680us/step - loss: 0.9024 - mae: 0.6001\n", + "Epoch 758/1000\n", + "13/13 [==============================] - 0s 669us/step - loss: 0.9133 - mae: 0.6772\n", + "Epoch 759/1000\n", + "13/13 [==============================] - 0s 664us/step - loss: 0.9304 - mae: 0.6316\n", + "Epoch 760/1000\n", + "13/13 [==============================] - 0s 682us/step - loss: 0.9067 - mae: 0.6439\n", + "Epoch 761/1000\n", + "13/13 [==============================] - 0s 684us/step - loss: 0.8976 - mae: 0.6400\n", + "Epoch 762/1000\n", + "13/13 [==============================] - 0s 662us/step - loss: 0.8753 - mae: 0.6192\n", + "Epoch 763/1000\n", + "13/13 [==============================] - 0s 673us/step - loss: 0.9955 - mae: 0.6582\n", + "Epoch 764/1000\n", + "13/13 [==============================] - 0s 675us/step - loss: 0.8828 - mae: 0.6010\n", + "Epoch 765/1000\n", + "13/13 [==============================] - 0s 678us/step - loss: 0.9489 - mae: 0.7005\n", + "Epoch 766/1000\n", + "13/13 [==============================] - 0s 670us/step - loss: 0.8944 - mae: 0.6641\n", + "Epoch 767/1000\n", + "13/13 [==============================] - 0s 670us/step - loss: 1.0178 - mae: 0.6370\n", + "Epoch 768/1000\n", + "13/13 [==============================] - 0s 659us/step - loss: 0.8267 - mae: 0.6581\n", + "Epoch 769/1000\n", + "13/13 [==============================] - 0s 695us/step - loss: 0.9696 - mae: 0.6854\n", + "Epoch 770/1000\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.8792 - mae: 0.6254\n", + "Epoch 771/1000\n", + "13/13 [==============================] - 0s 669us/step - loss: 0.9133 - mae: 0.6787\n", + "Epoch 772/1000\n", + "13/13 [==============================] - 0s 664us/step - loss: 0.9060 - mae: 0.6502\n", + "Epoch 773/1000\n", + "13/13 [==============================] - 0s 677us/step - loss: 0.8527 - mae: 0.6295\n", + "Epoch 774/1000\n", + "13/13 [==============================] - 0s 673us/step - loss: 0.9766 - mae: 0.6517\n", + "Epoch 775/1000\n", + "13/13 [==============================] - 0s 673us/step - loss: 0.9295 - mae: 0.6347\n", + "Epoch 776/1000\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.9095 - mae: 0.6767\n", + "Epoch 777/1000\n", + "13/13 [==============================] - 0s 676us/step - loss: 0.8664 - mae: 0.6247\n", + "Epoch 778/1000\n", + "13/13 [==============================] - 0s 673us/step - loss: 1.0412 - mae: 0.6591\n", + "Epoch 779/1000\n", + "13/13 [==============================] - 0s 680us/step - loss: 0.9604 - mae: 0.6753\n", + "Epoch 780/1000\n", + "13/13 [==============================] - 0s 678us/step - loss: 0.9804 - mae: 0.7015\n", + "Epoch 781/1000\n", + "13/13 [==============================] - 0s 697us/step - loss: 0.9300 - mae: 0.6117\n", + "Epoch 782/1000\n", + "13/13 [==============================] - 0s 672us/step - loss: 0.9188 - mae: 0.6820\n", + "Epoch 783/1000\n", + "13/13 [==============================] - 0s 691us/step - loss: 0.9256 - mae: 0.6728\n", + "Epoch 784/1000\n", + "13/13 [==============================] - 0s 674us/step - loss: 0.9359 - mae: 0.6778\n", + "Epoch 785/1000\n", + "13/13 [==============================] - 0s 679us/step - loss: 0.9449 - mae: 0.6697\n", + "Epoch 786/1000\n", + "13/13 [==============================] - 0s 684us/step - loss: 0.9057 - mae: 0.6455\n", + "Epoch 787/1000\n", + "13/13 [==============================] - 0s 680us/step - loss: 0.9747 - mae: 0.6478\n", + "Epoch 788/1000\n", + "13/13 [==============================] - 0s 664us/step - loss: 0.8409 - mae: 0.6265\n", + "Epoch 789/1000\n", + "13/13 [==============================] - 0s 660us/step - loss: 0.9122 - mae: 0.6545\n", + "Epoch 790/1000\n", + "13/13 [==============================] - 0s 668us/step - loss: 0.9003 - mae: 0.6532\n", + "Epoch 791/1000\n", + "13/13 [==============================] - 0s 683us/step - loss: 1.0775 - mae: 0.6866\n", + "Epoch 792/1000\n", + "13/13 [==============================] - 0s 717us/step - loss: 0.9941 - mae: 0.6462\n", + "Epoch 793/1000\n", + "13/13 [==============================] - 0s 669us/step - loss: 0.9286 - mae: 0.6654\n", + "Epoch 794/1000\n", + "13/13 [==============================] - 0s 666us/step - loss: 0.8916 - mae: 0.6707\n", + "Epoch 795/1000\n", + "13/13 [==============================] - 0s 656us/step - loss: 0.9097 - mae: 0.6749\n", + "Epoch 796/1000\n", + "13/13 [==============================] - 0s 671us/step - loss: 1.0268 - mae: 0.6411\n", + "Epoch 797/1000\n", + "13/13 [==============================] - 0s 666us/step - loss: 0.8840 - mae: 0.6636\n", + "Epoch 798/1000\n", + "13/13 [==============================] - 0s 676us/step - loss: 0.9841 - mae: 0.6566\n", + "Epoch 799/1000\n", + "13/13 [==============================] - 0s 696us/step - loss: 0.9692 - mae: 0.7151\n", + "Epoch 800/1000\n", + "13/13 [==============================] - 0s 715us/step - loss: 0.9564 - mae: 0.6393\n", + "Epoch 801/1000\n", + "13/13 [==============================] - 0s 666us/step - loss: 0.9850 - mae: 0.6649\n", + "Epoch 802/1000\n", + "13/13 [==============================] - 0s 692us/step - loss: 0.8547 - mae: 0.6212\n", + "Epoch 803/1000\n", + "13/13 [==============================] - 0s 669us/step - loss: 0.9253 - mae: 0.6064\n", + "Epoch 804/1000\n", + "13/13 [==============================] - 0s 674us/step - loss: 0.8854 - mae: 0.6245\n", + "Epoch 805/1000\n", + "13/13 [==============================] - 0s 681us/step - loss: 0.8614 - mae: 0.6243\n", + "Epoch 806/1000\n", + "13/13 [==============================] - 0s 684us/step - loss: 0.9469 - mae: 0.6475\n", + "Epoch 807/1000\n", + "13/13 [==============================] - 0s 725us/step - loss: 1.0488 - mae: 0.6196\n", + "Epoch 808/1000\n", + "13/13 [==============================] - 0s 656us/step - loss: 0.9014 - mae: 0.6560\n", + "Epoch 809/1000\n", + "13/13 [==============================] - 0s 668us/step - loss: 0.8938 - mae: 0.6216\n", + "Epoch 810/1000\n", + "13/13 [==============================] - 0s 668us/step - loss: 0.9498 - mae: 0.6113\n", + "Epoch 811/1000\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.9880 - mae: 0.6630\n", + "Epoch 812/1000\n", + "13/13 [==============================] - 0s 664us/step - loss: 0.8998 - mae: 0.6531\n", + "Epoch 813/1000\n", + "13/13 [==============================] - 0s 676us/step - loss: 1.0513 - mae: 0.6925\n", + "Epoch 814/1000\n", + "13/13 [==============================] - 0s 667us/step - loss: 0.8766 - mae: 0.6398\n", + "Epoch 815/1000\n", + "13/13 [==============================] - 0s 670us/step - loss: 0.8569 - mae: 0.6171\n", + "Epoch 816/1000\n", + "13/13 [==============================] - 0s 674us/step - loss: 0.9315 - mae: 0.6102\n", + "Epoch 817/1000\n", + "13/13 [==============================] - 0s 691us/step - loss: 0.9130 - mae: 0.6436\n", + "Epoch 818/1000\n", + "13/13 [==============================] - 0s 710us/step - loss: 0.9475 - mae: 0.6180\n", + "Epoch 819/1000\n", + "13/13 [==============================] - 0s 660us/step - loss: 0.9523 - mae: 0.6573\n", + "Epoch 820/1000\n", + "13/13 [==============================] - 0s 662us/step - loss: 0.8863 - mae: 0.6313\n", + "Epoch 821/1000\n", + "13/13 [==============================] - 0s 671us/step - loss: 0.8845 - mae: 0.6719\n", + "Epoch 822/1000\n", + "13/13 [==============================] - 0s 684us/step - loss: 0.9260 - mae: 0.7047\n", + "Epoch 823/1000\n", + "13/13 [==============================] - 0s 657us/step - loss: 0.9099 - mae: 0.6496\n", + "Epoch 824/1000\n", + "13/13 [==============================] - 0s 671us/step - loss: 0.8912 - mae: 0.6114\n", + "Epoch 825/1000\n", + "13/13 [==============================] - 0s 732us/step - loss: 0.9043 - mae: 0.6479\n", + "Epoch 826/1000\n", + "13/13 [==============================] - 0s 662us/step - loss: 0.8948 - mae: 0.6582\n", + "Epoch 827/1000\n", + "13/13 [==============================] - 0s 684us/step - loss: 0.8979 - mae: 0.6602\n", + "Epoch 828/1000\n", + "13/13 [==============================] - 0s 684us/step - loss: 0.8104 - mae: 0.5786\n", + "Epoch 829/1000\n", + "13/13 [==============================] - 0s 676us/step - loss: 0.8845 - mae: 0.6275\n", + "Epoch 830/1000\n", + "13/13 [==============================] - 0s 684us/step - loss: 0.9337 - mae: 0.6312\n", + "Epoch 831/1000\n", + "13/13 [==============================] - 0s 679us/step - loss: 0.8500 - mae: 0.6480\n", + "Epoch 832/1000\n", + "13/13 [==============================] - 0s 668us/step - loss: 1.0051 - mae: 0.6679\n", + "Epoch 833/1000\n", + "13/13 [==============================] - 0s 666us/step - loss: 0.8852 - mae: 0.6053\n", + "Epoch 834/1000\n", + "13/13 [==============================] - 0s 671us/step - loss: 0.8914 - mae: 0.6827\n", + "Epoch 835/1000\n", + "13/13 [==============================] - 0s 673us/step - loss: 0.9534 - mae: 0.6319\n", + "Epoch 836/1000\n", + "13/13 [==============================] - 0s 667us/step - loss: 0.8878 - mae: 0.6350\n", + "Epoch 837/1000\n", + "13/13 [==============================] - 0s 668us/step - loss: 0.8955 - mae: 0.6626\n", + "Epoch 838/1000\n", + "13/13 [==============================] - 0s 678us/step - loss: 0.9222 - mae: 0.5960\n", + "Epoch 839/1000\n", + "13/13 [==============================] - 0s 689us/step - loss: 0.8924 - mae: 0.6211\n", + "Epoch 840/1000\n", + "13/13 [==============================] - 0s 665us/step - loss: 0.9759 - mae: 0.6827\n", + "Epoch 841/1000\n", + "13/13 [==============================] - 0s 676us/step - loss: 0.8711 - mae: 0.6343\n", + "Epoch 842/1000\n", + "13/13 [==============================] - 0s 796us/step - loss: 0.9586 - mae: 0.6765\n", + "Epoch 843/1000\n", + "13/13 [==============================] - 0s 681us/step - loss: 0.9813 - mae: 0.6791\n", + "Epoch 844/1000\n", + "13/13 [==============================] - 0s 678us/step - loss: 0.8637 - mae: 0.6642\n", + "Epoch 845/1000\n", + "13/13 [==============================] - 0s 689us/step - loss: 0.8300 - mae: 0.6041\n", + "Epoch 846/1000\n", + "13/13 [==============================] - 0s 683us/step - loss: 0.9238 - mae: 0.6545\n", + "Epoch 847/1000\n", + "13/13 [==============================] - 0s 680us/step - loss: 0.8924 - mae: 0.6218\n", + "Epoch 848/1000\n", + "13/13 [==============================] - 0s 661us/step - loss: 0.9457 - mae: 0.5951\n", + "Epoch 849/1000\n", + "13/13 [==============================] - 0s 673us/step - loss: 0.8536 - mae: 0.5993\n", + "Epoch 850/1000\n", + "13/13 [==============================] - 0s 660us/step - loss: 0.9131 - mae: 0.6311\n", + "Epoch 851/1000\n", + "13/13 [==============================] - 0s 666us/step - loss: 0.9190 - mae: 0.6338\n", + "Epoch 852/1000\n", + "13/13 [==============================] - 0s 665us/step - loss: 0.8840 - mae: 0.6877\n", + "Epoch 853/1000\n", + "13/13 [==============================] - 0s 668us/step - loss: 0.8921 - mae: 0.6092\n", + "Epoch 854/1000\n", + "13/13 [==============================] - 0s 665us/step - loss: 0.9118 - mae: 0.6328\n", + "Epoch 855/1000\n", + "13/13 [==============================] - 0s 672us/step - loss: 0.9459 - mae: 0.6718\n", + "Epoch 856/1000\n", + "13/13 [==============================] - 0s 671us/step - loss: 0.9559 - mae: 0.6242\n", + "Epoch 857/1000\n", + "13/13 [==============================] - 0s 666us/step - loss: 0.9788 - mae: 0.6702\n", + "Epoch 858/1000\n", + "13/13 [==============================] - 0s 667us/step - loss: 0.8521 - mae: 0.6661\n", + "Epoch 859/1000\n", + "13/13 [==============================] - 0s 670us/step - loss: 0.8340 - mae: 0.6384\n", + "Epoch 860/1000\n", + "13/13 [==============================] - 0s 666us/step - loss: 0.8642 - mae: 0.5715\n", + "Epoch 861/1000\n", + "13/13 [==============================] - 0s 665us/step - loss: 0.9054 - mae: 0.6508\n", + "Epoch 862/1000\n", + "13/13 [==============================] - 0s 665us/step - loss: 0.9357 - mae: 0.6525\n", + "Epoch 863/1000\n", + "13/13 [==============================] - 0s 669us/step - loss: 1.0083 - mae: 0.7038\n", + "Epoch 864/1000\n", + "13/13 [==============================] - 0s 671us/step - loss: 0.8687 - mae: 0.6307\n", + "Epoch 865/1000\n", + "13/13 [==============================] - 0s 667us/step - loss: 0.9458 - mae: 0.6090\n", + "Epoch 866/1000\n", + "13/13 [==============================] - 0s 688us/step - loss: 1.0141 - mae: 0.6121\n", + "Epoch 867/1000\n", + "13/13 [==============================] - 0s 681us/step - loss: 0.9600 - mae: 0.6177\n", + "Epoch 868/1000\n", + "13/13 [==============================] - 0s 688us/step - loss: 0.8777 - mae: 0.6182\n", + "Epoch 869/1000\n", + "13/13 [==============================] - 0s 682us/step - loss: 0.8430 - mae: 0.6349\n", + "Epoch 870/1000\n", + "13/13 [==============================] - 0s 681us/step - loss: 0.9009 - mae: 0.6421\n", + "Epoch 871/1000\n", + "13/13 [==============================] - 0s 669us/step - loss: 0.8772 - mae: 0.5898\n", + "Epoch 872/1000\n", + "13/13 [==============================] - 0s 662us/step - loss: 0.9176 - mae: 0.6549\n", + "Epoch 873/1000\n", + "13/13 [==============================] - 0s 676us/step - loss: 0.9263 - mae: 0.6419\n", + "Epoch 874/1000\n", + "13/13 [==============================] - 0s 673us/step - loss: 0.9309 - mae: 0.6356\n", + "Epoch 875/1000\n", + "13/13 [==============================] - 0s 681us/step - loss: 1.0051 - mae: 0.6685\n", + "Epoch 876/1000\n", + "13/13 [==============================] - 0s 678us/step - loss: 0.8497 - mae: 0.6141\n", + "Epoch 877/1000\n", + "13/13 [==============================] - 0s 684us/step - loss: 0.8649 - mae: 0.6018\n", + "Epoch 878/1000\n", + "13/13 [==============================] - 0s 668us/step - loss: 0.8448 - mae: 0.6197\n", + "Epoch 879/1000\n", + "13/13 [==============================] - 0s 667us/step - loss: 0.8724 - mae: 0.6292\n", + "Epoch 880/1000\n", + "13/13 [==============================] - 0s 678us/step - loss: 0.9893 - mae: 0.6056\n", + "Epoch 881/1000\n", + "13/13 [==============================] - 0s 670us/step - loss: 0.9559 - mae: 0.6803\n", + "Epoch 882/1000\n", + "13/13 [==============================] - 0s 673us/step - loss: 1.0274 - mae: 0.6735\n", + "Epoch 883/1000\n", + "13/13 [==============================] - 0s 676us/step - loss: 1.0428 - mae: 0.6520\n", + "Epoch 884/1000\n", + "13/13 [==============================] - 0s 664us/step - loss: 0.8945 - mae: 0.6120\n", + "Epoch 885/1000\n", + "13/13 [==============================] - 0s 664us/step - loss: 0.8896 - mae: 0.6088\n", + "Epoch 886/1000\n", + "13/13 [==============================] - 0s 673us/step - loss: 0.9693 - mae: 0.7024\n", + "Epoch 887/1000\n", + "13/13 [==============================] - 0s 765us/step - loss: 0.9230 - mae: 0.6363\n", + "Epoch 888/1000\n", + "13/13 [==============================] - 0s 741us/step - loss: 0.8127 - mae: 0.6065\n", + "Epoch 889/1000\n", + "13/13 [==============================] - 0s 661us/step - loss: 0.9512 - mae: 0.6563\n", + "Epoch 890/1000\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.9387 - mae: 0.6269\n", + "Epoch 891/1000\n", + "13/13 [==============================] - 0s 668us/step - loss: 0.8594 - mae: 0.6695\n", + "Epoch 892/1000\n", + "13/13 [==============================] - 0s 669us/step - loss: 0.8414 - mae: 0.6119\n", + "Epoch 893/1000\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.9360 - mae: 0.6113\n", + "Epoch 894/1000\n", + "13/13 [==============================] - 0s 660us/step - loss: 0.9393 - mae: 0.6140\n", + "Epoch 895/1000\n", + "13/13 [==============================] - 0s 656us/step - loss: 0.9504 - mae: 0.6583\n", + "Epoch 896/1000\n", + "13/13 [==============================] - 0s 661us/step - loss: 0.8640 - mae: 0.5813\n", + "Epoch 897/1000\n", + "13/13 [==============================] - 0s 670us/step - loss: 0.9562 - mae: 0.6452\n", + "Epoch 898/1000\n", + "13/13 [==============================] - 0s 661us/step - loss: 0.9107 - mae: 0.6424\n", + "Epoch 899/1000\n", + "13/13 [==============================] - 0s 665us/step - loss: 0.9808 - mae: 0.6517\n", + "Epoch 900/1000\n", + "13/13 [==============================] - 0s 673us/step - loss: 0.8329 - mae: 0.6417\n", + "Epoch 901/1000\n", + "13/13 [==============================] - 0s 668us/step - loss: 0.8889 - mae: 0.5925\n", + "Epoch 902/1000\n", + "13/13 [==============================] - 0s 664us/step - loss: 0.8329 - mae: 0.6586\n", + "Epoch 903/1000\n", + "13/13 [==============================] - 0s 666us/step - loss: 0.9360 - mae: 0.6542\n", + "Epoch 904/1000\n", + "13/13 [==============================] - 0s 669us/step - loss: 0.8557 - mae: 0.6316\n", + "Epoch 905/1000\n", + "13/13 [==============================] - 0s 662us/step - loss: 0.9280 - mae: 0.6233\n", + "Epoch 906/1000\n", + "13/13 [==============================] - 0s 671us/step - loss: 0.9131 - mae: 0.5984\n", + "Epoch 907/1000\n", + "13/13 [==============================] - 0s 680us/step - loss: 0.9027 - mae: 0.6364\n", + "Epoch 908/1000\n", + "13/13 [==============================] - 0s 673us/step - loss: 0.9144 - mae: 0.6279\n", + "Epoch 909/1000\n", + "13/13 [==============================] - 0s 678us/step - loss: 0.8567 - mae: 0.6319\n", + "Epoch 910/1000\n", + "13/13 [==============================] - 0s 688us/step - loss: 0.8347 - mae: 0.6468\n", + "Epoch 911/1000\n", + "13/13 [==============================] - 0s 670us/step - loss: 0.9488 - mae: 0.6043\n", + "Epoch 912/1000\n", + "13/13 [==============================] - 0s 682us/step - loss: 0.8613 - mae: 0.6500\n", + "Epoch 913/1000\n", + "13/13 [==============================] - 0s 674us/step - loss: 0.9443 - mae: 0.6545\n", + "Epoch 914/1000\n", + "13/13 [==============================] - 0s 685us/step - loss: 0.9339 - mae: 0.6681\n", + "Epoch 915/1000\n", + "13/13 [==============================] - 0s 656us/step - loss: 0.9047 - mae: 0.6530\n", + "Epoch 916/1000\n", + "13/13 [==============================] - 0s 664us/step - loss: 0.8846 - mae: 0.6016\n", + "Epoch 917/1000\n", + "13/13 [==============================] - 0s 677us/step - loss: 0.9579 - mae: 0.5759\n", + "Epoch 918/1000\n", + "13/13 [==============================] - 0s 667us/step - loss: 0.8795 - mae: 0.6258\n", + "Epoch 919/1000\n", + "13/13 [==============================] - 0s 667us/step - loss: 0.9162 - mae: 0.5899\n", + "Epoch 920/1000\n", + "13/13 [==============================] - 0s 677us/step - loss: 0.8837 - mae: 0.5705\n", + "Epoch 921/1000\n", + "13/13 [==============================] - 0s 673us/step - loss: 0.9130 - mae: 0.6377\n", + "Epoch 922/1000\n", + "13/13 [==============================] - 0s 659us/step - loss: 0.8912 - mae: 0.5795\n", + "Epoch 923/1000\n", + "13/13 [==============================] - 0s 667us/step - loss: 0.9350 - mae: 0.6128\n", + "Epoch 924/1000\n", + "13/13 [==============================] - 0s 655us/step - loss: 0.9236 - mae: 0.6664\n", + "Epoch 925/1000\n", + "13/13 [==============================] - 0s 676us/step - loss: 0.8979 - mae: 0.6497\n", + "Epoch 926/1000\n", + "13/13 [==============================] - 0s 673us/step - loss: 0.9331 - mae: 0.6374\n", + "Epoch 927/1000\n", + "13/13 [==============================] - 0s 671us/step - loss: 0.8559 - mae: 0.6439\n", + "Epoch 928/1000\n", + "13/13 [==============================] - 0s 666us/step - loss: 0.8772 - mae: 0.5624\n", + "Epoch 929/1000\n", + "13/13 [==============================] - 0s 680us/step - loss: 0.9054 - mae: 0.6472\n", + "Epoch 930/1000\n", + "13/13 [==============================] - 0s 678us/step - loss: 0.9363 - mae: 0.6916\n", + "Epoch 931/1000\n", + "13/13 [==============================] - 0s 681us/step - loss: 0.9024 - mae: 0.6157\n", + "Epoch 932/1000\n", + "13/13 [==============================] - 0s 666us/step - loss: 0.8841 - mae: 0.6003\n", + "Epoch 933/1000\n", + "13/13 [==============================] - 0s 670us/step - loss: 0.8851 - mae: 0.6454\n", + "Epoch 934/1000\n", + "13/13 [==============================] - 0s 675us/step - loss: 0.8571 - mae: 0.5572\n", + "Epoch 935/1000\n", + "13/13 [==============================] - 0s 667us/step - loss: 0.9207 - mae: 0.6658\n", + "Epoch 936/1000\n", + "13/13 [==============================] - 0s 675us/step - loss: 0.9756 - mae: 0.6047\n", + "Epoch 937/1000\n", + "13/13 [==============================] - 0s 681us/step - loss: 0.8342 - mae: 0.6111\n", + "Epoch 938/1000\n", + "13/13 [==============================] - 0s 676us/step - loss: 0.9049 - mae: 0.6024\n", + "Epoch 939/1000\n", + "13/13 [==============================] - 0s 683us/step - loss: 0.8583 - mae: 0.6443\n", + "Epoch 940/1000\n", + "13/13 [==============================] - 0s 670us/step - loss: 0.8795 - mae: 0.6179\n", + "Epoch 941/1000\n", + "13/13 [==============================] - 0s 682us/step - loss: 1.0684 - mae: 0.6755\n", + "Epoch 942/1000\n", + "13/13 [==============================] - 0s 701us/step - loss: 0.8674 - mae: 0.6315\n", + "Epoch 943/1000\n", + "13/13 [==============================] - 0s 659us/step - loss: 0.9135 - mae: 0.6527\n", + "Epoch 944/1000\n", + "13/13 [==============================] - 0s 665us/step - loss: 0.8993 - mae: 0.5718\n", + "Epoch 945/1000\n", + "13/13 [==============================] - 0s 669us/step - loss: 0.9662 - mae: 0.6664\n", + "Epoch 946/1000\n", + "13/13 [==============================] - 0s 680us/step - loss: 0.8543 - mae: 0.6694\n", + "Epoch 947/1000\n", + "13/13 [==============================] - 0s 674us/step - loss: 0.9741 - mae: 0.6972\n", + "Epoch 948/1000\n", + "13/13 [==============================] - 0s 675us/step - loss: 0.9941 - mae: 0.6451\n", + "Epoch 949/1000\n", + "13/13 [==============================] - 0s 683us/step - loss: 0.8610 - mae: 0.6077\n", + "Epoch 950/1000\n", + "13/13 [==============================] - 0s 685us/step - loss: 0.8332 - mae: 0.5494\n", + "Epoch 951/1000\n", + "13/13 [==============================] - 0s 679us/step - loss: 0.8701 - mae: 0.6167\n", + "Epoch 952/1000\n", + "13/13 [==============================] - 0s 665us/step - loss: 0.8660 - mae: 0.6231\n", + "Epoch 953/1000\n", + "13/13 [==============================] - 0s 681us/step - loss: 0.8554 - mae: 0.5794\n", + "Epoch 954/1000\n", + "13/13 [==============================] - 0s 681us/step - loss: 0.9726 - mae: 0.6627\n", + "Epoch 955/1000\n", + "13/13 [==============================] - 0s 669us/step - loss: 0.8465 - mae: 0.6001\n", + "Epoch 956/1000\n", + "13/13 [==============================] - 0s 670us/step - loss: 0.8106 - mae: 0.5592\n", + "Epoch 957/1000\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.9143 - mae: 0.6332\n", + "Epoch 958/1000\n", + "13/13 [==============================] - 0s 710us/step - loss: 0.9994 - mae: 0.6760\n", + "Epoch 959/1000\n", + "13/13 [==============================] - 0s 668us/step - loss: 0.8923 - mae: 0.6213\n", + "Epoch 960/1000\n", + "13/13 [==============================] - 0s 671us/step - loss: 0.9485 - mae: 0.5858\n", + "Epoch 961/1000\n", + "13/13 [==============================] - 0s 674us/step - loss: 0.8839 - mae: 0.6114\n", + "Epoch 962/1000\n", + "13/13 [==============================] - 0s 670us/step - loss: 0.9193 - mae: 0.6446\n", + "Epoch 963/1000\n", + "13/13 [==============================] - 0s 662us/step - loss: 0.9119 - mae: 0.6525\n", + "Epoch 964/1000\n", + "13/13 [==============================] - 0s 659us/step - loss: 0.9493 - mae: 0.6499\n", + "Epoch 965/1000\n", + "13/13 [==============================] - 0s 660us/step - loss: 0.8635 - mae: 0.6072\n", + "Epoch 966/1000\n", + "13/13 [==============================] - 0s 673us/step - loss: 0.7920 - mae: 0.6301\n", + "Epoch 967/1000\n", + "13/13 [==============================] - 0s 674us/step - loss: 0.8206 - mae: 0.5821\n", + "Epoch 968/1000\n", + "13/13 [==============================] - 0s 675us/step - loss: 0.9185 - mae: 0.6171\n", + "Epoch 969/1000\n", + "13/13 [==============================] - 0s 675us/step - loss: 0.8848 - mae: 0.6243\n", + "Epoch 970/1000\n", + "13/13 [==============================] - 0s 672us/step - loss: 0.9081 - mae: 0.5896\n", + "Epoch 971/1000\n", + "13/13 [==============================] - 0s 696us/step - loss: 0.9083 - mae: 0.6017\n", + "Epoch 972/1000\n", + "13/13 [==============================] - 0s 655us/step - loss: 0.9222 - mae: 0.6108\n", + "Epoch 973/1000\n", + "13/13 [==============================] - 0s 683us/step - loss: 0.9052 - mae: 0.6062\n", + "Epoch 974/1000\n", + "13/13 [==============================] - 0s 678us/step - loss: 0.9138 - mae: 0.6045\n", + "Epoch 975/1000\n", + "13/13 [==============================] - 0s 669us/step - loss: 0.9567 - mae: 0.6517\n", + "Epoch 976/1000\n", + "13/13 [==============================] - 0s 670us/step - loss: 0.8662 - mae: 0.6251\n", + "Epoch 977/1000\n", + "13/13 [==============================] - 0s 672us/step - loss: 0.8549 - mae: 0.5982\n", + "Epoch 978/1000\n", + "13/13 [==============================] - 0s 673us/step - loss: 0.8864 - mae: 0.6377\n", + "Epoch 979/1000\n", + "13/13 [==============================] - 0s 706us/step - loss: 0.8863 - mae: 0.6402\n", + "Epoch 980/1000\n", + "13/13 [==============================] - 0s 672us/step - loss: 0.9288 - mae: 0.6293\n", + "Epoch 981/1000\n", + "13/13 [==============================] - 0s 659us/step - loss: 0.8140 - mae: 0.6009\n", + "Epoch 982/1000\n", + "13/13 [==============================] - 0s 674us/step - loss: 0.8201 - mae: 0.6252\n", + "Epoch 983/1000\n", + "13/13 [==============================] - 0s 669us/step - loss: 0.8769 - mae: 0.6250\n", + "Epoch 984/1000\n", + "13/13 [==============================] - 0s 659us/step - loss: 0.8663 - mae: 0.6246\n", + "Epoch 985/1000\n", + "13/13 [==============================] - 0s 664us/step - loss: 0.8793 - mae: 0.5914\n", + "Epoch 986/1000\n", + "13/13 [==============================] - 0s 670us/step - loss: 1.0172 - mae: 0.6094\n", + "Epoch 987/1000\n", + "13/13 [==============================] - 0s 686us/step - loss: 0.8807 - mae: 0.6113\n", + "Epoch 988/1000\n", + "13/13 [==============================] - 0s 665us/step - loss: 0.9012 - mae: 0.6326\n", + "Epoch 989/1000\n", + "13/13 [==============================] - 0s 677us/step - loss: 0.9572 - mae: 0.6663\n", + "Epoch 990/1000\n", + "13/13 [==============================] - 0s 670us/step - loss: 0.8179 - mae: 0.6196\n", + "Epoch 991/1000\n", + "13/13 [==============================] - 0s 681us/step - loss: 0.8132 - mae: 0.5889\n", + "Epoch 992/1000\n", + "13/13 [==============================] - 0s 665us/step - loss: 0.9078 - mae: 0.5776\n", + "Epoch 993/1000\n", + "13/13 [==============================] - 0s 668us/step - loss: 0.8381 - mae: 0.5540\n", + "Epoch 994/1000\n", + "13/13 [==============================] - 0s 688us/step - loss: 0.8214 - mae: 0.5652\n", + "Epoch 995/1000\n", + "13/13 [==============================] - 0s 669us/step - loss: 0.9313 - mae: 0.5881\n", + "Epoch 996/1000\n", + "13/13 [==============================] - 0s 672us/step - loss: 0.9535 - mae: 0.6475\n", + "Epoch 997/1000\n", + "13/13 [==============================] - 0s 678us/step - loss: 0.9188 - mae: 0.6248\n", + "Epoch 998/1000\n", + "13/13 [==============================] - 0s 666us/step - loss: 0.8771 - mae: 0.5518\n", + "Epoch 999/1000\n", + "13/13 [==============================] - 0s 670us/step - loss: 0.9438 - mae: 0.6431\n", + "Epoch 1000/1000\n", + "13/13 [==============================] - 0s 668us/step - loss: 0.8970 - mae: 0.6265\n" + ] + } + ], + "source": [ + "bnn.train(Xsr, Ysr, epochs=1000)" + ] + }, + { + "cell_type": "markdown", + "id": "behind-breach", + "metadata": {}, + "source": [ + "Make predictions, uncertainies are returned too." + ] + }, + { + "cell_type": "code", + "execution_count": 137, + "id": "hollow-october", + "metadata": {}, + "outputs": [], + "source": [ + "preds, uncs = bnn.predict(Xst)" + ] + }, + { + "cell_type": "markdown", + "id": "million-blast", + "metadata": {}, + "source": [ + "Unscale the target values and uncertainties" + ] + }, + { + "cell_type": "code", + "execution_count": 138, + "id": "saved-accreditation", + "metadata": {}, + "outputs": [], + "source": [ + "preds = scale.inverse_transform(preds)\n", + "Yst = scale.inverse_transform(Yst)\n", + "uncs *= numpy.sqrt(scale.var_)" + ] + }, + { + "cell_type": "code", + "execution_count": 139, + "id": "expected-yesterday", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 139, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAX4AAAEGCAYAAABiq/5QAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8QVMy6AAAACXBIWXMAAAsTAAALEwEAmpwYAAA7+ElEQVR4nO3deXhTVfrA8e+btrSForUWFVkEl1ERlWoVkdERdBSX0epPB0dxGDdwQXFDwRV1HBhAQWcUQXDfUERARRkEGRUVBIvsDiqIFIRWrFDompzfHze3pmmSJm1ulvb9PE+fNLdJ7ttLee+555z7HjHGoJRSquVwxTsApZRSsaWJXymlWhhN/Eop1cJo4ldKqRZGE79SSrUwqfEOIBy5ubmmS5cu8Q5DKaUSX1UVbNwIu3axDEqMMe38X5IUib9Lly4sXbo03mEopVTicrvhySfh7rtBBP79b2TIkB8CvdTRrh4R2SgiK0VkuYgs9W7LEZF5IrLe+7iPkzEopVSzt3YtnHoqDB0Kp5wCq1bBjTcGfXks+vj7GGN6GGPyvc+HA/ONMYcB873PlVJKRaq6Gh55BHr0gHXr4MUXYc4cOOigkG+Lx+DuBcAL3u9fAAriEINSSiW3r76CE06Ae++FCy6ANWvgiiusbp4GOJ34DfAfEVkmIoO82/Y3xmwF8D7uF+iNIjJIRJaKyNLi4mKHw1RKqSRRXg7Dh8OJJ8K2bTBjBrzxBuy/f9gf4fTgbm9jzBYR2Q+YJyLrwn2jMWYyMBkgPz9fCwoppdTHH8M118D69XD11TB2LOwT+TCpoy1+Y8wW7+N24G3gRGCbiLQH8D5udzIGpZRKejt3WoO1f/iD1a8/bx5MmdKopA8OJn4RaSMibe3vgTOBVcBsYKD3ZQOBWU7FoJRSSe/996F7d5g4EW65xZqxc8YZTfpIJ7t69gfeFmugIRV41RjzgYh8CbwhIlcDm4BLHIxBKaWS088/w623wksvwZFHwqJF0KtXVD7ascRvjPkeODbA9p+B053ar1JKJTVj4M03YcgQ+OUXuO8+uOceSE+P2i6S4s5dpZRqEbZsgRtugFmz4Pjjrb78Y+u1n5tMi7QppVS8GQNTp0K3bjB3LowZA1984UjSB23xK6VUfH3/PVx7LSxYYJVdmDIFDjvM0V1qi18ppeLB7YYJE+Doo+HLL61ZOx995HjSB23xK6VU7K1ebd2AtXgxnHMOPP00dOoUs91ri18ppWKlqgoefhjy8uDbb+Hll+Hdd2Oa9EFb/EopFRtffmm18leuhEsvhccfh/0ClipznLb4lVLKSXv2wLBhcNJJ1k1Zs2bBa6/FLemDtviVUso5CxdaM3a+/dZ6HDsW9t473lFpi18ppaLu11/huuugTx/weGD+fJg8OSGSPmjiV0qp6HrvPTjqKHjmGbjtNqtPv2/feEdVhyZ+pZSKhuJiuPxyOO88yM6Gzz6DRx+F1q3jHVk9mviVUqopjLEGa7t1s4qrjRxpLYvYs2e8IwtKB3eVUqqxNm+G66+35uKfeKJVb6d793hH1SBt8SulVKQ8Hmuw9qijrIHbRx+1unaSIOlDDBK/iKSISKGIvOt9PlJEikRkuffrHKdjUEqpqPn2Wzj9dBg82CqdvHKlNYibkhLvyMIWixb/UGCt37bxxpge3q85MYhBKaWaxu22WvbHHGP14U+ebLX2Dzkk3pFFzNHELyIdgXOBKU7uRymlHLVqlbXs4R13WOvdrllj3ZBlLS2bdJxu8U8A7gQ8ftuHiMgKEXlWRAIuEy8ig0RkqYgsLS4udjhMpZQKoLLSmqVz3HGwYYM1e2fWLOjQId6RNYljiV9EzgO2G2OW+f1oInAI0APYCjwa6P3GmMnGmHxjTH67du2cClMppQJbvNjqw3/wQfjzn2HtWqu4WpK28n052eLvDZwvIhuB14G+IvKyMWabMcZtjPEAzwAnOhiDUkpFZvdua7C2Vy+r9MK771rlk3Nz4x1Z1DiW+I0xI4wxHY0xXYBLgQXGmAEi0t7nZRcCq5yKQSmlIrJggTV4O368NWtn9Wo499x4RxV18biBa4yI9AAMsBEYHIcYlFLqN6WlVunkKVPg0EOtqpp/+EO8o3JMTBK/MWYhsND7/RWx2KdSSoVl9mzr7tuffrKS/8iRCVlfJ5r0zl2lVMu0fbs1WHvBBbDvvtZg7pgxzT7pgyZ+pVRLY4w1WHvkkfD229YauEuXQn5+vCOLGS3SppRqOX780VogZc4caynEqVOtqpotjLb4lVLNn8cDEydaRdUWLoQJE+DTT1tk0gdt8Sulmrv16+Gaa+Djj63iapMnw8EHxzuquNIWv1KqeaqpsQZrjzkGvv7a6taZN6/FJ33QFr9SKkb6T/ocgGmDezm/s6+/hquvhmXLoKAAnnwSDjzQ+f02QSyPj7b4lVLNR2Ul3HcfNccdT+n/voc33oAZMxI+6ceatviVUs3D559brfy1a1l00tm8ePHNTL2kX7yjSkia+JVSya2sDO69F554Ajp2hDlzeGpTdryjSmja1aOUSl7z5sHRR8Pjj8MNN1hF1c4+O95RJTxN/Eqp5PPLL1a3zplnQqtW1lTNf/8b2raNd2RJQRO/Ui1Y/0mf184mSRpvvw3duuF+/gVmnnWFNYPnlFPiHVVS0cSvlEoOP/0El1wCF10EBxzAPcOn8NqF10NGRrwjSzqa+JVSic0YePFFq7zC7NnwyCOwZAkbOh8e78hCSuSrKccTv4ikiEihiLzrfZ4jIvNEZL33MeBi60opxQ8/WIO1Awda1TS//hruvhvS0uIdWVKLRYt/KLDW5/lwYL4x5jBgvve5Us1epC3ARG4xOs7jse627d7dKqb2xBPwySdwxBHxjqxZcDTxi0hH4Fxgis/mC4AXvN+/ABQ4GYNSKsl884217OGQIXDyybBqFdx0E7gan65a9Ek0AKdb/BOAOwGPz7b9jTFbAbyP+zkcg1IqGVRXw+jRcOyx1nz855+HDz6ALl3iHVmz41jiF5HzgO3GmGWNfP8gEVkqIkuLi4ujHJ1SKqEUFkLPnjBiBJx3HqxZY/Xri8Q7smbJyRZ/b+B8EdkIvA70FZGXgW0i0h7A+7g90JuNMZONMfnGmPx27do5GKZSKm4qKqzB2hNOgC1bYPp06+uAA+IdWbPmWOI3xowwxnQ0xnQBLgUWGGMGALOBgd6XDQRmORWDUiqBLVoEPXrAqFFwxRVWK////i/eUbUI8ZjHPxr4o4isB/7ofa6USlBRHxjdtcsarD3lFKvFP3cuPPcc5OREbx8qpJhU5zTGLAQWer//GTg9FvtVKhZiusBIkjt29RfQ/VJr0fObbrJuxsrKindYSakpf3dallnFjSbMFmTHDq5//u+c9sUcay7+J59A797xjqrF0pINSilnTZ8ORx7JKUvmMuPsgdYMHk36caUtfqWUM7ZutW7CmjED8vIYce0Yfuj0Oy7Sompxpy1+pVR0GWMN1nbrBu+9Z92UtWQJP3T6Xbwja1bWbN3Jmq07G/VebfErpaJn40YYNMhaGeuUU+CZZ+DwxK6i2RJpi18p1XRut1VIrXt3a9HzJ5+EhQs16ScobfErpZpm7VprGcTPP4d+/WDSJOjcOd5RqRC0xa+Uapzqamsefo8eVkXNF1+EOXM06ScBbfErFSUt6r6EZcvgqqtgxQr485+tbp799493VCpM2uJXSoWvvByGD7cqaW7fbi18Pm2aJv0koy1+pVR4Pv4YrrkG1q+3+vTHjoV9dOXUZKSJXykVUmb5bv4ycyL8dwZ07Qoffgina7mtZKaJXykV3Jw5jHvoKnJKt8Mtt8Df/w5t2sQ7KtVE2sevlKqvpMSqkX/uuZRntOb+YZNg/HhN+s2EtviVUr8xBt5806qx88svcN99DN/vdGrSWsU7MhVF2uJXSlm2bIELL4T+/a25+MuWwUMPRSXpzywsonBTKYs37KD36AXMLCyKQsDNR6yPj5OLrWeIyBIR+VpEVovIg97tI0WkSESWe7/OcSoGpVQYjIGpU62ianPnWrN1vvgCjjkmKh8/s7CIETNWUuX2AFBUWs6IGSsdS27JdpKJ9fEBZ1v8lUBfY8yxQA+gn4ic5P3ZeGNMD+/XHAdjUEqFsF9xEfdOuNmaptmjB6xcCXfcAanR6wUeO/cbyqvddbaVV7sZO/ebqO3DFiyJlpRVRn1f0RLL42NzcrF1Y4wp8z5N834Zp/anVCJYs3VndNendYrbDePHM/bhKzjkh7Xw9NOwYAEcemjUd7WltDyi7U0RLIn+uCP6+4qWWB4fm6N9/CKSIiLLge3APGPMYu+PhojIChF5VkQC3gEiIoNEZKmILC0uLnYyTKValtWrrRWwbruN1Ycfx+0PvAqDB4PLmXRwYHZmRNubIliytK8AElEsj4/N0cRvjHEbY3oAHYETRaQ7MBE4BKv7ZyvwaJD3TjbG5Btj8tu1a+dkmEq1DFVV8NBDkJcH337L0kf+xV/Ou4f3S1Md7QsfdtbhZKal1NmWmZbCsLMaX7I5WD9+sGTZKiVx57E4cXwaEpOjYYwpBRYC/Ywx27wnBA/wDHBiLGJQqkX78kvIz4cHHoCLL2bOGwu4ovxQqjxW76uTA4oFeR0YddHRtcm3Q3Ymoy46moK8Do36vFCDocGSaKcc51rPTRXt4xMOJ2f1tBORbO/3mcAZwDoRae/zsguBVU7FoFSLt2cPDBsGJ50EO3bA7Nnw6qs8sqQkpgOKBXkdyOucTc+uOSwa3rdJSS3UYGiwJJqbld6k+CMV6cyiaB6fcDjZ4m8PfCQiK4Avsfr43wXGiMhK7/Y+wK0OxqBUQog0EURlSuLChXDssTBunDVrZ/Vq+NOfgPgMKEZLQ7HHOon6i8f0zEg5OatnhTEmzxhzjDGmuzHmIe/2K4wxR3u3n2+M2epUDEolgkgTQZMTx6+/wnXXQZ8+1hz9BQusVbH23rv2JfEYUIyWSGOP9bz+eEzPjFTijngo1UxEmgialDjefReOOspa5Pz2262FUvr0qfeyeAwoRkskscej9Z0MV1Oa+JVyWKSJoFGJo7gYLrvM6srZZx9r/dtx46B164Avj8eAYrREEns8Wt/JcDWliV8ph0WaCCLabgy89ppVbmH6dBg50qqxc2LDk+Xi3RfeFOHGHo/WdzJcTWniVwmp/6TPk+MO2DBEmgjCfv3mzXD++VZL/+CD4auvrOmaraJXSTPZ6t74i0frOxmupjTxK9UEvomxcFMp1QHuEI00ETT4eo8HJk+2+vLnz4fHHoPPPoPu3aP+uyX67JSGxKv17fTV1MzCIsoqathVUdOoE3LISkwicluonxtjHotob0p52Qmzyu2h9+gFDDvr8IRqEYXDPzFWuT3gJmBBsIK8Dry2ZBMA0wb3avCzg77+22/h2mutqZp9+liDuIcc0vRfJoCG5ssnAzvOO6evoMrtoUN2ZlL+rfmy/+7swmf2CRkI+/dqqARfW+/j4cAJwGzv8z8BH0cUrVJewVqSEP4fbiIIlBgBZwqC1dTAhAlw331WV84zz1gLnotEf19eyTA7JRyRnnQTXTROyCETvzHGrqH/H+A4Y8wu7/ORwJuNiFmpJv3h2v3+ifAfOGYFwVautJL8l19as3YmToQOzp8gD8zOpCjA75hIs1NaomickMPt4+8MVPk8rwK6hL0X1WIFGqRtLi1JpwuCpVZXcck7U+C442DjRnj9dZg1KyZJH5JjdkpLtHdmWkTbAwl3tYWXgCUi8jZWTf0LgRfD3otSPppLS3LYWYczYsbKelcvUSkItngxo0ddSactG2DAAGuh89zcpn9uBOyrr1unLcdAs+gfbw6C9e5F0usXVtPEGPMIcCXwC1AKXGmM+Uf4u1HqN82lJek/+6ZViouMNFfTCoLt3g233Qa9epFZvpvRN46Fl16KedK3FeR1ICsjlbYZqUk317+5Kt1THdH2QCK5Jm0N7DTGPA5sFpGuEbxXtQDhzr2PxTznWN0HYE/bO6RdGwAqqj0Ubipt3JTHBQusdW7Hj4frruOO+1+h8OjeUY5YJbto3JsQVuIXkQeAu4AR3k1pwMth70UpP8l816i/krJKNpTsrjOtM6L57qWl1hTN00+3VsFauBCeeoryzDaOxaySVzSumMNt8V8InA/sBjDGbOG3qZ6qGWpOd8467ccd5Xj8VpMOux7MrFlWuYVnn4U777SKqv3hD84EqpoF+4rZ7tJvzBVzuIm/yhhj8C6WLiLaFFHKK9j0zZCzlLZvh0svhYICaNcOFi+Gf/4TMpNrgFvFR1PHXsJN/G+IyCQgW0SuBT4EpoR6g4hkiMgSEflaRFaLiH1PQI6IzBOR9d7HgIutK5Usgk3fDFpU7eWX4cgj4e234eGHYelSa1lEpWIkrOmcxphxIvJHYCfWXbz3G2PmNfC2SqCvMaZMRNKAT0XkfeAiYL4xZrSIDAeGY40fKJWUOuVksqFkd53unkB9rvvu2MY1r46BVZ9bSyFOnWp18ygVY2ElfhH5pzHmLmBegG0BebuGyrxP07xfBrgAOM27/QWsRdg18aukZU/f/HFHOVVuD61SXPWLqk2axLiH7sDl8VilF4YMgZSU4B+qlIPC7er5Y4BtZzf0JhFJEZHlwHasNXcXA/vbyy16H/cL8t5BIrJURJYWFxeHGaZS8ZGblU5e52zaZqSS1zn7t6T/v//BaafBDTfwbZejuOP+l2HoUE36Kq5CJn4RuV5EVgJHiMgKn68NwMqGPtwY4zbG9AA6AieKSNh1Y40xk40x+caY/Hbt2oX7NtUCrNm6M/FnHNXUwJgx1mLnK1fCs8/yyNAJFOceGO/IlGqwxf8qViXOWd5H++t4Y8zl4e7EGFOK1aXTD9gmIu0BvI/bI45axYxO6wxfSVklZRU17Fy8lHUHdYO77oKzz4Y1a+DKKx2tpKlUJEImfmPMr8aYjcDjwA5jzA/GmB+AahHpGeq9ItJORLK932cCZwDrsEo7D/S+bCDWSUWppFZSVknRT6Xc+vFLzH7hVvYtLWbo/93NzHufgPbt4x2eUnWEW6RtInCcz/PdAbb5aw+8ICIpWCeYN4wx74rI51jTQ68GNgGXRB62Uoll3xVf8fR7Ezjs5x95q3tfHu57DaWZe7H0P/+j4LiO8Q5PqTrCTfzinaUDgDHGIyIN1fJfAeQF2P4zcHpEUSoVgf6TPmfN1p10a7+X8zsrK2PgGxPot+BNtuyVy8BLHuS/Bx9f++NkKzWtWoZwE//3InIzVisf4Abge2dCUipJzJsHgwZxzsaNvHz8eYw65a/sTm9d5yXJVmpatQzhTue8DjgZKAI2Az2BQU4FpVRC++UXuOoqOPNMaNWKB25/iuf+cjvlGXWTfjKWmlYtQ7h37m4HLnU4FqUS39tvww03QHExjBgB99/PuhcKsavlf1+8WxctUQkvZOIXkTuNMWNE5F+A8f+5MeZmxyJTKpH89BPcdBNMnw49esB771lLIvrIzUpn+65KurXfKyHWBFYqmIZa/Gu9j0udDkQp+36Bv5zYmcJNpVS5PfQevSC+LWdjrBWwbrkF9uyBf/wD7rgD0sJf31SpRNPQzJx3vI8vxCYc1dKVlFUyYsbK2lLHRaXljJhh3SQe8+T/ww8weDDMnQsnn2wVVTviiNjG0IzoVVBosTw+DXX1vEOALh6bMeb8qEekWjS70Jkve1GTmCV+jweeegqGD7ee/+tfVr++K5KVSpuHmYVFlFXUYCD+V18R0JNMaA39JY8DHgU2AOXAM96vMmCVs6GpWItGeYam1tFp1KIm0fTNN3DqqVZ//u9/D6tXW5U0W2jSHzFjZW3Lz776atR6wiqhNFSy4b/GmP8CecaY/saYd7xflwG/j02IKlnNLCyicFMpizfsoPfoBWEljFCLmtift6uipvELmgdTXQ2jRllF1dasgeefh/ffh4MOit4+kszYud9QXu2usy3sJSVVQgv3Bq52InKwMeZ7ABHpCmjJTBWU3VqMtK++U04mW0or6iSczLQU+hzRrs7n2QuaN/R5YSkshKuvth4vvtjq2jnggKZ9ZpII1SUS7CorUe5G1u6cxgv3+vVWYKGILBSRhcBHwC1OBaWSj39rfOTs1QFbi3dOXxHyc3Kz0hl10dG1LX97IemP1hVHv/VZUQF33w0nnABbtsBbb8Gbb7aYpN+QYHcd693I4Zk2uFfCnpzCvYHrAxE5DLCnNKwzxlQ6F5ZKRHbfvf8fs3/rvsrtoao8cF99sD58XwV5HXhtyaY6+7p12vKArw3U+rTLI9tdTAEHJD/9FK65xurTv/JKePRR2MfZ5Z8jTQLxThrDzjqcETNW1rv60ruRk1+4Sy+2Bm4DDjLGXCsih4nI4caYd50NT/kKlnijzW6928sIdsoJ3cIL1BccTLA+/IYcmJ1JUYAk79/6nFlYxIaS3fUGJMHbJbRrl3XH7ZNPQpcu1lTNM89sVEzNnX2yvHXa8qS/GzneJ9FEE+7/wueAKsA+epuBvzsSkYor/3n0VW4PG0p2c/qjC1mzdWfA94Tb5+sSGjyJBJsVNOysw8lMq7tcYaDW59i539RZ9Bx8uoTmzoXu3a2pmjffbK2MpUk/pIK8DmRlpNI2I5VFw/smZdJX9YWb+A8xxowBqgGMMeWALifUDH1fvLte691jrPn1wQTr8011Se0fSYfsTLrmtqldmDxSBXkd6vT911vQHOuKKNBVwd7lu7jtlUegXz9o3drq5nn8ccjKalQsSiW7cBN/lXcVLQMgIocAIfv4RaSTiHwkImtFZLWIDPVuHykiRSKy3Pt1TpN+AxVVwe7WC9U3H6w1ftC+rcnKSKVn1xwWDe/b6KRvK8jrEHhBcx/+rZGz133Kh1Ou54I1/4V77rFm7px8cpPiaGm6td8rNmsbqJgJdzrnA8AHQCcReQXoDfytgffUALcbY74SkbbAMhGZ5/3ZeGPMuMYErJwTrCsHQvfN2wn4zukrascFRl10NK8t2cT2XcHbB070u6anuaio9tCubAcPzXuas//3GasPOJRVU6fR51JnunXs30PXJlax1JSTcYMtfhFxAfsAF2El+9eAfGPMwlDvM8ZsNcZ85f1+F1bBN+0gTHAiVl+8r3D65sNpjUeD/4wd/5u40lzCZas/5MMp19P3uy95qt81fDv7Q8eSvlLJqMEWv3eZxSHGmDeA9xqzExHpgrUM42Ksq4UhIvJXrKqftxtjfgnwnkF4F3vp3LlzY3arGsEFpKW6qKz21Hb72H38ja1a0NQyDraGZuy0K9nC5Jcfodf3hSzr3J2Xrr6XCff3b/J+lWpuwu3qmScidwDTsBZaB8AYs6OhN4pIFvAWcIsxZqeITAQexupOfhirFtBV/u8zxkwGJgPk5+cHLRSnoi/N261TVeOpnSFT5faA22pxB2Kvc+ukYDN2Hn1/DQWfvMW4h+7CjfDwOUN4M/8cjtw/29F4lIqnpnSVhpv4r8JK1Df4bT841JtEJA0r6b9ijJkBYIzZ5vPzZwC9FyAB+bb4fYWa3eO0QNNGDyn5kTEvPQ5b1rH2qF7c+cfr+Wnv/eIQnVLJI9zE3w0r6f8e6wTwCfB0qDeIiABTgbXGmMd8trc3xmz1Pr0QrfKZkBozu8dpvjdxpbprGLz4LW7+7DUqWmXCSy8xuuxgfiz6lUpvGWG7kFu8557H6sY7pcIVbq/tC8CRwBPAv7zfN7Q4S2/gCqCv39TNMSKyUkRWAH2w6gCpBBPsJo3G3nkbDcPOOhyXQPefvuWdF25h2CcvseDwk1n0zicwYAAlu6uo8LlSsQu5xaqM8LTBvXTao0oK4bb4DzfGHOvz/CMR+TrUG4wxnxI4f8wJNzjlnIZaoelprjp9/LZOOZlxa8EWHJFDxRcvc/F/3+DnNtkMH/AgJ912dW2LPlA3VMwXcVEqCYSb+AtF5CRjzBcAItITWORcWCre0lJcdMjOrF0Rq1WKC5cLtu+qrF1QPJRIBnvDmvHz8cdwzTVcun490/P68d4Vt/LcrXWnaMZ9ERelkkS4ib8n8FcR2eR93hlYKyIrAWOMOcaR6FQdTs+a8ZeblV7nblsn919SVll7kincVEqnnExr3zt3WksgTpwIXbvy8C1P8Mbev6Nb67b1PqNViitg8tcywkrVFW7i7+doFKpFKymrZEPJ7jpTRzeU7ObEtV/AI/1h82a49VZ4+GFWvbwCgpyAOuVk8l3x7jrbtIywUvWFW4//B6cDUcljT2VNVFv/P+4orzOWsM+eX7lvwRQuWv0RdOsGn30GJ53U4OfkZqVTVFpeOxU1UCE3pVT4LX7VzM0sLKLMOw2yrKKG9LTIZ+9Uuz1UVntYvGFH7ZhAWhizgGq7Z4zh3HWf8uCHT7N3RRmPn/wXhi54DtLDL+6WluKq3We39nvFPOnrlE2VDDTxq9oVtOxGtwEqqj2UlFWGVVGz/6TP+XpzKRXVv/Wv23f6QsPJv1WKi+zSYv4+byJnrv+Crw84jAH9/8737Q9haARJXykVHk38KugKWj/uKA+7lHJldeAZNRXV1lXAhpLdlO6ppsrtqbscojEMXr+Aa2dPpJW7mr/3uYrn8i/ApKTQtYHCcEqpxtHEr4JOd/SdIWNXxbSvCqr9Zs+EKqZkoE55Zru4WuvNP3DmhPu4fcEClh98LHeceRPf7n1A7XKPTa3fr5QKTBO/CrqerX2Xrj3rxje5V1R76pRDEEInf18uj5vLPpvJqf98CTJaMfnyO1nQ+3z2dbnYtnUn3drvxZqtO0PW8ldKNV787r9XEbEHX3dV1ASsQ98UgVbQAshunQbAxp/31LuDF6wuIlu4g8GHFf/AWy/fyX0LprCo8zGwZg3zTynAuFx1au2XVdTUu6pQSkWHtviTgP/gq38d+qYqyOvA0h928PIXm+pst0swuwNlfep2EdkDuMGqeqa5q7n+i+kM+Wwau9Jbc/OfhrGs11mc3rEj8GO9qwp7gDncImszC4tY9sMv1PjEGqyEtFItnbb4k0CgwVe7Bk20fLSuuN42jyFkd4v/HbFpKS6yMlI5pF2bOqt4HbP1f7zz/C3c9ukrzDmiN3+8ZiLzju3LsH5H1L7Gfy6/LZzfcWZhEcOmf10n6YO1cHysCrQplUy0xZ8Egg2+NlSDJpJiaoH6+BuSEaR7xx6U3bLlZ2795BWuXjqLkrY5DL7kAeYefAIdsjO5357V49WUOjtj535Dtbv+WcN4f6Y3cClVlyb+JBBs8DVaNWhmFhZFNDhrCzXr5tSi1Vz5/CN0/mUrbxx/DnMG3EJ5ZhY9CXwiakqdnVAnBy3Q1nR6U1rzo109SSDQ4KtLiFoNmrFzvwmZ9APV1hafjfag7K6KGuTXX7ni+X/wwPghANx+3WPce9YQFv5UReGm0qD97p1yMgMu8u7/OwaqeR/q5KAF2pSqz7HELyKdROQjEVkrIqtFZKh3e46IzBOR9d7HfZyKobkoyOvAqIuOrk3ArVJcdM1tE3EXRv9JnwcsgdxQqzjFJbVTOwUr6dt/OL4LoPf9dglzp9zA2YvnMO0P/Tl/0JO8nf272pa8XXwtUL97blY6XXPbNOp3HHbW4aSl1D89CdE7OSrVnDjZ4q8BbjfGHAmcBNwoIt2A4cB8Y8xhwHzvc9WAgrwOZGWk0jYjlbzO2VG9uamhVnGNx5DXOZu2GalkZaTW+aMZO/cbsnf/yuOzx/LsWw/xa0YWFw0Yy329B/IrreoN2HpM8AHb3Kx0sjJS6dk1J6LfsSCvA2MvPpZUv0uGg9tFfnKMtpmFRRRuKmXxhh1Rn4arVGM5lviNMVuNMV95v98FrAU6ABfw27KNLwAFTsWgAvNPRn2OaBdwHr/Nd7nFarcHtwG3gcIffuH4zz5g3pTrOfubRTz2+8v5098m8PWBh1PlDjytE8Lvd1+zdWd4i7RgJf/jD9qHtt6TY8+uOcy//bSw3usUexqufcVjT8PV5K/iLSZ9/CLSBcgDFgP724utex/3C/KeQSKyVESWFhfXn2qoGqekrLJeMnprWRH/d3zwlnEnb82cimp3bSG2A3aW8NQbI3ninbFsym7PuX97nCd6/4XqlLQGY2gp/e6xmIarVGM4PqtHRLKAt4BbjDE7RYIt412XMWYyMBkgPz8/0gknystuNdszM+xVrnyVV7v5aF0xbTNSKausAUNtPXuXy+qCKSmrpNptEOPhL1/PZcRHz5Lq8fBw32t47vg/4XEFv2Lw5T9ga199+C7v2Fw0dhquUk5zNPGLSBpW0n/FGDPDu3mbiLQ3xmwVkfbAdidjaC586+XbSxM2Rqj58nb/feuM1NqZM/aCKz/uKOegX7Yw+oN/0WvTShYddAzD+93Mj9kHhL1vu/ia3e/u3xVil3IOtxx0onN6Gq5SjeXkrB4BpgJrjTGP+fxoNjDQ+/1AYJZTMTQX/iUbQs2OaUirILXxD8zOZE9lDQHug8LlrmHgZ9OZ++wQjvrpO+7qdxOX93+kNukLkCLQs2tOyH37D9iGKgfdHASahqtLQapE4GSLvzdwBbBSRJZ7t90NjAbeEJGrgU3AJQ7GkJT877gNlCDt2TGRzlrplJPJltKKOp9nd7/c/sbyeq8/y1PMuOefgC+/ZN6hPbn3zOvZ1ja3zmtSUwSPd/pOsBuxAp1wwikHHUiy3FBk/9vcOX0FVW4PHbIzf1uHQKk4cizxG2M+JfC9PwCnO7XfZBBJKQWIbl9xblY6N/U9rDYZ+Xa/+Cb+1OoqLvzgRQrefwH2zeGhAffz7IEn1L1zy8sul1C4qZTs1mmUlFXWmcbpEgJ2TTVUDro5KMjrwGtLrOJ3yXLCUs1f8/kfFmfBbo6KhmB9wo3tKy7I60Be5+yg8+WP2byW0f+4kovfe5bPTjgD1q5l9e/7BUz6vqrcvy3X6H8jVqA++2DloBs7fqGUCo8m/jgL54QRbsmGpp58MqsquHf+M7w09TYyK3Yz6sZxPHnlA7DvvmF/hsdA6Z5qsjJSSZH6/fq+7DuS7RZ+qxQXGWmuJg/sdmu/l7aulQpBi7QlAbtP+NZpy2unWfrOjomK+fOZM/VGOv+6jdfzz+ODy4dSntmmzktEwIQxsbbK7Ql7YRbfrhCwZhGVlFXWTjst3FQadk1+pVR4NPEniYK8Dtw3axV7KmtIb6BVHEmrv/WeXQx469+w6B3c+xzIny8bzdrDetDNL+mDdXlYfw5OfU3po6/2zliyxwiq3J6oLjqjlNKunhap/6TPWbN1J/nLP+bRBy/ntM/nwF13ce5V/2JJp+5hfUaKWPX4A1XUbGwf/bTBvfB4qFffR+92VSq6tMUfB/ZNUf7fR1Og7hLbXjt3MGb6WM5e/V82djyMMTeMYfQ/rqRyxHthF+V3G3BXe0hx/db/Y3dB5WalN3qh9KYsyKKUCo8mfodFOnWzIR6oXZC89+gF9eaFr9m6k2q3h6oaT73ukgP3TqdgzUL+9sYE0ivKef38Qcw+awDulNB/BvYVQiD2erziHci1f8+jR85t1O/XlAVZlFLh0cTvI9pJOtqq3Z46g6vBFl0PtOD5Pj9v5d5Xn6TP98v4qsOR3PunobTtcUyd17ROT2VPZQ1Q94ohnBo64Qz6hqNTTmadPn7Qu12VijZN/Emksrp+S9ju//ZN/L45WIyHy5d/wPCFz+EyHkaePogXjzsXjyuFQ4LUxAk0wIq7wWn8UWHH43vSGXXR0Tqwq1QUaeKPsf6TPmdPZQ2t0yM/9OHWt7fXz+26o4jR7z9Bz82r+eSgHozoN4TNPkXVftxRHjDxB7piCBlAEK3TU5k2uFfEV1J2TN8X76bK7akd2NXkr1R0aOJPIsEWRPfv/26dYrjis7e45dNXqUxJY9jZQ3nz6DPqNdmDDaQGy++G4HP5fT/at5Jo79ELIr4pq6SssnY5RwjepaWUahxN/Enk4HZt+K54d51t/nfw/u6n73l49mN02/otc3/Xi3v/eD3FWYGrZgaab986PbU2aQd6vdvjqTOXP8UluD0GY6xaPffOXMlby4rqJG3/KZ8N+XFHedApnbFO/Ik+7qNUY2jiD4OT//ntwdRwbN9VabWsfRZKqb2Dt6KC/rMm8ae5L/Nr5l6M/OtIXjwwv14C9RVsvn16mqt2pS3/128ssU48dsvf7bODKreHV77YVO+k4THhl1qeNrgXXYe/F/BnDU3p1OSsVHg08cdQqGmR4fJfKAWAzz6Dq6/monXrmHXsGYw5azDFqW3wBOnK8Z2lY69+1ePB/1Be5abGYxAgLUUQhCq3B8EqvfzjjvLaev3BZvEEO880VGrZly5gopSzkvrOXScrYjrFXqx8V0UNvUcvwD8d+i+EHmqxlfSKPQycNh5+/3vYs4d/3PQY9xbcwc7MtiETbV7nbAA2lOyufV1peTU13ta7wWrJd8rJpGfXHNLTXFS7TUTJ218kZRyGnXV4ve4hndKpVPQ4uQLXsyKyXURW+WwbKSJFIrLc+3WOU/t3kv8JJ1iy9m/hl5RV1ulCKSotxxhqk7//UoT2oGag5H/0miWMe/gKzvnoTbjxRli1iq+POqn258ESrb29stoTshvIt3sm0DTSUPy79DPTUhhz8TEBXxtIQV4Huua2qf2cDtmZOqVTqShysqvneeDfwIt+28cbY8Y5uN+YCpasAwnWz22MNftl286K2la3zX9Qc6+KMu7/4FkuXP4fivbvzP13TOShsdfVWbRcgHZt0wMuiOJyWXf3hjMz0/6dIpnFmZmWwv8d36G2r7+xq07ZZR+0xLJS0efkClwfi0gXpz6/MZwYpA20LKKdrDvuU7dPOlRXSaA+bZs9qHn62k+5+70nydnzK2/3+ytvnXsl1Wnp9U4+ht8WLC/eVVmbgDPSXLU1dIJNDfVlXx2E81qAfVqn8cCfjqIgrwOzlm8BYNHwvmG8s3nTE5dKNPEY3B0iIn8FlgK3G2N+CfQiERkEDALo3Llzgx/q5MybUAOyoZZF9E/8werQNKR7SjlcfDET3nqL1fsdzJWXjGT9gYfh2lZOdc3u2pa+L4+xZgFlpLk4tmN27Y1UduI/uF2bemvv+vKtshlslo+vjDQXhfefGfHvppSKvVgn/onAw1gNyIeBR4GrAr3QGDMZmAyQn58fpUow0RfJDJROOZn15uGHZAwXr17A/Qum4HZX8eipf2XyiRdRk5IKPmUU3CFOJhXV1nKItmq3h8pqD7sqasjOTKPa7aHGY8jOTKOssoYaj6lTZRMgLSV04k8R6zW+6sw6aoC2iJWKrZjO6jHGbDPGuI0xHuAZ4MRY7t8JgZZFDDYDJTcrnbSU0HczpbiEVikuOvy6ndfffohx741ny4FdGXDj0zzV689W0vdhTMMzZuyxBXtw2T6LlpZX4zGGQ9q1YfkDZ3L8QfsEXYc3WNQxKN+jlIqymLb4RaS9MWar9+mFwKpQr08G9qDlndNXUOX21BnM/NeC9XVKKFe7PVS7Q1+85GamMnj1XC6bOZGMtBSmXno78069kMU/lAZ9T6eczJDdNnY3UKDB5YZuriopqwx6Jy9Y3UDVNY2f5qmUij3HEr+IvAacBuSKyGbgAeA0EemB1dWzERjs1P4j5TsrJlCd+1B81421uy1mFhbVqzfTkIN/3szYV57g+M1rWN6tJz3mTOM/H1iDpMG6lMC6kuh1yL68/MWmgD+3rwiCjS8E2+5fM8f/M10uq4tHE79SycXJWT1/CbB5qlP7a4pQUzIbO3d87NxvQs6T95XqrmHQkhkMXfQa5WnpPPm3+/i4Zz+mHXQQYCX+YWcdzm1vLK/3mXZxtI/WFQf9fHuQNtjgcrCuokA1c+zX53XOrh30bp2eGlGfvlIqvpL6zt1oCTUls7HCXSrwqG3fMevF27jz4xf58NATOXvQJD4+6ex6lTT9b2pqleKy1rwNY392f32g2jyh1siN9ApBKZUcNPETekpmYzVUVya9poo7//s8s164lXa7f2Fwwd3cdOEIWnc+MOh7crPSycpIpW1GKp1yMqmstso/FG4qJbt1WsD3iN/7M9Jcde6I7Zrbps5AbklZZe1dyMFEUn6hsaYN7hX3q4hIymcolUw08RM8SR+YnVlbW96urRPuf/5A9WZs+ZtXM+e5m7jhi+nM6N6XM66ZyIdH9CY3Kz1o3Xo7Ce3yxvJ98W9971VuD2UVNfVm2LjEGnz1lZbiIisjlZ5dc1g0vG+9pO9bvyeYYFcIzUkk5TOUSjaa+Ak+JbPPEe0YMWNlvQVBfOfFB+PfNeMSaFO5hwfnTWT6K3fRyl3DgD8/zJ3n3MLOjCzcxlBSVhnws0vKKuskIah/J221x+BySZ2uoMf+3INjO2aHdQwgeJ9+S+RE959SiSJpE7//ZXg4yTiYgrwOjLro6NouDLso2EfrigP+5/+ueHeDl/8zC4v4cUc5Bkh1Cad8t4y5z97IFV/N4dnjz+esq/7Np13z6rwn2NTKH3eUB52q6cvtMbVdQXmdsyMemA637/7HHeUJ0RXjJCe6/5RKFElZjz/QZXikqzz5CzQl89Zpy0O+J1hBNt/4sst3ct+CKfzfqgWs37cTFw8Yw1cdjgz6mYGSb7gJuaG+d985+YWbSuudtMItKWG/JtDnNZcKmromgGrOkrLFH+gyPJJVnsIVzn/yQJf/Y+d+Q3lVDWev+5R5U27g/DX/5Yle/Tn3b0+ETPq2wk2lda5gwhlMzUxLCdn3PrOwiC2lFXXGBfy7rTrlZIZ1Am2V4qp3n4L9ec2lDzySO7KVSjZJmfiDXW6Haq02ZobGsLMOb7DEQqB4qjcX8fTMfzBx1mi27pXL+QPH89ipV1CVGnjmjb8qt4cNJbtrY+yUk1kvCaX5ZeiMtND/lMH6rH1PlrlZ6XTNbVN7otmndVq9/djxBLpPIdp94NMG94pbHZ9g3X/N5YpGtWxJ2dUT6i7WQHfdNukGrTAGO2uvDIyB557jw6k306qmilGn/Y0pJ1yI25US+gMC8Bh48J3V7K50W11GPgXVWqW4OKFLNou++23K5S97qvm1vJpWqa56BdMg/JOl78yiaYN7MbOwiFunLa9d49flsl6zJMh0T3s/zaHwWqDuP6Wag6Rs8Qe6DLcFmnbX2BkaY+d+Q3UD01zsy/92JVu45/Fb4KqrqOp2FBde+ySTel5cm/TTUoSUCAciftlTXWdpRI8xZKRZlTM/+65+4vWY+qtl2a3mYN1WDXUjFeR1qDNgbJ9UQk2BVUoltqRM/P6X4f78k3pjZ2g09PMO2ZmMuqAbBZ+8xbiHBnDoxtUwcSK5X37G4GvPro2vVYqLsRcfS/5B+zTYJROKndjt2UKBBNserM/ad1wgkq4V7QNXKnklZeIHK/nbi4YH4pu0G9s6DdVK7tk1h0XnH0DBzZfC0KGsPSyPO+5/Ba67Dlyu2vj8p1ampbj8qzEgEPQKxp8h9FhGsGsK/5NlqxQXoy46mvm3nxZ2svedwql94Eolr6RN/LZgrX7fpN3Y1mmw93XZO42L5jwHeXnwv//Byy8zesg4fs7ZP6yYs9KtrpOeXXNIEevmLv8kmp0ZvARDqO4Z/zt1fQU7GTWW/Xn2XcCa9JVKDkk5uOsrUC16/6QeqmZ+KPbP7cHNDtmZ/L1TJQffdT0HFX0L/fvDE0/AfvsxrYm/h/9Aoj0g7ft7uQRapbrokF3/d7YXWA/nRi+lVMuW9C3+3Kz0sLocGts6tQc3c1PcLPplLn3+eh5ty0oZe91oeP112G+/JsXfOj2V1un1z7+BulK65rYhLcVV+zv7lmcY378HS+45w/G7aeM5xVIpFR1OLsTyLHAesN0Y0927LQeYBnTBWojlz8EWW4+E09Pujv9hJSNnT4AdRXDttdx+5MXsad026vvx5/979Z/0OblZ6bW/432zVrGnsqZet0239ntpclZKBeVki/95oJ/ftuHAfGPMYcB87/PEtXMn3HADzz8/jBTjgfnzYfLkmCR9J+mJQamWzckVuD4WkS5+my/AWo4R4AVgIXCXUzE0yZw5MHgwbNnCCyddxJN9/sqSvn0j/phASda3OybaXTOa0JVSDYn14O7+9mLrxpitIhK0g1xEBgGDADp37hyT4KYN7gUlJTBgALzyCnTrBtOnM+790pjsP1E15yqcSrVECTu4a4yZbIzJN8bkt2vXLhY7hGnTrGQ/bRo88AB89RX07On8vpVSKoZinfi3iUh7AO/j9hjvP7AtW6CgAC69FLp0sRL+yJGQHng1rKbQ/nWlVLzFuqtnNjAQGO19nBXj/ddlDEydCnfcAVVVMG4c3HILpEReVC0eurXfizVbd0b9c/XE9Bs9Fqo5cnI652tYA7m5IrIZeAAr4b8hIlcDm4BLnNp/g77/Hq69FhYsgNNOg2eegUMPDfhSJ/u4I0ks8UpCmvyUal6cnNXzlyA/Ot2pfYZDPG4YPx7uuQfS0mDSJLjmGnA53+ulCVQplQiSumRDpIm0U9F3DH5pFGxcA+edBxMnQseOju1XE71SKhEldeIPW1UVjBrF6H/8nT2ZWfDqq9ZArn+ZzBhqLt02enJTKvk0/8T/5Zdw1VWwahWpl13GXhMmQCymhzZCY5KozhJSSkVKjAljbcE4y8/PN0uXLo3sTXv2wP33W/357dvD009b3TtKKdVCiMgyY0y+//bm2eJfuNAasP3uO6vswj//CXvvHe+olFIqISTsnbuN8uuvVqLv08d6/tFHVktfk75SStVqPon/nXescgtTplg3ZK1YYc3PV0opVUfyJ/7iYrjsMjj/fNh3X/jiCxg7Flq3jndkSimVkJI38RtjTcs88kiYPh0eegiWLoUTToh3ZEopldCSc3B382a4/np4912reubUqXDUUfGOSimlkkJytfg9HqvEQrduVo2d8eNh0SJN+kopFYHkafF/+61VVG3hQjj9dJg8GQ4+ON5RKaVU0kmOxL9tGxx9tFUff8oU607cOJZbUEqpZJYciX/zZrjgAnjqKTjwwHhHo5RSSS0pSjaISDHwQxx2nQuUxGG/oWhM4UnEmCAx49KYwpOMMR1kjKlXnCwpEn+8iMjSQHUu4kljCk8ixgSJGZfGFJ7mFFNyzepRSinVZJr4lVKqhdHEH9rkeAcQgMYUnkSMCRIzLo0pPM0mJu3jV0qpFkZb/Eop1cJo4ldKqRZGEz8gIs+KyHYRWeWzLUdE5onIeu/jPgkS10gRKRKR5d6vc2IYTycR+UhE1orIahEZ6t0e12MVIq54HqsMEVkiIl97Y3rQuz1uxypETHE7Tj6xpYhIoYi8632eCP///GNKhOO0UURWeve/1Lst4mOlid/yPNDPb9twYL4x5jBgvvd5rD1P/bgAxhtjeni/5sQwnhrgdmPMkcBJwI0i0o34H6tgcUH8jlUl0NcYcyzQA+gnIicR32MVLCaI33GyDQXW+jyP999UoJgg/scJoI93//b8/YiPlSZ+wBjzMbDDb/MFwAve718ACmIZEwSNK26MMVuNMV95v9+F9Z+iA3E+ViHiihtjKfM+TfN+GeJ4rELEFFci0hE4F5jiszmuf1NBYkpUER8rTfzB7W+M2QpWYgH2i3M8voaIyApvV1DML4EBRKQLkAcsJoGOlV9cEMdj5e0qWA5sB+YZY+J+rILEBPH9m5oA3Al4fLbF+28qUEwQ//97BviPiCwTkUHebREfK038yWcicAjWpfpW4NFYByAiWcBbwC3GmJ2x3n8wAeKK67EyxriNMT2AjsCJItI9lvsPJEhMcTtOInIesN0YsyxW+2xIiJji/n8P6G2MOQ44G6tL89TGfIgm/uC2iUh7AO/j9jjHA4AxZpv3P68HeAY4MZb7F5E0rOT6ijFmhndz3I9VoLjifaxsxphSYCHWeE3cj5V/THE+Tr2B80VkI/A60FdEXia+xylgTInw92SM2eJ93A687Y0h4mOliT+42cBA7/cDgVlxjKWW/Q/sdSGwKthrHdi3AFOBtcaYx3x+FNdjFSyuOB+rdiKS7f0+EzgDWEccj1WwmOJ5nIwxI4wxHY0xXYBLgQXGmAHE8TgFiymexwlARNqISFv7e+BMbwyRHytjTIv/Al7DunSrBjYDVwP7Yo2Qr/c+5iRIXC8BK4EV3n/w9jGM5/dYfYwrgOXer3PifaxCxBXPY3UMUOjd9yrgfu/2uB2rEDHF7Tj5xXca8G68j1OImOJ6nICDga+9X6uBexp7rLRkg1JKtTDa1aOUUi2MJn6llGphNPErpVQLo4lfKaVaGE38SinVwmjiVyoAEckWkRviHYdSTtDEr1Rg2UC9xC8iKbEPRano0sSvVGCjgUO8dc+/9Nb7fxVYKSJdpO4aCXeIyEjv94eIyAfeIlqfiMgRcYpfqaBS4x2AUglqONDdGNNDRE4D3vM+3+CtABrMZOA6Y8x6EekJPAX0dTpYpSKhiV+p8CwxxmwI9QJvddCTgTet8kEApDsdmFKR0sSvVHh2+3xfQ91u0gzvowsoNVbZY6USlvbxKxXYLqBtkJ9tA/YTkX1FJB04D8BYawBsEJFLwKoaKiLHxiRapSKgLX6lAjDG/Cwii7yDuOVYyd7+WbWIPIS1ytcGrHLLtsuBiSJyL9bShq9jVVNUKmFodU6llGphtKtHKaVaGE38SinVwmjiV0qpFkYTv1JKtTCa+JVSqoXRxK+UUi2MJn6llGph/h8IPvTjEkj/GwAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots()\n", + "ax.scatter(Yst, preds)\n", + "minv, maxv = Yst.min()-1, Yst.max()+1\n", + "ax.set_xlim(minv, maxv)\n", + "ax.set_ylim(minv, maxv)\n", + "ax.plot([minv, maxv], [minv, maxv], c='r')\n", + "ax.set_xlabel('true')\n", + "ax.set_ylabel('predicted')\n", + "ax.errorbar(Yst, preds, yerr=uncs.reshape(-1), ls='none')\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "spoken-vietnamese", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "stuck-requirement", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "golden-count", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "parallel-computer", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "legendary-hanging", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ranking-ensemble", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "subjective-electronics", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "usual-automation", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/gandy/models/bnns.py b/gandy/models/bnns.py index 45a518d..4470c66 100644 --- a/gandy/models/bnns.py +++ b/gandy/models/bnns.py @@ -141,18 +141,13 @@ def negative_loglikelihood(self, targets, estimated_distribution) -> Array: except AttributeError: raise AttributeError('Passed distribution does not have the\ log_prob method') - if not isinstance(nll, float): - raise ValueError( - 'the returned value should be a float, not {}'.format(type(nll) - ) - ) return -nll # overridden method from UncertaintyModel class def _build(self, train_size: int, task_type: str = 'regression', - activation: Union[Callable, str] = 'relu', + activation: Union[Callable, str] = 'sigmoid', optimizer: Union[Callable, str] = 'adam', neurons: Tuple[int] = (12, 12, 12), metrics=['MSE'], @@ -282,6 +277,7 @@ def _build(self, def _train(self, Xs: Array, Ys: Array, + metric: Callable = None, *args, **kwargs) -> Any: ''' diff --git a/gandy/tests/test_models/test_bnns.py b/gandy/tests/test_models/test_bnns.py index 82e28ad..cbfcbc6 100644 --- a/gandy/tests/test_models/test_bnns.py +++ b/gandy/tests/test_models/test_bnns.py @@ -68,11 +68,6 @@ def callable_wo_log_prob(): mocked_dist) mocked_dist.log_prob.assert_called_with('targets') - # ability to catch non float - mocked_dist.log_prob.return_value = 'string' - with self.assertRaises(ValueError): - subject.negative_loglikelihood('targets', - mocked_dist) return def test__build(self): From edb975a106cbe7c83f81e28d5647ea43fc4163d5 Mon Sep 17 00:00:00 2001 From: evankomp Date: Thu, 11 Mar 2021 18:42:03 -0800 Subject: [PATCH 75/99] prettier BNN demo --- examples/BNN_demo.ipynb | 2132 +++++++++++++++++++-------------------- 1 file changed, 1049 insertions(+), 1083 deletions(-) diff --git a/examples/BNN_demo.ipynb b/examples/BNN_demo.ipynb index 34a64fb..9a1630a 100644 --- a/examples/BNN_demo.ipynb +++ b/examples/BNN_demo.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "hybrid-postage", + "id": "ranking-continuity", "metadata": {}, "source": [ "# Demonstration of BNNs as uncertainty models" @@ -10,8 +10,8 @@ }, { "cell_type": "code", - "execution_count": 39, - "id": "short-connecticut", + "execution_count": 1, + "id": "greek-treasure", "metadata": {}, "outputs": [], "source": [ @@ -30,8 +30,8 @@ }, { "cell_type": "code", - "execution_count": 133, - "id": "waiting-result", + "execution_count": 2, + "id": "accessible-central", "metadata": {}, "outputs": [], "source": [ @@ -50,8 +50,8 @@ }, { "cell_type": "code", - "execution_count": 134, - "id": "complete-expense", + "execution_count": 3, + "id": "apparent-temperature", "metadata": {}, "outputs": [ { @@ -70,7 +70,7 @@ }, { "cell_type": "markdown", - "id": "fifth-defensive", + "id": "valid-ladder", "metadata": {}, "source": [ "Instantiate our model - using two hidden layers with 3 neurons each" @@ -78,11 +78,22 @@ }, { "cell_type": "code", - "execution_count": 135, - "id": "accessible-ready", + "execution_count": 4, + "id": "proper-process", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "WARNING:tensorflow:From /Users/ek/miniconda3/envs/gandy_env/lib/python3.6/site-packages/tensorflow/python/ops/linalg/linear_operator_lower_triangular.py:167: calling LinearOperator.__init__ (from tensorflow.python.ops.linalg.linear_operator) with graph_parents is deprecated and will be removed in a future version.\n", + "Instructions for updating:\n", + "Do not pass `graph_parents`. They will no longer be used.\n" + ] + } + ], "source": [ + "# we have to specify ahead of time the training set size because of the kernel sizing\n", "bnn = gandy.models.bnns.BNN(Xs.shape[1:], (1,), \n", " train_size=len(Xsr), \n", " metrics=['mae'],\n", @@ -91,7 +102,7 @@ }, { "cell_type": "markdown", - "id": "informal-outside", + "id": "descending-helen", "metadata": {}, "source": [ "Train it for 1000 epochs, default batch size." @@ -99,8 +110,8 @@ }, { "cell_type": "code", - "execution_count": 136, - "id": "natural-couple", + "execution_count": 5, + "id": "boxed-right", "metadata": { "collapsed": true, "jupyter": { @@ -113,2005 +124,2005 @@ "output_type": "stream", "text": [ "Epoch 1/1000\n", - "13/13 [==============================] - 1s 847us/step - loss: 2.8041 - mae: 1.2213\n", + "13/13 [==============================] - 1s 819us/step - loss: 8.5399 - mae: 1.1827\n", "Epoch 2/1000\n", - "13/13 [==============================] - 0s 770us/step - loss: 2.6672 - mae: 1.1379\n", + "13/13 [==============================] - 0s 649us/step - loss: 12.8265 - mae: 1.2603\n", "Epoch 3/1000\n", - "13/13 [==============================] - 0s 757us/step - loss: 2.5625 - mae: 1.1922\n", + "13/13 [==============================] - 0s 652us/step - loss: 9.4922 - mae: 1.2110\n", "Epoch 4/1000\n", - "13/13 [==============================] - 0s 764us/step - loss: 2.2123 - mae: 1.0164\n", + "13/13 [==============================] - 0s 641us/step - loss: 8.1711 - mae: 1.1651\n", "Epoch 5/1000\n", - "13/13 [==============================] - 0s 759us/step - loss: 2.0716 - mae: 1.0783\n", + "13/13 [==============================] - 0s 644us/step - loss: 7.6288 - mae: 1.1726\n", "Epoch 6/1000\n", - "13/13 [==============================] - 0s 754us/step - loss: 2.0409 - mae: 1.0826\n", + "13/13 [==============================] - 0s 647us/step - loss: 9.7296 - mae: 1.2097\n", "Epoch 7/1000\n", - "13/13 [==============================] - 0s 775us/step - loss: 1.7491 - mae: 0.9929\n", + "13/13 [==============================] - 0s 641us/step - loss: 6.5541 - mae: 1.1019\n", "Epoch 8/1000\n", - "13/13 [==============================] - 0s 785us/step - loss: 2.1113 - mae: 1.0331\n", + "13/13 [==============================] - 0s 639us/step - loss: 5.6294 - mae: 1.0557\n", "Epoch 9/1000\n", - "13/13 [==============================] - 0s 785us/step - loss: 1.8911 - mae: 1.0953\n", + "13/13 [==============================] - 0s 640us/step - loss: 8.6134 - mae: 1.1177\n", "Epoch 10/1000\n", - "13/13 [==============================] - 0s 761us/step - loss: 2.0516 - mae: 1.1019\n", + "13/13 [==============================] - 0s 635us/step - loss: 4.9813 - mae: 1.0462\n", "Epoch 11/1000\n", - "13/13 [==============================] - 0s 769us/step - loss: 1.8686 - mae: 1.0198\n", + "13/13 [==============================] - 0s 615us/step - loss: 6.2197 - mae: 1.0871\n", "Epoch 12/1000\n", - "13/13 [==============================] - 0s 778us/step - loss: 1.7792 - mae: 1.0496\n", + "13/13 [==============================] - 0s 634us/step - loss: 4.9794 - mae: 1.0473\n", "Epoch 13/1000\n", - "13/13 [==============================] - 0s 760us/step - loss: 1.8546 - mae: 1.0699\n", + "13/13 [==============================] - 0s 632us/step - loss: 5.8736 - mae: 1.1351\n", "Epoch 14/1000\n", - "13/13 [==============================] - 0s 780us/step - loss: 1.8324 - mae: 1.1628\n", + "13/13 [==============================] - 0s 638us/step - loss: 4.2527 - mae: 1.0332\n", "Epoch 15/1000\n", - "13/13 [==============================] - 0s 771us/step - loss: 1.6721 - mae: 1.1144\n", + "13/13 [==============================] - 0s 635us/step - loss: 4.5242 - mae: 0.9960\n", "Epoch 16/1000\n", - "13/13 [==============================] - 0s 792us/step - loss: 1.8686 - mae: 0.9695\n", + "13/13 [==============================] - 0s 638us/step - loss: 5.2721 - mae: 1.0758\n", "Epoch 17/1000\n", - "13/13 [==============================] - 0s 799us/step - loss: 1.6657 - mae: 1.0399\n", + "13/13 [==============================] - 0s 635us/step - loss: 3.3385 - mae: 1.0166\n", "Epoch 18/1000\n", - "13/13 [==============================] - 0s 811us/step - loss: 1.5542 - mae: 0.9580\n", + "13/13 [==============================] - 0s 632us/step - loss: 2.7598 - mae: 0.8816\n", "Epoch 19/1000\n", - "13/13 [==============================] - 0s 799us/step - loss: 1.6353 - mae: 1.1670\n", + "13/13 [==============================] - 0s 630us/step - loss: 4.1290 - mae: 1.0146\n", "Epoch 20/1000\n", - "13/13 [==============================] - 0s 802us/step - loss: 1.8236 - mae: 1.0519\n", + "13/13 [==============================] - 0s 640us/step - loss: 4.4147 - mae: 1.0053\n", "Epoch 21/1000\n", - "13/13 [==============================] - 0s 794us/step - loss: 1.6438 - mae: 0.9863\n", + "13/13 [==============================] - 0s 621us/step - loss: 3.5675 - mae: 1.0196\n", "Epoch 22/1000\n", - "13/13 [==============================] - 0s 795us/step - loss: 1.7089 - mae: 0.9593\n", + "13/13 [==============================] - 0s 637us/step - loss: 4.1756 - mae: 0.9634\n", "Epoch 23/1000\n", - "13/13 [==============================] - 0s 796us/step - loss: 1.6273 - mae: 1.0677\n", + "13/13 [==============================] - 0s 643us/step - loss: 2.7879 - mae: 0.9542\n", "Epoch 24/1000\n", - "13/13 [==============================] - 0s 813us/step - loss: 1.5083 - mae: 1.0911\n", + "13/13 [==============================] - 0s 644us/step - loss: 2.7931 - mae: 0.9273\n", "Epoch 25/1000\n", - "13/13 [==============================] - 0s 848us/step - loss: 1.6686 - mae: 1.0366\n", + "13/13 [==============================] - 0s 647us/step - loss: 3.5842 - mae: 0.9611\n", "Epoch 26/1000\n", - "13/13 [==============================] - 0s 830us/step - loss: 1.4822 - mae: 1.0088\n", + "13/13 [==============================] - 0s 650us/step - loss: 3.8581 - mae: 0.9845\n", "Epoch 27/1000\n", - "13/13 [==============================] - 0s 847us/step - loss: 1.5268 - mae: 1.0164\n", + "13/13 [==============================] - 0s 640us/step - loss: 2.8753 - mae: 0.9658\n", "Epoch 28/1000\n", - "13/13 [==============================] - 0s 822us/step - loss: 1.4263 - mae: 1.0035\n", + "13/13 [==============================] - 0s 643us/step - loss: 2.7052 - mae: 0.9795\n", "Epoch 29/1000\n", - "13/13 [==============================] - 0s 810us/step - loss: 1.6339 - mae: 1.1277\n", + "13/13 [==============================] - 0s 642us/step - loss: 2.6099 - mae: 0.8982\n", "Epoch 30/1000\n", - "13/13 [==============================] - 0s 821us/step - loss: 1.6162 - mae: 1.0669\n", + "13/13 [==============================] - 0s 644us/step - loss: 3.2863 - mae: 0.9993\n", "Epoch 31/1000\n", - "13/13 [==============================] - 0s 822us/step - loss: 1.5061 - mae: 1.0290\n", + "13/13 [==============================] - 0s 648us/step - loss: 2.8441 - mae: 0.9691\n", "Epoch 32/1000\n", - "13/13 [==============================] - 0s 810us/step - loss: 1.5248 - mae: 1.0701\n", + "13/13 [==============================] - 0s 646us/step - loss: 3.6793 - mae: 0.9926\n", "Epoch 33/1000\n", - "13/13 [==============================] - 0s 818us/step - loss: 1.5036 - mae: 1.0835\n", + "13/13 [==============================] - 0s 642us/step - loss: 2.7080 - mae: 0.8691\n", "Epoch 34/1000\n", - "13/13 [==============================] - 0s 809us/step - loss: 1.5248 - mae: 1.0530\n", + "13/13 [==============================] - 0s 640us/step - loss: 3.5203 - mae: 1.0729\n", "Epoch 35/1000\n", - "13/13 [==============================] - 0s 803us/step - loss: 1.4409 - mae: 1.0619\n", + "13/13 [==============================] - 0s 638us/step - loss: 2.3461 - mae: 0.8759\n", "Epoch 36/1000\n", - "13/13 [==============================] - 0s 827us/step - loss: 1.4561 - mae: 1.0495\n", + "13/13 [==============================] - 0s 651us/step - loss: 3.1568 - mae: 0.9551\n", "Epoch 37/1000\n", - "13/13 [==============================] - 0s 812us/step - loss: 1.5414 - mae: 1.1210\n", + "13/13 [==============================] - 0s 645us/step - loss: 2.9889 - mae: 1.0392\n", "Epoch 38/1000\n", - "13/13 [==============================] - 0s 843us/step - loss: 1.4531 - mae: 1.0074\n", + "13/13 [==============================] - 0s 641us/step - loss: 2.4367 - mae: 0.9189\n", "Epoch 39/1000\n", - "13/13 [==============================] - 0s 852us/step - loss: 1.3954 - mae: 1.1062\n", + "13/13 [==============================] - 0s 642us/step - loss: 2.6035 - mae: 0.9544\n", "Epoch 40/1000\n", - "13/13 [==============================] - 0s 848us/step - loss: 1.5333 - mae: 1.1108\n", + "13/13 [==============================] - 0s 643us/step - loss: 2.3638 - mae: 0.9495\n", "Epoch 41/1000\n", - "13/13 [==============================] - 0s 864us/step - loss: 1.4864 - mae: 1.0744\n", + "13/13 [==============================] - 0s 653us/step - loss: 2.5315 - mae: 0.9381\n", "Epoch 42/1000\n", - "13/13 [==============================] - 0s 850us/step - loss: 1.4114 - mae: 1.0213\n", + "13/13 [==============================] - 0s 627us/step - loss: 2.6102 - mae: 0.8905\n", "Epoch 43/1000\n", - "13/13 [==============================] - 0s 838us/step - loss: 1.5156 - mae: 1.0719\n", + "13/13 [==============================] - 0s 647us/step - loss: 2.4211 - mae: 0.8990\n", "Epoch 44/1000\n", - "13/13 [==============================] - 0s 847us/step - loss: 1.4592 - mae: 1.0409\n", + "13/13 [==============================] - 0s 641us/step - loss: 2.2749 - mae: 0.9141\n", "Epoch 45/1000\n", - "13/13 [==============================] - 0s 854us/step - loss: 1.4734 - mae: 1.0406\n", + "13/13 [==============================] - 0s 644us/step - loss: 3.1940 - mae: 0.9913\n", "Epoch 46/1000\n", - "13/13 [==============================] - 0s 840us/step - loss: 1.3832 - mae: 1.0677\n", + "13/13 [==============================] - 0s 648us/step - loss: 2.3525 - mae: 0.9218\n", "Epoch 47/1000\n", - "13/13 [==============================] - 0s 831us/step - loss: 1.4911 - mae: 1.0674\n", + "13/13 [==============================] - 0s 635us/step - loss: 2.2745 - mae: 0.9261\n", "Epoch 48/1000\n", - "13/13 [==============================] - 0s 900us/step - loss: 1.5189 - mae: 1.1181\n", + "13/13 [==============================] - 0s 629us/step - loss: 2.3441 - mae: 0.9476\n", "Epoch 49/1000\n", - "13/13 [==============================] - 0s 895us/step - loss: 1.5473 - mae: 1.1260\n", + "13/13 [==============================] - 0s 630us/step - loss: 2.1042 - mae: 1.0016\n", "Epoch 50/1000\n", - "13/13 [==============================] - 0s 899us/step - loss: 1.4789 - mae: 1.0493\n", + "13/13 [==============================] - 0s 652us/step - loss: 2.1258 - mae: 0.9345\n", "Epoch 51/1000\n", - "13/13 [==============================] - 0s 869us/step - loss: 1.4645 - mae: 1.0138\n", + "13/13 [==============================] - 0s 639us/step - loss: 1.9811 - mae: 0.9070\n", "Epoch 52/1000\n", - "13/13 [==============================] - 0s 873us/step - loss: 1.4312 - mae: 0.9815\n", + "13/13 [==============================] - 0s 633us/step - loss: 2.4655 - mae: 0.9795\n", "Epoch 53/1000\n", - "13/13 [==============================] - 0s 895us/step - loss: 1.4594 - mae: 1.0390\n", + "13/13 [==============================] - 0s 638us/step - loss: 2.1093 - mae: 0.9318\n", "Epoch 54/1000\n", - "13/13 [==============================] - 0s 882us/step - loss: 1.5313 - mae: 1.1021\n", + "13/13 [==============================] - 0s 658us/step - loss: 2.6232 - mae: 0.9897\n", "Epoch 55/1000\n", - "13/13 [==============================] - 0s 873us/step - loss: 1.4747 - mae: 1.0939\n", + "13/13 [==============================] - 0s 643us/step - loss: 2.4856 - mae: 0.9137\n", "Epoch 56/1000\n", - "13/13 [==============================] - 0s 863us/step - loss: 1.4639 - mae: 1.1567\n", + "13/13 [==============================] - 0s 636us/step - loss: 1.9708 - mae: 0.9148\n", "Epoch 57/1000\n", - "13/13 [==============================] - 0s 877us/step - loss: 1.5190 - mae: 1.1255\n", + "13/13 [==============================] - 0s 658us/step - loss: 2.0096 - mae: 0.9208\n", "Epoch 58/1000\n", - "13/13 [==============================] - 0s 872us/step - loss: 1.5896 - mae: 1.1388\n", + "13/13 [==============================] - 0s 662us/step - loss: 2.2146 - mae: 0.9238\n", "Epoch 59/1000\n", - "13/13 [==============================] - 0s 872us/step - loss: 1.4557 - mae: 1.0124\n", + "13/13 [==============================] - 0s 641us/step - loss: 1.9905 - mae: 0.8402\n", "Epoch 60/1000\n", - "13/13 [==============================] - 0s 857us/step - loss: 1.4523 - mae: 1.0818\n", + "13/13 [==============================] - 0s 636us/step - loss: 1.7901 - mae: 0.9014\n", "Epoch 61/1000\n", - "13/13 [==============================] - 0s 866us/step - loss: 1.4284 - mae: 1.0534\n", + "13/13 [==============================] - 0s 648us/step - loss: 2.0977 - mae: 0.9380\n", "Epoch 62/1000\n", - "13/13 [==============================] - 0s 877us/step - loss: 1.4086 - mae: 1.0728\n", + "13/13 [==============================] - 0s 625us/step - loss: 1.7484 - mae: 0.8728\n", "Epoch 63/1000\n", - "13/13 [==============================] - 0s 880us/step - loss: 1.4584 - mae: 1.1574\n", + "13/13 [==============================] - 0s 640us/step - loss: 1.9327 - mae: 0.8831\n", "Epoch 64/1000\n", - "13/13 [==============================] - 0s 895us/step - loss: 1.5075 - mae: 1.1098\n", + "13/13 [==============================] - 0s 639us/step - loss: 2.3569 - mae: 0.9818\n", "Epoch 65/1000\n", - "13/13 [==============================] - 0s 902us/step - loss: 1.4098 - mae: 1.0823\n", + "13/13 [==============================] - 0s 647us/step - loss: 1.7523 - mae: 0.8896\n", "Epoch 66/1000\n", - "13/13 [==============================] - 0s 925us/step - loss: 1.4535 - mae: 1.1136\n", + "13/13 [==============================] - 0s 644us/step - loss: 2.0452 - mae: 0.9329\n", "Epoch 67/1000\n", - "13/13 [==============================] - 0s 905us/step - loss: 1.4222 - mae: 1.0069\n", + "13/13 [==============================] - 0s 643us/step - loss: 1.7410 - mae: 0.9467\n", "Epoch 68/1000\n", - "13/13 [==============================] - 0s 898us/step - loss: 1.5527 - mae: 1.1695\n", + "13/13 [==============================] - 0s 639us/step - loss: 1.8951 - mae: 0.9216\n", "Epoch 69/1000\n", - "13/13 [==============================] - 0s 902us/step - loss: 1.4791 - mae: 1.0986\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.7062 - mae: 0.9828\n", "Epoch 70/1000\n", - "13/13 [==============================] - 0s 907us/step - loss: 1.4573 - mae: 1.1400\n", + "13/13 [==============================] - 0s 640us/step - loss: 1.7207 - mae: 0.8969\n", "Epoch 71/1000\n", - "13/13 [==============================] - 0s 917us/step - loss: 1.4321 - mae: 1.1190\n", + "13/13 [==============================] - 0s 655us/step - loss: 1.8864 - mae: 0.8875\n", "Epoch 72/1000\n", - "13/13 [==============================] - 0s 888us/step - loss: 1.4167 - mae: 1.1090\n", + "13/13 [==============================] - 0s 634us/step - loss: 1.9934 - mae: 0.9869\n", "Epoch 73/1000\n", - "13/13 [==============================] - 0s 900us/step - loss: 1.3810 - mae: 1.0624\n", + "13/13 [==============================] - 0s 680us/step - loss: 1.7435 - mae: 0.8605\n", "Epoch 74/1000\n", - "13/13 [==============================] - 0s 928us/step - loss: 1.4078 - mae: 1.1006\n", + "13/13 [==============================] - 0s 644us/step - loss: 2.1962 - mae: 0.9600\n", "Epoch 75/1000\n", - "13/13 [==============================] - 0s 957us/step - loss: 1.3842 - mae: 1.1005\n", + "13/13 [==============================] - 0s 643us/step - loss: 1.7224 - mae: 0.9095\n", "Epoch 76/1000\n", - "13/13 [==============================] - 0s 958us/step - loss: 1.3951 - mae: 1.0928\n", + "13/13 [==============================] - 0s 639us/step - loss: 1.6143 - mae: 0.9264\n", "Epoch 77/1000\n", - "13/13 [==============================] - 0s 942us/step - loss: 1.4130 - mae: 1.0825\n", + "13/13 [==============================] - 0s 637us/step - loss: 1.7808 - mae: 0.9108\n", "Epoch 78/1000\n", - "13/13 [==============================] - 0s 957us/step - loss: 1.3785 - mae: 1.0648\n", + "13/13 [==============================] - 0s 640us/step - loss: 1.7384 - mae: 0.9420\n", "Epoch 79/1000\n", - "13/13 [==============================] - 0s 933us/step - loss: 1.4826 - mae: 1.1419\n", + "13/13 [==============================] - 0s 643us/step - loss: 1.5089 - mae: 0.9334\n", "Epoch 80/1000\n", - "13/13 [==============================] - 0s 939us/step - loss: 1.4074 - mae: 1.0122\n", + "13/13 [==============================] - 0s 631us/step - loss: 1.7280 - mae: 0.9089\n", "Epoch 81/1000\n", - "13/13 [==============================] - 0s 945us/step - loss: 1.4325 - mae: 1.1177\n", + "13/13 [==============================] - 0s 634us/step - loss: 1.7340 - mae: 0.9067\n", "Epoch 82/1000\n", - "13/13 [==============================] - 0s 935us/step - loss: 1.3885 - mae: 1.0421\n", + "13/13 [==============================] - 0s 639us/step - loss: 1.6748 - mae: 0.9452\n", "Epoch 83/1000\n", - "13/13 [==============================] - 0s 943us/step - loss: 1.4094 - mae: 1.0313\n", + "13/13 [==============================] - 0s 627us/step - loss: 1.6754 - mae: 0.9433\n", "Epoch 84/1000\n", - "13/13 [==============================] - 0s 950us/step - loss: 1.4906 - mae: 1.1246\n", + "13/13 [==============================] - 0s 642us/step - loss: 1.6613 - mae: 0.9360\n", "Epoch 85/1000\n", - "13/13 [==============================] - 0s 935us/step - loss: 1.4798 - mae: 1.1306\n", + "13/13 [==============================] - 0s 639us/step - loss: 1.6924 - mae: 0.9704\n", "Epoch 86/1000\n", - "13/13 [==============================] - 0s 945us/step - loss: 1.4694 - mae: 1.1967\n", + "13/13 [==============================] - 0s 626us/step - loss: 1.8996 - mae: 0.9662\n", "Epoch 87/1000\n", - "13/13 [==============================] - 0s 920us/step - loss: 1.4313 - mae: 1.1507\n", + "13/13 [==============================] - 0s 655us/step - loss: 1.5692 - mae: 0.9176\n", "Epoch 88/1000\n", - "13/13 [==============================] - 0s 923us/step - loss: 1.4427 - mae: 1.0576\n", + "13/13 [==============================] - 0s 627us/step - loss: 1.7478 - mae: 0.9230\n", "Epoch 89/1000\n", - "13/13 [==============================] - 0s 931us/step - loss: 1.4085 - mae: 1.0421\n", + "13/13 [==============================] - 0s 647us/step - loss: 1.7784 - mae: 0.8677\n", "Epoch 90/1000\n", - "13/13 [==============================] - 0s 943us/step - loss: 1.3677 - mae: 1.1546\n", + "13/13 [==============================] - 0s 643us/step - loss: 1.8111 - mae: 0.9675\n", "Epoch 91/1000\n", - "13/13 [==============================] - 0s 938us/step - loss: 1.4941 - mae: 1.1076\n", + "13/13 [==============================] - 0s 636us/step - loss: 1.6428 - mae: 0.9312\n", "Epoch 92/1000\n", - "13/13 [==============================] - 0s 953us/step - loss: 1.4996 - mae: 1.1621\n", + "13/13 [==============================] - 0s 658us/step - loss: 1.6917 - mae: 0.9883\n", "Epoch 93/1000\n", - "13/13 [==============================] - 0s 944us/step - loss: 1.4746 - mae: 1.1574\n", + "13/13 [==============================] - 0s 660us/step - loss: 1.7629 - mae: 0.9456\n", "Epoch 94/1000\n", - "13/13 [==============================] - 0s 975us/step - loss: 1.3825 - mae: 1.1321\n", + "13/13 [==============================] - 0s 652us/step - loss: 1.5848 - mae: 0.8895\n", "Epoch 95/1000\n", - "13/13 [==============================] - 0s 972us/step - loss: 1.4842 - mae: 1.1361\n", + "13/13 [==============================] - 0s 647us/step - loss: 1.4911 - mae: 0.8871\n", "Epoch 96/1000\n", - "13/13 [==============================] - 0s 958us/step - loss: 1.4512 - mae: 1.1130\n", + "13/13 [==============================] - 0s 656us/step - loss: 1.5333 - mae: 0.9028\n", "Epoch 97/1000\n", - "13/13 [==============================] - 0s 972us/step - loss: 1.4580 - mae: 1.0604\n", + "13/13 [==============================] - 0s 652us/step - loss: 1.7337 - mae: 0.9273\n", "Epoch 98/1000\n", - "13/13 [==============================] - 0s 956us/step - loss: 1.4712 - mae: 1.0486\n", + "13/13 [==============================] - 0s 634us/step - loss: 1.6696 - mae: 0.9440\n", "Epoch 99/1000\n", - "13/13 [==============================] - 0s 981us/step - loss: 1.4129 - mae: 1.0610\n", + "13/13 [==============================] - 0s 626us/step - loss: 1.6303 - mae: 0.9214\n", "Epoch 100/1000\n", - "13/13 [==============================] - 0s 958us/step - loss: 1.4546 - mae: 1.2158\n", + "13/13 [==============================] - 0s 638us/step - loss: 1.7884 - mae: 0.9594\n", "Epoch 101/1000\n", - "13/13 [==============================] - 0s 986us/step - loss: 1.3667 - mae: 1.0784\n", + "13/13 [==============================] - 0s 643us/step - loss: 1.6706 - mae: 0.8799\n", "Epoch 102/1000\n", - "13/13 [==============================] - 0s 998us/step - loss: 1.4008 - mae: 1.0758\n", + "13/13 [==============================] - 0s 627us/step - loss: 1.5809 - mae: 0.9863\n", "Epoch 103/1000\n", - "13/13 [==============================] - 0s 969us/step - loss: 1.4155 - mae: 1.1386\n", + "13/13 [==============================] - 0s 637us/step - loss: 1.7480 - mae: 1.0171\n", "Epoch 104/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.5099 - mae: 1.1587\n", + "13/13 [==============================] - 0s 631us/step - loss: 1.5740 - mae: 0.9668\n", "Epoch 105/1000\n", - "13/13 [==============================] - 0s 998us/step - loss: 1.5019 - mae: 1.2244\n", + "13/13 [==============================] - 0s 660us/step - loss: 1.7516 - mae: 0.9403\n", "Epoch 106/1000\n", - "13/13 [==============================] - 0s 974us/step - loss: 1.4925 - mae: 1.1256\n", + "13/13 [==============================] - 0s 630us/step - loss: 1.6075 - mae: 0.9550\n", "Epoch 107/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4489 - mae: 1.0856\n", + "13/13 [==============================] - 0s 658us/step - loss: 1.6465 - mae: 0.9268\n", "Epoch 108/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4478 - mae: 1.0526\n", + "13/13 [==============================] - 0s 655us/step - loss: 1.6373 - mae: 0.9675\n", "Epoch 109/1000\n", - "13/13 [==============================] - 0s 994us/step - loss: 1.4070 - mae: 1.0286\n", + "13/13 [==============================] - 0s 653us/step - loss: 1.6379 - mae: 1.0527\n", "Epoch 110/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4812 - mae: 1.1863\n", + "13/13 [==============================] - 0s 640us/step - loss: 1.6238 - mae: 0.8895\n", "Epoch 111/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3567 - mae: 1.0501\n", + "13/13 [==============================] - 0s 643us/step - loss: 1.8451 - mae: 0.9929\n", "Epoch 112/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4505 - mae: 1.1223\n", + "13/13 [==============================] - 0s 620us/step - loss: 1.7553 - mae: 0.9585\n", "Epoch 113/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.5051 - mae: 1.1636\n", + "13/13 [==============================] - 0s 640us/step - loss: 1.7708 - mae: 1.0150\n", "Epoch 114/1000\n", - "13/13 [==============================] - 0s 999us/step - loss: 1.4662 - mae: 1.1563\n", + "13/13 [==============================] - 0s 645us/step - loss: 1.5166 - mae: 0.8482\n", "Epoch 115/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3864 - mae: 1.2185\n", + "13/13 [==============================] - 0s 648us/step - loss: 1.5947 - mae: 0.9167\n", "Epoch 116/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3915 - mae: 1.0704\n", + "13/13 [==============================] - 0s 626us/step - loss: 1.7532 - mae: 0.9873\n", "Epoch 117/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4927 - mae: 1.2071\n", + "13/13 [==============================] - 0s 635us/step - loss: 1.7613 - mae: 0.9747\n", "Epoch 118/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4515 - mae: 1.0965\n", + "13/13 [==============================] - 0s 652us/step - loss: 1.6559 - mae: 0.9917\n", "Epoch 119/1000\n", - "13/13 [==============================] - 0s 990us/step - loss: 1.4482 - mae: 1.0812\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.5482 - mae: 0.9191\n", "Epoch 120/1000\n", - "13/13 [==============================] - 0s 999us/step - loss: 1.4458 - mae: 1.0661\n", + "13/13 [==============================] - 0s 646us/step - loss: 1.6148 - mae: 0.9622\n", "Epoch 121/1000\n", - "13/13 [==============================] - 0s 997us/step - loss: 1.3957 - mae: 1.0994\n", + "13/13 [==============================] - 0s 635us/step - loss: 1.5680 - mae: 0.9079\n", "Epoch 122/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4234 - mae: 1.1830\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.5484 - mae: 1.0277\n", "Epoch 123/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4279 - mae: 1.1275\n", + "13/13 [==============================] - 0s 653us/step - loss: 1.5219 - mae: 0.9141\n", "Epoch 124/1000\n", - "13/13 [==============================] - 0s 998us/step - loss: 1.4155 - mae: 1.2371\n", + "13/13 [==============================] - 0s 645us/step - loss: 1.6300 - mae: 0.9594\n", "Epoch 125/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4152 - mae: 1.1662\n", + "13/13 [==============================] - 0s 640us/step - loss: 1.5096 - mae: 0.9763\n", "Epoch 126/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4436 - mae: 1.1533\n", + "13/13 [==============================] - 0s 631us/step - loss: 1.6539 - mae: 1.0001\n", "Epoch 127/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4548 - mae: 1.1175\n", + "13/13 [==============================] - 0s 640us/step - loss: 1.4960 - mae: 0.9623\n", "Epoch 128/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4318 - mae: 1.1338\n", + "13/13 [==============================] - 0s 647us/step - loss: 1.5247 - mae: 0.9862\n", "Epoch 129/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.5312 - mae: 1.1411\n", + "13/13 [==============================] - 0s 626us/step - loss: 1.5198 - mae: 0.9700\n", "Epoch 130/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4388 - mae: 1.0560\n", + "13/13 [==============================] - 0s 648us/step - loss: 1.6232 - mae: 0.9396\n", "Epoch 131/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.5117 - mae: 1.1848\n", + "13/13 [==============================] - 0s 638us/step - loss: 1.6247 - mae: 1.0041\n", "Epoch 132/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4703 - mae: 1.0648\n", + "13/13 [==============================] - 0s 650us/step - loss: 1.6638 - mae: 1.0133\n", "Epoch 133/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4517 - mae: 1.1471\n", + "13/13 [==============================] - 0s 645us/step - loss: 1.5732 - mae: 0.9841\n", "Epoch 134/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4161 - mae: 1.1008\n", + "13/13 [==============================] - 0s 647us/step - loss: 1.5201 - mae: 0.9276\n", "Epoch 135/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4105 - mae: 1.1198\n", + "13/13 [==============================] - 0s 647us/step - loss: 1.5256 - mae: 0.9914\n", "Epoch 136/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4175 - mae: 1.1254\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.5087 - mae: 0.9662\n", "Epoch 137/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4433 - mae: 1.1344\n", + "13/13 [==============================] - 0s 632us/step - loss: 1.5116 - mae: 1.0069\n", "Epoch 138/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4042 - mae: 1.0484\n", + "13/13 [==============================] - 0s 632us/step - loss: 1.6382 - mae: 0.9843\n", "Epoch 139/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4717 - mae: 1.1784\n", + "13/13 [==============================] - 0s 650us/step - loss: 1.5154 - mae: 0.9589\n", "Epoch 140/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4704 - mae: 1.2275\n", + "13/13 [==============================] - 0s 637us/step - loss: 1.4336 - mae: 0.9557\n", "Epoch 141/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4378 - mae: 1.0697\n", + "13/13 [==============================] - 0s 640us/step - loss: 1.5007 - mae: 0.9494\n", "Epoch 142/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4532 - mae: 1.1211\n", + "13/13 [==============================] - 0s 647us/step - loss: 1.4457 - mae: 0.9469\n", "Epoch 143/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.5064 - mae: 1.1429\n", + "13/13 [==============================] - 0s 634us/step - loss: 1.4996 - mae: 0.8806\n", "Epoch 144/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4500 - mae: 1.1237\n", + "13/13 [==============================] - 0s 630us/step - loss: 1.4972 - mae: 0.9696\n", "Epoch 145/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4458 - mae: 1.0803\n", + "13/13 [==============================] - 0s 665us/step - loss: 1.5694 - mae: 0.9185\n", "Epoch 146/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4293 - mae: 1.0545\n", + "13/13 [==============================] - 0s 642us/step - loss: 1.6480 - mae: 1.0554\n", "Epoch 147/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.5060 - mae: 1.1389\n", + "13/13 [==============================] - 0s 635us/step - loss: 1.5179 - mae: 0.9523\n", "Epoch 148/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4679 - mae: 1.1980\n", + "13/13 [==============================] - 0s 650us/step - loss: 1.4244 - mae: 0.9693\n", "Epoch 149/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3984 - mae: 1.1468\n", + "13/13 [==============================] - 0s 635us/step - loss: 1.4927 - mae: 0.9811\n", "Epoch 150/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4316 - mae: 1.0610\n", + "13/13 [==============================] - 0s 630us/step - loss: 1.4506 - mae: 0.9982\n", "Epoch 151/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4072 - mae: 1.1534\n", + "13/13 [==============================] - 0s 640us/step - loss: 1.5764 - mae: 0.9634\n", "Epoch 152/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4424 - mae: 1.1180\n", + "13/13 [==============================] - 0s 632us/step - loss: 1.4011 - mae: 1.0361\n", "Epoch 153/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4519 - mae: 1.1100\n", + "13/13 [==============================] - 0s 647us/step - loss: 1.5033 - mae: 0.9764\n", "Epoch 154/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4555 - mae: 1.0739\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.4870 - mae: 1.0066\n", "Epoch 155/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4339 - mae: 1.0568\n", + "13/13 [==============================] - 0s 630us/step - loss: 1.4613 - mae: 1.0509\n", "Epoch 156/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3793 - mae: 1.1191\n", + "13/13 [==============================] - 0s 618us/step - loss: 1.4820 - mae: 1.0030\n", "Epoch 157/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4543 - mae: 1.1675\n", + "13/13 [==============================] - 0s 629us/step - loss: 1.4443 - mae: 0.9462\n", "Epoch 158/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4539 - mae: 1.0906\n", + "13/13 [==============================] - 0s 638us/step - loss: 1.7484 - mae: 1.0175\n", "Epoch 159/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4183 - mae: 1.1206\n", + "13/13 [==============================] - 0s 650us/step - loss: 1.4348 - mae: 0.9880\n", "Epoch 160/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3898 - mae: 1.0607\n", + "13/13 [==============================] - 0s 652us/step - loss: 1.5664 - mae: 0.9574\n", "Epoch 161/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4136 - mae: 1.1150\n", + "13/13 [==============================] - 0s 661us/step - loss: 1.4385 - mae: 0.9223\n", "Epoch 162/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3454 - mae: 1.0815\n", + "13/13 [==============================] - 0s 636us/step - loss: 1.5126 - mae: 0.9812\n", "Epoch 163/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3876 - mae: 1.0861\n", + "13/13 [==============================] - 0s 648us/step - loss: 1.5459 - mae: 1.0407\n", "Epoch 164/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4491 - mae: 1.1364\n", + "13/13 [==============================] - 0s 658us/step - loss: 1.4698 - mae: 1.0157\n", "Epoch 165/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4617 - mae: 1.1473\n", + "13/13 [==============================] - 0s 641us/step - loss: 1.5378 - mae: 1.0144\n", "Epoch 166/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4929 - mae: 1.1003\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.5192 - mae: 1.0061\n", "Epoch 167/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4696 - mae: 1.1653\n", + "13/13 [==============================] - 0s 638us/step - loss: 1.6111 - mae: 1.0766\n", "Epoch 168/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4618 - mae: 1.1927\n", + "13/13 [==============================] - 0s 656us/step - loss: 1.5028 - mae: 1.0115\n", "Epoch 169/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4022 - mae: 1.1112\n", + "13/13 [==============================] - 0s 645us/step - loss: 1.5140 - mae: 0.9101\n", "Epoch 170/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3900 - mae: 1.0553\n", + "13/13 [==============================] - 0s 624us/step - loss: 1.5935 - mae: 1.0091\n", "Epoch 171/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3635 - mae: 1.0454\n", + "13/13 [==============================] - 0s 660us/step - loss: 1.4186 - mae: 1.0227\n", "Epoch 172/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4193 - mae: 1.0488\n", + "13/13 [==============================] - 0s 635us/step - loss: 1.5608 - mae: 0.9972\n", "Epoch 173/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4260 - mae: 1.1317\n", + "13/13 [==============================] - 0s 638us/step - loss: 1.5511 - mae: 1.0044\n", "Epoch 174/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4509 - mae: 1.0852\n", + "13/13 [==============================] - 0s 648us/step - loss: 1.4623 - mae: 1.0025\n", "Epoch 175/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4026 - mae: 1.1447\n", + "13/13 [==============================] - 0s 663us/step - loss: 1.4922 - mae: 0.9619\n", "Epoch 176/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4139 - mae: 1.1094\n", + "13/13 [==============================] - 0s 654us/step - loss: 1.5592 - mae: 0.9662\n", "Epoch 177/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4403 - mae: 1.0539\n", + "13/13 [==============================] - 0s 628us/step - loss: 1.5126 - mae: 1.0361\n", "Epoch 178/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4273 - mae: 1.1456\n", + "13/13 [==============================] - 0s 648us/step - loss: 1.4516 - mae: 0.9689\n", "Epoch 179/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4206 - mae: 1.0858\n", + "13/13 [==============================] - 0s 641us/step - loss: 1.5347 - mae: 1.0710\n", "Epoch 180/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4507 - mae: 1.1371\n", + "13/13 [==============================] - 0s 635us/step - loss: 1.4688 - mae: 0.9706\n", "Epoch 181/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3969 - mae: 1.1018\n", + "13/13 [==============================] - 0s 638us/step - loss: 1.5323 - mae: 1.0292\n", "Epoch 182/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4687 - mae: 1.1706\n", + "13/13 [==============================] - 0s 636us/step - loss: 1.5730 - mae: 1.0849\n", "Epoch 183/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3763 - mae: 1.0392\n", + "13/13 [==============================] - 0s 640us/step - loss: 1.4287 - mae: 1.0363\n", "Epoch 184/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4257 - mae: 1.1660\n", + "13/13 [==============================] - 0s 639us/step - loss: 1.4924 - mae: 1.0370\n", "Epoch 185/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3890 - mae: 1.1016\n", + "13/13 [==============================] - 0s 637us/step - loss: 1.5801 - mae: 1.1517\n", "Epoch 186/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.5051 - mae: 1.1150\n", + "13/13 [==============================] - 0s 646us/step - loss: 1.5125 - mae: 1.0138\n", "Epoch 187/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4265 - mae: 1.0968\n", + "13/13 [==============================] - 0s 641us/step - loss: 1.4424 - mae: 1.0298\n", "Epoch 188/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4344 - mae: 1.0635\n", + "13/13 [==============================] - 0s 640us/step - loss: 1.5145 - mae: 1.0168\n", "Epoch 189/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4134 - mae: 1.0309\n", + "13/13 [==============================] - 0s 642us/step - loss: 1.5302 - mae: 1.0283\n", "Epoch 190/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4469 - mae: 1.1526\n", + "13/13 [==============================] - 0s 637us/step - loss: 1.4465 - mae: 1.0142\n", "Epoch 191/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4014 - mae: 1.0707\n", + "13/13 [==============================] - 0s 653us/step - loss: 1.3887 - mae: 0.9946\n", "Epoch 192/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3780 - mae: 1.0792\n", + "13/13 [==============================] - 0s 649us/step - loss: 1.3984 - mae: 0.9860\n", "Epoch 193/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4298 - mae: 1.0989\n", + "13/13 [==============================] - 0s 655us/step - loss: 1.3562 - mae: 0.9370\n", "Epoch 194/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4207 - mae: 1.1027\n", + "13/13 [==============================] - 0s 637us/step - loss: 1.5545 - mae: 1.0683\n", "Epoch 195/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4494 - mae: 1.0948\n", + "13/13 [==============================] - 0s 624us/step - loss: 1.4892 - mae: 0.9979\n", "Epoch 196/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4553 - mae: 1.0744\n", + "13/13 [==============================] - 0s 637us/step - loss: 1.4450 - mae: 1.0403\n", "Epoch 197/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4252 - mae: 1.0903\n", + "13/13 [==============================] - 0s 658us/step - loss: 1.4414 - mae: 0.9809\n", "Epoch 198/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.5019 - mae: 1.1662\n", + "13/13 [==============================] - 0s 667us/step - loss: 1.4657 - mae: 1.0839\n", "Epoch 199/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3536 - mae: 1.0670\n", + "13/13 [==============================] - 0s 647us/step - loss: 1.5070 - mae: 1.0313\n", "Epoch 200/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4674 - mae: 1.1114\n", + "13/13 [==============================] - 0s 639us/step - loss: 1.5329 - mae: 1.0561\n", "Epoch 201/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4358 - mae: 1.1330\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.5004 - mae: 1.0397\n", "Epoch 202/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4712 - mae: 1.0960\n", + "13/13 [==============================] - 0s 652us/step - loss: 1.4895 - mae: 0.9958\n", "Epoch 203/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4580 - mae: 1.0123\n", + "13/13 [==============================] - 0s 641us/step - loss: 1.5325 - mae: 1.0761\n", "Epoch 204/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4451 - mae: 1.0419\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.4622 - mae: 1.1257\n", "Epoch 205/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4194 - mae: 1.1035\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.4578 - mae: 1.0207\n", "Epoch 206/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4119 - mae: 1.0680\n", + "13/13 [==============================] - 0s 634us/step - loss: 1.4589 - mae: 1.0654\n", "Epoch 207/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3898 - mae: 1.1389\n", + "13/13 [==============================] - 0s 653us/step - loss: 1.4978 - mae: 1.0926\n", "Epoch 208/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3646 - mae: 1.0445\n", + "13/13 [==============================] - 0s 629us/step - loss: 1.3849 - mae: 0.9387\n", "Epoch 209/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4179 - mae: 1.0710\n", + "13/13 [==============================] - 0s 634us/step - loss: 1.3791 - mae: 0.9364\n", "Epoch 210/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4066 - mae: 1.1556\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.5317 - mae: 1.0753\n", "Epoch 211/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4642 - mae: 1.0534\n", + "13/13 [==============================] - 0s 629us/step - loss: 1.5006 - mae: 1.0372\n", "Epoch 212/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3396 - mae: 1.0155\n", + "13/13 [==============================] - 0s 634us/step - loss: 1.4434 - mae: 1.0353\n", "Epoch 213/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3908 - mae: 1.0178\n", + "13/13 [==============================] - 0s 661us/step - loss: 1.4006 - mae: 1.0527\n", "Epoch 214/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4165 - mae: 1.1241\n", + "13/13 [==============================] - 0s 634us/step - loss: 1.5590 - mae: 1.0523\n", "Epoch 215/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3601 - mae: 1.1163\n", + "13/13 [==============================] - 0s 652us/step - loss: 1.4124 - mae: 1.0100\n", "Epoch 216/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3955 - mae: 1.0983\n", + "13/13 [==============================] - 0s 635us/step - loss: 1.4926 - mae: 1.0157\n", "Epoch 217/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3550 - mae: 1.1349\n", + "13/13 [==============================] - 0s 629us/step - loss: 1.4372 - mae: 1.0041\n", "Epoch 218/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4429 - mae: 1.0960\n", + "13/13 [==============================] - 0s 642us/step - loss: 1.4754 - mae: 1.0757\n", "Epoch 219/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4126 - mae: 1.0760\n", + "13/13 [==============================] - 0s 644us/step - loss: 1.4367 - mae: 0.9671\n", "Epoch 220/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4488 - mae: 1.0348\n", + "13/13 [==============================] - 0s 639us/step - loss: 1.3763 - mae: 1.0326\n", "Epoch 221/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3783 - mae: 1.0768\n", + "13/13 [==============================] - 0s 671us/step - loss: 1.4553 - mae: 1.0335\n", "Epoch 222/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4126 - mae: 1.1821\n", + "13/13 [==============================] - 0s 636us/step - loss: 1.3957 - mae: 0.9989\n", "Epoch 223/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4493 - mae: 1.0870\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.4673 - mae: 0.9801\n", "Epoch 224/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4316 - mae: 1.0866\n", + "13/13 [==============================] - 0s 650us/step - loss: 1.3840 - mae: 0.9864\n", "Epoch 225/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3617 - mae: 1.0272\n", + "13/13 [==============================] - 0s 640us/step - loss: 1.4062 - mae: 1.0606\n", "Epoch 226/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4311 - mae: 1.0801\n", + "13/13 [==============================] - 0s 643us/step - loss: 1.4016 - mae: 1.0351\n", "Epoch 227/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3766 - mae: 1.0237\n", + "13/13 [==============================] - 0s 644us/step - loss: 1.4567 - mae: 1.0332\n", "Epoch 228/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3768 - mae: 1.0927\n", + "13/13 [==============================] - 0s 629us/step - loss: 1.4177 - mae: 1.0691\n", "Epoch 229/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3702 - mae: 1.1019\n", + "13/13 [==============================] - 0s 645us/step - loss: 1.4051 - mae: 0.9966\n", "Epoch 230/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4597 - mae: 1.0844\n", + "13/13 [==============================] - 0s 646us/step - loss: 1.4438 - mae: 1.0195\n", "Epoch 231/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3559 - mae: 1.1091\n", + "13/13 [==============================] - 0s 637us/step - loss: 1.4782 - mae: 1.0280\n", "Epoch 232/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3307 - mae: 0.9692\n", + "13/13 [==============================] - 0s 648us/step - loss: 1.3593 - mae: 1.0605\n", "Epoch 233/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3811 - mae: 1.0638\n", + "13/13 [==============================] - 0s 633us/step - loss: 1.4293 - mae: 1.1014\n", "Epoch 234/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3365 - mae: 1.0219\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.5078 - mae: 1.0944\n", "Epoch 235/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4375 - mae: 1.1433\n", + "13/13 [==============================] - 0s 664us/step - loss: 1.4271 - mae: 1.0523\n", "Epoch 236/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3709 - mae: 1.0442\n", + "13/13 [==============================] - 0s 647us/step - loss: 1.5177 - mae: 1.0656\n", "Epoch 237/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4716 - mae: 1.0361\n", + "13/13 [==============================] - 0s 650us/step - loss: 1.4250 - mae: 1.0536\n", "Epoch 238/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3150 - mae: 1.0131\n", + "13/13 [==============================] - 0s 643us/step - loss: 1.4595 - mae: 1.0713\n", "Epoch 239/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3716 - mae: 1.0514\n", + "13/13 [==============================] - 0s 641us/step - loss: 1.4146 - mae: 1.0586\n", "Epoch 240/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4206 - mae: 1.0263\n", + "13/13 [==============================] - 0s 647us/step - loss: 1.4250 - mae: 1.0492\n", "Epoch 241/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3698 - mae: 1.0536\n", + "13/13 [==============================] - 0s 649us/step - loss: 1.4593 - mae: 1.1145\n", "Epoch 242/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3475 - mae: 1.0447\n", + "13/13 [==============================] - 0s 652us/step - loss: 1.4053 - mae: 1.0981\n", "Epoch 243/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3520 - mae: 1.0334\n", + "13/13 [==============================] - 0s 638us/step - loss: 1.4219 - mae: 1.0310\n", "Epoch 244/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3906 - mae: 1.0037\n", + "13/13 [==============================] - 0s 629us/step - loss: 1.4737 - mae: 1.0732\n", "Epoch 245/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4072 - mae: 1.0776\n", + "13/13 [==============================] - 0s 661us/step - loss: 1.3823 - mae: 1.0596\n", "Epoch 246/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4641 - mae: 1.0672\n", + "13/13 [==============================] - 0s 655us/step - loss: 1.3628 - mae: 1.0629\n", "Epoch 247/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4054 - mae: 1.0361\n", + "13/13 [==============================] - 0s 645us/step - loss: 1.4076 - mae: 1.0659\n", "Epoch 248/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4063 - mae: 1.0778\n", + "13/13 [==============================] - 0s 642us/step - loss: 1.5478 - mae: 1.1876\n", "Epoch 249/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4060 - mae: 0.9787\n", + "13/13 [==============================] - 0s 669us/step - loss: 1.4586 - mae: 1.0901\n", "Epoch 250/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4357 - mae: 1.0881\n", + "13/13 [==============================] - 0s 648us/step - loss: 1.5000 - mae: 1.0900\n", "Epoch 251/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3815 - mae: 1.1110\n", + "13/13 [==============================] - 0s 654us/step - loss: 1.3421 - mae: 1.0415\n", "Epoch 252/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4169 - mae: 1.1697\n", + "13/13 [==============================] - 0s 648us/step - loss: 1.4420 - mae: 1.0716\n", "Epoch 253/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4173 - mae: 1.0704\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.4530 - mae: 1.1409\n", "Epoch 254/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4199 - mae: 1.0168\n", + "13/13 [==============================] - 0s 654us/step - loss: 1.4306 - mae: 1.0997\n", "Epoch 255/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3377 - mae: 0.9985\n", + "13/13 [==============================] - 0s 663us/step - loss: 1.4035 - mae: 1.0528\n", "Epoch 256/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4374 - mae: 1.0961\n", + "13/13 [==============================] - 0s 642us/step - loss: 1.4137 - mae: 1.0579\n", "Epoch 257/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3814 - mae: 1.1579\n", + "13/13 [==============================] - 0s 650us/step - loss: 1.5363 - mae: 1.0772\n", "Epoch 258/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3711 - mae: 1.1116\n", + "13/13 [==============================] - 0s 615us/step - loss: 1.4172 - mae: 1.1001\n", "Epoch 259/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3700 - mae: 1.0208\n", + "13/13 [==============================] - 0s 632us/step - loss: 1.4106 - mae: 1.0258\n", "Epoch 260/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3743 - mae: 1.0677\n", + "13/13 [==============================] - 0s 647us/step - loss: 1.3481 - mae: 1.0375\n", "Epoch 261/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4519 - mae: 1.0735\n", + "13/13 [==============================] - 0s 672us/step - loss: 1.4265 - mae: 1.0811\n", "Epoch 262/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3609 - mae: 1.0508\n", + "13/13 [==============================] - 0s 629us/step - loss: 1.4610 - mae: 1.1102\n", "Epoch 263/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3751 - mae: 0.9989\n", + "13/13 [==============================] - 0s 648us/step - loss: 1.3899 - mae: 0.9655\n", "Epoch 264/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3971 - mae: 1.1097\n", + "13/13 [==============================] - 0s 646us/step - loss: 1.5149 - mae: 1.0775\n", "Epoch 265/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4257 - mae: 1.1199\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.3959 - mae: 1.0247\n", "Epoch 266/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4009 - mae: 1.1247\n", + "13/13 [==============================] - 0s 635us/step - loss: 1.3690 - mae: 0.9982\n", "Epoch 267/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3495 - mae: 1.0148\n", + "13/13 [==============================] - 0s 646us/step - loss: 1.3805 - mae: 1.0332\n", "Epoch 268/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3731 - mae: 1.0270\n", + "13/13 [==============================] - 0s 641us/step - loss: 1.4247 - mae: 0.9862\n", "Epoch 269/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4216 - mae: 1.1150\n", + "13/13 [==============================] - 0s 666us/step - loss: 1.3931 - mae: 0.9603\n", "Epoch 270/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3853 - mae: 1.0409\n", + "13/13 [==============================] - 0s 642us/step - loss: 1.4533 - mae: 1.0694\n", "Epoch 271/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3627 - mae: 1.0601\n", + "13/13 [==============================] - 0s 653us/step - loss: 1.5269 - mae: 1.0184\n", "Epoch 272/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2881 - mae: 0.8983\n", + "13/13 [==============================] - 0s 652us/step - loss: 1.4305 - mae: 0.9501\n", "Epoch 273/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3816 - mae: 1.0624\n", + "13/13 [==============================] - 0s 660us/step - loss: 1.4175 - mae: 1.0809\n", "Epoch 274/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4065 - mae: 1.0191\n", + "13/13 [==============================] - 0s 637us/step - loss: 1.4299 - mae: 1.0581\n", "Epoch 275/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3922 - mae: 1.0573\n", + "13/13 [==============================] - 0s 656us/step - loss: 1.5089 - mae: 1.1245\n", "Epoch 276/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3804 - mae: 1.0753\n", + "13/13 [==============================] - 0s 652us/step - loss: 1.4565 - mae: 1.0927\n", "Epoch 277/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3318 - mae: 0.9744\n", + "13/13 [==============================] - 0s 631us/step - loss: 1.4470 - mae: 1.1335\n", "Epoch 278/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3151 - mae: 1.0394\n", + "13/13 [==============================] - 0s 658us/step - loss: 1.3635 - mae: 1.0790\n", "Epoch 279/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4031 - mae: 1.0457\n", + "13/13 [==============================] - 0s 634us/step - loss: 1.4010 - mae: 1.1432\n", "Epoch 280/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3545 - mae: 0.9898\n", + "13/13 [==============================] - 0s 633us/step - loss: 1.4004 - mae: 1.0831\n", "Epoch 281/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4046 - mae: 1.0330\n", + "13/13 [==============================] - 0s 644us/step - loss: 1.4277 - mae: 1.1172\n", "Epoch 282/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3846 - mae: 1.0994\n", + "13/13 [==============================] - 0s 656us/step - loss: 1.4281 - mae: 1.0523\n", "Epoch 283/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3291 - mae: 0.9781\n", + "13/13 [==============================] - 0s 644us/step - loss: 1.3799 - mae: 0.9757\n", "Epoch 284/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3181 - mae: 1.0067\n", + "13/13 [==============================] - 0s 647us/step - loss: 1.4090 - mae: 1.1907\n", "Epoch 285/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4034 - mae: 1.0580\n", + "13/13 [==============================] - 0s 645us/step - loss: 1.4110 - mae: 0.9749\n", "Epoch 286/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3650 - mae: 1.0047\n", + "13/13 [==============================] - 0s 653us/step - loss: 1.3956 - mae: 1.0537\n", "Epoch 287/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3452 - mae: 0.9806\n", + "13/13 [==============================] - 0s 640us/step - loss: 1.4565 - mae: 1.0720\n", "Epoch 288/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3122 - mae: 1.0002\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.4686 - mae: 1.0685\n", "Epoch 289/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3106 - mae: 0.9986\n", + "13/13 [==============================] - 0s 655us/step - loss: 1.4373 - mae: 1.0355\n", "Epoch 290/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3102 - mae: 0.9707\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.3858 - mae: 1.0257\n", "Epoch 291/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2999 - mae: 0.9772\n", + "13/13 [==============================] - 0s 642us/step - loss: 1.4667 - mae: 1.0506\n", "Epoch 292/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3821 - mae: 1.0038\n", + "13/13 [==============================] - 0s 655us/step - loss: 1.4262 - mae: 1.0011\n", "Epoch 293/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2979 - mae: 0.9733\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.4718 - mae: 1.1064\n", "Epoch 294/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3538 - mae: 0.9642\n", + "13/13 [==============================] - 0s 634us/step - loss: 1.3907 - mae: 1.0669\n", "Epoch 295/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3757 - mae: 1.0256\n", + "13/13 [==============================] - 0s 658us/step - loss: 1.4435 - mae: 1.0920\n", "Epoch 296/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3683 - mae: 0.9878\n", + "13/13 [==============================] - 0s 645us/step - loss: 1.3641 - mae: 0.9929\n", "Epoch 297/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3551 - mae: 1.0032\n", + "13/13 [==============================] - 0s 675us/step - loss: 1.4544 - mae: 1.0590\n", "Epoch 298/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2970 - mae: 0.9042\n", + "13/13 [==============================] - 0s 663us/step - loss: 1.4354 - mae: 1.0181\n", "Epoch 299/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2925 - mae: 0.8828\n", + "13/13 [==============================] - 0s 653us/step - loss: 1.3789 - mae: 1.0611\n", "Epoch 300/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3228 - mae: 1.0090\n", + "13/13 [==============================] - 0s 640us/step - loss: 1.3931 - mae: 1.0950\n", "Epoch 301/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3804 - mae: 1.0105\n", + "13/13 [==============================] - 0s 633us/step - loss: 1.4682 - mae: 1.0913\n", "Epoch 302/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4004 - mae: 1.0395\n", + "13/13 [==============================] - 0s 643us/step - loss: 1.4092 - mae: 1.1213\n", "Epoch 303/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2707 - mae: 0.9581\n", + "13/13 [==============================] - 0s 666us/step - loss: 1.4532 - mae: 1.1037\n", "Epoch 304/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3450 - mae: 0.9836\n", + "13/13 [==============================] - 0s 647us/step - loss: 1.4152 - mae: 1.0101\n", "Epoch 305/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3481 - mae: 0.9992\n", + "13/13 [==============================] - 0s 652us/step - loss: 1.5016 - mae: 1.1909\n", "Epoch 306/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3580 - mae: 0.9691\n", + "13/13 [==============================] - 0s 643us/step - loss: 1.3997 - mae: 1.1619\n", "Epoch 307/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2759 - mae: 0.9563\n", + "13/13 [==============================] - 0s 644us/step - loss: 1.4725 - mae: 1.1227\n", "Epoch 308/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2966 - mae: 0.9460\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.4034 - mae: 0.9974\n", "Epoch 309/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3679 - mae: 1.0041\n", + "13/13 [==============================] - 0s 640us/step - loss: 1.4267 - mae: 1.0492\n", "Epoch 310/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3029 - mae: 0.9814\n", + "13/13 [==============================] - 0s 644us/step - loss: 1.3971 - mae: 0.9841\n", "Epoch 311/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3397 - mae: 1.0416\n", + "13/13 [==============================] - 0s 641us/step - loss: 1.3467 - mae: 1.0212\n", "Epoch 312/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2394 - mae: 0.9486\n", + "13/13 [==============================] - 0s 658us/step - loss: 1.4328 - mae: 1.0610\n", "Epoch 313/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2849 - mae: 0.9956\n", + "13/13 [==============================] - 0s 655us/step - loss: 1.4048 - mae: 1.0827\n", "Epoch 314/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3054 - mae: 0.9717\n", + "13/13 [==============================] - 0s 666us/step - loss: 1.4259 - mae: 1.0222\n", "Epoch 315/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3355 - mae: 0.9764\n", + "13/13 [==============================] - 0s 645us/step - loss: 1.3838 - mae: 0.9699\n", "Epoch 316/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.4716 - mae: 0.9585\n", + "13/13 [==============================] - 0s 639us/step - loss: 1.3719 - mae: 0.9980\n", "Epoch 317/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3308 - mae: 0.9959\n", + "13/13 [==============================] - 0s 625us/step - loss: 1.4325 - mae: 1.1159\n", "Epoch 318/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3799 - mae: 0.9705\n", + "13/13 [==============================] - 0s 653us/step - loss: 1.3712 - mae: 1.0608\n", "Epoch 319/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3515 - mae: 0.9779\n", + "13/13 [==============================] - 0s 648us/step - loss: 1.4305 - mae: 1.0576\n", "Epoch 320/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3561 - mae: 1.0423\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.4500 - mae: 1.0845\n", "Epoch 321/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2573 - mae: 0.9230\n", + "13/13 [==============================] - 0s 665us/step - loss: 1.4280 - mae: 1.1203\n", "Epoch 322/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2579 - mae: 0.9112\n", + "13/13 [==============================] - 0s 624us/step - loss: 1.4285 - mae: 1.0748\n", "Epoch 323/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3880 - mae: 1.0073\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.3656 - mae: 1.0377\n", "Epoch 324/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3188 - mae: 0.9909\n", + "13/13 [==============================] - 0s 632us/step - loss: 1.4172 - mae: 1.0297\n", "Epoch 325/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3340 - mae: 1.0011\n", + "13/13 [==============================] - 0s 643us/step - loss: 1.4668 - mae: 1.0293\n", "Epoch 326/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3093 - mae: 0.8827\n", + "13/13 [==============================] - 0s 636us/step - loss: 1.3837 - mae: 1.0203\n", "Epoch 327/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3167 - mae: 0.9909\n", + "13/13 [==============================] - 0s 662us/step - loss: 1.4434 - mae: 1.0197\n", "Epoch 328/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3192 - mae: 0.8914\n", + "13/13 [==============================] - 0s 640us/step - loss: 1.4440 - mae: 1.1180\n", "Epoch 329/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3188 - mae: 0.9601\n", + "13/13 [==============================] - 0s 637us/step - loss: 1.3297 - mae: 1.0652\n", "Epoch 330/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2191 - mae: 0.9426\n", + "13/13 [==============================] - 0s 652us/step - loss: 1.4390 - mae: 1.0228\n", "Epoch 331/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2706 - mae: 0.9724\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.4090 - mae: 1.0765\n", "Epoch 332/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3553 - mae: 0.9878\n", + "13/13 [==============================] - 0s 623us/step - loss: 1.4363 - mae: 0.9989\n", "Epoch 333/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2976 - mae: 0.9449\n", + "13/13 [==============================] - 0s 654us/step - loss: 1.4181 - mae: 1.0599\n", "Epoch 334/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2408 - mae: 0.8863\n", + "13/13 [==============================] - 0s 648us/step - loss: 1.3960 - mae: 1.0490\n", "Epoch 335/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2350 - mae: 0.8682\n", + "13/13 [==============================] - 0s 652us/step - loss: 1.4272 - mae: 1.0592\n", "Epoch 336/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3005 - mae: 0.9586\n", + "13/13 [==============================] - 0s 645us/step - loss: 1.3940 - mae: 1.0941\n", "Epoch 337/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3313 - mae: 0.9738\n", + "13/13 [==============================] - 0s 639us/step - loss: 1.3656 - mae: 1.0872\n", "Epoch 338/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3260 - mae: 0.9836\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.4262 - mae: 1.0304\n", "Epoch 339/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3030 - mae: 1.0137\n", + "13/13 [==============================] - 0s 636us/step - loss: 1.4111 - mae: 1.1385\n", "Epoch 340/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3649 - mae: 0.9831\n", + "13/13 [==============================] - 0s 638us/step - loss: 1.3597 - mae: 1.0335\n", "Epoch 341/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2762 - mae: 0.8921\n", + "13/13 [==============================] - 0s 664us/step - loss: 1.3816 - mae: 1.1061\n", "Epoch 342/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3749 - mae: 0.9424\n", + "13/13 [==============================] - 0s 650us/step - loss: 1.4523 - mae: 1.1091\n", "Epoch 343/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3569 - mae: 0.9381\n", + "13/13 [==============================] - 0s 663us/step - loss: 1.3701 - mae: 1.1359\n", "Epoch 344/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2987 - mae: 0.9401\n", + "13/13 [==============================] - 0s 642us/step - loss: 1.4732 - mae: 1.1087\n", "Epoch 345/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3114 - mae: 0.9312\n", + "13/13 [==============================] - 0s 654us/step - loss: 1.3424 - mae: 1.0069\n", "Epoch 346/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2179 - mae: 0.9180\n", + "13/13 [==============================] - 0s 649us/step - loss: 1.4262 - mae: 1.0790\n", "Epoch 347/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.2580 - mae: 0.8628\n", + "13/13 [==============================] - 0s 667us/step - loss: 1.3813 - mae: 1.0342\n", "Epoch 348/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2506 - mae: 0.9207\n", + "13/13 [==============================] - 0s 648us/step - loss: 1.4536 - mae: 1.0638\n", "Epoch 349/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3042 - mae: 0.9366\n", + "13/13 [==============================] - 0s 629us/step - loss: 1.3851 - mae: 1.0788\n", "Epoch 350/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2449 - mae: 0.9246\n", + "13/13 [==============================] - 0s 653us/step - loss: 1.3903 - mae: 1.0683\n", "Epoch 351/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2686 - mae: 0.9312\n", + "13/13 [==============================] - 0s 662us/step - loss: 1.4601 - mae: 1.0671\n", "Epoch 352/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2654 - mae: 0.9023\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.3982 - mae: 1.1628\n", "Epoch 353/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2542 - mae: 0.9143\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.3846 - mae: 1.0436\n", "Epoch 354/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2062 - mae: 0.8260\n", + "13/13 [==============================] - 0s 645us/step - loss: 1.4308 - mae: 1.0638\n", "Epoch 355/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2481 - mae: 0.8962\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.4102 - mae: 1.0185\n", "Epoch 356/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1724 - mae: 0.8837\n", + "13/13 [==============================] - 0s 660us/step - loss: 1.3995 - mae: 1.0532\n", "Epoch 357/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.3018 - mae: 0.8667\n", + "13/13 [==============================] - 0s 675us/step - loss: 1.3973 - mae: 1.0172\n", "Epoch 358/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2597 - mae: 0.9563\n", + "13/13 [==============================] - 0s 663us/step - loss: 1.4657 - mae: 1.1519\n", "Epoch 359/1000\n", - "13/13 [==============================] - 0s 1ms/step - loss: 1.2375 - mae: 0.8965\n", + "13/13 [==============================] - 0s 661us/step - loss: 1.3933 - mae: 1.0702\n", "Epoch 360/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.2523 - mae: 0.8764\n", + "13/13 [==============================] - 0s 662us/step - loss: 1.4170 - mae: 1.0992\n", "Epoch 361/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.2523 - mae: 0.8820\n", + "13/13 [==============================] - 0s 658us/step - loss: 1.4554 - mae: 1.1766\n", "Epoch 362/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1654 - mae: 0.8510\n", + "13/13 [==============================] - 0s 652us/step - loss: 1.3680 - mae: 1.0265\n", "Epoch 363/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.2615 - mae: 0.9494\n", + "13/13 [==============================] - 0s 669us/step - loss: 1.3885 - mae: 1.1401\n", "Epoch 364/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.2091 - mae: 0.8526\n", + "13/13 [==============================] - 0s 646us/step - loss: 1.3438 - mae: 1.0981\n", "Epoch 365/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.2567 - mae: 0.8699\n", + "13/13 [==============================] - 0s 653us/step - loss: 1.4517 - mae: 1.0932\n", "Epoch 366/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.2400 - mae: 0.7986\n", + "13/13 [==============================] - 0s 648us/step - loss: 1.4118 - mae: 1.0530\n", "Epoch 367/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.2199 - mae: 0.8998\n", + "13/13 [==============================] - 0s 653us/step - loss: 1.4038 - mae: 1.0900\n", "Epoch 368/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.2047 - mae: 0.8307\n", + "13/13 [==============================] - 0s 673us/step - loss: 1.4845 - mae: 1.0197\n", "Epoch 369/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.2536 - mae: 0.8524\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.3919 - mae: 1.1767\n", "Epoch 370/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.2153 - mae: 0.8543\n", + "13/13 [==============================] - 0s 650us/step - loss: 1.4109 - mae: 1.0978\n", "Epoch 371/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1903 - mae: 0.8496\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.3496 - mae: 1.1065\n", "Epoch 372/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1050 - mae: 0.7483\n", + "13/13 [==============================] - 0s 643us/step - loss: 1.4524 - mae: 1.0724\n", "Epoch 373/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1939 - mae: 0.8472\n", + "13/13 [==============================] - 0s 649us/step - loss: 1.4140 - mae: 1.0281\n", "Epoch 374/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.2777 - mae: 0.8579\n", + "13/13 [==============================] - 0s 649us/step - loss: 1.4490 - mae: 1.0609\n", "Epoch 375/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.2180 - mae: 0.8837\n", + "13/13 [==============================] - 0s 652us/step - loss: 1.4145 - mae: 1.0669\n", "Epoch 376/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1828 - mae: 0.8337\n", + "13/13 [==============================] - 0s 636us/step - loss: 1.3830 - mae: 1.1164\n", "Epoch 377/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1469 - mae: 0.8331\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.3660 - mae: 1.0000\n", "Epoch 378/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1648 - mae: 0.8029\n", + "13/13 [==============================] - 0s 660us/step - loss: 1.4014 - mae: 1.0564\n", "Epoch 379/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.2378 - mae: 0.8549\n", + "13/13 [==============================] - 0s 649us/step - loss: 1.4312 - mae: 1.0371\n", "Epoch 380/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1562 - mae: 0.8353\n", + "13/13 [==============================] - 0s 660us/step - loss: 1.4227 - mae: 1.0570\n", "Epoch 381/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1793 - mae: 0.8119\n", + "13/13 [==============================] - 0s 640us/step - loss: 1.4168 - mae: 1.0461\n", "Epoch 382/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.2375 - mae: 0.9082\n", + "13/13 [==============================] - 0s 644us/step - loss: 1.3829 - mae: 1.0750\n", "Epoch 383/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1672 - mae: 0.8202\n", + "13/13 [==============================] - 0s 665us/step - loss: 1.4286 - mae: 1.0979\n", "Epoch 384/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1737 - mae: 0.8478\n", + "13/13 [==============================] - 0s 638us/step - loss: 1.3632 - mae: 1.0623\n", "Epoch 385/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1915 - mae: 0.7800\n", + "13/13 [==============================] - 0s 641us/step - loss: 1.3749 - mae: 1.0561\n", "Epoch 386/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.2353 - mae: 0.8952\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.3834 - mae: 1.0240\n", "Epoch 387/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1744 - mae: 0.8278\n", + "13/13 [==============================] - 0s 641us/step - loss: 1.4747 - mae: 1.0424\n", "Epoch 388/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.2533 - mae: 0.8196\n", + "13/13 [==============================] - 0s 640us/step - loss: 1.3597 - mae: 0.9999\n", "Epoch 389/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1627 - mae: 0.8103\n", + "13/13 [==============================] - 0s 627us/step - loss: 1.4405 - mae: 1.0911\n", "Epoch 390/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.2364 - mae: 0.9476\n", + "13/13 [==============================] - 0s 655us/step - loss: 1.3251 - mae: 1.0412\n", "Epoch 391/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1521 - mae: 0.7725\n", + "13/13 [==============================] - 0s 653us/step - loss: 1.4050 - mae: 1.0358\n", "Epoch 392/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1804 - mae: 0.8209\n", + "13/13 [==============================] - 0s 664us/step - loss: 1.4025 - mae: 1.0826\n", "Epoch 393/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1876 - mae: 0.8413\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.4125 - mae: 1.1132\n", "Epoch 394/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1976 - mae: 0.8075\n", + "13/13 [==============================] - 0s 639us/step - loss: 1.3460 - mae: 1.0077\n", "Epoch 395/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1492 - mae: 0.7516\n", + "13/13 [==============================] - 0s 648us/step - loss: 1.3792 - mae: 1.0372\n", "Epoch 396/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1908 - mae: 0.8421\n", + "13/13 [==============================] - 0s 661us/step - loss: 1.4237 - mae: 1.0777\n", "Epoch 397/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.2111 - mae: 0.8548\n", + "13/13 [==============================] - 0s 656us/step - loss: 1.4186 - mae: 1.0574\n", "Epoch 398/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1247 - mae: 0.7436\n", + "13/13 [==============================] - 0s 636us/step - loss: 1.3876 - mae: 1.0523\n", "Epoch 399/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1391 - mae: 0.8428\n", + "13/13 [==============================] - 0s 650us/step - loss: 1.2867 - mae: 1.0059\n", "Epoch 400/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0591 - mae: 0.7827\n", + "13/13 [==============================] - 0s 645us/step - loss: 1.3613 - mae: 1.0111\n", "Epoch 401/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1312 - mae: 0.8123\n", + "13/13 [==============================] - 0s 650us/step - loss: 1.3992 - mae: 1.0500\n", "Epoch 402/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1807 - mae: 0.8410\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.3650 - mae: 0.9854\n", "Epoch 403/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1785 - mae: 0.8390\n", + "13/13 [==============================] - 0s 646us/step - loss: 1.3863 - mae: 1.0437\n", "Epoch 404/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0862 - mae: 0.7550\n", + "13/13 [==============================] - 0s 650us/step - loss: 1.4476 - mae: 1.1440\n", "Epoch 405/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1053 - mae: 0.8037\n", + "13/13 [==============================] - 0s 664us/step - loss: 1.3687 - mae: 1.0582\n", "Epoch 406/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1036 - mae: 0.7469\n", + "13/13 [==============================] - 0s 662us/step - loss: 1.3261 - mae: 1.0230\n", "Epoch 407/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1193 - mae: 0.7569\n", + "13/13 [==============================] - 0s 655us/step - loss: 1.3275 - mae: 1.0408\n", "Epoch 408/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1342 - mae: 0.7912\n", + "13/13 [==============================] - 0s 637us/step - loss: 1.4581 - mae: 1.1252\n", "Epoch 409/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0842 - mae: 0.6955\n", + "13/13 [==============================] - 0s 658us/step - loss: 1.4216 - mae: 1.0485\n", "Epoch 410/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1213 - mae: 0.8091\n", + "13/13 [==============================] - 0s 655us/step - loss: 1.3494 - mae: 1.0849\n", "Epoch 411/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1367 - mae: 0.7890\n", + "13/13 [==============================] - 0s 632us/step - loss: 1.3693 - mae: 1.0438\n", "Epoch 412/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1624 - mae: 0.7712\n", + "13/13 [==============================] - 0s 661us/step - loss: 1.4145 - mae: 1.0359\n", "Epoch 413/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1338 - mae: 0.7917\n", + "13/13 [==============================] - 0s 658us/step - loss: 1.3543 - mae: 1.0167\n", "Epoch 414/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0648 - mae: 0.7577\n", + "13/13 [==============================] - 0s 640us/step - loss: 1.3506 - mae: 1.0632\n", "Epoch 415/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0779 - mae: 0.7155\n", + "13/13 [==============================] - 0s 670us/step - loss: 1.4469 - mae: 1.0900\n", "Epoch 416/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.2355 - mae: 0.8060\n", + "13/13 [==============================] - 0s 655us/step - loss: 1.3741 - mae: 1.0328\n", "Epoch 417/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1009 - mae: 0.7640\n", + "13/13 [==============================] - 0s 653us/step - loss: 1.4015 - mae: 1.0403\n", "Epoch 418/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1067 - mae: 0.7614\n", + "13/13 [==============================] - 0s 640us/step - loss: 1.4380 - mae: 1.0166\n", "Epoch 419/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0416 - mae: 0.7663\n", + "13/13 [==============================] - 0s 662us/step - loss: 1.4170 - mae: 1.0493\n", "Epoch 420/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0207 - mae: 0.7772\n", + "13/13 [==============================] - 0s 650us/step - loss: 1.3644 - mae: 1.0294\n", "Epoch 421/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0101 - mae: 0.7161\n", + "13/13 [==============================] - 0s 637us/step - loss: 1.4690 - mae: 1.0821\n", "Epoch 422/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1530 - mae: 0.8079\n", + "13/13 [==============================] - 0s 658us/step - loss: 1.3399 - mae: 0.9932\n", "Epoch 423/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1539 - mae: 0.8004\n", + "13/13 [==============================] - 0s 663us/step - loss: 1.4132 - mae: 1.0803\n", "Epoch 424/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1353 - mae: 0.8147\n", + "13/13 [==============================] - 0s 669us/step - loss: 1.3264 - mae: 1.1028\n", "Epoch 425/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1474 - mae: 0.8064\n", + "13/13 [==============================] - 0s 650us/step - loss: 1.3907 - mae: 1.0005\n", "Epoch 426/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0758 - mae: 0.7396\n", + "13/13 [==============================] - 0s 660us/step - loss: 1.3411 - mae: 1.0644\n", "Epoch 427/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0998 - mae: 0.7893\n", + "13/13 [==============================] - 0s 646us/step - loss: 1.3779 - mae: 1.0278\n", "Epoch 428/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1141 - mae: 0.7933\n", + "13/13 [==============================] - 0s 653us/step - loss: 1.4120 - mae: 1.0988\n", "Epoch 429/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1287 - mae: 0.7507\n", + "13/13 [==============================] - 0s 653us/step - loss: 1.3707 - mae: 1.0571\n", "Epoch 430/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1291 - mae: 0.8470\n", + "13/13 [==============================] - 0s 653us/step - loss: 1.3942 - mae: 1.0595\n", "Epoch 431/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0642 - mae: 0.7205\n", + "13/13 [==============================] - 0s 661us/step - loss: 1.3583 - mae: 0.9404\n", "Epoch 432/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0449 - mae: 0.7432\n", + "13/13 [==============================] - 0s 655us/step - loss: 1.4215 - mae: 1.1486\n", "Epoch 433/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0809 - mae: 0.7647\n", + "13/13 [==============================] - 0s 647us/step - loss: 1.3447 - mae: 1.0202\n", "Epoch 434/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1058 - mae: 0.7925\n", + "13/13 [==============================] - 0s 641us/step - loss: 1.3755 - mae: 1.0167\n", "Epoch 435/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0410 - mae: 0.7574\n", + "13/13 [==============================] - 0s 669us/step - loss: 1.3361 - mae: 1.0748\n", "Epoch 436/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1070 - mae: 0.8321\n", + "13/13 [==============================] - 0s 629us/step - loss: 1.4022 - mae: 1.0774\n", "Epoch 437/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0045 - mae: 0.7238\n", + "13/13 [==============================] - 0s 663us/step - loss: 1.3613 - mae: 1.0937\n", "Epoch 438/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0762 - mae: 0.7573\n", + "13/13 [==============================] - 0s 654us/step - loss: 1.3631 - mae: 0.9389\n", "Epoch 439/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0049 - mae: 0.6891\n", + "13/13 [==============================] - 0s 655us/step - loss: 1.3658 - mae: 1.0372\n", "Epoch 440/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0113 - mae: 0.7949\n", + "13/13 [==============================] - 0s 649us/step - loss: 1.3622 - mae: 1.0675\n", "Epoch 441/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0224 - mae: 0.7225\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.3746 - mae: 1.0692\n", "Epoch 442/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0270 - mae: 0.7590\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.3472 - mae: 1.0490\n", "Epoch 443/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0455 - mae: 0.7325\n", + "13/13 [==============================] - 0s 633us/step - loss: 1.3744 - mae: 1.0743\n", "Epoch 444/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1146 - mae: 0.7376\n", + "13/13 [==============================] - 0s 643us/step - loss: 1.3134 - mae: 1.0805\n", "Epoch 445/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0765 - mae: 0.7308\n", + "13/13 [==============================] - 0s 664us/step - loss: 1.3151 - mae: 1.0017\n", "Epoch 446/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0845 - mae: 0.7239\n", + "13/13 [==============================] - 0s 650us/step - loss: 1.2826 - mae: 0.9367\n", "Epoch 447/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0830 - mae: 0.7073\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.3432 - mae: 1.0045\n", "Epoch 448/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0544 - mae: 0.7463\n", + "13/13 [==============================] - 0s 642us/step - loss: 1.3757 - mae: 0.8902\n", "Epoch 449/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9880 - mae: 0.6733\n", + "13/13 [==============================] - 0s 650us/step - loss: 1.3737 - mae: 1.1266\n", "Epoch 450/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0729 - mae: 0.7659\n", + "13/13 [==============================] - 0s 652us/step - loss: 1.3281 - mae: 1.0603\n", "Epoch 451/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0104 - mae: 0.6597\n", + "13/13 [==============================] - 0s 660us/step - loss: 1.3819 - mae: 0.9863\n", "Epoch 452/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9973 - mae: 0.7342\n", + "13/13 [==============================] - 0s 661us/step - loss: 1.4037 - mae: 1.0844\n", "Epoch 453/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0431 - mae: 0.7639\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.3626 - mae: 1.0504\n", "Epoch 454/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9905 - mae: 0.6933\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.3758 - mae: 1.0245\n", "Epoch 455/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0183 - mae: 0.7226\n", + "13/13 [==============================] - 0s 648us/step - loss: 1.3841 - mae: 1.0244\n", "Epoch 456/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0221 - mae: 0.6665\n", + "13/13 [==============================] - 0s 648us/step - loss: 1.3097 - mae: 0.9759\n", "Epoch 457/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0425 - mae: 0.7150\n", + "13/13 [==============================] - 0s 647us/step - loss: 1.3484 - mae: 1.0089\n", "Epoch 458/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0335 - mae: 0.8074\n", + "13/13 [==============================] - 0s 642us/step - loss: 1.3314 - mae: 0.9405\n", "Epoch 459/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0531 - mae: 0.6937\n", + "13/13 [==============================] - 0s 666us/step - loss: 1.4136 - mae: 1.0122\n", "Epoch 460/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9994 - mae: 0.7127\n", + "13/13 [==============================] - 0s 650us/step - loss: 1.4204 - mae: 0.9942\n", "Epoch 461/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0666 - mae: 0.6969\n", + "13/13 [==============================] - 0s 656us/step - loss: 1.3469 - mae: 1.0693\n", "Epoch 462/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0737 - mae: 0.7531\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.3743 - mae: 1.0507\n", "Epoch 463/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1061 - mae: 0.7882\n", + "13/13 [==============================] - 0s 648us/step - loss: 1.3502 - mae: 1.0247\n", "Epoch 464/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1530 - mae: 0.7317\n", + "13/13 [==============================] - 0s 642us/step - loss: 1.3369 - mae: 0.9874\n", "Epoch 465/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9757 - mae: 0.7364\n", + "13/13 [==============================] - 0s 666us/step - loss: 1.3976 - mae: 1.0797\n", "Epoch 466/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9979 - mae: 0.7472\n", + "13/13 [==============================] - 0s 644us/step - loss: 1.3828 - mae: 1.0676\n", "Epoch 467/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0501 - mae: 0.7099\n", + "13/13 [==============================] - 0s 649us/step - loss: 1.3267 - mae: 1.0024\n", "Epoch 468/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1220 - mae: 0.7865\n", + "13/13 [==============================] - 0s 647us/step - loss: 1.3612 - mae: 0.9743\n", "Epoch 469/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0627 - mae: 0.7668\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.4270 - mae: 1.0463\n", "Epoch 470/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9729 - mae: 0.7477\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.3990 - mae: 1.0233\n", "Epoch 471/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0815 - mae: 0.7196\n", + "13/13 [==============================] - 0s 650us/step - loss: 1.3658 - mae: 1.0001\n", "Epoch 472/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0283 - mae: 0.6645\n", + "13/13 [==============================] - 0s 639us/step - loss: 1.3263 - mae: 0.9380\n", "Epoch 473/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0420 - mae: 0.7726\n", + "13/13 [==============================] - 0s 653us/step - loss: 1.3972 - mae: 1.0995\n", "Epoch 474/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0440 - mae: 0.7474\n", + "13/13 [==============================] - 0s 641us/step - loss: 1.3562 - mae: 1.0037\n", "Epoch 475/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9326 - mae: 0.6692\n", + "13/13 [==============================] - 0s 666us/step - loss: 1.3219 - mae: 0.9936\n", "Epoch 476/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1927 - mae: 0.8322\n", + "13/13 [==============================] - 0s 639us/step - loss: 1.3827 - mae: 1.0826\n", "Epoch 477/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0293 - mae: 0.7161\n", + "13/13 [==============================] - 0s 648us/step - loss: 1.3266 - mae: 1.0584\n", "Epoch 478/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0220 - mae: 0.6862\n", + "13/13 [==============================] - 0s 640us/step - loss: 1.3193 - mae: 1.0162\n", "Epoch 479/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0299 - mae: 0.6844\n", + "13/13 [==============================] - 0s 665us/step - loss: 1.3132 - mae: 0.9875\n", "Epoch 480/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9837 - mae: 0.7416\n", + "13/13 [==============================] - 0s 636us/step - loss: 1.3611 - mae: 1.0324\n", "Epoch 481/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0260 - mae: 0.7712\n", + "13/13 [==============================] - 0s 658us/step - loss: 1.3182 - mae: 0.9515\n", "Epoch 482/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0674 - mae: 0.7280\n", + "13/13 [==============================] - 0s 672us/step - loss: 1.4680 - mae: 1.0560\n", "Epoch 483/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9487 - mae: 0.7073\n", + "13/13 [==============================] - 0s 670us/step - loss: 1.3118 - mae: 0.9418\n", "Epoch 484/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0442 - mae: 0.7065\n", + "13/13 [==============================] - 0s 650us/step - loss: 1.3699 - mae: 0.9997\n", "Epoch 485/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0375 - mae: 0.7709\n", + "13/13 [==============================] - 0s 628us/step - loss: 1.4118 - mae: 1.1014\n", "Epoch 486/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0373 - mae: 0.6770\n", + "13/13 [==============================] - 0s 656us/step - loss: 1.3776 - mae: 1.0570\n", "Epoch 487/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0419 - mae: 0.7704\n", + "13/13 [==============================] - 0s 713us/step - loss: 1.4388 - mae: 1.0545\n", "Epoch 488/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9805 - mae: 0.6623\n", + "13/13 [==============================] - 0s 664us/step - loss: 1.4213 - mae: 1.0178\n", "Epoch 489/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9987 - mae: 0.6935\n", + "13/13 [==============================] - 0s 649us/step - loss: 1.3273 - mae: 0.8866\n", "Epoch 490/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0146 - mae: 0.7805\n", + "13/13 [==============================] - 0s 660us/step - loss: 1.3502 - mae: 0.9753\n", "Epoch 491/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0035 - mae: 0.7163\n", + "13/13 [==============================] - 0s 643us/step - loss: 1.3175 - mae: 0.9491\n", "Epoch 492/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0197 - mae: 0.7562\n", + "13/13 [==============================] - 0s 642us/step - loss: 1.2873 - mae: 1.0150\n", "Epoch 493/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9680 - mae: 0.7125\n", + "13/13 [==============================] - 0s 667us/step - loss: 1.3323 - mae: 0.9358\n", "Epoch 494/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9976 - mae: 0.7223\n", + "13/13 [==============================] - 0s 645us/step - loss: 1.3817 - mae: 0.9944\n", "Epoch 495/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9890 - mae: 0.7102\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.3351 - mae: 1.0172\n", "Epoch 496/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0462 - mae: 0.7120\n", + "13/13 [==============================] - 0s 664us/step - loss: 1.3402 - mae: 0.9915\n", "Epoch 497/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.1398 - mae: 0.7285\n", + "13/13 [==============================] - 0s 665us/step - loss: 1.3342 - mae: 1.0304\n", "Epoch 498/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0073 - mae: 0.6793\n", + "13/13 [==============================] - 0s 665us/step - loss: 1.3099 - mae: 0.9277\n", "Epoch 499/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9787 - mae: 0.6892\n", + "13/13 [==============================] - 0s 645us/step - loss: 1.3357 - mae: 0.9902\n", "Epoch 500/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9933 - mae: 0.7069\n", + "13/13 [==============================] - 0s 641us/step - loss: 1.2573 - mae: 0.9598\n", "Epoch 501/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9947 - mae: 0.6394\n", + "13/13 [==============================] - 0s 668us/step - loss: 1.3374 - mae: 1.0109\n", "Epoch 502/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0193 - mae: 0.7283\n", + "13/13 [==============================] - 0s 653us/step - loss: 1.3503 - mae: 1.0101\n", "Epoch 503/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0114 - mae: 0.7200\n", + "13/13 [==============================] - 0s 645us/step - loss: 1.3414 - mae: 1.0082\n", "Epoch 504/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0481 - mae: 0.7057\n", + "13/13 [==============================] - 0s 675us/step - loss: 1.3264 - mae: 0.9815\n", "Epoch 505/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0068 - mae: 0.6846\n", + "13/13 [==============================] - 0s 633us/step - loss: 1.3310 - mae: 0.9854\n", "Epoch 506/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0019 - mae: 0.6956\n", + "13/13 [==============================] - 0s 652us/step - loss: 1.3705 - mae: 1.0641\n", "Epoch 507/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9632 - mae: 0.6675\n", + "13/13 [==============================] - 0s 660us/step - loss: 1.3687 - mae: 1.0918\n", "Epoch 508/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0915 - mae: 0.7144\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.3792 - mae: 1.0380\n", "Epoch 509/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9666 - mae: 0.7038\n", + "13/13 [==============================] - 0s 655us/step - loss: 1.3493 - mae: 1.0226\n", "Epoch 510/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9761 - mae: 0.6764\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.3948 - mae: 1.0405\n", "Epoch 511/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0235 - mae: 0.6803\n", + "13/13 [==============================] - 0s 655us/step - loss: 1.3537 - mae: 1.0837\n", "Epoch 512/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0028 - mae: 0.6805\n", + "13/13 [==============================] - 0s 675us/step - loss: 1.4101 - mae: 0.9778\n", "Epoch 513/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9491 - mae: 0.6851\n", + "13/13 [==============================] - 0s 674us/step - loss: 1.3710 - mae: 1.0498\n", "Epoch 514/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9345 - mae: 0.7146\n", + "13/13 [==============================] - 0s 664us/step - loss: 1.2558 - mae: 0.9384\n", "Epoch 515/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9499 - mae: 0.6754\n", + "13/13 [==============================] - 0s 664us/step - loss: 1.3523 - mae: 0.9777\n", "Epoch 516/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.8865 - mae: 0.6329\n", + "13/13 [==============================] - 0s 652us/step - loss: 1.3262 - mae: 1.0013\n", "Epoch 517/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9468 - mae: 0.6663\n", + "13/13 [==============================] - 0s 648us/step - loss: 1.2933 - mae: 0.9553\n", "Epoch 518/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9348 - mae: 0.7288\n", + "13/13 [==============================] - 0s 669us/step - loss: 1.3445 - mae: 0.9920\n", "Epoch 519/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9925 - mae: 0.7702\n", + "13/13 [==============================] - 0s 655us/step - loss: 1.3917 - mae: 1.0810\n", "Epoch 520/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0558 - mae: 0.6812\n", + "13/13 [==============================] - 0s 663us/step - loss: 1.2782 - mae: 0.9555\n", "Epoch 521/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9872 - mae: 0.6579\n", + "13/13 [==============================] - 0s 661us/step - loss: 1.3172 - mae: 1.0622\n", "Epoch 522/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9837 - mae: 0.6923\n", + "13/13 [==============================] - 0s 670us/step - loss: 1.3204 - mae: 1.0288\n", "Epoch 523/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9819 - mae: 0.7159\n", + "13/13 [==============================] - 0s 644us/step - loss: 1.3549 - mae: 0.9917\n", "Epoch 524/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9355 - mae: 0.6829\n", + "13/13 [==============================] - 0s 649us/step - loss: 1.3413 - mae: 0.9673\n", "Epoch 525/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0993 - mae: 0.7169\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.3128 - mae: 1.0361\n", "Epoch 526/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0079 - mae: 0.7160\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.2571 - mae: 0.9067\n", "Epoch 527/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0137 - mae: 0.6863\n", + "13/13 [==============================] - 0s 649us/step - loss: 1.3800 - mae: 1.0777\n", "Epoch 528/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9374 - mae: 0.6758\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.3731 - mae: 0.9597\n", "Epoch 529/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0669 - mae: 0.7774\n", + "13/13 [==============================] - 0s 646us/step - loss: 1.4221 - mae: 1.1473\n", "Epoch 530/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0262 - mae: 0.6905\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.3364 - mae: 0.9693\n", "Epoch 531/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9662 - mae: 0.6891\n", + "13/13 [==============================] - 0s 652us/step - loss: 1.2655 - mae: 0.9735\n", "Epoch 532/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0688 - mae: 0.6822\n", + "13/13 [==============================] - 0s 655us/step - loss: 1.3454 - mae: 0.9802\n", "Epoch 533/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9341 - mae: 0.6710\n", + "13/13 [==============================] - 0s 670us/step - loss: 1.3894 - mae: 1.0543\n", "Epoch 534/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9274 - mae: 0.6571\n", + "13/13 [==============================] - 0s 661us/step - loss: 1.3037 - mae: 0.8802\n", "Epoch 535/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9730 - mae: 0.7311\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.3011 - mae: 0.9473\n", "Epoch 536/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9876 - mae: 0.6757\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.2748 - mae: 0.9862\n", "Epoch 537/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9977 - mae: 0.6728\n", + "13/13 [==============================] - 0s 660us/step - loss: 1.3449 - mae: 0.9634\n", "Epoch 538/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9813 - mae: 0.7262\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.2975 - mae: 1.0026\n", "Epoch 539/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9276 - mae: 0.6685\n", + "13/13 [==============================] - 0s 658us/step - loss: 1.2482 - mae: 0.9487\n", "Epoch 540/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9856 - mae: 0.7533\n", + "13/13 [==============================] - 0s 649us/step - loss: 1.3005 - mae: 1.0100\n", "Epoch 541/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9876 - mae: 0.6583\n", + "13/13 [==============================] - 0s 654us/step - loss: 1.3016 - mae: 0.9492\n", "Epoch 542/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9366 - mae: 0.6826\n", + "13/13 [==============================] - 0s 653us/step - loss: 1.3844 - mae: 0.9692\n", "Epoch 543/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0193 - mae: 0.7318\n", + "13/13 [==============================] - 0s 645us/step - loss: 1.3257 - mae: 0.9927\n", "Epoch 544/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0361 - mae: 0.7375\n", + "13/13 [==============================] - 0s 658us/step - loss: 1.3231 - mae: 0.9548\n", "Epoch 545/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9746 - mae: 0.7431\n", + "13/13 [==============================] - 0s 660us/step - loss: 1.3778 - mae: 0.9743\n", "Epoch 546/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9808 - mae: 0.6623\n", + "13/13 [==============================] - 0s 658us/step - loss: 1.3025 - mae: 0.9140\n", "Epoch 547/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.8979 - mae: 0.6730\n", + "13/13 [==============================] - 0s 650us/step - loss: 1.3278 - mae: 0.9517\n", "Epoch 548/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9269 - mae: 0.6322\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.2854 - mae: 0.9082\n", "Epoch 549/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9848 - mae: 0.7138\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.2632 - mae: 1.0298\n", "Epoch 550/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9809 - mae: 0.6604\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.2997 - mae: 0.8863\n", "Epoch 551/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0266 - mae: 0.6987\n", + "13/13 [==============================] - 0s 642us/step - loss: 1.2375 - mae: 0.9331\n", "Epoch 552/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0103 - mae: 0.6949\n", + "13/13 [==============================] - 0s 654us/step - loss: 1.2933 - mae: 0.9483\n", "Epoch 553/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9666 - mae: 0.7182\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.2430 - mae: 0.9007\n", "Epoch 554/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0044 - mae: 0.7149\n", + "13/13 [==============================] - 0s 663us/step - loss: 1.3170 - mae: 0.9109\n", "Epoch 555/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9957 - mae: 0.6571\n", + "13/13 [==============================] - 0s 643us/step - loss: 1.3459 - mae: 0.9528\n", "Epoch 556/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9750 - mae: 0.7169\n", + "13/13 [==============================] - 0s 666us/step - loss: 1.2829 - mae: 0.9681\n", "Epoch 557/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9770 - mae: 0.7086\n", + "13/13 [==============================] - 0s 655us/step - loss: 1.3130 - mae: 0.9723\n", "Epoch 558/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9775 - mae: 0.7113\n", + "13/13 [==============================] - 0s 644us/step - loss: 1.3463 - mae: 1.0047\n", "Epoch 559/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9991 - mae: 0.7117\n", + "13/13 [==============================] - 0s 649us/step - loss: 1.2985 - mae: 0.9505\n", "Epoch 560/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9727 - mae: 0.7136\n", + "13/13 [==============================] - 0s 661us/step - loss: 1.3328 - mae: 1.0190\n", "Epoch 561/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9534 - mae: 0.6586\n", + "13/13 [==============================] - 0s 646us/step - loss: 1.2852 - mae: 0.9106\n", "Epoch 562/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9043 - mae: 0.6668\n", + "13/13 [==============================] - 0s 645us/step - loss: 1.2532 - mae: 0.8913\n", "Epoch 563/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9450 - mae: 0.6822\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.3073 - mae: 0.8921\n", "Epoch 564/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0149 - mae: 0.6654\n", + "13/13 [==============================] - 0s 656us/step - loss: 1.3029 - mae: 0.9899\n", "Epoch 565/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9401 - mae: 0.7412\n", + "13/13 [==============================] - 0s 649us/step - loss: 1.3383 - mae: 0.9821\n", "Epoch 566/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0031 - mae: 0.7679\n", + "13/13 [==============================] - 0s 640us/step - loss: 1.3419 - mae: 0.9608\n", "Epoch 567/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9743 - mae: 0.7240\n", + "13/13 [==============================] - 0s 652us/step - loss: 1.3185 - mae: 1.0007\n", "Epoch 568/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9939 - mae: 0.6685\n", + "13/13 [==============================] - 0s 665us/step - loss: 1.3037 - mae: 1.0428\n", "Epoch 569/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9406 - mae: 0.6359\n", + "13/13 [==============================] - 0s 653us/step - loss: 1.3238 - mae: 0.9549\n", "Epoch 570/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9726 - mae: 0.6978\n", + "13/13 [==============================] - 0s 665us/step - loss: 1.3075 - mae: 0.8758\n", "Epoch 571/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9392 - mae: 0.6764\n", + "13/13 [==============================] - 0s 652us/step - loss: 1.2600 - mae: 0.8897\n", "Epoch 572/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9638 - mae: 0.6435\n", + "13/13 [==============================] - 0s 660us/step - loss: 1.2178 - mae: 0.8660\n", "Epoch 573/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0292 - mae: 0.7358\n", + "13/13 [==============================] - 0s 664us/step - loss: 1.2502 - mae: 0.9463\n", "Epoch 574/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0044 - mae: 0.7013\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.2347 - mae: 0.9283\n", "Epoch 575/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.8938 - mae: 0.6308\n", + "13/13 [==============================] - 0s 658us/step - loss: 1.2790 - mae: 0.9683\n", "Epoch 576/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9505 - mae: 0.6570\n", + "13/13 [==============================] - 0s 652us/step - loss: 1.2359 - mae: 0.9439\n", "Epoch 577/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0897 - mae: 0.7329\n", + "13/13 [==============================] - 0s 644us/step - loss: 1.3307 - mae: 0.9421\n", "Epoch 578/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9876 - mae: 0.7095\n", + "13/13 [==============================] - 0s 660us/step - loss: 1.3036 - mae: 0.8678\n", "Epoch 579/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9337 - mae: 0.7114\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.2043 - mae: 0.9387\n", "Epoch 580/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0306 - mae: 0.6997\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.2323 - mae: 0.8860\n", "Epoch 581/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0232 - mae: 0.6967\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.3869 - mae: 0.9706\n", "Epoch 582/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.8562 - mae: 0.6840\n", + "13/13 [==============================] - 0s 637us/step - loss: 1.2473 - mae: 0.9895\n", "Epoch 583/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9489 - mae: 0.6562\n", + "13/13 [==============================] - 0s 663us/step - loss: 1.2874 - mae: 0.9389\n", "Epoch 584/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9789 - mae: 0.7250\n", + "13/13 [==============================] - 0s 644us/step - loss: 1.3607 - mae: 0.9789\n", "Epoch 585/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9213 - mae: 0.6204\n", + "13/13 [==============================] - 0s 641us/step - loss: 1.2948 - mae: 1.0100\n", "Epoch 586/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0197 - mae: 0.6918\n", + "13/13 [==============================] - 0s 642us/step - loss: 1.2617 - mae: 0.9307\n", "Epoch 587/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0650 - mae: 0.7133\n", + "13/13 [==============================] - 0s 663us/step - loss: 1.2250 - mae: 0.8440\n", "Epoch 588/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9591 - mae: 0.6350\n", + "13/13 [==============================] - 0s 656us/step - loss: 1.2051 - mae: 0.8706\n", "Epoch 589/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9732 - mae: 0.6696\n", + "13/13 [==============================] - 0s 658us/step - loss: 1.3023 - mae: 0.9618\n", "Epoch 590/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9665 - mae: 0.6170\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.2880 - mae: 0.8848\n", "Epoch 591/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9270 - mae: 0.6916\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.2995 - mae: 0.8952\n", "Epoch 592/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9719 - mae: 0.6948\n", + "13/13 [==============================] - 0s 649us/step - loss: 1.2929 - mae: 0.8810\n", "Epoch 593/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9269 - mae: 0.7624\n", + "13/13 [==============================] - 0s 670us/step - loss: 1.2866 - mae: 0.9099\n", "Epoch 594/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9312 - mae: 0.7360\n", + "13/13 [==============================] - 0s 647us/step - loss: 1.2294 - mae: 0.8939\n", "Epoch 595/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9339 - mae: 0.6128\n", + "13/13 [==============================] - 0s 642us/step - loss: 1.3330 - mae: 0.9358\n", "Epoch 596/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9246 - mae: 0.7412\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.2629 - mae: 0.8967\n", "Epoch 597/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9162 - mae: 0.6608\n", + "13/13 [==============================] - 0s 671us/step - loss: 1.2815 - mae: 0.9382\n", "Epoch 598/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9372 - mae: 0.6584\n", + "13/13 [==============================] - 0s 652us/step - loss: 1.2707 - mae: 0.9278\n", "Epoch 599/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9577 - mae: 0.6330\n", + "13/13 [==============================] - 0s 663us/step - loss: 1.2087 - mae: 0.8912\n", "Epoch 600/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9852 - mae: 0.6806\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.2065 - mae: 0.8522\n", "Epoch 601/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9778 - mae: 0.6780\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.2498 - mae: 0.8926\n", "Epoch 602/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.8745 - mae: 0.7223\n", + "13/13 [==============================] - 0s 656us/step - loss: 1.2515 - mae: 0.9105\n", "Epoch 603/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9533 - mae: 0.6927\n", + "13/13 [==============================] - 0s 656us/step - loss: 1.2512 - mae: 0.9312\n", "Epoch 604/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0004 - mae: 0.6122\n", + "13/13 [==============================] - 0s 654us/step - loss: 1.2695 - mae: 0.8867\n", "Epoch 605/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9659 - mae: 0.6663\n", + "13/13 [==============================] - 0s 665us/step - loss: 1.2144 - mae: 0.8872\n", "Epoch 606/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9373 - mae: 0.6434\n", + "13/13 [==============================] - 0s 666us/step - loss: 1.2489 - mae: 0.8893\n", "Epoch 607/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0905 - mae: 0.7298\n", + "13/13 [==============================] - 0s 661us/step - loss: 1.2265 - mae: 0.9485\n", "Epoch 608/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9710 - mae: 0.6670\n", + "13/13 [==============================] - 0s 663us/step - loss: 1.1789 - mae: 0.9110\n", "Epoch 609/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9560 - mae: 0.7128\n", + "13/13 [==============================] - 0s 652us/step - loss: 1.2028 - mae: 0.8359\n", "Epoch 610/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9750 - mae: 0.6696\n", + "13/13 [==============================] - 0s 658us/step - loss: 1.2528 - mae: 0.9570\n", "Epoch 611/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0666 - mae: 0.7303\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.2124 - mae: 0.9017\n", "Epoch 612/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9732 - mae: 0.6789\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.1489 - mae: 0.8665\n", "Epoch 613/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9511 - mae: 0.6860\n", + "13/13 [==============================] - 0s 654us/step - loss: 1.1376 - mae: 0.8657\n", "Epoch 614/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9889 - mae: 0.7495\n", + "13/13 [==============================] - 0s 658us/step - loss: 1.1558 - mae: 0.8374\n", "Epoch 615/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9462 - mae: 0.7184\n", + "13/13 [==============================] - 0s 641us/step - loss: 1.2387 - mae: 0.9767\n", "Epoch 616/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.8943 - mae: 0.6675\n", + "13/13 [==============================] - 0s 639us/step - loss: 1.1504 - mae: 0.8836\n", "Epoch 617/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9909 - mae: 0.7063\n", + "13/13 [==============================] - 0s 656us/step - loss: 1.1956 - mae: 0.8741\n", "Epoch 618/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9148 - mae: 0.6035\n", + "13/13 [==============================] - 0s 656us/step - loss: 1.2223 - mae: 0.8672\n", "Epoch 619/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9146 - mae: 0.6885\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.2485 - mae: 0.9252\n", "Epoch 620/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0144 - mae: 0.6983\n", + "13/13 [==============================] - 0s 658us/step - loss: 1.2844 - mae: 0.9277\n", "Epoch 621/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9903 - mae: 0.6452\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.2726 - mae: 0.9478\n", "Epoch 622/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9180 - mae: 0.6920\n", + "13/13 [==============================] - 0s 654us/step - loss: 1.2401 - mae: 0.9040\n", "Epoch 623/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9455 - mae: 0.6332\n", + "13/13 [==============================] - 0s 662us/step - loss: 1.1796 - mae: 0.8982\n", "Epoch 624/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9227 - mae: 0.6808\n", + "13/13 [==============================] - 0s 645us/step - loss: 1.2116 - mae: 0.8710\n", "Epoch 625/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9559 - mae: 0.6649\n", + "13/13 [==============================] - 0s 655us/step - loss: 1.2586 - mae: 0.8895\n", "Epoch 626/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.8585 - mae: 0.6163\n", + "13/13 [==============================] - 0s 655us/step - loss: 1.1525 - mae: 0.8409\n", "Epoch 627/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9367 - mae: 0.6608\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.1301 - mae: 0.8725\n", "Epoch 628/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9570 - mae: 0.6693\n", + "13/13 [==============================] - 0s 661us/step - loss: 1.1833 - mae: 0.7961\n", "Epoch 629/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0781 - mae: 0.7018\n", + "13/13 [==============================] - 0s 669us/step - loss: 1.2512 - mae: 0.8690\n", "Epoch 630/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9390 - mae: 0.6874\n", + "13/13 [==============================] - 0s 661us/step - loss: 1.1586 - mae: 0.8215\n", "Epoch 631/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9813 - mae: 0.6707\n", + "13/13 [==============================] - 0s 664us/step - loss: 1.3023 - mae: 0.8667\n", "Epoch 632/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9447 - mae: 0.6739\n", + "13/13 [==============================] - 0s 663us/step - loss: 1.1611 - mae: 0.8814\n", "Epoch 633/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9656 - mae: 0.7142\n", + "13/13 [==============================] - 0s 669us/step - loss: 1.1687 - mae: 0.8254\n", "Epoch 634/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9500 - mae: 0.7504\n", + "13/13 [==============================] - 0s 661us/step - loss: 1.2002 - mae: 0.7993\n", "Epoch 635/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.8984 - mae: 0.6757\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.2066 - mae: 0.8504\n", "Epoch 636/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9544 - mae: 0.6650\n", + "13/13 [==============================] - 0s 669us/step - loss: 1.1414 - mae: 0.8142\n", "Epoch 637/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9152 - mae: 0.6983\n", + "13/13 [==============================] - 0s 668us/step - loss: 1.1671 - mae: 0.7824\n", "Epoch 638/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0307 - mae: 0.6761\n", + "13/13 [==============================] - 0s 649us/step - loss: 1.1833 - mae: 0.7891\n", "Epoch 639/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0771 - mae: 0.6549\n", + "13/13 [==============================] - 0s 652us/step - loss: 1.1754 - mae: 0.8567\n", "Epoch 640/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9853 - mae: 0.7302\n", + "13/13 [==============================] - 0s 673us/step - loss: 1.1052 - mae: 0.7831\n", "Epoch 641/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9435 - mae: 0.6950\n", + "13/13 [==============================] - 0s 666us/step - loss: 1.0811 - mae: 0.7910\n", "Epoch 642/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9499 - mae: 0.6684\n", + "13/13 [==============================] - 0s 666us/step - loss: 1.1649 - mae: 0.8561\n", "Epoch 643/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0300 - mae: 0.6751\n", + "13/13 [==============================] - 0s 679us/step - loss: 1.1608 - mae: 0.8746\n", "Epoch 644/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9876 - mae: 0.6668\n", + "13/13 [==============================] - 0s 670us/step - loss: 1.1576 - mae: 0.8803\n", "Epoch 645/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9487 - mae: 0.6707\n", + "13/13 [==============================] - 0s 645us/step - loss: 1.1783 - mae: 0.8476\n", "Epoch 646/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0138 - mae: 0.7292\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.1541 - mae: 0.8320\n", "Epoch 647/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9796 - mae: 0.6854\n", + "13/13 [==============================] - 0s 668us/step - loss: 1.1112 - mae: 0.8842\n", "Epoch 648/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9104 - mae: 0.6351\n", + "13/13 [==============================] - 0s 649us/step - loss: 1.0898 - mae: 0.7404\n", "Epoch 649/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9268 - mae: 0.6530\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.1377 - mae: 0.8549\n", "Epoch 650/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.8668 - mae: 0.6372\n", + "13/13 [==============================] - 0s 664us/step - loss: 1.1858 - mae: 0.8325\n", "Epoch 651/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9242 - mae: 0.6461\n", + "13/13 [==============================] - 0s 643us/step - loss: 1.1702 - mae: 0.8593\n", "Epoch 652/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0430 - mae: 0.7228\n", + "13/13 [==============================] - 0s 650us/step - loss: 1.0814 - mae: 0.7751\n", "Epoch 653/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9002 - mae: 0.6815\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.1162 - mae: 0.8464\n", "Epoch 654/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9330 - mae: 0.7201\n", + "13/13 [==============================] - 0s 664us/step - loss: 1.1341 - mae: 0.8392\n", "Epoch 655/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9396 - mae: 0.6944\n", + "13/13 [==============================] - 0s 655us/step - loss: 1.1637 - mae: 0.8549\n", "Epoch 656/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9561 - mae: 0.7073\n", + "13/13 [==============================] - 0s 665us/step - loss: 1.1294 - mae: 0.8537\n", "Epoch 657/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9303 - mae: 0.6976\n", + "13/13 [==============================] - 0s 668us/step - loss: 1.1399 - mae: 0.8336\n", "Epoch 658/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9081 - mae: 0.6670\n", + "13/13 [==============================] - 0s 656us/step - loss: 1.1952 - mae: 0.7793\n", "Epoch 659/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.8791 - mae: 0.6433\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.1084 - mae: 0.7840\n", "Epoch 660/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9248 - mae: 0.6678\n", + "13/13 [==============================] - 0s 664us/step - loss: 1.0986 - mae: 0.8640\n", "Epoch 661/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9460 - mae: 0.6702\n", + "13/13 [==============================] - 0s 639us/step - loss: 1.0934 - mae: 0.7722\n", "Epoch 662/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.8670 - mae: 0.6600\n", + "13/13 [==============================] - 0s 656us/step - loss: 1.0696 - mae: 0.8044\n", "Epoch 663/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9054 - mae: 0.6614\n", + "13/13 [==============================] - 0s 667us/step - loss: 1.1171 - mae: 0.8668\n", "Epoch 664/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9638 - mae: 0.6502\n", + "13/13 [==============================] - 0s 647us/step - loss: 1.1038 - mae: 0.8280\n", "Epoch 665/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0127 - mae: 0.6519\n", + "13/13 [==============================] - 0s 663us/step - loss: 1.0405 - mae: 0.7713\n", "Epoch 666/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9006 - mae: 0.6122\n", + "13/13 [==============================] - 0s 668us/step - loss: 1.1055 - mae: 0.7558\n", "Epoch 667/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 1.0285 - mae: 0.7438\n", + "13/13 [==============================] - 0s 664us/step - loss: 1.1023 - mae: 0.7728\n", "Epoch 668/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9254 - mae: 0.7385\n", + "13/13 [==============================] - 0s 664us/step - loss: 1.1221 - mae: 0.7579\n", "Epoch 669/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9375 - mae: 0.6586\n", + "13/13 [==============================] - 0s 674us/step - loss: 1.1735 - mae: 0.8617\n", "Epoch 670/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9071 - mae: 0.6411\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.1124 - mae: 0.7970\n", "Epoch 671/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9711 - mae: 0.7181\n", + "13/13 [==============================] - 0s 655us/step - loss: 1.1744 - mae: 0.8509\n", "Epoch 672/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9213 - mae: 0.6585\n", + "13/13 [==============================] - 0s 646us/step - loss: 1.0784 - mae: 0.8013\n", "Epoch 673/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9522 - mae: 0.6837\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.0929 - mae: 0.7805\n", "Epoch 674/1000\n", - "13/13 [==============================] - 0s 2ms/step - loss: 0.9390 - mae: 0.6395\n", + "13/13 [==============================] - 0s 658us/step - loss: 1.1507 - mae: 0.8431\n", "Epoch 675/1000\n", - "13/13 [==============================] - 6s 486ms/step - loss: 0.9395 - mae: 0.6616\n", + "13/13 [==============================] - 0s 667us/step - loss: 1.1281 - mae: 0.7866\n", "Epoch 676/1000\n", - "13/13 [==============================] - 0s 773us/step - loss: 0.9404 - mae: 0.6579\n", + "13/13 [==============================] - 0s 658us/step - loss: 1.1220 - mae: 0.8020\n", "Epoch 677/1000\n", - "13/13 [==============================] - 0s 711us/step - loss: 0.9536 - mae: 0.6595\n", + "13/13 [==============================] - 0s 660us/step - loss: 1.0734 - mae: 0.7136\n", "Epoch 678/1000\n", - "13/13 [==============================] - 0s 650us/step - loss: 0.9002 - mae: 0.6330\n", + "13/13 [==============================] - 0s 656us/step - loss: 1.0905 - mae: 0.8034\n", "Epoch 679/1000\n", - "13/13 [==============================] - 0s 654us/step - loss: 0.9020 - mae: 0.6923\n", + "13/13 [==============================] - 0s 658us/step - loss: 1.1359 - mae: 0.7658\n", "Epoch 680/1000\n", - "13/13 [==============================] - 0s 642us/step - loss: 0.9729 - mae: 0.6938\n", + "13/13 [==============================] - 0s 660us/step - loss: 1.1165 - mae: 0.8147\n", "Epoch 681/1000\n", - "13/13 [==============================] - 0s 637us/step - loss: 0.9532 - mae: 0.7114\n", + "13/13 [==============================] - 0s 663us/step - loss: 1.0889 - mae: 0.7777\n", "Epoch 682/1000\n", - "13/13 [==============================] - 0s 675us/step - loss: 0.9242 - mae: 0.6162\n", + "13/13 [==============================] - 0s 667us/step - loss: 1.0193 - mae: 0.7541\n", "Epoch 683/1000\n", - "13/13 [==============================] - 0s 664us/step - loss: 0.9785 - mae: 0.6953\n", + "13/13 [==============================] - 0s 661us/step - loss: 1.1041 - mae: 0.7068\n", "Epoch 684/1000\n", - "13/13 [==============================] - 0s 745us/step - loss: 0.9290 - mae: 0.6540\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.0968 - mae: 0.8017\n", "Epoch 685/1000\n", - "13/13 [==============================] - 0s 690us/step - loss: 0.9727 - mae: 0.7079\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.0725 - mae: 0.8111\n", "Epoch 686/1000\n", - "13/13 [==============================] - 0s 682us/step - loss: 0.8937 - mae: 0.6736\n", + "13/13 [==============================] - 0s 670us/step - loss: 1.2043 - mae: 0.7898\n", "Epoch 687/1000\n", - "13/13 [==============================] - 0s 706us/step - loss: 0.8786 - mae: 0.5720\n", + "13/13 [==============================] - 0s 671us/step - loss: 1.0701 - mae: 0.7806\n", "Epoch 688/1000\n", - "13/13 [==============================] - 0s 663us/step - loss: 0.9142 - mae: 0.6149\n", + "13/13 [==============================] - 0s 669us/step - loss: 1.0544 - mae: 0.6971\n", "Epoch 689/1000\n", - "13/13 [==============================] - 0s 641us/step - loss: 0.8481 - mae: 0.6182\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.0305 - mae: 0.7555\n", "Epoch 690/1000\n", - "13/13 [==============================] - 0s 655us/step - loss: 0.9556 - mae: 0.6492\n", + "13/13 [==============================] - 0s 671us/step - loss: 1.1086 - mae: 0.7551\n", "Epoch 691/1000\n", - "13/13 [==============================] - 0s 648us/step - loss: 0.9248 - mae: 0.6467\n", + "13/13 [==============================] - 0s 670us/step - loss: 1.0288 - mae: 0.8050\n", "Epoch 692/1000\n", - "13/13 [==============================] - 0s 662us/step - loss: 1.0155 - mae: 0.6936\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.0473 - mae: 0.7025\n", "Epoch 693/1000\n", - "13/13 [==============================] - 0s 661us/step - loss: 0.9536 - mae: 0.6806\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.0356 - mae: 0.7938\n", "Epoch 694/1000\n", - "13/13 [==============================] - 0s 670us/step - loss: 0.9494 - mae: 0.6066\n", + "13/13 [==============================] - 0s 648us/step - loss: 1.0589 - mae: 0.6879\n", "Epoch 695/1000\n", - "13/13 [==============================] - 0s 638us/step - loss: 0.8689 - mae: 0.6513\n", + "13/13 [==============================] - 0s 655us/step - loss: 1.1179 - mae: 0.7883\n", "Epoch 696/1000\n", - "13/13 [==============================] - 0s 705us/step - loss: 1.0367 - mae: 0.6678\n", + "13/13 [==============================] - 0s 645us/step - loss: 1.1610 - mae: 0.8009\n", "Epoch 697/1000\n", - "13/13 [==============================] - 0s 666us/step - loss: 0.8563 - mae: 0.5858\n", + "13/13 [==============================] - 0s 662us/step - loss: 1.0481 - mae: 0.6997\n", "Epoch 698/1000\n", - "13/13 [==============================] - 0s 670us/step - loss: 0.9306 - mae: 0.6681\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.0748 - mae: 0.7822\n", "Epoch 699/1000\n", - "13/13 [==============================] - 0s 655us/step - loss: 0.9484 - mae: 0.6776\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.0880 - mae: 0.7217\n", "Epoch 700/1000\n", - "13/13 [==============================] - 0s 667us/step - loss: 0.9206 - mae: 0.6694\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.0721 - mae: 0.8003\n", "Epoch 701/1000\n", - "13/13 [==============================] - 0s 674us/step - loss: 0.9859 - mae: 0.6836\n", + "13/13 [==============================] - 0s 665us/step - loss: 1.0650 - mae: 0.7761\n", "Epoch 702/1000\n", - "13/13 [==============================] - 0s 667us/step - loss: 0.9226 - mae: 0.6461\n", + "13/13 [==============================] - 0s 656us/step - loss: 0.9731 - mae: 0.7603\n", "Epoch 703/1000\n", - "13/13 [==============================] - 0s 678us/step - loss: 0.9429 - mae: 0.6929\n", + "13/13 [==============================] - 0s 678us/step - loss: 1.1315 - mae: 0.7247\n", "Epoch 704/1000\n", - "13/13 [==============================] - 0s 696us/step - loss: 0.9397 - mae: 0.6893\n", + "13/13 [==============================] - 0s 654us/step - loss: 0.9877 - mae: 0.7492\n", "Epoch 705/1000\n", - "13/13 [==============================] - 0s 663us/step - loss: 0.9507 - mae: 0.6694\n", + "13/13 [==============================] - 0s 669us/step - loss: 1.0086 - mae: 0.7316\n", "Epoch 706/1000\n", - "13/13 [==============================] - 0s 683us/step - loss: 0.9593 - mae: 0.6445\n", + "13/13 [==============================] - 0s 668us/step - loss: 1.0231 - mae: 0.7403\n", "Epoch 707/1000\n", - "13/13 [==============================] - 0s 682us/step - loss: 0.8954 - mae: 0.6565\n", + "13/13 [==============================] - 0s 660us/step - loss: 1.0048 - mae: 0.7323\n", "Epoch 708/1000\n", - "13/13 [==============================] - 0s 663us/step - loss: 0.8906 - mae: 0.6630\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.0174 - mae: 0.7638\n", "Epoch 709/1000\n", - "13/13 [==============================] - 0s 672us/step - loss: 0.8717 - mae: 0.6719\n", + "13/13 [==============================] - 0s 662us/step - loss: 1.1173 - mae: 0.7572\n", "Epoch 710/1000\n", - "13/13 [==============================] - 0s 675us/step - loss: 0.8877 - mae: 0.6110\n", + "13/13 [==============================] - 0s 665us/step - loss: 1.0787 - mae: 0.7557\n", "Epoch 711/1000\n", - "13/13 [==============================] - 0s 647us/step - loss: 0.9397 - mae: 0.6178\n", + "13/13 [==============================] - 0s 661us/step - loss: 1.0666 - mae: 0.7396\n", "Epoch 712/1000\n", - "13/13 [==============================] - 0s 674us/step - loss: 0.9319 - mae: 0.6265\n", + "13/13 [==============================] - 0s 671us/step - loss: 1.0074 - mae: 0.6945\n", "Epoch 713/1000\n", - "13/13 [==============================] - 0s 667us/step - loss: 1.0016 - mae: 0.6740\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.0635 - mae: 0.7130\n", "Epoch 714/1000\n", - "13/13 [==============================] - 0s 679us/step - loss: 0.9758 - mae: 0.6951\n", + "13/13 [==============================] - 0s 655us/step - loss: 1.0392 - mae: 0.6702\n", "Epoch 715/1000\n", - "13/13 [==============================] - 0s 688us/step - loss: 0.9185 - mae: 0.5984\n", + "13/13 [==============================] - 0s 658us/step - loss: 1.0297 - mae: 0.7458\n", "Epoch 716/1000\n", - "13/13 [==============================] - 0s 678us/step - loss: 0.9343 - mae: 0.6118\n", + "13/13 [==============================] - 0s 660us/step - loss: 1.0366 - mae: 0.7479\n", "Epoch 717/1000\n", - "13/13 [==============================] - 0s 664us/step - loss: 0.9254 - mae: 0.6560\n", + "13/13 [==============================] - 0s 658us/step - loss: 0.9860 - mae: 0.7133\n", "Epoch 718/1000\n", - "13/13 [==============================] - 0s 676us/step - loss: 0.9446 - mae: 0.6687\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.0933 - mae: 0.7147\n", "Epoch 719/1000\n", - "13/13 [==============================] - 0s 679us/step - loss: 0.9086 - mae: 0.6782\n", + "13/13 [==============================] - 0s 666us/step - loss: 1.0559 - mae: 0.7218\n", "Epoch 720/1000\n", - "13/13 [==============================] - 0s 673us/step - loss: 0.9473 - mae: 0.6471\n", + "13/13 [==============================] - 0s 656us/step - loss: 0.9884 - mae: 0.6924\n", "Epoch 721/1000\n", - "13/13 [==============================] - 0s 660us/step - loss: 0.9186 - mae: 0.6327\n", + "13/13 [==============================] - 0s 669us/step - loss: 1.0044 - mae: 0.7287\n", "Epoch 722/1000\n", - "13/13 [==============================] - 0s 666us/step - loss: 0.9395 - mae: 0.5917\n", + "13/13 [==============================] - 0s 665us/step - loss: 1.0178 - mae: 0.7157\n", "Epoch 723/1000\n", - "13/13 [==============================] - 0s 657us/step - loss: 0.9441 - mae: 0.6698\n", + "13/13 [==============================] - 0s 649us/step - loss: 1.0581 - mae: 0.7197\n", "Epoch 724/1000\n", - "13/13 [==============================] - 0s 662us/step - loss: 0.9252 - mae: 0.5886\n", + "13/13 [==============================] - 0s 658us/step - loss: 0.9965 - mae: 0.7405\n", "Epoch 725/1000\n", - "13/13 [==============================] - 0s 681us/step - loss: 0.8959 - mae: 0.6606\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.0419 - mae: 0.7135\n", "Epoch 726/1000\n", - "13/13 [==============================] - 0s 691us/step - loss: 0.9295 - mae: 0.7067\n", + "13/13 [==============================] - 0s 649us/step - loss: 0.9924 - mae: 0.7294\n", "Epoch 727/1000\n", - "13/13 [==============================] - 0s 674us/step - loss: 0.9559 - mae: 0.6335\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.0423 - mae: 0.7130\n", "Epoch 728/1000\n", - "13/13 [==============================] - 0s 680us/step - loss: 0.8978 - mae: 0.6123\n", + "13/13 [==============================] - 0s 657us/step - loss: 0.9892 - mae: 0.7199\n", "Epoch 729/1000\n", - "13/13 [==============================] - 0s 669us/step - loss: 0.9529 - mae: 0.6525\n", + "13/13 [==============================] - 0s 668us/step - loss: 1.0784 - mae: 0.7584\n", "Epoch 730/1000\n", - "13/13 [==============================] - 0s 663us/step - loss: 0.9286 - mae: 0.6361\n", + "13/13 [==============================] - 0s 656us/step - loss: 1.0175 - mae: 0.6990\n", "Epoch 731/1000\n", - "13/13 [==============================] - 0s 667us/step - loss: 0.9408 - mae: 0.6527\n", + "13/13 [==============================] - 0s 656us/step - loss: 0.9787 - mae: 0.7359\n", "Epoch 732/1000\n", - "13/13 [==============================] - 0s 686us/step - loss: 1.0436 - mae: 0.6497\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.0701 - mae: 0.6496\n", "Epoch 733/1000\n", - "13/13 [==============================] - 0s 685us/step - loss: 0.8778 - mae: 0.6822\n", + "13/13 [==============================] - 0s 648us/step - loss: 1.0422 - mae: 0.7172\n", "Epoch 734/1000\n", - "13/13 [==============================] - 0s 684us/step - loss: 0.8845 - mae: 0.5915\n", + "13/13 [==============================] - 0s 659us/step - loss: 0.9569 - mae: 0.7421\n", "Epoch 735/1000\n", - "13/13 [==============================] - 0s 683us/step - loss: 0.9345 - mae: 0.6229\n", + "13/13 [==============================] - 0s 671us/step - loss: 1.0346 - mae: 0.7424\n", "Epoch 736/1000\n", - "13/13 [==============================] - 0s 679us/step - loss: 0.9239 - mae: 0.6617\n", + "13/13 [==============================] - 0s 669us/step - loss: 0.9783 - mae: 0.7175\n", "Epoch 737/1000\n", - "13/13 [==============================] - 0s 676us/step - loss: 0.8933 - mae: 0.6105\n", + "13/13 [==============================] - 0s 664us/step - loss: 0.9450 - mae: 0.6936\n", "Epoch 738/1000\n", - "13/13 [==============================] - 0s 741us/step - loss: 0.9027 - mae: 0.6738\n", + "13/13 [==============================] - 0s 654us/step - loss: 1.0079 - mae: 0.6792\n", "Epoch 739/1000\n", - "13/13 [==============================] - 0s 676us/step - loss: 0.8509 - mae: 0.6187\n", + "13/13 [==============================] - 0s 670us/step - loss: 0.9729 - mae: 0.7018\n", "Epoch 740/1000\n", - "13/13 [==============================] - 0s 670us/step - loss: 1.0304 - mae: 0.6125\n", + "13/13 [==============================] - 0s 646us/step - loss: 0.9219 - mae: 0.6731\n", "Epoch 741/1000\n", - "13/13 [==============================] - 0s 676us/step - loss: 0.8714 - mae: 0.6493\n", + "13/13 [==============================] - 0s 669us/step - loss: 0.9469 - mae: 0.6546\n", "Epoch 742/1000\n", - "13/13 [==============================] - 0s 673us/step - loss: 0.9233 - mae: 0.6406\n", + "13/13 [==============================] - 0s 654us/step - loss: 1.0558 - mae: 0.7528\n", "Epoch 743/1000\n", - "13/13 [==============================] - 0s 671us/step - loss: 0.8425 - mae: 0.6198\n", + "13/13 [==============================] - 0s 658us/step - loss: 0.9605 - mae: 0.7179\n", "Epoch 744/1000\n", - "13/13 [==============================] - 0s 668us/step - loss: 0.9021 - mae: 0.6752\n", + "13/13 [==============================] - 0s 660us/step - loss: 0.9563 - mae: 0.6468\n", "Epoch 745/1000\n", - "13/13 [==============================] - 0s 668us/step - loss: 0.9291 - mae: 0.6491\n", + "13/13 [==============================] - 0s 668us/step - loss: 0.9742 - mae: 0.7279\n", "Epoch 746/1000\n", - "13/13 [==============================] - 0s 662us/step - loss: 0.9153 - mae: 0.6835\n", + "13/13 [==============================] - 0s 659us/step - loss: 0.9333 - mae: 0.6756\n", "Epoch 747/1000\n", - "13/13 [==============================] - 0s 656us/step - loss: 0.9909 - mae: 0.6985\n", + "13/13 [==============================] - 0s 666us/step - loss: 1.0925 - mae: 0.7285\n", "Epoch 748/1000\n", - "13/13 [==============================] - 0s 669us/step - loss: 0.9020 - mae: 0.6599\n", + "13/13 [==============================] - 0s 654us/step - loss: 0.9920 - mae: 0.6987\n", "Epoch 749/1000\n", - "13/13 [==============================] - 0s 666us/step - loss: 0.9416 - mae: 0.7135\n", + "13/13 [==============================] - 0s 656us/step - loss: 0.9643 - mae: 0.7189\n", "Epoch 750/1000\n", - "13/13 [==============================] - 0s 673us/step - loss: 0.9239 - mae: 0.6534\n", + "13/13 [==============================] - 0s 669us/step - loss: 0.9781 - mae: 0.6956\n", "Epoch 751/1000\n", - "13/13 [==============================] - 0s 688us/step - loss: 0.9028 - mae: 0.6799\n", + "13/13 [==============================] - 0s 670us/step - loss: 0.9125 - mae: 0.6913\n", "Epoch 752/1000\n", - "13/13 [==============================] - 0s 668us/step - loss: 0.9319 - mae: 0.7216\n", + "13/13 [==============================] - 0s 660us/step - loss: 0.9615 - mae: 0.6666\n", "Epoch 753/1000\n", - "13/13 [==============================] - 0s 683us/step - loss: 0.9617 - mae: 0.6958\n", + "13/13 [==============================] - 0s 662us/step - loss: 0.9754 - mae: 0.7654\n", "Epoch 754/1000\n", - "13/13 [==============================] - 0s 675us/step - loss: 0.8968 - mae: 0.6227\n", + "13/13 [==============================] - 0s 659us/step - loss: 0.9794 - mae: 0.6844\n", "Epoch 755/1000\n", - "13/13 [==============================] - 0s 683us/step - loss: 0.8889 - mae: 0.6964\n", + "13/13 [==============================] - 0s 664us/step - loss: 0.9755 - mae: 0.7174\n", "Epoch 756/1000\n", - "13/13 [==============================] - 0s 666us/step - loss: 0.8900 - mae: 0.6236\n", + "13/13 [==============================] - 0s 678us/step - loss: 1.0135 - mae: 0.7266\n", "Epoch 757/1000\n", - "13/13 [==============================] - 0s 680us/step - loss: 0.9024 - mae: 0.6001\n", + "13/13 [==============================] - 0s 660us/step - loss: 1.0061 - mae: 0.6897\n", "Epoch 758/1000\n", - "13/13 [==============================] - 0s 669us/step - loss: 0.9133 - mae: 0.6772\n", + "13/13 [==============================] - 0s 659us/step - loss: 0.9248 - mae: 0.6830\n", "Epoch 759/1000\n", - "13/13 [==============================] - 0s 664us/step - loss: 0.9304 - mae: 0.6316\n", + "13/13 [==============================] - 0s 657us/step - loss: 0.9529 - mae: 0.6756\n", "Epoch 760/1000\n", - "13/13 [==============================] - 0s 682us/step - loss: 0.9067 - mae: 0.6439\n", + "13/13 [==============================] - 0s 656us/step - loss: 0.9113 - mae: 0.6774\n", "Epoch 761/1000\n", - "13/13 [==============================] - 0s 684us/step - loss: 0.8976 - mae: 0.6400\n", + "13/13 [==============================] - 0s 681us/step - loss: 1.0127 - mae: 0.7380\n", "Epoch 762/1000\n", - "13/13 [==============================] - 0s 662us/step - loss: 0.8753 - mae: 0.6192\n", + "13/13 [==============================] - 0s 662us/step - loss: 1.0125 - mae: 0.6969\n", "Epoch 763/1000\n", - "13/13 [==============================] - 0s 673us/step - loss: 0.9955 - mae: 0.6582\n", + "13/13 [==============================] - 0s 660us/step - loss: 1.0251 - mae: 0.7099\n", "Epoch 764/1000\n", - "13/13 [==============================] - 0s 675us/step - loss: 0.8828 - mae: 0.6010\n", + "13/13 [==============================] - 0s 656us/step - loss: 0.9605 - mae: 0.6911\n", "Epoch 765/1000\n", - "13/13 [==============================] - 0s 678us/step - loss: 0.9489 - mae: 0.7005\n", + "13/13 [==============================] - 0s 662us/step - loss: 0.9387 - mae: 0.6895\n", "Epoch 766/1000\n", - "13/13 [==============================] - 0s 670us/step - loss: 0.8944 - mae: 0.6641\n", + "13/13 [==============================] - 0s 665us/step - loss: 0.9324 - mae: 0.6465\n", "Epoch 767/1000\n", - "13/13 [==============================] - 0s 670us/step - loss: 1.0178 - mae: 0.6370\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.1968 - mae: 0.7585\n", "Epoch 768/1000\n", - "13/13 [==============================] - 0s 659us/step - loss: 0.8267 - mae: 0.6581\n", + "13/13 [==============================] - 0s 642us/step - loss: 1.0158 - mae: 0.6965\n", "Epoch 769/1000\n", - "13/13 [==============================] - 0s 695us/step - loss: 0.9696 - mae: 0.6854\n", + "13/13 [==============================] - 0s 653us/step - loss: 1.0066 - mae: 0.7402\n", "Epoch 770/1000\n", - "13/13 [==============================] - 0s 663us/step - loss: 0.8792 - mae: 0.6254\n", + "13/13 [==============================] - 0s 664us/step - loss: 1.0335 - mae: 0.7283\n", "Epoch 771/1000\n", - "13/13 [==============================] - 0s 669us/step - loss: 0.9133 - mae: 0.6787\n", + "13/13 [==============================] - 0s 655us/step - loss: 0.9695 - mae: 0.6990\n", "Epoch 772/1000\n", - "13/13 [==============================] - 0s 664us/step - loss: 0.9060 - mae: 0.6502\n", + "13/13 [==============================] - 0s 668us/step - loss: 1.0247 - mae: 0.7078\n", "Epoch 773/1000\n", - "13/13 [==============================] - 0s 677us/step - loss: 0.8527 - mae: 0.6295\n", + "13/13 [==============================] - 0s 661us/step - loss: 0.9131 - mae: 0.6792\n", "Epoch 774/1000\n", - "13/13 [==============================] - 0s 673us/step - loss: 0.9766 - mae: 0.6517\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.9767 - mae: 0.7322\n", "Epoch 775/1000\n", - "13/13 [==============================] - 0s 673us/step - loss: 0.9295 - mae: 0.6347\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.0418 - mae: 0.7703\n", "Epoch 776/1000\n", - "13/13 [==============================] - 0s 663us/step - loss: 0.9095 - mae: 0.6767\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.9286 - mae: 0.6574\n", "Epoch 777/1000\n", - "13/13 [==============================] - 0s 676us/step - loss: 0.8664 - mae: 0.6247\n", + "13/13 [==============================] - 0s 660us/step - loss: 0.9508 - mae: 0.6754\n", "Epoch 778/1000\n", - "13/13 [==============================] - 0s 673us/step - loss: 1.0412 - mae: 0.6591\n", + "13/13 [==============================] - 0s 665us/step - loss: 0.9646 - mae: 0.7019\n", "Epoch 779/1000\n", - "13/13 [==============================] - 0s 680us/step - loss: 0.9604 - mae: 0.6753\n", + "13/13 [==============================] - 0s 665us/step - loss: 0.9293 - mae: 0.6411\n", "Epoch 780/1000\n", - "13/13 [==============================] - 0s 678us/step - loss: 0.9804 - mae: 0.7015\n", + "13/13 [==============================] - 0s 658us/step - loss: 0.9545 - mae: 0.6692\n", "Epoch 781/1000\n", - "13/13 [==============================] - 0s 697us/step - loss: 0.9300 - mae: 0.6117\n", + "13/13 [==============================] - 0s 665us/step - loss: 0.9656 - mae: 0.6348\n", "Epoch 782/1000\n", - "13/13 [==============================] - 0s 672us/step - loss: 0.9188 - mae: 0.6820\n", + "13/13 [==============================] - 0s 651us/step - loss: 0.9440 - mae: 0.6980\n", "Epoch 783/1000\n", - "13/13 [==============================] - 0s 691us/step - loss: 0.9256 - mae: 0.6728\n", + "13/13 [==============================] - 0s 722us/step - loss: 1.0399 - mae: 0.7014\n", "Epoch 784/1000\n", - "13/13 [==============================] - 0s 674us/step - loss: 0.9359 - mae: 0.6778\n", + "13/13 [==============================] - 0s 653us/step - loss: 0.9433 - mae: 0.6659\n", "Epoch 785/1000\n", - "13/13 [==============================] - 0s 679us/step - loss: 0.9449 - mae: 0.6697\n", + "13/13 [==============================] - 0s 648us/step - loss: 1.0913 - mae: 0.6710\n", "Epoch 786/1000\n", - "13/13 [==============================] - 0s 684us/step - loss: 0.9057 - mae: 0.6455\n", + "13/13 [==============================] - 0s 664us/step - loss: 0.9283 - mae: 0.6510\n", "Epoch 787/1000\n", - "13/13 [==============================] - 0s 680us/step - loss: 0.9747 - mae: 0.6478\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.0532 - mae: 0.7504\n", "Epoch 788/1000\n", - "13/13 [==============================] - 0s 664us/step - loss: 0.8409 - mae: 0.6265\n", + "13/13 [==============================] - 0s 667us/step - loss: 0.9145 - mae: 0.7010\n", "Epoch 789/1000\n", - "13/13 [==============================] - 0s 660us/step - loss: 0.9122 - mae: 0.6545\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.9135 - mae: 0.6499\n", "Epoch 790/1000\n", - "13/13 [==============================] - 0s 668us/step - loss: 0.9003 - mae: 0.6532\n", + "13/13 [==============================] - 0s 654us/step - loss: 0.9830 - mae: 0.6467\n", "Epoch 791/1000\n", - "13/13 [==============================] - 0s 683us/step - loss: 1.0775 - mae: 0.6866\n", + "13/13 [==============================] - 0s 659us/step - loss: 1.0437 - mae: 0.7265\n", "Epoch 792/1000\n", - "13/13 [==============================] - 0s 717us/step - loss: 0.9941 - mae: 0.6462\n", + "13/13 [==============================] - 0s 657us/step - loss: 0.9307 - mae: 0.7124\n", "Epoch 793/1000\n", - "13/13 [==============================] - 0s 669us/step - loss: 0.9286 - mae: 0.6654\n", + "13/13 [==============================] - 0s 666us/step - loss: 0.9708 - mae: 0.6406\n", "Epoch 794/1000\n", - "13/13 [==============================] - 0s 666us/step - loss: 0.8916 - mae: 0.6707\n", + "13/13 [==============================] - 0s 666us/step - loss: 1.0299 - mae: 0.6825\n", "Epoch 795/1000\n", - "13/13 [==============================] - 0s 656us/step - loss: 0.9097 - mae: 0.6749\n", + "13/13 [==============================] - 0s 658us/step - loss: 0.9187 - mae: 0.6414\n", "Epoch 796/1000\n", - "13/13 [==============================] - 0s 671us/step - loss: 1.0268 - mae: 0.6411\n", + "13/13 [==============================] - 0s 667us/step - loss: 1.0448 - mae: 0.7198\n", "Epoch 797/1000\n", - "13/13 [==============================] - 0s 666us/step - loss: 0.8840 - mae: 0.6636\n", + "13/13 [==============================] - 0s 664us/step - loss: 1.0344 - mae: 0.7029\n", "Epoch 798/1000\n", - "13/13 [==============================] - 0s 676us/step - loss: 0.9841 - mae: 0.6566\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.9465 - mae: 0.6565\n", "Epoch 799/1000\n", - "13/13 [==============================] - 0s 696us/step - loss: 0.9692 - mae: 0.7151\n", + "13/13 [==============================] - 0s 653us/step - loss: 0.9454 - mae: 0.6895\n", "Epoch 800/1000\n", - "13/13 [==============================] - 0s 715us/step - loss: 0.9564 - mae: 0.6393\n", + "13/13 [==============================] - 0s 666us/step - loss: 0.9180 - mae: 0.6743\n", "Epoch 801/1000\n", - "13/13 [==============================] - 0s 666us/step - loss: 0.9850 - mae: 0.6649\n", + "13/13 [==============================] - 0s 669us/step - loss: 0.9396 - mae: 0.7266\n", "Epoch 802/1000\n", - "13/13 [==============================] - 0s 692us/step - loss: 0.8547 - mae: 0.6212\n", + "13/13 [==============================] - 0s 665us/step - loss: 0.9064 - mae: 0.6785\n", "Epoch 803/1000\n", - "13/13 [==============================] - 0s 669us/step - loss: 0.9253 - mae: 0.6064\n", + "13/13 [==============================] - 0s 645us/step - loss: 0.9260 - mae: 0.6570\n", "Epoch 804/1000\n", - "13/13 [==============================] - 0s 674us/step - loss: 0.8854 - mae: 0.6245\n", + "13/13 [==============================] - 0s 660us/step - loss: 1.0186 - mae: 0.7197\n", "Epoch 805/1000\n", - "13/13 [==============================] - 0s 681us/step - loss: 0.8614 - mae: 0.6243\n", + "13/13 [==============================] - 0s 665us/step - loss: 0.9757 - mae: 0.6802\n", "Epoch 806/1000\n", - "13/13 [==============================] - 0s 684us/step - loss: 0.9469 - mae: 0.6475\n", + "13/13 [==============================] - 0s 662us/step - loss: 0.9850 - mae: 0.7052\n", "Epoch 807/1000\n", - "13/13 [==============================] - 0s 725us/step - loss: 1.0488 - mae: 0.6196\n", + "13/13 [==============================] - 0s 660us/step - loss: 0.9409 - mae: 0.7147\n", "Epoch 808/1000\n", - "13/13 [==============================] - 0s 656us/step - loss: 0.9014 - mae: 0.6560\n", + "13/13 [==============================] - 0s 669us/step - loss: 0.9199 - mae: 0.7042\n", "Epoch 809/1000\n", - "13/13 [==============================] - 0s 668us/step - loss: 0.8938 - mae: 0.6216\n", + "13/13 [==============================] - 0s 654us/step - loss: 0.9340 - mae: 0.6678\n", "Epoch 810/1000\n", - "13/13 [==============================] - 0s 668us/step - loss: 0.9498 - mae: 0.6113\n", + "13/13 [==============================] - 0s 660us/step - loss: 0.9757 - mae: 0.7496\n", "Epoch 811/1000\n", - "13/13 [==============================] - 0s 663us/step - loss: 0.9880 - mae: 0.6630\n", + "13/13 [==============================] - 0s 659us/step - loss: 0.9114 - mae: 0.6714\n", "Epoch 812/1000\n", - "13/13 [==============================] - 0s 664us/step - loss: 0.8998 - mae: 0.6531\n", + "13/13 [==============================] - 0s 658us/step - loss: 0.9120 - mae: 0.6685\n", "Epoch 813/1000\n", - "13/13 [==============================] - 0s 676us/step - loss: 1.0513 - mae: 0.6925\n", + "13/13 [==============================] - 0s 660us/step - loss: 0.9924 - mae: 0.7149\n", "Epoch 814/1000\n", - "13/13 [==============================] - 0s 667us/step - loss: 0.8766 - mae: 0.6398\n", + "13/13 [==============================] - 0s 662us/step - loss: 0.9464 - mae: 0.6453\n", "Epoch 815/1000\n", - "13/13 [==============================] - 0s 670us/step - loss: 0.8569 - mae: 0.6171\n", + "13/13 [==============================] - 0s 669us/step - loss: 0.8697 - mae: 0.7005\n", "Epoch 816/1000\n", - "13/13 [==============================] - 0s 674us/step - loss: 0.9315 - mae: 0.6102\n", + "13/13 [==============================] - 0s 666us/step - loss: 1.0321 - mae: 0.7055\n", "Epoch 817/1000\n", - "13/13 [==============================] - 0s 691us/step - loss: 0.9130 - mae: 0.6436\n", + "13/13 [==============================] - 0s 662us/step - loss: 0.9512 - mae: 0.6626\n", "Epoch 818/1000\n", - "13/13 [==============================] - 0s 710us/step - loss: 0.9475 - mae: 0.6180\n", + "13/13 [==============================] - 0s 659us/step - loss: 0.9247 - mae: 0.6874\n", "Epoch 819/1000\n", - "13/13 [==============================] - 0s 660us/step - loss: 0.9523 - mae: 0.6573\n", + "13/13 [==============================] - 0s 655us/step - loss: 0.9639 - mae: 0.7135\n", "Epoch 820/1000\n", - "13/13 [==============================] - 0s 662us/step - loss: 0.8863 - mae: 0.6313\n", + "13/13 [==============================] - 0s 664us/step - loss: 0.9440 - mae: 0.6025\n", "Epoch 821/1000\n", - "13/13 [==============================] - 0s 671us/step - loss: 0.8845 - mae: 0.6719\n", + "13/13 [==============================] - 0s 678us/step - loss: 0.9245 - mae: 0.6734\n", "Epoch 822/1000\n", - "13/13 [==============================] - 0s 684us/step - loss: 0.9260 - mae: 0.7047\n", + "13/13 [==============================] - 0s 652us/step - loss: 0.9395 - mae: 0.6603\n", "Epoch 823/1000\n", - "13/13 [==============================] - 0s 657us/step - loss: 0.9099 - mae: 0.6496\n", + "13/13 [==============================] - 0s 651us/step - loss: 0.9958 - mae: 0.7147\n", "Epoch 824/1000\n", - "13/13 [==============================] - 0s 671us/step - loss: 0.8912 - mae: 0.6114\n", + "13/13 [==============================] - 0s 676us/step - loss: 0.9026 - mae: 0.6576\n", "Epoch 825/1000\n", - "13/13 [==============================] - 0s 732us/step - loss: 0.9043 - mae: 0.6479\n", + "13/13 [==============================] - 0s 656us/step - loss: 0.9077 - mae: 0.6214\n", "Epoch 826/1000\n", - "13/13 [==============================] - 0s 662us/step - loss: 0.8948 - mae: 0.6582\n", + "13/13 [==============================] - 0s 672us/step - loss: 1.0171 - mae: 0.6912\n", "Epoch 827/1000\n", - "13/13 [==============================] - 0s 684us/step - loss: 0.8979 - mae: 0.6602\n", + "13/13 [==============================] - 0s 669us/step - loss: 0.8352 - mae: 0.6057\n", "Epoch 828/1000\n", - "13/13 [==============================] - 0s 684us/step - loss: 0.8104 - mae: 0.5786\n", + "13/13 [==============================] - 0s 658us/step - loss: 1.0318 - mae: 0.7117\n", "Epoch 829/1000\n", - "13/13 [==============================] - 0s 676us/step - loss: 0.8845 - mae: 0.6275\n", + "13/13 [==============================] - 0s 663us/step - loss: 1.0118 - mae: 0.6844\n", "Epoch 830/1000\n", - "13/13 [==============================] - 0s 684us/step - loss: 0.9337 - mae: 0.6312\n", + "13/13 [==============================] - 0s 661us/step - loss: 1.0261 - mae: 0.7385\n", "Epoch 831/1000\n", - "13/13 [==============================] - 0s 679us/step - loss: 0.8500 - mae: 0.6480\n", + "13/13 [==============================] - 0s 674us/step - loss: 0.9766 - mae: 0.6936\n", "Epoch 832/1000\n", - "13/13 [==============================] - 0s 668us/step - loss: 1.0051 - mae: 0.6679\n", + "13/13 [==============================] - 0s 660us/step - loss: 0.9483 - mae: 0.7029\n", "Epoch 833/1000\n", - "13/13 [==============================] - 0s 666us/step - loss: 0.8852 - mae: 0.6053\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.9197 - mae: 0.6459\n", "Epoch 834/1000\n", - "13/13 [==============================] - 0s 671us/step - loss: 0.8914 - mae: 0.6827\n", + "13/13 [==============================] - 0s 647us/step - loss: 0.9428 - mae: 0.6486\n", "Epoch 835/1000\n", - "13/13 [==============================] - 0s 673us/step - loss: 0.9534 - mae: 0.6319\n", + "13/13 [==============================] - 0s 659us/step - loss: 0.9148 - mae: 0.6910\n", "Epoch 836/1000\n", - "13/13 [==============================] - 0s 667us/step - loss: 0.8878 - mae: 0.6350\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.9045 - mae: 0.6901\n", "Epoch 837/1000\n", - "13/13 [==============================] - 0s 668us/step - loss: 0.8955 - mae: 0.6626\n", + "13/13 [==============================] - 0s 655us/step - loss: 0.9095 - mae: 0.6403\n", "Epoch 838/1000\n", - "13/13 [==============================] - 0s 678us/step - loss: 0.9222 - mae: 0.5960\n", + "13/13 [==============================] - 0s 662us/step - loss: 0.9502 - mae: 0.6671\n", "Epoch 839/1000\n", - "13/13 [==============================] - 0s 689us/step - loss: 0.8924 - mae: 0.6211\n", + "13/13 [==============================] - 0s 676us/step - loss: 0.8911 - mae: 0.6266\n", "Epoch 840/1000\n", - "13/13 [==============================] - 0s 665us/step - loss: 0.9759 - mae: 0.6827\n", + "13/13 [==============================] - 0s 665us/step - loss: 0.9924 - mae: 0.7008\n", "Epoch 841/1000\n", - "13/13 [==============================] - 0s 676us/step - loss: 0.8711 - mae: 0.6343\n", + "13/13 [==============================] - 0s 664us/step - loss: 1.0126 - mae: 0.7439\n", "Epoch 842/1000\n", - "13/13 [==============================] - 0s 796us/step - loss: 0.9586 - mae: 0.6765\n", + "13/13 [==============================] - 0s 664us/step - loss: 0.9367 - mae: 0.6595\n", "Epoch 843/1000\n", - "13/13 [==============================] - 0s 681us/step - loss: 0.9813 - mae: 0.6791\n", + "13/13 [==============================] - 0s 662us/step - loss: 0.9416 - mae: 0.6708\n", "Epoch 844/1000\n", - "13/13 [==============================] - 0s 678us/step - loss: 0.8637 - mae: 0.6642\n", + "13/13 [==============================] - 0s 672us/step - loss: 1.0205 - mae: 0.6314\n", "Epoch 845/1000\n", - "13/13 [==============================] - 0s 689us/step - loss: 0.8300 - mae: 0.6041\n", + "13/13 [==============================] - 0s 665us/step - loss: 1.0269 - mae: 0.6796\n", "Epoch 846/1000\n", - "13/13 [==============================] - 0s 683us/step - loss: 0.9238 - mae: 0.6545\n", + "13/13 [==============================] - 0s 654us/step - loss: 0.8666 - mae: 0.7007\n", "Epoch 847/1000\n", - "13/13 [==============================] - 0s 680us/step - loss: 0.8924 - mae: 0.6218\n", + "13/13 [==============================] - 0s 666us/step - loss: 0.9269 - mae: 0.6818\n", "Epoch 848/1000\n", - "13/13 [==============================] - 0s 661us/step - loss: 0.9457 - mae: 0.5951\n", + "13/13 [==============================] - 0s 671us/step - loss: 0.8978 - mae: 0.6251\n", "Epoch 849/1000\n", - "13/13 [==============================] - 0s 673us/step - loss: 0.8536 - mae: 0.5993\n", + "13/13 [==============================] - 0s 655us/step - loss: 0.8769 - mae: 0.5902\n", "Epoch 850/1000\n", - "13/13 [==============================] - 0s 660us/step - loss: 0.9131 - mae: 0.6311\n", + "13/13 [==============================] - 0s 655us/step - loss: 0.9592 - mae: 0.6945\n", "Epoch 851/1000\n", - "13/13 [==============================] - 0s 666us/step - loss: 0.9190 - mae: 0.6338\n", + "13/13 [==============================] - 0s 664us/step - loss: 0.9339 - mae: 0.6682\n", "Epoch 852/1000\n", - "13/13 [==============================] - 0s 665us/step - loss: 0.8840 - mae: 0.6877\n", + "13/13 [==============================] - 0s 657us/step - loss: 0.9724 - mae: 0.6296\n", "Epoch 853/1000\n", - "13/13 [==============================] - 0s 668us/step - loss: 0.8921 - mae: 0.6092\n", + "13/13 [==============================] - 0s 656us/step - loss: 0.9593 - mae: 0.6655\n", "Epoch 854/1000\n", - "13/13 [==============================] - 0s 665us/step - loss: 0.9118 - mae: 0.6328\n", + "13/13 [==============================] - 0s 666us/step - loss: 0.8747 - mae: 0.6316\n", "Epoch 855/1000\n", - "13/13 [==============================] - 0s 672us/step - loss: 0.9459 - mae: 0.6718\n", + "13/13 [==============================] - 0s 662us/step - loss: 0.9880 - mae: 0.6939\n", "Epoch 856/1000\n", - "13/13 [==============================] - 0s 671us/step - loss: 0.9559 - mae: 0.6242\n", + "13/13 [==============================] - 0s 671us/step - loss: 1.0268 - mae: 0.7116\n", "Epoch 857/1000\n", - "13/13 [==============================] - 0s 666us/step - loss: 0.9788 - mae: 0.6702\n", + "13/13 [==============================] - 0s 671us/step - loss: 0.8769 - mae: 0.6241\n", "Epoch 858/1000\n", - "13/13 [==============================] - 0s 667us/step - loss: 0.8521 - mae: 0.6661\n", + "13/13 [==============================] - 0s 667us/step - loss: 0.9900 - mae: 0.6991\n", "Epoch 859/1000\n", - "13/13 [==============================] - 0s 670us/step - loss: 0.8340 - mae: 0.6384\n", + "13/13 [==============================] - 0s 669us/step - loss: 1.0271 - mae: 0.7121\n", "Epoch 860/1000\n", - "13/13 [==============================] - 0s 666us/step - loss: 0.8642 - mae: 0.5715\n", + "13/13 [==============================] - 0s 672us/step - loss: 0.9155 - mae: 0.6876\n", "Epoch 861/1000\n", - "13/13 [==============================] - 0s 665us/step - loss: 0.9054 - mae: 0.6508\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.9768 - mae: 0.6430\n", "Epoch 862/1000\n", - "13/13 [==============================] - 0s 665us/step - loss: 0.9357 - mae: 0.6525\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.9640 - mae: 0.6926\n", "Epoch 863/1000\n", - "13/13 [==============================] - 0s 669us/step - loss: 1.0083 - mae: 0.7038\n", + "13/13 [==============================] - 0s 665us/step - loss: 0.9618 - mae: 0.6710\n", "Epoch 864/1000\n", - "13/13 [==============================] - 0s 671us/step - loss: 0.8687 - mae: 0.6307\n", + "13/13 [==============================] - 0s 657us/step - loss: 0.9456 - mae: 0.6199\n", "Epoch 865/1000\n", - "13/13 [==============================] - 0s 667us/step - loss: 0.9458 - mae: 0.6090\n", + "13/13 [==============================] - 0s 664us/step - loss: 0.8909 - mae: 0.6352\n", "Epoch 866/1000\n", - "13/13 [==============================] - 0s 688us/step - loss: 1.0141 - mae: 0.6121\n", + "13/13 [==============================] - 0s 668us/step - loss: 0.9189 - mae: 0.6475\n", "Epoch 867/1000\n", - "13/13 [==============================] - 0s 681us/step - loss: 0.9600 - mae: 0.6177\n", + "13/13 [==============================] - 0s 653us/step - loss: 0.8935 - mae: 0.6589\n", "Epoch 868/1000\n", - "13/13 [==============================] - 0s 688us/step - loss: 0.8777 - mae: 0.6182\n", + "13/13 [==============================] - 0s 672us/step - loss: 0.9480 - mae: 0.6842\n", "Epoch 869/1000\n", - "13/13 [==============================] - 0s 682us/step - loss: 0.8430 - mae: 0.6349\n", + "13/13 [==============================] - 0s 670us/step - loss: 0.8989 - mae: 0.6447\n", "Epoch 870/1000\n", - "13/13 [==============================] - 0s 681us/step - loss: 0.9009 - mae: 0.6421\n", + "13/13 [==============================] - 0s 660us/step - loss: 0.8987 - mae: 0.6638\n", "Epoch 871/1000\n", - "13/13 [==============================] - 0s 669us/step - loss: 0.8772 - mae: 0.5898\n", + "13/13 [==============================] - 0s 664us/step - loss: 0.9818 - mae: 0.6477\n", "Epoch 872/1000\n", - "13/13 [==============================] - 0s 662us/step - loss: 0.9176 - mae: 0.6549\n", + "13/13 [==============================] - 0s 659us/step - loss: 0.9296 - mae: 0.6520\n", "Epoch 873/1000\n", - "13/13 [==============================] - 0s 676us/step - loss: 0.9263 - mae: 0.6419\n", + "13/13 [==============================] - 0s 662us/step - loss: 0.8812 - mae: 0.6922\n", "Epoch 874/1000\n", - "13/13 [==============================] - 0s 673us/step - loss: 0.9309 - mae: 0.6356\n", + "13/13 [==============================] - 0s 652us/step - loss: 0.9858 - mae: 0.7136\n", "Epoch 875/1000\n", - "13/13 [==============================] - 0s 681us/step - loss: 1.0051 - mae: 0.6685\n", + "13/13 [==============================] - 0s 658us/step - loss: 0.9854 - mae: 0.7586\n", "Epoch 876/1000\n", - "13/13 [==============================] - 0s 678us/step - loss: 0.8497 - mae: 0.6141\n", + "13/13 [==============================] - 0s 671us/step - loss: 0.8779 - mae: 0.5943\n", "Epoch 877/1000\n", - "13/13 [==============================] - 0s 684us/step - loss: 0.8649 - mae: 0.6018\n", + "13/13 [==============================] - 0s 675us/step - loss: 0.9296 - mae: 0.6983\n", "Epoch 878/1000\n", - "13/13 [==============================] - 0s 668us/step - loss: 0.8448 - mae: 0.6197\n", + "13/13 [==============================] - 0s 674us/step - loss: 1.0063 - mae: 0.6803\n", "Epoch 879/1000\n", - "13/13 [==============================] - 0s 667us/step - loss: 0.8724 - mae: 0.6292\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.8684 - mae: 0.6721\n", "Epoch 880/1000\n", - "13/13 [==============================] - 0s 678us/step - loss: 0.9893 - mae: 0.6056\n", + "13/13 [==============================] - 0s 669us/step - loss: 0.8861 - mae: 0.6465\n", "Epoch 881/1000\n", - "13/13 [==============================] - 0s 670us/step - loss: 0.9559 - mae: 0.6803\n", + "13/13 [==============================] - 0s 656us/step - loss: 0.9542 - mae: 0.6254\n", "Epoch 882/1000\n", - "13/13 [==============================] - 0s 673us/step - loss: 1.0274 - mae: 0.6735\n", + "13/13 [==============================] - 0s 687us/step - loss: 0.9602 - mae: 0.6530\n", "Epoch 883/1000\n", - "13/13 [==============================] - 0s 676us/step - loss: 1.0428 - mae: 0.6520\n", + "13/13 [==============================] - 0s 662us/step - loss: 0.9086 - mae: 0.5864\n", "Epoch 884/1000\n", - "13/13 [==============================] - 0s 664us/step - loss: 0.8945 - mae: 0.6120\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.9079 - mae: 0.6680\n", "Epoch 885/1000\n", - "13/13 [==============================] - 0s 664us/step - loss: 0.8896 - mae: 0.6088\n", + "13/13 [==============================] - 0s 673us/step - loss: 0.9290 - mae: 0.6208\n", "Epoch 886/1000\n", - "13/13 [==============================] - 0s 673us/step - loss: 0.9693 - mae: 0.7024\n", + "13/13 [==============================] - 0s 667us/step - loss: 0.9010 - mae: 0.6613\n", "Epoch 887/1000\n", - "13/13 [==============================] - 0s 765us/step - loss: 0.9230 - mae: 0.6363\n", + "13/13 [==============================] - 0s 659us/step - loss: 0.9595 - mae: 0.6769\n", "Epoch 888/1000\n", - "13/13 [==============================] - 0s 741us/step - loss: 0.8127 - mae: 0.6065\n", + "13/13 [==============================] - 0s 661us/step - loss: 0.9148 - mae: 0.6079\n", "Epoch 889/1000\n", - "13/13 [==============================] - 0s 661us/step - loss: 0.9512 - mae: 0.6563\n", + "13/13 [==============================] - 0s 668us/step - loss: 0.8898 - mae: 0.6473\n", "Epoch 890/1000\n", - "13/13 [==============================] - 0s 663us/step - loss: 0.9387 - mae: 0.6269\n", + "13/13 [==============================] - 0s 674us/step - loss: 0.9006 - mae: 0.5990\n", "Epoch 891/1000\n", - "13/13 [==============================] - 0s 668us/step - loss: 0.8594 - mae: 0.6695\n", + "13/13 [==============================] - 0s 660us/step - loss: 1.0298 - mae: 0.6751\n", "Epoch 892/1000\n", - "13/13 [==============================] - 0s 669us/step - loss: 0.8414 - mae: 0.6119\n", + "13/13 [==============================] - 0s 664us/step - loss: 0.9461 - mae: 0.6289\n", "Epoch 893/1000\n", - "13/13 [==============================] - 0s 663us/step - loss: 0.9360 - mae: 0.6113\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.8961 - mae: 0.6747\n", "Epoch 894/1000\n", - "13/13 [==============================] - 0s 660us/step - loss: 0.9393 - mae: 0.6140\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.9947 - mae: 0.7113\n", "Epoch 895/1000\n", - "13/13 [==============================] - 0s 656us/step - loss: 0.9504 - mae: 0.6583\n", + "13/13 [==============================] - 0s 661us/step - loss: 0.9751 - mae: 0.6983\n", "Epoch 896/1000\n", - "13/13 [==============================] - 0s 661us/step - loss: 0.8640 - mae: 0.5813\n", + "13/13 [==============================] - 0s 657us/step - loss: 1.0238 - mae: 0.6393\n", "Epoch 897/1000\n", - "13/13 [==============================] - 0s 670us/step - loss: 0.9562 - mae: 0.6452\n", + "13/13 [==============================] - 0s 661us/step - loss: 1.0180 - mae: 0.6784\n", "Epoch 898/1000\n", - "13/13 [==============================] - 0s 661us/step - loss: 0.9107 - mae: 0.6424\n", + "13/13 [==============================] - 0s 661us/step - loss: 0.9328 - mae: 0.6268\n", "Epoch 899/1000\n", - "13/13 [==============================] - 0s 665us/step - loss: 0.9808 - mae: 0.6517\n", + "13/13 [==============================] - 0s 659us/step - loss: 0.9058 - mae: 0.6815\n", "Epoch 900/1000\n", - "13/13 [==============================] - 0s 673us/step - loss: 0.8329 - mae: 0.6417\n", + "13/13 [==============================] - 0s 656us/step - loss: 0.9367 - mae: 0.5773\n", "Epoch 901/1000\n", - "13/13 [==============================] - 0s 668us/step - loss: 0.8889 - mae: 0.5925\n", + "13/13 [==============================] - 0s 666us/step - loss: 0.9879 - mae: 0.6092\n", "Epoch 902/1000\n", - "13/13 [==============================] - 0s 664us/step - loss: 0.8329 - mae: 0.6586\n", + "13/13 [==============================] - 0s 666us/step - loss: 0.8808 - mae: 0.6552\n", "Epoch 903/1000\n", - "13/13 [==============================] - 0s 666us/step - loss: 0.9360 - mae: 0.6542\n", + "13/13 [==============================] - 0s 669us/step - loss: 0.9508 - mae: 0.6474\n", "Epoch 904/1000\n", - "13/13 [==============================] - 0s 669us/step - loss: 0.8557 - mae: 0.6316\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.8814 - mae: 0.6233\n", "Epoch 905/1000\n", - "13/13 [==============================] - 0s 662us/step - loss: 0.9280 - mae: 0.6233\n", + "13/13 [==============================] - 0s 656us/step - loss: 0.9677 - mae: 0.6604\n", "Epoch 906/1000\n", - "13/13 [==============================] - 0s 671us/step - loss: 0.9131 - mae: 0.5984\n", + "13/13 [==============================] - 0s 658us/step - loss: 0.9446 - mae: 0.6182\n", "Epoch 907/1000\n", - "13/13 [==============================] - 0s 680us/step - loss: 0.9027 - mae: 0.6364\n", + "13/13 [==============================] - 0s 654us/step - loss: 0.9086 - mae: 0.6228\n", "Epoch 908/1000\n", - "13/13 [==============================] - 0s 673us/step - loss: 0.9144 - mae: 0.6279\n", + "13/13 [==============================] - 0s 661us/step - loss: 0.8839 - mae: 0.6316\n", "Epoch 909/1000\n", - "13/13 [==============================] - 0s 678us/step - loss: 0.8567 - mae: 0.6319\n", + "13/13 [==============================] - 0s 660us/step - loss: 0.9470 - mae: 0.6173\n", "Epoch 910/1000\n", - "13/13 [==============================] - 0s 688us/step - loss: 0.8347 - mae: 0.6468\n", + "13/13 [==============================] - 0s 661us/step - loss: 0.9180 - mae: 0.6447\n", "Epoch 911/1000\n", - "13/13 [==============================] - 0s 670us/step - loss: 0.9488 - mae: 0.6043\n", + "13/13 [==============================] - 0s 659us/step - loss: 0.8957 - mae: 0.6588\n", "Epoch 912/1000\n", - "13/13 [==============================] - 0s 682us/step - loss: 0.8613 - mae: 0.6500\n", + "13/13 [==============================] - 0s 669us/step - loss: 0.8769 - mae: 0.6928\n", "Epoch 913/1000\n", - "13/13 [==============================] - 0s 674us/step - loss: 0.9443 - mae: 0.6545\n", + "13/13 [==============================] - 0s 660us/step - loss: 0.8669 - mae: 0.6480\n", "Epoch 914/1000\n", - "13/13 [==============================] - 0s 685us/step - loss: 0.9339 - mae: 0.6681\n", + "13/13 [==============================] - 0s 670us/step - loss: 0.9317 - mae: 0.6457\n", "Epoch 915/1000\n", - "13/13 [==============================] - 0s 656us/step - loss: 0.9047 - mae: 0.6530\n", + "13/13 [==============================] - 0s 671us/step - loss: 0.9012 - mae: 0.5977\n", "Epoch 916/1000\n", - "13/13 [==============================] - 0s 664us/step - loss: 0.8846 - mae: 0.6016\n", + "13/13 [==============================] - 0s 667us/step - loss: 0.9420 - mae: 0.6895\n", "Epoch 917/1000\n", - "13/13 [==============================] - 0s 677us/step - loss: 0.9579 - mae: 0.5759\n", + "13/13 [==============================] - 0s 656us/step - loss: 0.9185 - mae: 0.6843\n", "Epoch 918/1000\n", - "13/13 [==============================] - 0s 667us/step - loss: 0.8795 - mae: 0.6258\n", + "13/13 [==============================] - 0s 665us/step - loss: 0.9561 - mae: 0.6725\n", "Epoch 919/1000\n", - "13/13 [==============================] - 0s 667us/step - loss: 0.9162 - mae: 0.5899\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.9532 - mae: 0.6566\n", "Epoch 920/1000\n", - "13/13 [==============================] - 0s 677us/step - loss: 0.8837 - mae: 0.5705\n", + "13/13 [==============================] - 0s 657us/step - loss: 0.8828 - mae: 0.6612\n", "Epoch 921/1000\n", - "13/13 [==============================] - 0s 673us/step - loss: 0.9130 - mae: 0.6377\n", + "13/13 [==============================] - 0s 656us/step - loss: 0.9390 - mae: 0.6420\n", "Epoch 922/1000\n", - "13/13 [==============================] - 0s 659us/step - loss: 0.8912 - mae: 0.5795\n", + "13/13 [==============================] - 0s 671us/step - loss: 0.9279 - mae: 0.6885\n", "Epoch 923/1000\n", - "13/13 [==============================] - 0s 667us/step - loss: 0.9350 - mae: 0.6128\n", + "13/13 [==============================] - 0s 674us/step - loss: 0.9520 - mae: 0.6890\n", "Epoch 924/1000\n", - "13/13 [==============================] - 0s 655us/step - loss: 0.9236 - mae: 0.6664\n", + "13/13 [==============================] - 0s 669us/step - loss: 1.0154 - mae: 0.6525\n", "Epoch 925/1000\n", - "13/13 [==============================] - 0s 676us/step - loss: 0.8979 - mae: 0.6497\n", + "13/13 [==============================] - 0s 651us/step - loss: 1.0409 - mae: 0.6682\n", "Epoch 926/1000\n", - "13/13 [==============================] - 0s 673us/step - loss: 0.9331 - mae: 0.6374\n", + "13/13 [==============================] - 0s 660us/step - loss: 0.9001 - mae: 0.6301\n", "Epoch 927/1000\n", - "13/13 [==============================] - 0s 671us/step - loss: 0.8559 - mae: 0.6439\n", + "13/13 [==============================] - 0s 659us/step - loss: 0.9523 - mae: 0.6299\n", "Epoch 928/1000\n", - "13/13 [==============================] - 0s 666us/step - loss: 0.8772 - mae: 0.5624\n", + "13/13 [==============================] - 0s 657us/step - loss: 0.8980 - mae: 0.6354\n", "Epoch 929/1000\n", - "13/13 [==============================] - 0s 680us/step - loss: 0.9054 - mae: 0.6472\n", + "13/13 [==============================] - 0s 661us/step - loss: 0.8604 - mae: 0.5734\n", "Epoch 930/1000\n", - "13/13 [==============================] - 0s 678us/step - loss: 0.9363 - mae: 0.6916\n", + "13/13 [==============================] - 0s 657us/step - loss: 0.9635 - mae: 0.6930\n", "Epoch 931/1000\n", - "13/13 [==============================] - 0s 681us/step - loss: 0.9024 - mae: 0.6157\n", + "13/13 [==============================] - 0s 674us/step - loss: 0.8545 - mae: 0.6482\n", "Epoch 932/1000\n", - "13/13 [==============================] - 0s 666us/step - loss: 0.8841 - mae: 0.6003\n", + "13/13 [==============================] - 0s 668us/step - loss: 0.9890 - mae: 0.6753\n", "Epoch 933/1000\n", - "13/13 [==============================] - 0s 670us/step - loss: 0.8851 - mae: 0.6454\n", + "13/13 [==============================] - 0s 658us/step - loss: 0.9301 - mae: 0.6198\n", "Epoch 934/1000\n", - "13/13 [==============================] - 0s 675us/step - loss: 0.8571 - mae: 0.5572\n", + "13/13 [==============================] - 0s 667us/step - loss: 0.8839 - mae: 0.5906\n", "Epoch 935/1000\n", - "13/13 [==============================] - 0s 667us/step - loss: 0.9207 - mae: 0.6658\n", + "13/13 [==============================] - 0s 665us/step - loss: 0.8318 - mae: 0.6100\n", "Epoch 936/1000\n", - "13/13 [==============================] - 0s 675us/step - loss: 0.9756 - mae: 0.6047\n", + "13/13 [==============================] - 0s 662us/step - loss: 0.9505 - mae: 0.7379\n", "Epoch 937/1000\n", - "13/13 [==============================] - 0s 681us/step - loss: 0.8342 - mae: 0.6111\n", + "13/13 [==============================] - 0s 659us/step - loss: 0.9741 - mae: 0.6713\n", "Epoch 938/1000\n", - "13/13 [==============================] - 0s 676us/step - loss: 0.9049 - mae: 0.6024\n", + "13/13 [==============================] - 0s 659us/step - loss: 0.9486 - mae: 0.6749\n", "Epoch 939/1000\n", - "13/13 [==============================] - 0s 683us/step - loss: 0.8583 - mae: 0.6443\n", + "13/13 [==============================] - 0s 655us/step - loss: 0.8814 - mae: 0.6689\n", "Epoch 940/1000\n", - "13/13 [==============================] - 0s 670us/step - loss: 0.8795 - mae: 0.6179\n", + "13/13 [==============================] - 0s 642us/step - loss: 0.8976 - mae: 0.6439\n", "Epoch 941/1000\n", - "13/13 [==============================] - 0s 682us/step - loss: 1.0684 - mae: 0.6755\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.8727 - mae: 0.6318\n", "Epoch 942/1000\n", - "13/13 [==============================] - 0s 701us/step - loss: 0.8674 - mae: 0.6315\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.8262 - mae: 0.6201\n", "Epoch 943/1000\n", - "13/13 [==============================] - 0s 659us/step - loss: 0.9135 - mae: 0.6527\n", + "13/13 [==============================] - 0s 662us/step - loss: 0.8902 - mae: 0.6736\n", "Epoch 944/1000\n", - "13/13 [==============================] - 0s 665us/step - loss: 0.8993 - mae: 0.5718\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.8894 - mae: 0.6051\n", "Epoch 945/1000\n", - "13/13 [==============================] - 0s 669us/step - loss: 0.9662 - mae: 0.6664\n", + "13/13 [==============================] - 0s 661us/step - loss: 0.9201 - mae: 0.6301\n", "Epoch 946/1000\n", - "13/13 [==============================] - 0s 680us/step - loss: 0.8543 - mae: 0.6694\n", + "13/13 [==============================] - 0s 656us/step - loss: 0.8925 - mae: 0.6014\n", "Epoch 947/1000\n", - "13/13 [==============================] - 0s 674us/step - loss: 0.9741 - mae: 0.6972\n", + "13/13 [==============================] - 0s 662us/step - loss: 0.8801 - mae: 0.6056\n", "Epoch 948/1000\n", - "13/13 [==============================] - 0s 675us/step - loss: 0.9941 - mae: 0.6451\n", + "13/13 [==============================] - 0s 658us/step - loss: 0.9181 - mae: 0.6430\n", "Epoch 949/1000\n", - "13/13 [==============================] - 0s 683us/step - loss: 0.8610 - mae: 0.6077\n", + "13/13 [==============================] - 0s 666us/step - loss: 0.9945 - mae: 0.6737\n", "Epoch 950/1000\n", - "13/13 [==============================] - 0s 685us/step - loss: 0.8332 - mae: 0.5494\n", + "13/13 [==============================] - 0s 653us/step - loss: 0.8904 - mae: 0.6587\n", "Epoch 951/1000\n", - "13/13 [==============================] - 0s 679us/step - loss: 0.8701 - mae: 0.6167\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.9518 - mae: 0.6580\n", "Epoch 952/1000\n", - "13/13 [==============================] - 0s 665us/step - loss: 0.8660 - mae: 0.6231\n", + "13/13 [==============================] - 0s 667us/step - loss: 0.9170 - mae: 0.6237\n", "Epoch 953/1000\n", - "13/13 [==============================] - 0s 681us/step - loss: 0.8554 - mae: 0.5794\n", + "13/13 [==============================] - 0s 670us/step - loss: 0.9681 - mae: 0.6881\n", "Epoch 954/1000\n", - "13/13 [==============================] - 0s 681us/step - loss: 0.9726 - mae: 0.6627\n", + "13/13 [==============================] - 0s 660us/step - loss: 0.8937 - mae: 0.6329\n", "Epoch 955/1000\n", - "13/13 [==============================] - 0s 669us/step - loss: 0.8465 - mae: 0.6001\n", + "13/13 [==============================] - 0s 656us/step - loss: 0.9503 - mae: 0.6888\n", "Epoch 956/1000\n", - "13/13 [==============================] - 0s 670us/step - loss: 0.8106 - mae: 0.5592\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.9304 - mae: 0.6477\n", "Epoch 957/1000\n", - "13/13 [==============================] - 0s 663us/step - loss: 0.9143 - mae: 0.6332\n", + "13/13 [==============================] - 0s 673us/step - loss: 0.9389 - mae: 0.6420\n", "Epoch 958/1000\n", - "13/13 [==============================] - 0s 710us/step - loss: 0.9994 - mae: 0.6760\n", + "13/13 [==============================] - 0s 660us/step - loss: 1.0139 - mae: 0.6627\n", "Epoch 959/1000\n", - "13/13 [==============================] - 0s 668us/step - loss: 0.8923 - mae: 0.6213\n", + "13/13 [==============================] - 0s 653us/step - loss: 0.8506 - mae: 0.6397\n", "Epoch 960/1000\n", - "13/13 [==============================] - 0s 671us/step - loss: 0.9485 - mae: 0.5858\n", + "13/13 [==============================] - 0s 665us/step - loss: 1.0357 - mae: 0.6514\n", "Epoch 961/1000\n", - "13/13 [==============================] - 0s 674us/step - loss: 0.8839 - mae: 0.6114\n", + "13/13 [==============================] - 0s 654us/step - loss: 0.9592 - mae: 0.6355\n", "Epoch 962/1000\n", - "13/13 [==============================] - 0s 670us/step - loss: 0.9193 - mae: 0.6446\n", + "13/13 [==============================] - 0s 672us/step - loss: 0.8983 - mae: 0.6551\n", "Epoch 963/1000\n", - "13/13 [==============================] - 0s 662us/step - loss: 0.9119 - mae: 0.6525\n", + "13/13 [==============================] - 0s 665us/step - loss: 0.9574 - mae: 0.6514\n", "Epoch 964/1000\n", - "13/13 [==============================] - 0s 659us/step - loss: 0.9493 - mae: 0.6499\n", + "13/13 [==============================] - 0s 651us/step - loss: 0.8646 - mae: 0.5955\n", "Epoch 965/1000\n", - "13/13 [==============================] - 0s 660us/step - loss: 0.8635 - mae: 0.6072\n", + "13/13 [==============================] - 0s 667us/step - loss: 0.8803 - mae: 0.6327\n", "Epoch 966/1000\n", - "13/13 [==============================] - 0s 673us/step - loss: 0.7920 - mae: 0.6301\n", + "13/13 [==============================] - 0s 668us/step - loss: 0.8854 - mae: 0.6353\n", "Epoch 967/1000\n", - "13/13 [==============================] - 0s 674us/step - loss: 0.8206 - mae: 0.5821\n", + "13/13 [==============================] - 0s 660us/step - loss: 0.9335 - mae: 0.5771\n", "Epoch 968/1000\n", - "13/13 [==============================] - 0s 675us/step - loss: 0.9185 - mae: 0.6171\n", + "13/13 [==============================] - 0s 655us/step - loss: 0.8880 - mae: 0.6663\n", "Epoch 969/1000\n", - "13/13 [==============================] - 0s 675us/step - loss: 0.8848 - mae: 0.6243\n", + "13/13 [==============================] - 0s 666us/step - loss: 0.8707 - mae: 0.6447\n", "Epoch 970/1000\n", - "13/13 [==============================] - 0s 672us/step - loss: 0.9081 - mae: 0.5896\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.9696 - mae: 0.7230\n", "Epoch 971/1000\n", - "13/13 [==============================] - 0s 696us/step - loss: 0.9083 - mae: 0.6017\n", + "13/13 [==============================] - 0s 664us/step - loss: 0.9298 - mae: 0.6351\n", "Epoch 972/1000\n", - "13/13 [==============================] - 0s 655us/step - loss: 0.9222 - mae: 0.6108\n", + "13/13 [==============================] - 0s 673us/step - loss: 0.9771 - mae: 0.6760\n", "Epoch 973/1000\n", - "13/13 [==============================] - 0s 683us/step - loss: 0.9052 - mae: 0.6062\n", + "13/13 [==============================] - 0s 658us/step - loss: 0.9621 - mae: 0.6584\n", "Epoch 974/1000\n", - "13/13 [==============================] - 0s 678us/step - loss: 0.9138 - mae: 0.6045\n", + "13/13 [==============================] - 0s 665us/step - loss: 0.8483 - mae: 0.6339\n", "Epoch 975/1000\n", - "13/13 [==============================] - 0s 669us/step - loss: 0.9567 - mae: 0.6517\n", + "13/13 [==============================] - 0s 661us/step - loss: 0.9619 - mae: 0.6307\n", "Epoch 976/1000\n", - "13/13 [==============================] - 0s 670us/step - loss: 0.8662 - mae: 0.6251\n", + "13/13 [==============================] - 0s 637us/step - loss: 0.9979 - mae: 0.6907\n", "Epoch 977/1000\n", - "13/13 [==============================] - 0s 672us/step - loss: 0.8549 - mae: 0.5982\n", + "13/13 [==============================] - 0s 665us/step - loss: 0.9625 - mae: 0.6380\n", "Epoch 978/1000\n", - "13/13 [==============================] - 0s 673us/step - loss: 0.8864 - mae: 0.6377\n", + "13/13 [==============================] - 0s 675us/step - loss: 0.8953 - mae: 0.6701\n", "Epoch 979/1000\n", - "13/13 [==============================] - 0s 706us/step - loss: 0.8863 - mae: 0.6402\n", + "13/13 [==============================] - 0s 668us/step - loss: 0.8751 - mae: 0.6227\n", "Epoch 980/1000\n", - "13/13 [==============================] - 0s 672us/step - loss: 0.9288 - mae: 0.6293\n", + "13/13 [==============================] - 0s 677us/step - loss: 1.1511 - mae: 0.6615\n", "Epoch 981/1000\n", - "13/13 [==============================] - 0s 659us/step - loss: 0.8140 - mae: 0.6009\n", + "13/13 [==============================] - 0s 670us/step - loss: 1.0293 - mae: 0.7082\n", "Epoch 982/1000\n", - "13/13 [==============================] - 0s 674us/step - loss: 0.8201 - mae: 0.6252\n", + "13/13 [==============================] - 0s 667us/step - loss: 0.8772 - mae: 0.6147\n", "Epoch 983/1000\n", - "13/13 [==============================] - 0s 669us/step - loss: 0.8769 - mae: 0.6250\n", + "13/13 [==============================] - 0s 656us/step - loss: 0.9275 - mae: 0.6296\n", "Epoch 984/1000\n", - "13/13 [==============================] - 0s 659us/step - loss: 0.8663 - mae: 0.6246\n", + "13/13 [==============================] - 0s 663us/step - loss: 0.8801 - mae: 0.6027\n", "Epoch 985/1000\n", - "13/13 [==============================] - 0s 664us/step - loss: 0.8793 - mae: 0.5914\n", + "13/13 [==============================] - 0s 674us/step - loss: 0.8773 - mae: 0.6378\n", "Epoch 986/1000\n", - "13/13 [==============================] - 0s 670us/step - loss: 1.0172 - mae: 0.6094\n", + "13/13 [==============================] - 0s 655us/step - loss: 0.9471 - mae: 0.7142\n", "Epoch 987/1000\n", - "13/13 [==============================] - 0s 686us/step - loss: 0.8807 - mae: 0.6113\n", + "13/13 [==============================] - 0s 676us/step - loss: 0.8537 - mae: 0.6394\n", "Epoch 988/1000\n", - "13/13 [==============================] - 0s 665us/step - loss: 0.9012 - mae: 0.6326\n", + "13/13 [==============================] - 0s 660us/step - loss: 0.9512 - mae: 0.6726\n", "Epoch 989/1000\n", - "13/13 [==============================] - 0s 677us/step - loss: 0.9572 - mae: 0.6663\n", + "13/13 [==============================] - 0s 661us/step - loss: 0.8767 - mae: 0.6185\n", "Epoch 990/1000\n", - "13/13 [==============================] - 0s 670us/step - loss: 0.8179 - mae: 0.6196\n", + "13/13 [==============================] - 0s 664us/step - loss: 0.9111 - mae: 0.6584\n", "Epoch 991/1000\n", - "13/13 [==============================] - 0s 681us/step - loss: 0.8132 - mae: 0.5889\n", + "13/13 [==============================] - 0s 662us/step - loss: 0.9109 - mae: 0.6308\n", "Epoch 992/1000\n", - "13/13 [==============================] - 0s 665us/step - loss: 0.9078 - mae: 0.5776\n", + "13/13 [==============================] - 0s 667us/step - loss: 0.8721 - mae: 0.6153\n", "Epoch 993/1000\n", - "13/13 [==============================] - 0s 668us/step - loss: 0.8381 - mae: 0.5540\n", + "13/13 [==============================] - 0s 668us/step - loss: 0.8505 - mae: 0.5873\n", "Epoch 994/1000\n", - "13/13 [==============================] - 0s 688us/step - loss: 0.8214 - mae: 0.5652\n", + "13/13 [==============================] - 0s 659us/step - loss: 0.9473 - mae: 0.6333\n", "Epoch 995/1000\n", - "13/13 [==============================] - 0s 669us/step - loss: 0.9313 - mae: 0.5881\n", + "13/13 [==============================] - 0s 664us/step - loss: 0.9225 - mae: 0.6605\n", "Epoch 996/1000\n", - "13/13 [==============================] - 0s 672us/step - loss: 0.9535 - mae: 0.6475\n", + "13/13 [==============================] - 0s 660us/step - loss: 0.9383 - mae: 0.6480\n", "Epoch 997/1000\n", - "13/13 [==============================] - 0s 678us/step - loss: 0.9188 - mae: 0.6248\n", + "13/13 [==============================] - 0s 669us/step - loss: 0.8620 - mae: 0.6204\n", "Epoch 998/1000\n", - "13/13 [==============================] - 0s 666us/step - loss: 0.8771 - mae: 0.5518\n", + "13/13 [==============================] - 0s 667us/step - loss: 0.8416 - mae: 0.6341\n", "Epoch 999/1000\n", - "13/13 [==============================] - 0s 670us/step - loss: 0.9438 - mae: 0.6431\n", + "13/13 [==============================] - 0s 662us/step - loss: 0.8385 - mae: 0.6067\n", "Epoch 1000/1000\n", - "13/13 [==============================] - 0s 668us/step - loss: 0.8970 - mae: 0.6265\n" + "13/13 [==============================] - 0s 661us/step - loss: 0.9507 - mae: 0.6729\n" ] } ], @@ -2121,7 +2132,7 @@ }, { "cell_type": "markdown", - "id": "behind-breach", + "id": "pursuant-gilbert", "metadata": {}, "source": [ "Make predictions, uncertainies are returned too." @@ -2129,8 +2140,8 @@ }, { "cell_type": "code", - "execution_count": 137, - "id": "hollow-october", + "execution_count": 6, + "id": "occupational-ghost", "metadata": {}, "outputs": [], "source": [ @@ -2139,7 +2150,7 @@ }, { "cell_type": "markdown", - "id": "million-blast", + "id": "original-seafood", "metadata": {}, "source": [ "Unscale the target values and uncertainties" @@ -2147,35 +2158,35 @@ }, { "cell_type": "code", - "execution_count": 138, - "id": "saved-accreditation", + "execution_count": 7, + "id": "white-structure", "metadata": {}, "outputs": [], "source": [ "preds = scale.inverse_transform(preds)\n", "Yst = scale.inverse_transform(Yst)\n", - "uncs *= numpy.sqrt(scale.var_)" + "uncs *= np.sqrt(scale.var_)" ] }, { "cell_type": "code", - "execution_count": 139, - "id": "expected-yesterday", + "execution_count": 8, + "id": "skilled-wrong", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "" + "Text(0.5, 1.0, 'True vs predicted with uncertainty')" ] }, - "execution_count": 139, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -2193,71 +2204,26 @@ "ax.set_xlim(minv, maxv)\n", "ax.set_ylim(minv, maxv)\n", "ax.plot([minv, maxv], [minv, maxv], c='r')\n", - "ax.set_xlabel('true')\n", - "ax.set_ylabel('predicted')\n", - "ax.errorbar(Yst, preds, yerr=uncs.reshape(-1), ls='none')\n" + "ax.set_xlabel('true', size=18)\n", + "ax.set_ylabel('predicted', size=18)\n", + "ax.errorbar(Yst, preds, yerr=uncs.reshape(-1), ls='none')\n", + "ax.set_title('True vs predicted with uncertainty', size=20)" ] }, { - "cell_type": "code", - "execution_count": null, - "id": "spoken-vietnamese", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "stuck-requirement", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "golden-count", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "parallel-computer", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "legendary-hanging", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ranking-ensemble", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "subjective-electronics", + "cell_type": "markdown", + "id": "fabulous-horizon", "metadata": {}, - "outputs": [], - "source": [] + "source": [ + "## Uncertainties are variable to the data, as we expect!\n", + "\n", + "Uncertainties are larger in areas with fewer training dataum, eg. when the target is > 40 as we expect. Uncertainty is always on the range of including the true value, showing that our model is quite good at capturing what it \"doesn't know\"" + ] }, { "cell_type": "code", "execution_count": null, - "id": "usual-automation", + "id": "premium-coordinator", "metadata": {}, "outputs": [], "source": [] From e0bbf5b64e34fdbcd939e52b79010fe1e25d93c4 Mon Sep 17 00:00:00 2001 From: evankomp Date: Thu, 11 Mar 2021 19:13:57 -0800 Subject: [PATCH 76/99] removed psuedocode and fixed getter issue --- gandy/models/bnns.py | 74 ++-------------------------- gandy/tests/test_models/test_bnns.py | 2 +- 2 files changed, 4 insertions(+), 72 deletions(-) diff --git a/gandy/models/bnns.py b/gandy/models/bnns.py index 4470c66..18d1fe6 100644 --- a/gandy/models/bnns.py +++ b/gandy/models/bnns.py @@ -48,18 +48,6 @@ def prior(self, kernel_size, bias_size, dtype=None) -> Callable: prior model type == Keras sequential model ''' - # from keras tutorial: - # Note: this is hard-coded to be unit normal! - # n = kernel_size + bias_size - # prior_model = keras.Sequential( - # [ - # tfp.layers.DistributionLambda( - # lambda t: tfp.distributions.MultivariateNormalDiag( - # loc=tf.zeros(n), scale_diag=tf.ones(n) - # ) - # ) - # ] - # ) try: kernel_size = int(kernel_size) bias_size = int(bias_size) @@ -91,16 +79,6 @@ def posterior(self, kernel_size, bias_size, dtype=None) -> Callable: posterior model type == Keras sequential model ''' - # n = kernel_size + bias_size - # posterior_model = keras.Sequential( - # [ - # tfp.layers.VariableLayer( - # tfp.layers.MultivariateNormalTriL.params_size(n), - # dtype=dtype - # ), - # tfp.layers.MultivariateNormalTriL(n), - # ] - # ) try: kernel_size = int(kernel_size) bias_size = int(bias_size) @@ -133,9 +111,6 @@ def negative_loglikelihood(self, targets, estimated_distribution) -> Array: negative log likelihood type == ndarray ''' - # do something like: - # https://keras.io/examples/keras_recipes/bayesian_neural_networks/ - # return -estimated_distribution.log_prob(targets) try: nll = estimated_distribution.log_prob(targets) except AttributeError: @@ -148,7 +123,7 @@ def _build(self, train_size: int, task_type: str = 'regression', activation: Union[Callable, str] = 'sigmoid', - optimizer: Union[Callable, str] = 'adam', + optimizer: Union[Callable, str] = 'Adam', neurons: Tuple[int] = (12, 12, 12), metrics=['MSE'], **kwargs) -> Callable: @@ -166,49 +141,6 @@ def _build(self, or anything needed to compile model (think about default vals for required params) ''' - # do something like: - # https://keras.io/examples/keras_recipes/bayesian_neural_networks/ - - # if features is None: - # features = np.arange(xshape[0]) - - # default activation = 'relu' - # default optimizer = tf.keras.optimizers.adam - # default loss = tf.keras.losses.MSE - - # # making appropriate loss: - # estimated_distribution = loss - # make this a hyperparamter or Gaussian? - # loss = negative_loglikelihood(targets, estimated_distribution) - # get train_size, i.e., train_size = xshape[0] - - # inputs = keras.Input(self.xshape) - # input_values = list(inputs.values()) - # features = tf.keras.layers.concatenate(input_values) - # features = tf.keras.layers.BatchNormalization()(features) - - # Deterministic BNNs = layer weights using Dense layers whereas - # Probabilistic BNNs = layer weights using DenseVariational layers. - # for unit in units: - # features = tfp.layers.DenseVariational( - # units=unit, - # make_prior_fn=self.prior, - # make_posterior_fn=self.posterior, - # kl_weight=1 / train_size, - # activation=activation, - # )(features) - - # Create a probabilistic output (Normal distribution), - # and use the Dense layer to produce the parameters of - # the distribution. - # We set units=2 to learn both the mean and the variance of the - # Normal distribution. - # distribution_params = layers.Dense(units=2)(features) - # outputs = tfp.layers.IndependentNormal(1)(distribution_params) - - # model = keras.Model(inputs=inputs, outputs=outputs) - # model.compile(**kwargs) - # parse kwargs layer_kwargs = {} optimizer_kwargs = {} @@ -259,8 +191,8 @@ def _build(self, if not callable(optimizer): if isinstance(optimizer, str): - optimizer = tf.keras.optimizers.get(optimizer, - **optimizer_kwargs) + optimizer = getattr(tf.keras.optimizers, optimizer) + optimizer = optimizer(**optimizer_kwargs) else: pass else: diff --git a/gandy/tests/test_models/test_bnns.py b/gandy/tests/test_models/test_bnns.py index cbfcbc6..6d91b1f 100644 --- a/gandy/tests/test_models/test_bnns.py +++ b/gandy/tests/test_models/test_bnns.py @@ -94,7 +94,7 @@ def test__build(self): # test keyword assignment subject = gandy.models.bnns.BNN((2,), (4,), train_size=len(x), - optimizer='RMSProp') + optimizer='RMSprop') self.assertTrue(isinstance(subject.model.optimizer, tf.keras.optimizers.RMSprop)) subject = gandy.models.bnns.BNN((2,), (4,), From 2f35db20f08eb41f83717a73534972bd2b8b13ca Mon Sep 17 00:00:00 2001 From: Kyle Moskowitz Date: Thu, 11 Mar 2021 21:51:24 -0800 Subject: [PATCH 77/99] typo fixed --- gandy/quality_est/metrics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gandy/quality_est/metrics.py b/gandy/quality_est/metrics.py index 21b5566..7410399 100644 --- a/gandy/quality_est/metrics.py +++ b/gandy/quality_est/metrics.py @@ -138,7 +138,7 @@ def calculate(self, **kwargs) -> Tuple[float, Array]: RMSE_value = np.sqrt(np.mean(np.subtract(self.real, self.predictions) **2)) - # Define MSE_values as a list of RMSE deviations between data points + # Define RMSE_values as a list of RMSE deviations between data points RMSE_values = [] for i in range(len(self.predictions)): From 9570defe86b02358a8fe01ccf09f9bb1230c8fb6 Mon Sep 17 00:00:00 2001 From: Kyle Moskowitz Date: Thu, 11 Mar 2021 22:45:05 -0800 Subject: [PATCH 78/99] fixed imports within module --- gandy/quality_est/metrics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gandy/quality_est/metrics.py b/gandy/quality_est/metrics.py index 7410399..6bb3864 100644 --- a/gandy/quality_est/metrics.py +++ b/gandy/quality_est/metrics.py @@ -17,7 +17,7 @@ # Imports from typing import Type, Tuple -from sklearn import f1_score +from sklearn.metrics import f1_score import numpy as np From a183291a3f25d22eb563ca664ba9f38f34ecd8fa Mon Sep 17 00:00:00 2001 From: Kyle Moskowitz Date: Thu, 11 Mar 2021 22:58:24 -0800 Subject: [PATCH 79/99] flake8 changes --- gandy/tests/test_metrics/test_metrics.py | 55 ++++++++++++------------ 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/gandy/tests/test_metrics/test_metrics.py b/gandy/tests/test_metrics/test_metrics.py index bf5b421..4fbd859 100644 --- a/gandy/tests/test_metrics/test_metrics.py +++ b/gandy/tests/test_metrics/test_metrics.py @@ -1,5 +1,4 @@ """Unit tests for Metrics module""" - import unittest import unittest.mock @@ -49,7 +48,7 @@ def test_calculate(self): # ensure calculate method is called using mock function subject = metrics.Metric subject.calculate = unittest.mock.MagicMock(name='calculate') - subject.calculate.assert_called_once_with(kwargs) + subject.calculate.assert_called_once_with() class TestMSE(unittest.TestCase): @@ -60,18 +59,18 @@ def test_calculate(self): # failure case: data not iterable with self.assertRaises(TypeError): - subject = metric.MSE(predictions="0, 1, 2", - real=np.array([0, 1, 2])) + subject = metrics.MSE(predictions="0, 1, 2", + real=np.array([0, 1, 2])) with self.assertRaises(TypeError): - subject = metric.MSE(predictions=np.array([0, 1, 2]), - real="0, 1, 2") + subject = metrics.MSE(predictions=np.array([0, 1, 2]), + real="0, 1, 2") # failure case: uncertainties given when None expected with self.assertRaises(TypeError): - subject = metric.MSE(predictions=np.array([0, 1, 2]), - real=np.array([0, 1, 2]), - uncertainties=np.array([0, 1, 2])) + subject = metrics.MSE(predictions=np.array([0, 1, 2]), + real=np.array([0, 1, 2]), + uncertainties=np.array([0, 1, 2])) # check to make sure necessary attributes are inputted subject = metrics.MSE(predictions=np.array([0, 1, 2]), @@ -93,18 +92,18 @@ def test_calculate(self): # failure case: data not iterable with self.assertRaises(TypeError): - subject = metric.RMSE(predictions="0, 1, 2", - real=np.array([0, 1, 2])) + subject = metrics.RMSE(predictions="0, 1, 2", + real=np.array([0, 1, 2])) with self.assertRaises(TypeError): - subject = metric.RMSE(predictions=np.array([0, 1, 2]), - real="0, 1, 2") + subject = metrics.RMSE(predictions=np.array([0, 1, 2]), + real="0, 1, 2") # failure case: uncertainties given when None expected - with self.assertRaises(TypeError): - subject = metric.RMSE(predictions=np.array([0, 1, 2]), - real=np.array([0, 1, 2]), - uncertainties=np.array([0, 1, 2])) + with self.assertRaises(TypeError): + subject = metrics.RMSE(predictions=np.array([0, 1, 2]), + real=np.array([0, 1, 2]), + uncertainties=np.array([0, 1, 2])) # check to make sure necessary attributes are inputted subject = metrics.RMSE(predictions=np.array([0, 1, 2]), @@ -126,23 +125,23 @@ def test_calculate(self): # failure case: data not iterable with self.assertRaises(TypeError): - subject = metric.F1(predictions="0, 1, 2", - real=np.array([0, 1, 2])) + subject = metrics.F1(predictions="0, 1, 2", + real=np.array([0, 1, 2])) with self.assertRaises(TypeError): - subject = metric.F1(predictions=np.array([0, 1, 2]), - real="0, 1, 2") - - # failure case: uncertainties given when None expected + subject = metrics.F1(predictions=np.array([0, 1, 2]), + real="0, 1, 2") + + # failure case: uncertainties given when None expected with self.assertRaises(TypeError): - subject = metric.F1(predictions=np.array([0, 1, 2]), - real=np.array([0, 1, 2]), - uncertainties=np.array([0, 1, 2])) + subject = metrics.F1(predictions=np.array([0, 1, 2]), + real=np.array([0, 1, 2]), + uncertainties=np.array([0, 1, 2])) - # check to make sure necessary attributes are inputted + # check to make sure necessary attributes are inputted subject = metrics.F1(predictions=np.array([0, 1, 2]), real=np.array([0, 1, 2])) - + self.assertTrue(subject.predictions is not None) self.assertTrue(subject.real is not None) self.assertTrue(subject.uncertainties is None) From dfe75a1b41213bda297b5ac8acff0a87582a9aa6 Mon Sep 17 00:00:00 2001 From: Kyle Moskowitz Date: Thu, 11 Mar 2021 23:03:51 -0800 Subject: [PATCH 80/99] fixed typos --- gandy/tests/test_metrics/test_metrics.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gandy/tests/test_metrics/test_metrics.py b/gandy/tests/test_metrics/test_metrics.py index 4fbd859..35f104c 100644 --- a/gandy/tests/test_metrics/test_metrics.py +++ b/gandy/tests/test_metrics/test_metrics.py @@ -55,7 +55,7 @@ class TestMSE(unittest.TestCase): """Unit test for MSE subclass""" def test_calculate(self): - """Test the calculate function within the parent Metric class""" + """Test the calculate function within the MSE subclass""" # failure case: data not iterable with self.assertRaises(TypeError): @@ -88,7 +88,7 @@ class TestRMSE(unittest.TestCase): """Unit test for RMSE subclass""" def test_calculate(self): - """Test the calculate function within the parent Metric class""" + """Test the calculate function within the RMSE subclass""" # failure case: data not iterable with self.assertRaises(TypeError): @@ -121,7 +121,7 @@ class TestF1(unittest.TestCase): """Unit test for F1 subclass""" def test_calculate(self): - """Test the calculate function within the parent Metric class""" + """Test the calculate function within the F1 subclass""" # failure case: data not iterable with self.assertRaises(TypeError): From 3128be93fd7a09cc8669e36eaa5ce8f2695c3301 Mon Sep 17 00:00:00 2001 From: Kyle Moskowitz Date: Thu, 11 Mar 2021 23:21:37 -0800 Subject: [PATCH 81/99] updated unit tests --- gandy/tests/test_metrics/test_metrics.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/gandy/tests/test_metrics/test_metrics.py b/gandy/tests/test_metrics/test_metrics.py index 35f104c..26a54dc 100644 --- a/gandy/tests/test_metrics/test_metrics.py +++ b/gandy/tests/test_metrics/test_metrics.py @@ -46,9 +46,10 @@ def test_calculate(self): """Test the calculate function within the parent Metric class""" # ensure calculate method is called using mock function - subject = metrics.Metric + subject = metrics.Metric(predictions=np.array([0, 1, 2]), + real=np.array([0, 1, 2])) subject.calculate = unittest.mock.MagicMock(name='calculate') - subject.calculate.assert_called_once_with() + subject.calculate.assert_called_once() class TestMSE(unittest.TestCase): @@ -68,7 +69,7 @@ def test_calculate(self): # failure case: uncertainties given when None expected with self.assertRaises(TypeError): - subject = metrics.MSE(predictions=np.array([0, 1, 2]), + subject = metrics.MSE(predictions=[0, 1, 2], real=np.array([0, 1, 2]), uncertainties=np.array([0, 1, 2])) @@ -99,9 +100,9 @@ def test_calculate(self): subject = metrics.RMSE(predictions=np.array([0, 1, 2]), real="0, 1, 2") - # failure case: uncertainties given when None expected + # failure case: list given when numpy array expected with self.assertRaises(TypeError): - subject = metrics.RMSE(predictions=np.array([0, 1, 2]), + subject = metrics.RMSE(predictions=[0, 1, 2], real=np.array([0, 1, 2]), uncertainties=np.array([0, 1, 2])) @@ -124,17 +125,17 @@ def test_calculate(self): """Test the calculate function within the F1 subclass""" # failure case: data not iterable - with self.assertRaises(TypeError): + with self.assertRaises(ValueError): subject = metrics.F1(predictions="0, 1, 2", real=np.array([0, 1, 2])) - with self.assertRaises(TypeError): + with self.assertRaises(ValueError): subject = metrics.F1(predictions=np.array([0, 1, 2]), real="0, 1, 2") # failure case: uncertainties given when None expected - with self.assertRaises(TypeError): - subject = metrics.F1(predictions=np.array([0, 1, 2]), + with self.assertRaises(ValueError): + subject = metrics.F1(predictions=[0, 1, 2], real=np.array([0, 1, 2]), uncertainties=np.array([0, 1, 2])) From 5e173de7ba4f3627775d0f1ba6a68a2d6b9acf18 Mon Sep 17 00:00:00 2001 From: Kyle Moskowitz Date: Thu, 11 Mar 2021 23:23:15 -0800 Subject: [PATCH 82/99] updated F1 subclass code --- gandy/quality_est/metrics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gandy/quality_est/metrics.py b/gandy/quality_est/metrics.py index 6bb3864..d4e04a9 100644 --- a/gandy/quality_est/metrics.py +++ b/gandy/quality_est/metrics.py @@ -172,5 +172,5 @@ def calculate(self, **kwargs) -> float: Value of the F1 score computed ''' - F1_value = f1_score(self.real, self.predictions, **kwargs) + F1_value = f1_score(self.real, self.predictions) return F1_value From a62076da6efe67c38572039ba8432ff4f656801d Mon Sep 17 00:00:00 2001 From: Kyle Moskowitz Date: Thu, 11 Mar 2021 23:40:17 -0800 Subject: [PATCH 83/99] accuracy score metric added --- gandy/quality_est/metrics.py | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/gandy/quality_est/metrics.py b/gandy/quality_est/metrics.py index d4e04a9..0fff5f0 100644 --- a/gandy/quality_est/metrics.py +++ b/gandy/quality_est/metrics.py @@ -17,7 +17,7 @@ # Imports from typing import Type, Tuple -from sklearn.metrics import f1_score +from sklearn.metrics import f1_score, accuracy_score import numpy as np @@ -150,7 +150,7 @@ def calculate(self, **kwargs) -> Tuple[float, Array]: class F1(Metric): ''' - F1 score class which defines the structure used forcomputing the F1 score + F1 score class which defines the structure used for computing the F1 score between the passed in datasets. Inherets the properties of the parent class Metrics. ''' @@ -158,7 +158,7 @@ class Metrics. def calculate(self, **kwargs) -> float: ''' Method that defines the mathematical formula necessary to compute - the RMSE. + the F1 score. Args: @@ -172,5 +172,32 @@ def calculate(self, **kwargs) -> float: Value of the F1 score computed ''' - F1_value = f1_score(self.real, self.predictions) + F1_value = f1_score(self.real, self.predictions, average = 'macro') return F1_value + +class Accuracy(Metric): + ''' + Accuracy class which defines the structure used for computing the + accuracy score between the passed in datasets. Inherets the properties of + the parent class Metrics. + ''' + + def calculate(self, **kwargs) -> float: + ''' + Method that defines the mathematical formula necessary to compute + the Accuracy. + + Args: + + **kwargs: + Necessary keyword arguments to be passed into calculate() + method + + Returns: + + Accuracy_value(float): + Value of the Accuracy score computed + + ''' + Accuracy_value = accuracy_score(self.real, self.predictions) + return Accuracy_value From 77adfe7007fa4ced94b617abfeabcf8a278b76c6 Mon Sep 17 00:00:00 2001 From: evankomp Date: Fri, 12 Mar 2021 10:39:23 -0800 Subject: [PATCH 84/99] updated docstrings to be current. gave explicit init inputs to BNN and GPs --- gandy/models/bnns.py | 7 +++ gandy/models/gps.py | 27 +++++++---- gandy/models/models.py | 71 ++++++++++++---------------- gandy/tests/test_models/test_bnns.py | 6 +-- 4 files changed, 59 insertions(+), 52 deletions(-) diff --git a/gandy/models/bnns.py b/gandy/models/bnns.py index 18d1fe6..7d992e1 100644 --- a/gandy/models/bnns.py +++ b/gandy/models/bnns.py @@ -36,6 +36,13 @@ class BNN(gandy.models.models.UncertaintyModel): https://keras.io/examples/keras_recipes/bayesian_neural_networks/ for a guide to implementing a BNN with Keras. """ + def __init__(self, + xshape: Tuple[int], + yshape: Tuple[int], + train_size: int, + **kwargs): + super().__init__(xshape, yshape, train_size=train_size, **kwargs) + return def prior(self, kernel_size, bias_size, dtype=None) -> Callable: ''' diff --git a/gandy/models/gps.py b/gandy/models/gps.py index 3c363ca..9e13675 100644 --- a/gandy/models/gps.py +++ b/gandy/models/gps.py @@ -16,7 +16,7 @@ predictions, uncertainties = cfr.predict(Xst) Score the model on the test set using an mse metric: - score = cfr.evaluate(Xs, Ys, metric='mse') + score = cfr.evaluate(Xs, Ys, metric='MSE') """ # imports from typing import Type, Tuple, Callable @@ -44,9 +44,19 @@ class ucGaussianProcess(gandy.models.models.UncertaintyModel): shape of example data, excluding the first dimension yshape (tuple of int): shape of target data, excluding the first dimension + model_type (str): + 'regressor' or 'classifier' **kwargs: keyword arguments to pass to the build method """ + def __init__(self, + xshape: Tuple[int], + yshape: Tuple[int], + model_type: str, + **kwargs): + super().__init__(xshape, yshape, model_type=model_type, **kwargs) + return + def _build(self, model_type: str, @@ -90,9 +100,9 @@ def _train(self, associated with the covariance fit, so None is returned. Args: - Xs (Array): + Xs (ndarray): Examples data to train on. - Ys (Array): + Ys (ndarray): Label data that is targeted for metrics for training. **kwargs: Keyword arguments passed to model's fit method. @@ -119,10 +129,9 @@ def _predict(self, keyword arguments passed to predictor's predict method Returns: - tuple of ndarray: - array of predictions of targets with the same length as Xs - array of prediction uncertainties of targets withthe same - length as Xs + predictions (ndarray): predictions with the same length as Xs + uncertainties (ndarray): + uncertainties on returned predictions, same length """ if isinstance(self.model, sklearn.gaussian_process.GaussianProcessRegressor): @@ -142,7 +151,7 @@ def R(cls, *args, **kwargs): """Alternative to passing model_type as 'regressor' to object initialization. - Arguments: + Args: *args: positional arguments to pass to init **kwargs: @@ -155,7 +164,7 @@ def C(cls, *args, **kwargs): """Alternative to passing model_type as 'classifier' to object initialization. - Arguments: + Args: *args: positional arguments to pass to init **kwargs: diff --git a/gandy/models/models.py b/gandy/models/models.py index b99d554..884d506 100644 --- a/gandy/models/models.py +++ b/gandy/models/models.py @@ -11,6 +11,7 @@ """ # imports +import sys import time from typing import Tuple, Iterable, Any, Type, Callable, Union @@ -27,12 +28,13 @@ class NotImplimented(Exception): methods. Args: - inst - the class instance that raises this exception + inst (object): the class instance that raises this exception + caller (str): method name """ - def __init__(self, inst): - self.message = """This method has not yet been implimented by - this class: `{}`.""".format(inst.__class__) + def __init__(self, inst, caller): + self.message = """This method {} has not yet been implimented by + this class: `{}`.""".format(caller, inst.__class__) super().__init__(self.message) return @@ -59,13 +61,13 @@ class UncertaintyModel: keyword arguments to pass to the build method Attributes: - sessions (list of tuple): + sessions (dict): Stored losses from training sessions. When train is called, a new - tuple is appended of (session name, losses) where losses is + item is added of `session name`: `losses` where losses is determined by the output of _train. """ metrics = gandy.quality_est.metrics - """Available metrics defined in gandy.metrics""" + """Available metrics defined in gandy.quality_est.metrics.metrics""" def __init__(self, xshape: Tuple[int], @@ -92,10 +94,9 @@ def check(self, Ys (iterable): label data to check, if present. Default None. Returns: - tuple of ndarrays: - Xs, the formated X data - Ys, the formated Y data if present - """ + Xs (ndarray): the formated X data + (optional) Ys (ndarray): the formated Y data if present + """ if hasattr(Xs, 'shape'): pass else: @@ -154,26 +155,25 @@ def build(self, **kwargs): self._model = self._build(**kwargs) return - def _build(self, *args, **kwargs) -> Callable: + def _build(self, **kwargs) -> Callable: """Construct and return the predictor. Must be implimented in child class. To creates and returns a predictor with keyword argument inputs. Raises not implimented warning. Args: - *args: - arguments defined in child **kwargs: keyword arguments/hyperparemeters for predictor init. Raises: NotImplimented: warning that child class has not overloaded this method + Returns: None: children will return the predictor """ - raise NotImplimented(self) + raise NotImplimented(sys._getframe().f_code.co_name, self) model = None return model @@ -199,7 +199,7 @@ def train(self, use clock time. metric (str): Metric to use, a key in UncertaintyModel.metrics or a metric - objectthat takes as input true, predicted, and uncertainty + object that takes as input true, predicted, and uncertainty values. **kwargs: Keyword arguments to pass to `_train` and assign non-default \ @@ -219,7 +219,6 @@ def train(self, def _train(self, Xs: Array, Ys: Array, - *args, metric: Callable = None, **kwargs) -> Any: """Train the predictor. @@ -236,8 +235,6 @@ def _train(self, metric (callable): Metric to use, takes true, predicted, uncertainties to compute a score. - *args: - Positional arguments to be defined by child. **kwargs: Keyword arguments to assign non-default training parameters or pass to nested functions. @@ -247,7 +244,7 @@ def _train(self, Desired tracking of losses during training. Not implimented here, and returns None. """ - raise NotImplimented(self) + raise NotImplimented(sys._getframe().f_code.co_name, self) losses = None return losses @@ -270,12 +267,11 @@ def predict(self, **kwargs: keyword arguments to pass to `_predict` Returns: - tuple of ndarray: - array of predictions of targets with the same length as Xs - array of prediction uncertainties of targets withthe same - length as Xs - (optional) array of flags of uncertain predictions higher - than threshhold of same length as Xs + predictions (ndarray): predictions with the same length as Xs + uncertainties (ndarray): + uncertainties on returned predictions, same length + (optional) flags (ndarray): bools of uncertain predictions higher + than threshhold of same length as predictions. """ Xs_ = self.check(Xs) if uc_threshold is not None: @@ -307,10 +303,9 @@ def predict(self, def _predict(self, Xs: Array, - *args, **kwargs): - """Make predictions on a set of data and return predictions and uncertain- - ty arrays. + """Make predictions on a set of data and return predictions and + uncertainty arrays. Must be implimented by child class. Makes predictions on data using model at self.model and any other stored objects. @@ -318,24 +313,21 @@ def _predict(self, Args: Xs (ndarray): Example data to make predictions on. - *args: - Positional arguments to be defined by child. **kwargs: Keyword arguments for predicting. Returns: - tuple of ndarray: - array of predictions of targets with the same length as Xs - array of prediction uncertainties of targets withthe same - length as Xs + predictions (ndarray): predictions with the same length as Xs + uncertainties (ndarray): + uncertainties on returned predictions, same length """ - raise NotImplimented(self) + raise NotImplimented(sys._getframe().f_code.co_name, self) predictions, uncertainties = None, None return predictions, uncertainties def _get_metric(self, metric_in: Union[None, Callable, str]): """Accesses gandy metrics to retrieve the correct metric depending on - input + input. Args: metric_in (str, callable, None): @@ -392,7 +384,6 @@ def score(self, metric_value, metric_values = metric(Ys_, predictions, uncertainties) metric_values = numpy.array(metric_values).astype(numpy.float64) metric_values = metric_values.reshape(len(Xs), -1) - return metric_value, metric_values def save(self, @@ -405,7 +396,7 @@ def save(self, filename (str): path to save model to, no extension """ - raise NotImplimented(self) + raise NotImplimented(sys._getframe().f_code.co_name, self) return @classmethod @@ -423,7 +414,7 @@ def load(cls, Returns: instance of class: the loaded UncertaintyModel """ - raise NotImplimented(cls) + raise NotImplimented(sys._getframe().f_code.co_name, cls) instance = None return instance diff --git a/gandy/tests/test_models/test_bnns.py b/gandy/tests/test_models/test_bnns.py index 6d91b1f..005ffa0 100644 --- a/gandy/tests/test_models/test_bnns.py +++ b/gandy/tests/test_models/test_bnns.py @@ -19,7 +19,7 @@ def test_prior(self, mocked__build): """ kernel_size = 5 bias_size = 5 - subject = gandy.models.bnns.BNN((1,), (1,)) + subject = gandy.models.bnns.BNN((1,), (1,), train_size=5) # expected success must return a model prior = subject.prior(kernel_size, bias_size) self.assertTrue(isinstance(prior, tf.keras.Model)) @@ -37,7 +37,7 @@ def test_posterior(self, mocked__build): """ kernel_size = 5 bias_size = 5 - subject = gandy.models.bnns.BNN((1,), (1,)) + subject = gandy.models.bnns.BNN((1,), (1,), train_size=5) # expected success must return a model prior = subject.posterior(kernel_size, bias_size) self.assertTrue(isinstance(prior, tf.keras.Model)) @@ -53,7 +53,7 @@ def test_negative_loglikelihood(self, mocked_build): Distribution should impliment log_prob method """ - subject = gandy.models.bnns.BNN((1,), (1,)) + subject = gandy.models.bnns.BNN((1,), (1,), train_size=5) # failure mode, does not have method def callable_wo_log_prob(): From f15415f71bfcdf90d927911a53c8f00395c120e4 Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Fri, 12 Mar 2021 17:43:21 -0800 Subject: [PATCH 85/99] Flipped x and y since our GAN is special XP --- gandy/models/dcgan.py | 154 ++++++++++++++++++++++++++++++++++++++---- gandy/models/gans.py | 88 ++++++++---------------- 2 files changed, 172 insertions(+), 70 deletions(-) diff --git a/gandy/models/dcgan.py b/gandy/models/dcgan.py index d0a20a4..1b9867c 100644 --- a/gandy/models/dcgan.py +++ b/gandy/models/dcgan.py @@ -8,6 +8,9 @@ """ +# time +import time + # warnings import warnings @@ -45,14 +48,12 @@ class DCGAN(deepchem.models.GAN): This class builds off of the deepchem GAN class found at the url above. """ - def __init__(self, xshape, yshape, noise_shape, n_classes, **kwargs): + def __init__(self, xshape, yshape, noise_shape, **kwargs): """Override deepchem init function.""" - # These should be set by the gandy model when _build is called. self.xshape = xshape self.yshape = yshape self.noise_shape = noise_shape - self.n_classes = n_classes # base hyperparameters for generator and discirminator Base_hyperparams = dict(layer_dimensions=[128], @@ -163,8 +164,7 @@ def create_generator(self): gen = Dropout(dropout)(gen) # generator outputs - # is xhape[0] really what we want, or batch size? - gen = Dense(self.xshape[0], **layer_kwargs)(gen) + gen = Dense(self.yshape[0], **layer_kwargs)(gen) gen = Dropout(dropout)(gen) # final construction of Keras model @@ -227,7 +227,7 @@ def create_discriminator(self): - {'layer_dimensions', 'dropout'}} # construct input - data_in = Input(shape=self.xshape) + data_in = Input(shape=self.yshape) # build first layer of network discrim = Dense(layer_dimensions[0], **layer_kwargs)(data_in) # adding dropout to the weights @@ -248,6 +248,132 @@ def create_discriminator(self): outputs=[discrim_prob]) return discriminator + def fit_gan(self, + batches, + generator_steps=1.0, + max_checkpoints_to_keep=5, + checkpoint_interval=1000, + restore=False): + """Train this model on data. + + !! Adjusted from deepchem to return losses !! + + Parameters + ---------- + batches: iterable + batches of data to train the discriminator on, each + represented as a dict that maps Inputs to values. + It should specify values for all members of + data_inputs and conditional_inputs. + generator_steps: float + the number of training steps to perform for the generator + for each batch. This can be used to adjust the ratio of + training steps for the generator and discriminator. + For example, 2.0 will perform two training steps for + every batch, while 0.5 will only perform one training step + for every two batches. + max_checkpoints_to_keep: int + the maximum number of checkpoints to keep. + Older checkpoints are discarded. + checkpoint_interval: int + the frequency at which to write checkpoints, measured in + batches. Set this to 0 to disable automatic checkpointing. + restore: bool + if True, restore the model from the most recent checkpoint before + training it. + """ + self._ensure_built() + gen_train_fraction = 0.0 + discrim_error = 0.0 + gen_error = 0.0 + discrim_average_steps = 0 + gen_average_steps = 0 + time1 = time.time() + + # Added HERE + model_losses = [[], []] + + if checkpoint_interval > 0: + manager = tf.train.CheckpointManager(self._checkpoint, + self.model_dir, + max_checkpoints_to_keep) + for feed_dict in batches: + # Every call to fit_generator() will increment global_step, + # but we only want it to get incremented once for the entire + # batch, so record the value and keep resetting it. + + global_step = self.get_global_step() + + # Train the discriminator. + + inputs = [self.get_noise_batch(self.batch_size)] + for input in self.data_input_layers: + inputs.append(feed_dict[input.ref()]) + for input in self.conditional_input_layers: + inputs.append(feed_dict[input.ref()]) + discrim_error += self.fit_generator( + [(inputs, [], [])], + variables=self.discrim_variables, + loss=self.discrim_loss_fn, + checkpoint_interval=0, + restore=restore) + restore = False + discrim_average_steps += 1 + + # Train the generator. + + if generator_steps > 0.0: + gen_train_fraction += generator_steps + while gen_train_fraction >= 1.0: + inputs = [self.get_noise_batch( + self.batch_size)] + inputs[1:] + gen_error += self.fit_generator( + [(inputs, [], [])], + variables=self.gen_variables, + checkpoint_interval=0) + gen_average_steps += 1 + gen_train_fraction -= 1.0 + self._global_step.assign(global_step + 1) + + # Write checkpoints and report progress. + + if discrim_average_steps == checkpoint_interval: + manager.save() + discrim_loss = discrim_error / max(1, discrim_average_steps) + gen_loss = gen_error / max(1, gen_average_steps) + print( + f'Step {global_step+1}: \t' + + f'Avg gen loss {gen_loss}, \t' + + f'Avg discrim loss {discrim_loss}') + discrim_error = 0.0 + gen_error = 0.0 + discrim_average_steps = 0 + gen_average_steps = 0 + + # Added HERE + model_losses[0].append(gen_loss) + model_losses[1].append(discrim_loss) + + # Write out final results. + + if checkpoint_interval > 0: + if discrim_average_steps > 0 and gen_average_steps > 0: + discrim_loss = discrim_error / discrim_average_steps + gen_loss = gen_error / gen_average_steps + print( + f'Step {global_step+1}: \t' + + f'Avg gen loss {gen_loss}, \t' + + f'Avg discrim loss {discrim_loss}') + manager.save() + time2 = time.time() + print("TIMING: model fitting took %0.3f s" % (time2 - time1)) + + model_losses[0].append(gen_loss) + model_losses[1].append(discrim_loss) + + # ADDED Here + return model_losses + def get_noise_input_shape(self) -> Tuple[int]: """ Return the shape of the noise vector. @@ -261,16 +387,20 @@ def get_data_input_shapes(self) -> Tuple[int]: Return the shape of the data. This should be set by the gandy model when an build is called. + + Data input shape is y! """ - return [self.xshape] + return [self.yshape] def get_conditional_input_shapes(self) -> Array: """ Return the shape of the conditional input. This should be set by the gandy model when an build is called. + + This is x data! """ - return [(self.n_classes,)] + return [(self.xshape[0],)] class CondDCGAN(DCGAN): @@ -339,7 +469,7 @@ def create_generator(self): # construct input noise_in = Input(shape=self.get_noise_input_shape()) - conditional_in = Input(shape=(self.n_classes,)) + conditional_in = Input(shape=self.xshape) gen_input = Concatenate()([noise_in, conditional_in]) # build first layer of network @@ -352,7 +482,7 @@ def create_generator(self): gen = Dropout(dropout)(gen) # generator outputs - gen = Dense(self.xshape[0], **layer_kwargs)(gen) + gen = Dense(self.yshape[0], **layer_kwargs)(gen) gen = Dropout(dropout)(gen) # final construction of Keras model @@ -415,8 +545,8 @@ def create_discriminator(self): - {'layer_dimensions', 'dropout'}} # construct input - data_in = Input(shape=self.xshape) - conditional_in = Input(shape=(self.n_classes,)) + data_in = Input(shape=self.yshape) + conditional_in = Input(shape=self.xshape,) discrim_input = Concatenate()([data_in, conditional_in]) # build first layer of network diff --git a/gandy/models/gans.py b/gandy/models/gans.py index 4ab9c8e..49bc7ac 100644 --- a/gandy/models/gans.py +++ b/gandy/models/gans.py @@ -66,42 +66,19 @@ def _build(self, *args, **kwargs): # default is 10 dimensional noise_shape = kwargs.get('noise_shape', (10,)) # get n_classes from kwargs, default None - n_classes = kwargs.get('n_classes', None) - - # determine whether to use gan or conditional gan - if n_classes is not None: - # if number of classes is specified, assumes conditional GAN - self.conditional = True - # Should this be flagged somewhere?... - if self.yshape[0] == n_classes: - # Ys are already one hot encoded - self.one_hot = False - else: - # Ys are NOT one hot encoded - # Or this is regression, which would be == 1 - if n_classes == 1: - # this is regression! - self.one_hot = False - else: - # Ys are NOT one hot encoded, so we must convert them later - self.one_hot = True - else: - # if no n_classes specified, assumed to be regression - # and no need for conditional inputs - self.conditional = False - n_classes = kwargs.get('n_classes', self.yshape[0]) + self.conditional = kwargs.get('conditional', True) # get other kwargs as hyperparameters hyperparams = {key: kwargs[key] for key in kwargs.keys() - - {'n_classes', 'noise_shape'}} + {'noise_shape'}} # instantiating the model as the deepchem gan if self.conditional: model = dcgan.CondDCGAN(self.xshape, self.yshape, noise_shape, - n_classes, **hyperparams) + **hyperparams) else: model = dcgan.DCGAN(self.xshape, self.yshape, noise_shape, - n_classes, **hyperparams) + **hyperparams) return model def generate_data(self, @@ -131,9 +108,9 @@ def generate_data(self, # sample with replacement X, Y pairs of size batch_size n = len(Xs) indices = np.random.randint(0, high=n, size=(batch_size,)) - points = Xs[indices] - classes = Ys[indices] - return classes, points + features = Xs[indices] + targets = Ys[indices] + return targets, features def iterbatches(self, Xs: Array, @@ -159,12 +136,9 @@ def iterbatches(self, """ # training loop for i in range(batches): - classes, points = self.generate_data(Xs, Ys, self.model.batch_size) - if self.one_hot: - classes = deepchem.metrics.to_one_hot(classes, - self.model.n_classes) - batched_data = {self._model.data_inputs[0]: points, - self._model.conditional_inputs[0]: classes} + targets, features = self.generate_data(Xs, Ys, self.model.batch_size) + batched_data = {self._model.conditional_inputs[0]: features, + self._model.data_inputs[0]: targets} yield batched_data # overridden method from UncertaintyModel class @@ -190,21 +164,17 @@ def _train(self, """ # train GAN on data # self.model = deepchem GAN instance - self._model.fit_gan(self.iterbatches(Xs, Ys, batches)) - # The deepchem gan is a Keras model whose - # outputs are [gen_loss, disrcim_loss]. - # Thus the final losses for the generator - # and discriminator are self.model.outputs - # This is a list of 2 KerasTensors so must evaluate it. - # losses = self._model.outputs - losses = None - # compute metric + # generator + discriminator losses + losses = self._model.fit_gan(self.iterbatches(Xs, Ys, batches)) + # compute metric here + if metric is not None: + losses[0] = metric(losses[0]) # gen + losses[1] = metric(losses[1]) # discrim return losses # overridden method from UncertaintyModel class def _predict(self, Xs: Array, - Ys: Array = None, **kwargs): """ Predict on Xs. @@ -229,18 +199,14 @@ def _predict(self, type == ndarray """ # adapted from deepchem tutorial 14: + num_predictions = kwargs.get('num_predictions', 100) predictions = [] if self.conditional: - assert Ys is not None, "This is a cGAN.\ - Must specify Ys (Ys=) to call predict." - if self.one_hot: - # must one hot encode Ys - Ys = deepchem.metrics.to_one_hot(Ys, self.model.n_classes) for i in range(num_predictions): # generate data with conditional inputs generated_points = self._model.predict_gan_generator( - conditional_inputs=[Ys]) + conditional_inputs=[Xs]) predictions.append(generated_points) else: for i in range(num_predictions): @@ -263,12 +229,10 @@ def save(self, filename: str, **kwargs): filename (str): name of file to save model to """ - # save model aka generator and discriminator separately - if filename.endswith('.h5'): - self._model.save(filename) - else: - path_to_model = filename - self._model.save(path_to_model) + # save model + # filename could be a path or end with .h5 + print(f"Saving model as {filename}") + self._model.save(filename) return None @classmethod @@ -290,4 +254,12 @@ def load(cls, filename: str, **kwargs): else: path_to_model = filename model = tf.keras.model.load_model(path_to_model) + # get x and y shape + xshape = None + yshape = None + # instantiate + instance = cls.__new__(cls) + instance._xshape = xshape + instance._yshape = yshape + instance._model = model return model From 002ed276fc46b26ae6486bc54ba51259a61a3110 Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Fri, 12 Mar 2021 18:44:48 -0800 Subject: [PATCH 86/99] Trying to get that Lipschitz convergence --- gandy/models/dcgan.py | 25 ++++++++++++++++++------- gandy/models/gans.py | 4 ++-- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/gandy/models/dcgan.py b/gandy/models/dcgan.py index 1b9867c..9481d47 100644 --- a/gandy/models/dcgan.py +++ b/gandy/models/dcgan.py @@ -17,7 +17,7 @@ # deep learning imports import deepchem import tensorflow as tf -from tensorflow.keras.layers import Concatenate, Dense, Dropout, Input +from tensorflow.keras.layers import Concatenate, Dense, Dropout, Input, LeakyReLU # typing imports from typing import Tuple, Type @@ -56,8 +56,9 @@ def __init__(self, xshape, yshape, noise_shape, **kwargs): self.noise_shape = noise_shape # base hyperparameters for generator and discirminator - Base_hyperparams = dict(layer_dimensions=[128], - dropout=0.05, + + Base_hyperparams = dict(layer_dimensions=[32, 32], + dropout=0.1, activation='relu', use_bias=True, kernel_initializer="glorot_uniform", @@ -164,8 +165,9 @@ def create_generator(self): gen = Dropout(dropout)(gen) # generator outputs - gen = Dense(self.yshape[0], **layer_kwargs)(gen) - gen = Dropout(dropout)(gen) + final_layer_kwargs = layer_kwargs.copy() + final_layer_kwargs.update(activation='sigmoid') + gen = Dense(self.yshape[0], **final_layer_kwargs)(gen) # final construction of Keras model generator = tf.keras.Model(inputs=[noise_in], @@ -225,16 +227,20 @@ def create_discriminator(self): # every other kwarg is for the layers layer_kwargs = {key: kwargs[key] for key in kwargs.keys() - {'layer_dimensions', 'dropout'}} + # removing activation to implemetn LeakyReLU + layer_kwargs.update(activation=None) # construct input data_in = Input(shape=self.yshape) # build first layer of network discrim = Dense(layer_dimensions[0], **layer_kwargs)(data_in) + discrim = LeakyReLU()(discrim) # adding dropout to the weights discrim = Dropout(dropout)(discrim) # build subsequent layers for layer_dim in layer_dimensions[1:]: discrim = Dense(layer_dim, **layer_kwargs)(discrim) + discrim = LeakyReLU()(discrim) discrim = Dropout(dropout)(discrim) # To maintain the interpretation of a probability, @@ -482,8 +488,9 @@ def create_generator(self): gen = Dropout(dropout)(gen) # generator outputs - gen = Dense(self.yshape[0], **layer_kwargs)(gen) - gen = Dropout(dropout)(gen) + final_layer_kwargs = layer_kwargs.copy() + final_layer_kwargs.update(activation='sigmoid') + gen = Dense(self.yshape[0], **final_layer_kwargs)(gen) # final construction of Keras model generator = tf.keras.Model(inputs=[noise_in, conditional_in], @@ -543,6 +550,8 @@ def create_discriminator(self): # every other kwarg is for the layers layer_kwargs = {key: kwargs[key] for key in kwargs.keys() - {'layer_dimensions', 'dropout'}} + # removing activation to implemetn LeakyReLU + layer_kwargs.update(activation=None) # construct input data_in = Input(shape=self.yshape) @@ -551,11 +560,13 @@ def create_discriminator(self): # build first layer of network discrim = Dense(layer_dimensions[0], **layer_kwargs)(discrim_input) + discrim = LeakyReLU()(discrim) # adding dropout to the weights discrim = Dropout(dropout)(discrim) # build subsequent layers for layer_dim in layer_dimensions[1:]: discrim = Dense(layer_dim, **layer_kwargs)(discrim) + discrim = LeakyReLU()(discrim) discrim = Dropout(dropout)(discrim) # To maintain the interpretation of a probability, diff --git a/gandy/models/gans.py b/gandy/models/gans.py index 49bc7ac..1e86553 100644 --- a/gandy/models/gans.py +++ b/gandy/models/gans.py @@ -14,7 +14,6 @@ import gandy.quality_est.metrics # deep learning imports -import deepchem import gandy.models.dcgan as dcgan import tensorflow as tf @@ -136,7 +135,8 @@ def iterbatches(self, """ # training loop for i in range(batches): - targets, features = self.generate_data(Xs, Ys, self.model.batch_size) + targets, features = self.generate_data(Xs, Ys, + self.model.batch_size) batched_data = {self._model.conditional_inputs[0]: features, self._model.data_inputs[0]: targets} yield batched_data From f1cbf3ec181b270278f896774fbedaa0001bd9c7 Mon Sep 17 00:00:00 2001 From: Kyle Moskowitz Date: Fri, 12 Mar 2021 21:37:42 -0800 Subject: [PATCH 87/99] added unit test for accuracy method --- gandy/tests/test_metrics/test_metrics.py | 49 ++++++++++++++---------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/gandy/tests/test_metrics/test_metrics.py b/gandy/tests/test_metrics/test_metrics.py index 26a54dc..b38115b 100644 --- a/gandy/tests/test_metrics/test_metrics.py +++ b/gandy/tests/test_metrics/test_metrics.py @@ -67,12 +67,6 @@ def test_calculate(self): subject = metrics.MSE(predictions=np.array([0, 1, 2]), real="0, 1, 2") - # failure case: uncertainties given when None expected - with self.assertRaises(TypeError): - subject = metrics.MSE(predictions=[0, 1, 2], - real=np.array([0, 1, 2]), - uncertainties=np.array([0, 1, 2])) - # check to make sure necessary attributes are inputted subject = metrics.MSE(predictions=np.array([0, 1, 2]), real=np.array([0, 1, 2])) @@ -100,12 +94,6 @@ def test_calculate(self): subject = metrics.RMSE(predictions=np.array([0, 1, 2]), real="0, 1, 2") - # failure case: list given when numpy array expected - with self.assertRaises(TypeError): - subject = metrics.RMSE(predictions=[0, 1, 2], - real=np.array([0, 1, 2]), - uncertainties=np.array([0, 1, 2])) - # check to make sure necessary attributes are inputted subject = metrics.RMSE(predictions=np.array([0, 1, 2]), real=np.array([0, 1, 2])) @@ -125,20 +113,14 @@ def test_calculate(self): """Test the calculate function within the F1 subclass""" # failure case: data not iterable - with self.assertRaises(ValueError): + with self.assertRaises(TypeError): subject = metrics.F1(predictions="0, 1, 2", real=np.array([0, 1, 2])) - with self.assertRaises(ValueError): + with self.assertRaises(TypeError): subject = metrics.F1(predictions=np.array([0, 1, 2]), real="0, 1, 2") - # failure case: uncertainties given when None expected - with self.assertRaises(ValueError): - subject = metrics.F1(predictions=[0, 1, 2], - real=np.array([0, 1, 2]), - uncertainties=np.array([0, 1, 2])) - # check to make sure necessary attributes are inputted subject = metrics.F1(predictions=np.array([0, 1, 2]), real=np.array([0, 1, 2])) @@ -149,3 +131,30 @@ def test_calculate(self): # check to make sure output is correct type self.assertTrue(isinstance(subject, (float, int))) + + +class TestAccuracy(unittest.TestCase): + """Unit test for Accuracy subclass""" + + def test_calculate(self): + """Test the calculate function within the Accuracy subclass""" + + # failure case: data not iterable + with self.assertRaises(TypeError): + subject = metrics.Accuracy(predictions="0, 1, 2", + real=np.array([0, 1, 2])) + + with self.assertRaises(TypeError): + subject = metrics.Accuracy(predictions=np.array([0, 1, 2]), + real="0, 1, 2") + + # check to make sure necessary attributes are inputted + subject = metrics.Accuracy(predictions=np.array([0, 1, 2]), + real=np.array([0, 1, 2])) + + self.assertTrue(subject.predictions is not None) + self.assertTrue(subject.real is not None) + self.assertTrue(subject.uncertainties is None) + + # check to make sure output is correct type + self.assertTrue(isinstance(subject, (float, int))) From e945a4294271f4fef9c15cea0e85a1605ad6a224 Mon Sep 17 00:00:00 2001 From: Kyle Moskowitz Date: Fri, 12 Mar 2021 23:49:23 -0800 Subject: [PATCH 88/99] added conditional statements to metric classes --- gandy/quality_est/metrics.py | 80 ++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 45 deletions(-) diff --git a/gandy/quality_est/metrics.py b/gandy/quality_est/metrics.py index 0fff5f0..abad151 100644 --- a/gandy/quality_est/metrics.py +++ b/gandy/quality_est/metrics.py @@ -1,7 +1,6 @@ ''' Metrics module: contains some relevent metrics to assess the performance of machine learning models. - This module implements a parent metric class that contains necessary initialization arguments and automatically calls a calculate method to compute a given metric. Required intial arguments include the machine @@ -38,15 +37,12 @@ def __init__(self, predictions: Array, real: Array, uncertainties=None): Initializes an instance of the metric class, including the predictions, uncertainties (optional), and real data necessary for comparison. - Arg: predictions(ndarray): Array of predictions generated from the uncertainty model - real(ndarray): Array of real values that you want to compare the uncertainty model ouput to (eg. experimental data) - uncertainties(ndarray): Optional argument which contains array of uncertainty values generated from the uncertainty module @@ -76,32 +72,31 @@ def calculate(self, **kwargs) -> Tuple[float, Array]: ''' Method that defines the mathematical formula necessary to compute the MSE. - Args: - **kwargs: Necessary keyword arguments to be passed into calculate() method - Returns: - MSE_value(float): Total value of the MSE computed - MSE_values(ndarray): An array of MSE scores for each prediction - ''' - # Define MSE formula using numpy methods - MSE_value = np.mean(np.square(np.subtract(self.real, self.predictions) - )) + if self.uncertainties is not None: + raise TypeError("MSE metric does not take uncertainties as arg") - # Define MSE_values as a list of MSE deviations between each data point - MSE_values = [] + else: + # Define MSE formula using numpy methods + MSE_value = np.mean(np.square(np.subtract(self.real, self. + predictions))) - # Iterate through data points and add MSE value to list - for i in range(len(self.predictions)): - MSE_values.append((self.real[i] - self.predictions[i])**2) + # Define MSE_values as a list of MSE deviations between each data + # point + MSE_values = [] + + # Iterate through data points and add MSE value to list + for i in range(len(self.predictions)): + MSE_values.append((self.real[i] - self.predictions[i])**2) return MSE_value, MSE_values @@ -117,33 +112,30 @@ def calculate(self, **kwargs) -> Tuple[float, Array]: ''' Method that defines the mathematical formula necessary to compute the RMSE. - Args: - **kwargs: Necessary keyword arguments to be passed into calculate() method - Returns: - RMSE_value(float): Total value of the RMSE computed - RMSE_values(ndarray): Array of RMSE values for each prediction - ''' + if self.uncertainties is not None: + raise TypeError("RMSE metric does not take uncertainties as arg") + else: + # Define RMSE using numpy methods + RMSE_value = np.sqrt(np.mean(np.subtract(self.real, self. + predictions)**2)) - # Define RMSE using numpy methods - RMSE_value = np.sqrt(np.mean(np.subtract(self.real, self.predictions) - **2)) + # Define RMSE_values as a list of RMSE deviations between data + # points + RMSE_values = [] - # Define RMSE_values as a list of RMSE deviations between data points - RMSE_values = [] - - for i in range(len(self.predictions)): - RMSE_values.append(np.sqrt((self.real[i] - self.predictions[i])**2 - )) + for i in range(len(self.predictions)): + RMSE_values.append(np.sqrt((self.real[i] - self.predictions[i] + )**2)) return RMSE_value, RMSE_values @@ -159,22 +151,21 @@ def calculate(self, **kwargs) -> float: ''' Method that defines the mathematical formula necessary to compute the F1 score. - Args: - **kwargs: Necessary keyword arguments to be passed into calculate() method - Returns: - F1_value(float): Value of the F1 score computed - ''' - F1_value = f1_score(self.real, self.predictions, average = 'macro') + if self.uncertainties is not None: + raise TypeError("F1 metric does not take uncertainties as arg") + else: + F1_value = f1_score(self.real, self.predictions, average='macro') return F1_value + class Accuracy(Metric): ''' Accuracy class which defines the structure used for computing the @@ -186,18 +177,17 @@ def calculate(self, **kwargs) -> float: ''' Method that defines the mathematical formula necessary to compute the Accuracy. - Args: - **kwargs: Necessary keyword arguments to be passed into calculate() method - Returns: - Accuracy_value(float): Value of the Accuracy score computed - ''' - Accuracy_value = accuracy_score(self.real, self.predictions) + if self.uncertainties is not None: + raise TypeError("Accuracy metric does not take uncertainties as\ + arg") + else: + Accuracy_value = accuracy_score(self.real, self.predictions) return Accuracy_value From 83835b8ed7237bed2325f5dc17c426c1de5f24bc Mon Sep 17 00:00:00 2001 From: Kyle Moskowitz Date: Sat, 13 Mar 2021 00:19:36 -0800 Subject: [PATCH 89/99] updated tests now passing --- gandy/tests/test_metrics/test_metrics.py | 84 ++++++++++++------------ 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/gandy/tests/test_metrics/test_metrics.py b/gandy/tests/test_metrics/test_metrics.py index b38115b..0e499bb 100644 --- a/gandy/tests/test_metrics/test_metrics.py +++ b/gandy/tests/test_metrics/test_metrics.py @@ -9,30 +9,9 @@ class TestMetric(unittest.TestCase): """Unit test for Metric parent class""" - def test___init___(self): """Test proper initialization of class with proper inputs""" - # failure cases: data not iterable - with self.assertRaises(TypeError): - subject = metrics.Metric(predictions="0, 1, 2", - real=np.array([0, 1, 2]), - uncertainties=np.array([0, 0.5, 1])) - - with self.assertRaises(TypeError): - subject = metrics.Metric(predictions=np.array([0, 1, 2]), - real="0, 1, 2", - uncertainties=np.array([0, 0.5, 1])) - - with self.assertRaises(TypeError): - subject = metrics.Metric(predictions=np.array([0, 1, 2]), - real=np.array([0, 1, 2]), - uncertainties="0, 1, 2") - - with self.assertRaises(TypeError): - subject = metrics.Metric(predictions=np.array([0, 1, 2]), - real="0, 1, 2") - # success case subject = metrics.Metric(predictions=np.array([0, 1, 2]), real=np.array([0, 1, 2]), @@ -41,16 +20,11 @@ def test___init___(self): # check to make sure necessary attributes are inputted self.assertTrue(subject.predictions is not None) self.assertTrue(subject.real is not None) + self.assertTrue(subject.calculate() is None) def test_calculate(self): """Test the calculate function within the parent Metric class""" - # ensure calculate method is called using mock function - subject = metrics.Metric(predictions=np.array([0, 1, 2]), - real=np.array([0, 1, 2])) - subject.calculate = unittest.mock.MagicMock(name='calculate') - subject.calculate.assert_called_once() - class TestMSE(unittest.TestCase): """Unit test for MSE subclass""" @@ -61,11 +35,18 @@ def test_calculate(self): # failure case: data not iterable with self.assertRaises(TypeError): subject = metrics.MSE(predictions="0, 1, 2", - real=np.array([0, 1, 2])) + real=np.array([0, 1, 2])).calculate() with self.assertRaises(TypeError): subject = metrics.MSE(predictions=np.array([0, 1, 2]), - real="0, 1, 2") + real="0, 1, 2").calculate() + + # failure case: Uncertainties given when none expected + with self.assertRaises(TypeError): + subject = metrics.MSE(predictions=np.array([0, 1, 2]), + real=np.array([0, 1, 2]), + uncertainties=np.array([0, 0.5, 1])).\ + calculate() # check to make sure necessary attributes are inputted subject = metrics.MSE(predictions=np.array([0, 1, 2]), @@ -76,7 +57,7 @@ def test_calculate(self): self.assertTrue(subject.uncertainties is None) # check to make sure output is correct type - self.assertTrue(isinstance(subject, tuple)) + self.assertTrue(isinstance(subject.calculate(), tuple)) class TestRMSE(unittest.TestCase): @@ -88,11 +69,18 @@ def test_calculate(self): # failure case: data not iterable with self.assertRaises(TypeError): subject = metrics.RMSE(predictions="0, 1, 2", - real=np.array([0, 1, 2])) + real=np.array([0, 1, 2])).calculate() + + with self.assertRaises(TypeError): + subject = metrics.RMSE(predictions=np.array([0, 1, 2]), + real="0, 1, 2").calculate() + # failure case: Uncertainties given when none expected with self.assertRaises(TypeError): subject = metrics.RMSE(predictions=np.array([0, 1, 2]), - real="0, 1, 2") + real=np.array([0, 1, 2]), + uncertainties=np.array([0, 0.5, 1])).\ + calculate() # check to make sure necessary attributes are inputted subject = metrics.RMSE(predictions=np.array([0, 1, 2]), @@ -103,7 +91,7 @@ def test_calculate(self): self.assertTrue(subject.uncertainties is None) # check to make sure output is correct type - self.assertTrue(isinstance(subject, tuple)) + self.assertTrue(isinstance(subject.calculate(), tuple)) class TestF1(unittest.TestCase): @@ -113,13 +101,20 @@ def test_calculate(self): """Test the calculate function within the F1 subclass""" # failure case: data not iterable - with self.assertRaises(TypeError): + with self.assertRaises(ValueError): subject = metrics.F1(predictions="0, 1, 2", - real=np.array([0, 1, 2])) + real=np.array([0, 1, 2])).calculate() + with self.assertRaises(ValueError): + subject = metrics.F1(predictions=np.array([0, 1, 2]), + real="0, 1, 2").calculate() + + # failure case: Uncertainties given when none expected with self.assertRaises(TypeError): subject = metrics.F1(predictions=np.array([0, 1, 2]), - real="0, 1, 2") + real=np.array([0, 1, 2]), + uncertainties=np.array([0, 0.5, 1])).\ + calculate() # check to make sure necessary attributes are inputted subject = metrics.F1(predictions=np.array([0, 1, 2]), @@ -130,7 +125,7 @@ def test_calculate(self): self.assertTrue(subject.uncertainties is None) # check to make sure output is correct type - self.assertTrue(isinstance(subject, (float, int))) + self.assertTrue(isinstance(subject.calculate(), (float, int))) class TestAccuracy(unittest.TestCase): @@ -140,13 +135,20 @@ def test_calculate(self): """Test the calculate function within the Accuracy subclass""" # failure case: data not iterable - with self.assertRaises(TypeError): + with self.assertRaises(ValueError): subject = metrics.Accuracy(predictions="0, 1, 2", - real=np.array([0, 1, 2])) + real=np.array([0, 1, 2])).calculate() + + with self.assertRaises(ValueError): + subject = metrics.Accuracy(predictions=np.array([0, 1, 2]), + real="0, 1, 2").calculate() + # failure case: Uncertainties given when none expected with self.assertRaises(TypeError): subject = metrics.Accuracy(predictions=np.array([0, 1, 2]), - real="0, 1, 2") + real=np.array([0, 1, 2]), + uncertainties=np.array([0, 0.5, 1])).\ + calculate() # check to make sure necessary attributes are inputted subject = metrics.Accuracy(predictions=np.array([0, 1, 2]), @@ -157,4 +159,4 @@ def test_calculate(self): self.assertTrue(subject.uncertainties is None) # check to make sure output is correct type - self.assertTrue(isinstance(subject, (float, int))) + self.assertTrue(isinstance(subject.calculate(), (float, int))) From a58dbcf8258864a70372715126014d7e26be5971 Mon Sep 17 00:00:00 2001 From: evankomp Date: Sat, 13 Mar 2021 17:50:23 -0800 Subject: [PATCH 90/99] temporarily removed gans to move to main --- gandy/models/dcgan.py | 438 --------------------------- gandy/models/gans.py | 299 ------------------ gandy/tests/test_models/test_gans.py | 199 ------------ 3 files changed, 936 deletions(-) delete mode 100644 gandy/models/dcgan.py delete mode 100644 gandy/models/gans.py delete mode 100644 gandy/tests/test_models/test_gans.py diff --git a/gandy/models/dcgan.py b/gandy/models/dcgan.py deleted file mode 100644 index dba17cc..0000000 --- a/gandy/models/dcgan.py +++ /dev/null @@ -1,438 +0,0 @@ -""" -This class implements Deepchem's GAN class. - -Deepchem's tutorial on GANs (14_Conditional_Generative_Adversarial_Networks) -can be found here: -https://github.com/deepchem/deepchem/blob/master/examples/tutorials/ - 14_Conditional_Generative_Adversarial_Networks.ipynb - -""" - -# warnings -import warnings - -# deep learning imports -import deepchem -import tensorflow as tf -from tensorflow.keras.layers import Concatenate, Dense, Dropout, Input - -# typing imports -from typing import Tuple, Type - -# more typing -import numpy as np -Array = Type[np.ndarray] - - -class DCGAN(deepchem.models.GAN): - """ - Implement Generative Adversarial Networks. - - A Generative Adversarial Network (GAN) is a type of generative model. - It consists of two parts called the "generator" and the "discriminator". - The generator takes random noise as input and transforms it into an - output that (hopefully) resembles the training data. The discriminator - takes a set of samples as input and tries to distinguish the real - training samples from the ones created by the generator. Both of them - are trained together. The discriminator tries to get better and better - at telling real from false data, while the generator tries to get better - and better at fooling the discriminator. - - Thank you to deepchem at - https://github.com/deepchem/deepchem/blob/master/deepchem/models/gan.py#L14-L442 - for the information about GANS. - - This class builds off of the deepchem GAN class found at the url above. - """ - - def __init__(self, xshape, yshape, noise_shape, n_classes, **kwargs): - """Deepchem init function + class atributes.""" - super(DCGAN, self).__init__(**kwargs) - - # These should be set by the gandy model when _build is called. - self.xshape = xshape - self.yshape = yshape - self.noise_shape = noise_shape - self.n_classes = n_classes - - # base hyperparameters for generator and discirminator - Base_hyperparams = dict(layer_dimensions=[128], - dropout=0.05, - activation='relu', - use_bias=True, - kernel_initializer="glorot_uniform", - bias_initializer="zeros", - kernel_regularizer='l2', - bias_regularizer=None, - activity_regularizer=None, - kernel_constraint=None, - bias_constraint=None) - - # Create separate hyperparam dictionaries for the generator - # and discriminator - self.generator_hyperparameters = Base_hyperparams.copy() - self.discriminator_hyperparameters = Base_hyperparams.copy() - - # get network hyperparameters from kwargs - for key in kwargs.keys(): - if key.startswith('generator_'): - # generator param - param = key.replace('generator_', '') - # check if the key is a valid hyperparamter - if param in self.generator_hyperparameters.keys(): - self.generator_hyperparameters[param] = kwargs[key] - else: - warnings.warn(f"Incorrect key {key}. Must be in\ - {Base_hyperparams.keys()}") - elif key.startswith('discriminator_'): - # discriminator param - param = key.replace('discriminator_', '') - if param in self.discriminator_hyperparameters.keys(): - self.discriminator_hyperparameters[param] = kwargs[key] - else: - warnings.warn(f"Incorrect key {key}. Must be in\ - {Base_hyperparams.keys()}") - else: - warnings.warn(f"Incorrect key {key}.\ - Must start with generator_ or discriminator_") - - def create_generator(self): - """ - Create the generator as a keras model. - - kwargs contains the possible arguments for the generator. - See Arguments. - - Other kwargs for a Dense layer can be found at - https://keras.io/api/layers/core_layers/dense/ - - Arguments: - Kwargs for the model architecture: - - layer_dimensions - list of hidden dimension layers - Note: This should note include the output dimension. - Default - [128] - type == list of ndarray - dropout - layer dropout percetnage, - i.e., percent of weights that are randomly set to 0 - Can choose a flooat in [0.0, 1.0) - Default - 0.05 (5% dropout rate) - type == float - - The kwargs for each layer that are different than the Keras - default are: - - activation - hidden layer activation function. - Can choose from 'relu', 'tanh', 'sigmoid', 'softmax', - 'softplus', 'softsign', 'selu', 'elu', 'exponential', - or 'linear'. See https://keras.io/api/layers/activations/ - Default - 'relu' - type == str - kernel_regularizer - regularizer of kernel/ weights - Can choose from 'l2', 'l1', etc. - Default - 'l2' - type == str - - Returns: - generator - creates data from random noise - type == Keras model - - """ - # adapted from deepchem tutorial 14: - - kwargs = self.generator_hyperparameters - - # get hyperparameters from kwargs - layer_dimensions = kwargs.get('layer_dimensions', [128]) - dropout = kwargs.get('dropout', 0.05) - # every other kwarg is for the layers - layer_kwargs = {key: kwargs[key] for key in kwargs.keys() - - {'layer_dimensions', 'dropout'}} - - # construct input - noise_in = Input(shape=self.get_noise_input_shape()) - # build first layer of network - gen = Dense(layer_dimensions[0], **layer_kwargs)(noise_in) - # adding dropout to the weights - gen = Dropout(dropout)(gen) - # build subsequent layers - for layer_dim in layer_dimensions[1:]: - gen = Dense(layer_dim, **layer_kwargs)(gen) - gen = Dropout(dropout)(gen) - - # generator outputs - # is xhape[0] really what we want, or batch size? - gen = Dense(self.xshape[0], **layer_kwargs)(gen) - gen = Dropout(dropout)(gen) - - # final construction of Keras model - generator = tf.keras.Model(inputs=[noise_in], - outputs=[gen]) - return generator - - def create_discriminator(self): - """ - Create the discriminator as a keras model. - - kwargs contains the possible arguments for the discriminator. - See Arguments. - - Other kwargs for a Dense layer can be found at - https://keras.io/api/layers/core_layers/dense/ - - Arguments: - Kwargs for the model architecture: - - layer_dimensions - list of hidden dimension layers - Note: This should note include the output dimension. - Default - [128] - type == list of ndarray - dropout - layer dropout percetnage, - i.e., percent of weights that are randomly set to 0 - Can choose a flooat in [0.0, 1.0) - Default - 0.05 (5% dropout rate) - type == float - - The kwargs for each layer that are different than the Keras - default are: - - activation - hidden layer activation function. - Can choose from 'relu', 'tanh', 'sigmoid', 'softmax', - 'softplus', 'softsign', 'selu', 'elu', 'exponential', - or 'linear'. See https://keras.io/api/layers/activations/ - Default - 'relu' - type == str - kernel_regularizer - regularizer of kernel/ weights - Can choose from 'l2', 'l1', etc. - Default - 'l2' - type == str - - Returns: - discriminator - the discriminator outputs a probability that - the data is real or fake - type == Keras model - - """ - # adapted from deepchem tutorial 14: - - kwargs = self.discriminator_hyperparameters - - # get hyperparameters from kwargs - layer_dimensions = kwargs.get('layer_dimensions', [128]) - dropout = kwargs.get('dropout', 0.05) - # every other kwarg is for the layers - layer_kwargs = {key: kwargs[key] for key in kwargs.keys() - - {'layer_dimensions', 'dropout'}} - - # construct input - data_in = Input(shape=self.xshape) - # build first layer of network - discrim = Dense(layer_dimensions[0], **layer_kwargs)(data_in) - # adding dropout to the weights - discrim = Dropout(dropout)(discrim) - # build subsequent layers - for layer_dim in layer_dimensions[1:]: - discrim = Dense(layer_dim, **layer_kwargs)(discrim) - discrim = Dropout(dropout)(discrim) - - # To maintain the interpretation of a probability, - # the final activation function is not a kwarg - final_layer_kwargs = layer_kwargs.copy() - final_layer_kwargs.update(activation='sigmoid') - discrim_prob = Dense(1, **final_layer_kwargs)(discrim) - - # final construction of Keras model - discriminator = tf.keras.Model(inputs=[data_in], - outputs=[discrim_prob]) - return discriminator - - def get_noise_input_shape(self) -> Tuple[int]: - """ - Return the shape of the noise vector. - - This should be set by the gandy model when an build is called. - """ - return self.noise_shape - - def get_data_input_shapes(self) -> Tuple[int]: - """ - Return the shape of the data. - - This should be set by the gandy model when an build is called. - """ - return self.xshape - - -class CondDCGAN(DCGAN): - """ - Conditional GAN subcless of deepchem's GAN class. - - This class is a subclass of the gans class and instead implements - a cgan. A Conditional GAN (cGAN) has additional inputs to the - generator and discriminator, and learns a distribution that is - conditional on the values of those inputs. They are referred - to as "conditional inputs". - """ - - def get_conditional_input_shapes(self) -> Array: - """ - Return the shape of the conditional input. - - This should be set by the gandy model when an build is called. - """ - return [(self.n_classes,)] - - def create_generator(self): - """ - Create the generator as a keras model. - - kwargs contains the possible arguments for the generator. - See Arguments. - - Other kwargs for a Dense layer can be found at - https://keras.io/api/layers/core_layers/dense/ - - Arguments: - Kwargs for the model architecture: - - layer_dimensions - list of hidden dimension layers - Note: This should note include the output dimension. - Default - [128] - type == list of ndarray - dropout - layer dropout percetnage, - i.e., percent of weights that are randomly set to 0 - Can choose a flooat in [0.0, 1.0) - Default - 0.05 (5% dropout rate) - type == float - - The kwargs for each layer that are different than the Keras - default are: - - activation - hidden layer activation function. - Can choose from 'relu', 'tanh', 'sigmoid', 'softmax', - 'softplus', 'softsign', 'selu', 'elu', 'exponential', - or 'linear'. See https://keras.io/api/layers/activations/ - Default - 'relu' - type == str - kernel_regularizer - regularizer of kernel/ weights - Can choose from 'l2', 'l1', etc. - Default - 'l2' - type == str - - Returns: - generator - creates data from random noise - type == Keras model - - """ - # adapted from deepchem tutorial 14: - - kwargs = self.generator_hyperparameters - - # get hyperparameters from kwargs - layer_dimensions = kwargs.get('layer_dimensions', [128]) - dropout = kwargs.get('dropout', 0.05) - # every other kwarg is for the layers - layer_kwargs = {key: kwargs[key] for key in kwargs.keys() - - {'layer_dimensions', 'dropout'}} - - # construct input - noise_in = Input(shape=self.get_noise_input_shape()) - conditional_in = Input(shape=(self.n_classes,)) - gen_input = Concatenate()([noise_in, conditional_in]) - - # build first layer of network - gen = Dense(layer_dimensions[0], **layer_kwargs)(gen_input) - # adding dropout to the weights - gen = Dropout(dropout)(gen) - # build subsequent layers - for layer_dim in layer_dimensions[1:]: - gen = Dense(layer_dim, **layer_kwargs)(gen) - gen = Dropout(dropout)(gen) - - # generator outputs - gen = Dense(self.xshape[0], **layer_kwargs)(gen) - gen = Dropout(dropout)(gen) - - # final construction of Keras model - generator = tf.keras.Model(inputs=[noise_in, conditional_in], - outputs=[gen]) - return generator - - def create_discriminator(self): - """ - Create the discriminator as a keras model. - - kwargs contains the possible arguments for the discriminator. - See Arguments. - - Other kwargs for a Dense layer can be found at - https://keras.io/api/layers/core_layers/dense/ - - Arguments: - Kwargs for the model architecture: - - layer_dimensions - list of hidden dimension layers - Note: This should note include the output dimension. - Default - [128] - type == list of ndarray - dropout - layer dropout percetnage, - i.e., percent of weights that are randomly set to 0 - Can choose a flooat in [0.0, 1.0) - Default - 0.05 (5% dropout rate) - type == float - - The kwargs for each layer that are different than the Keras - default are: - - activation - hidden layer activation function. - Can choose from 'relu', 'tanh', 'sigmoid', 'softmax', - 'softplus', 'softsign', 'selu', 'elu', 'exponential', - or 'linear'. See https://keras.io/api/layers/activations/ - Default - 'relu' - type == str - kernel_regularizer - regularizer of kernel/ weights - Can choose from 'l2', 'l1', etc. - Default - 'l2' - type == str - - Returns: - discriminator - the discriminator outputs a probability that - the data is real or fake - type == Keras model - - """ - # adapted from deepchem tutorial 14: - - kwargs = self.discriminator_hyperparameters - - # get hyperparameters from kwargs - layer_dimensions = kwargs.get('layer_dimensions', [128]) - dropout = kwargs.get('dropout', 0.05) - # every other kwarg is for the layers - layer_kwargs = {key: kwargs[key] for key in kwargs.keys() - - {'layer_dimensions', 'dropout'}} - - # construct input - data_in = Input(shape=self.xshape) - conditional_in = Input(shape=(self.n_classes,)) - discrim_input = Concatenate()([data_in, conditional_in]) - - # build first layer of network - discrim = Dense(layer_dimensions[0], **layer_kwargs)(discrim_input) - # adding dropout to the weights - discrim = Dropout(dropout)(discrim) - # build subsequent layers - for layer_dim in layer_dimensions[1:]: - discrim = Dense(layer_dim, **layer_kwargs)(discrim) - discrim = Dropout(dropout)(discrim) - - # To maintain the interpretation of a probability, - # the final activation function is not a kwarg - final_layer_kwargs = layer_kwargs.copy() - final_layer_kwargs.update(activation='sigmoid') - discrim_prob = Dense(1, **final_layer_kwargs)(discrim) - - # final construction of Keras model - discriminator = tf.keras.Model(inputs=[data_in, conditional_in], - outputs=[discrim_prob]) - return discriminator diff --git a/gandy/models/gans.py b/gandy/models/gans.py deleted file mode 100644 index f0ee7e3..0000000 --- a/gandy/models/gans.py +++ /dev/null @@ -1,299 +0,0 @@ -""" -This class implements a GAN using deepchem's GAN class. - -Deepchem's tutorial on GANs (14_Conditional_Generative_Adversarial_Networks) -can be found here: -https://github.com/deepchem/deepchem/blob/master/examples/tutorials/ - 14_Conditional_Generative_Adversarial_Networks.ipynb - -See dcgan for the implemented deepchem GAN and conditional GAN. -""" - -# gandy imports -import gandy.models.models -import gandy.quality_est.metrics - -# deep learning imports -import deepchem -import gandy.models.dcgan as dcgan -import tensorflow as tf - -# typing imports -from typing import Any, Type, Callable - -# typing -import numpy as np -Array = Type[np.ndarray] - - -class GAN(gandy.models.models.UncertaintyModel): - """ - Implements Generative Adversarial Networks. - - A Generative Adversarial Network (GAN) is a type of generative model. - It consists of two parts called the "generator" and the "discriminator". - The generator takes random noise as input and transforms it into an - output that (hopefully) resembles the training data. The discriminator - takes a set of samples as input and tries to distinguish the real - training samples from the ones created by the generator. Both of them - are trained together. The discriminator tries to get better and better - at telling real from false data, while the generator tries to get better - and better at fooling the discriminator. - - Thank you to deepchem at - https://github.com/deepchem/deepchem/blob/master/deepchem/models/gan.py#L14-L442 - for the information about GANS. - """ - - # overridden method from UncertaintyModel class - def _build(self, *args, **kwargs): - """ - Construct the model. - - This instantiates the deepchem gan as the model. - - Arguments: - **kwargs - key word arguments for creating the generator - and discriminator. See dcgan.create_generator and - dcgan.create_discriminator for those kwargs. - type == dict - - Returns: - model - Deepchem GAN model found in dcgan - type == Keras model - """ - # get noise shape from kwargs - # default is 10 dimensional - noise_shape = kwargs.get('noise_shape', (10,)) - # get n_classes from kwargs, default None - n_classes = kwargs.get('n_classes', None) - - # determine whether to use gan or conditional gan - if n_classes is not None: - # if number of classes is specified, assumes conditional GAN - conditional = True - # Should this be flagged somewhere?... - if self.yshape[0] > 1: - # Ys are already one hot encoded - n_classes = kwargs.get('n_classes', self.yshape[0]) - else: - # Ys are NOT one hot encoded - # Or this is regression, which would be == 1 - n_classes = kwargs.get('n_classes', self.yshape[0]) - else: - # if no n_classes specified, assumed to be regression - # and no need for conditional inputs - conditional = False - - # get other kwargs as hyperparameters - hyperparams = {key: kwargs[key] for key in kwargs.keys() - - {'n_classes', 'noise_shape'}} - - # instantiating the model as the deepchem gan - if conditional: - model = dcgan.CondDCGAN(self.xshape, self.yshape, noise_shape, - n_classes, hyperparams) - else: - model = dcgan.DCGAN(self.xshape, self.yshape, noise_shape, - n_classes, hyperparams) - return model - - def generate_data(self, - Xs: Array, - Ys: Array, - batch_size: int): - """ - Generating function. - - Create a batch of bootstrapped data. _train helper function. - From deepchem tutorial 14. - - Arguments: - Xs/Ys - training examples/targets - type == ndarray - - batch_size - number of data points in a batch - type == int - - Returns: - classes - array of targets sampled from Ys - type == ndarray - - points - array of data points sampled from Xs - type == ndarray - """ - # sample with replacement X, Y pairs of size batch_size - n = len(Xs) - indices = np.random.randint(0, high=n, size=(batch_size,)) - classes = Xs[indices] - points = Ys[indices] - return classes, points - - def iterbatches(self, - Xs: Array, - Ys: Array, - **kwargs): - """ - Function that creates batches of generated data. - - The deepchem fit_gan unction reads in a dictionary for training. - This creates that dictionary for each batch. _train helper function. - From deepchem tutorial 14. - - Arguments: - Xs/Ys - training examples/targets - type == ndarray - - **kwargs - Specify training hyperparameters - batches - number of batches to train on - type == int - batch_size - number of data points in a batch - type == int - - Yields: - batched_data - data split into batches - type == dict - """ - # get training hyperparamters from kwargs - batches = kwargs.get('batches', 50) - batch_size = kwargs.get('batch_size', 32) - - # training loop - for i in range(batches): - classes, points = self.generate_data(Xs, Ys, batch_size) - if len(Ys.shape) == 2: - # Ys already one hot encoded - pass - else: - # must one hot encode Ys - classes = deepchem.metrics.to_one_hot(classes, - self.model.n_classes) - batched_data = {self.data_inputs[0]: points, - self.conditional_inputs[0]: classes} - yield batched_data - - # overridden method from UncertaintyModel class - def _train(self, - Xs: Array, - Ys: Array, - *args, # use args thoughtfully? - metric: Callable = None, - **kwargs) -> Any: - """ - Train GAN model on data. - - Arguments: - Xs/Ys - training examples/targets - type == ndarray - - **kwargs - keyword arguments to assign non-default training parame- - ters or pass to nested functions. - - Returns: - losses - array of loss for each epoch - type == ndarray - """ - # train GAN on data - # self.model = deepchem GAN instance - self._model.fit_gan(self.iterbatches(Xs, Ys, **kwargs)) - # The deepchem gan is a Keras model whose - # outputs are [gen_loss, disrcim_loss]. - # Thus the final losses for the generator - # and discriminator are self.model.outputs - # This is a list of 2 KerasTensors so must evaluate it. - losses = self.model.outputs - # compute metric - return losses - - # overridden method from UncertaintyModel class - def _predict(self, - Xs: Array, - *args, - **kwargs): - """ - Predict on Xs. - - Arguments: - Xs - example data to make predictions on - type == ndarray - - **kwargs - keyword arguments for predicting - - Returns: - predictions - array of predictions of targets with the same length - as Xs - type == ndarray - - uncertainties - array of prediction uncertainties of targets with - the same length as Xs - type == ndarray - """ - # adapted from deepchem tutorial 14: - num_predictions = kwargs.get('num_predictions', 100) - predictions = [] - if self.conditional: - Ys = kwargs.get('Ys', None) - assert Ys is not None, "This is a cGAN.\ - Must specify Ys (Ys=) to call predict." - if len(Ys.shape) == 2: - # assumes data is in bacthed form - # Ys already one hot encoded - one_hot_Ys = Ys - else: - # must one hot encode Ys - one_hot_Ys = deepchem.metrics.to_one_hot(Ys, - self.model.n_classes) - for i in range(num_predictions): - # generate data with conditional inputs - generated_points = self._model.predict_gan_generator( - conditional_inputs=[one_hot_Ys]) - predictions.append(generated_points) - else: - for i in range(num_predictions): - generated_points = self._model.predict_gan_generator() - predictions.append(generated_points) - # the above code generates points, but we need uncertainties as well - predictions = np.average(predictions, axis=1) - uncertainties = np.std(predictions, axis=1) - return predictions, uncertainties - - def save(self, filename: str, **kwargs): - """ - Method defined by child to save the predictor. - - Method must save into memory the object at self._model - For other functionalities to add, see - https://www.tensorflow.org/guide/keras/save_and_serialize - - Arguments: - filename (str): - name of file to save model to - """ - # save model aka generator and discriminator separately - if filename.endswith('.h5'): - self._model.save(filename) - else: - path_to_model = filename - self._model.save(path_to_model) - return None - - @classmethod - def load(cls, filename: str, **kwargs): - """ - Method defined by child to load a predictor into memory. - - Loads the object to be assigned to self._model. - For other functionalities to add, see - https://www.tensorflow.org/guide/keras/save_and_serialize - - Arguments: - filename (str): - path of file to load - """ - # call Keras.load function - if filename.endswith('.h5'): - model = tf.keras.model.load_model(filename, compile=False) - else: - path_to_model = filename - model = tf.keras.model.load_model(path_to_model) - return model diff --git a/gandy/tests/test_models/test_gans.py b/gandy/tests/test_models/test_gans.py deleted file mode 100644 index 980f86f..0000000 --- a/gandy/tests/test_models/test_gans.py +++ /dev/null @@ -1,199 +0,0 @@ -"""Testing functions for UncertaintyModel gan class.""" - -import unittest -import unittest.mock as mock - -import gandy.models.gans as gans -import gandy.models.models - - -class TestGAN(unittest.TestCase): - """Test GAN class.""" - - def test_inheritence(self): - """Ensure the subclass class inherits from parent class.""" - self.assertTrue(issubclass(gans.GAN, - gandy.models.models.UncertaintyModel)) - - def test__build(self): - """ - Test build function. - - The build function should create a generator and discriminator. - This checks both functions are called. It also checks that both - generator and discriminator are attributes with type == Keras model. - """ - # CHECK (normal) GAN - # create gan instance - subject = gans.GAN(xshape=(4,), yshape=(2,)) - kwargs = dict(noise_shape=(5,)) - subject._build(**kwargs) - # created mocked functions - subject._model.create_generator = unittest.mock.MagicMock( - name='create_generator') - subject._model.create_discriminator = unittest.mock.MagicMock( - name='create_discriminator') - # assert create generator function called - subject._model.create_generator.assert_called_once_with(kwargs) - # assert create discriminator function called - subject._model.create_discriminator.assert_called_once_with(kwargs) - # check attributes - self.assertTrue(subject.conditional, False) - self.assertTrue(subject.noise_shape, (5,)) - - # CHECK Conditional GAN - # create gan instance - subject = gans.CondGAN(xshape=(4,), yshape=(2, 4)) - self.assertTrue(issubclass(gans.CondGAN, gans.GAN)) - kwargs = dict(noise_shape=(5,), n_classes=4) - subject._build(**kwargs) - # assert create generator function called - subject._model.create_generator.assert_called_once_with(kwargs) - # assert create discriminator function called - subject._model.create_discriminator.assert_called_once_with(kwargs) - # check attributes - self.assertTrue(subject.conditional, True) - self.assertTrue(subject.noise_shape, (5,)) - self.assertTrue(subject.n_classes, 4) - return - - def test__train(self): - """ - Test train function. - - The train function calls the fit function for the generator and the - predict function for the discriminator. - This checks that there is a Keras callback History object returned. - """ - Xs = 'Xs' - Ys = 'Ys' - subject = gans.GAN(xshape=(4,), yshape=(2,)) - subject.iterbacthes = mock.MagicMock(name='iterbatches', - return_value="Batch1") - subject._model.fit_gan = mock.MagicMock(name='fit_gan') - kwargs = dict(option='x1') - subject._train(Xs, Ys, **kwargs) - - # assert fit_gan was called - subject.iterbacthes.assert_called_with(Xs, Ys, **kwargs) - subject._model.fit_gan.assert_called_with("Batch1") - return - - @unittest.mock.patch('gandy.models.gans.GAN._build', return_value='Model') - def test__predict(self, mocked__build): - """ - Test predict function. - - The predict function returns predictions and uncertainties. - This checks predictions and uncertainties are the appropriate shape - and the appropriate deepchem calls are made. - """ - Xs = 'Xs' - # CHECK (normal) GAN - subject = gans.GAN(xshape=(4,), yshape=(2,)) - subject.predict_gan_generator = mock.MagicMock( - name='predict_gan_generator', return_value='generated_points') - preds, ucs = subject._predict(Xs) - subject._model.predict_gan_generator.assert_called_with(None) - self.assertEqual(preds, 'generated_points') - self.assertEqual(ucs, None) - - # CHECK Conditional GAN - Ys = 'Ys' - subject = gans.GAN(xshape=(4,), yshape=(2, 3), n_classes=3) - subject._model.predict_gan_generator = mock.MagicMock( - name='predict_gan_generator', return_value='generated_points') - with mock.patch('deepchem.metrics.to_one_hot', - return_value=[10]) as mocked_one_hot: - preds, ucs = subject._predict(Xs, Ys=Ys) - mocked_one_hot.assert_called_with(Ys, 3) - subject._model.predict_gan_generator.assert_called_with( - conditional_inputs=[10]) - self.assertEqual(preds, 'generated_points') - self.assertEqual(ucs, None) - return - - @unittest.mock.patch('gandy.models.gans.GAN._build', return_value='Model') - def test_iterbacthes(self, mocked__build): - """ - Test iterbacthes function. - - The iterbacthes function calls the generate_data function to - create batches of boostrapped data. - """ - # check NOT one hot encoded Ys - Xs = 'Xs' - Ys = 'Ys' - subject = gans.GAN(xshape=(4,), yshape=(2,), n_classes=10) - subject.generate_data = mock.MagicMock( - name='generate_data', return_value=('classes', 'points')) - kwargs = dict(bacthes=1, batch_size=5) - with mock.patch('deepchem.metrics.to_one_hot', - return_value='one_hot_classes') as mocked_one_hot: - result = list(subject.iterbatches(Xs, Ys, **kwargs)) - subject.generate_data.assert_called_with(Xs, Ys, 5) - expected_result = {subject._model.data_inputs[0]: 'points', - subject._model.conditional_inputs[0]: - 'classes'} - self.assertEqual(expected_result, result) - # check one hot encoded Ys - Xs = 'Xs' - Ys = [[0, 1], [1, 0], [1, 0]] - subject = gans.GAN(xshape=(4,), yshape=(2,), n_classes=10) - subject.generate_data = mock.MagicMock( - name='generate_data', return_value=('classes', 'points')) - kwargs = dict(bacthes=1, batch_size=5) - with mock.patch('deepchem.metrics.to_one_hot', - return_value='one_hot_classes') as mocked_one_hot: - result = list(subject.iterbacthes(Xs, Ys, **kwargs)) - subject.generate_data.assert_called_with(Xs, Ys, 5) - mocked_one_hot.assert_called_with('classes', 10) - expected_result = {subject._model.data_inputs[0]: 'points', - subject._model.conditional_inputs[0]: - 'one_hot_classes'} - self.assertEqual(expected_result, result) - return - - @unittest.mock.patch('gandy.models.gans.GAN._build', return_value='Model') - def test_generate_data(self, mocked__build): - """ - Test generate_data function. - - The generate_data function creates batches of boostrapped data. - """ - Xs = ['x1', 'x2', 'x3'] - Ys = ['y1', 'y2', 'y3'] - subject = gans.GAN(xshape=(4,), yshape=(2,), n_classes=1) - classes, points = subject.generate_data(Xs, Ys, 5) - self.assertEqual(len(classes), 5) - self.assertEqual(len(points), 5) - return - - @unittest.mock.patch('gandy.models.gans.GAN._build', return_value='Model') - def test_save(self, mocked__build): - """ - Test save function. - - This checks that a file is written with the appropriate name. - """ - # test path save - subject = gans.GAN(xshape=(4,), yshape=(2,), n_classes=10) - subject._model.save = mock.MagicMock(name='save') - subject.save('path') - subject._model.save.assert_called_with('path') - # test h5 save - subject.save('test_model.h5') - subject._model.save.assert_called_with('test_model.h5') - return - - @unittest.mock.patch('tf.keras.models.load_model', return_value='Model') - def test_load(self, mocked_load): - """ - Test load function. - - This checks that a Keras model instance is returned. - """ - # test load - subject = gans.GAN.load('test_model.h5') - self.assertEqaul(subject, 'Model') - return From 3be9d091f4f44bb84d5ef92282046c7c712188bc Mon Sep 17 00:00:00 2001 From: kmosko <73141484+kmosko@users.noreply.github.com> Date: Sat, 13 Mar 2021 17:57:39 -0800 Subject: [PATCH 91/99] Update metrics.py --- gandy/quality_est/metrics.py | 1 - 1 file changed, 1 deletion(-) diff --git a/gandy/quality_est/metrics.py b/gandy/quality_est/metrics.py index abad151..2df6db8 100644 --- a/gandy/quality_est/metrics.py +++ b/gandy/quality_est/metrics.py @@ -50,7 +50,6 @@ def __init__(self, predictions: Array, real: Array, uncertainties=None): self.predictions = predictions self.real = real self.uncertainties = uncertainties - self.calculate() return def calculate(self, **kwargs): From c2b8f37d8aeb2511e89efc926615ff36e8f32a6a Mon Sep 17 00:00:00 2001 From: evankomp Date: Sat, 13 Mar 2021 18:45:10 -0800 Subject: [PATCH 92/99] dependancies updated --- environment.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/environment.yml b/environment.yml index d09ed89..af492dc 100644 --- a/environment.yml +++ b/environment.yml @@ -9,6 +9,8 @@ dependencies: - pip=20.3.3 - scikit-learn=0.23.2 - scipy=1.5.2 - - tensorflow - optuna=2.5.0 - - tensorflow_probability + - pip: + - tensorflow + - tensorflow-probability + - deepchem==2.5.0.dev20210311184720 From edc03299a923ad07a2fbeeb55919d4d1f025687b Mon Sep 17 00:00:00 2001 From: evankomp Date: Sat, 13 Mar 2021 18:52:09 -0800 Subject: [PATCH 93/99] pep8 --- gandy/models/gps.py | 1 - gandy/models/models.py | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/gandy/models/gps.py b/gandy/models/gps.py index 9e13675..755f048 100644 --- a/gandy/models/gps.py +++ b/gandy/models/gps.py @@ -57,7 +57,6 @@ def __init__(self, super().__init__(xshape, yshape, model_type=model_type, **kwargs) return - def _build(self, model_type: str, **kwargs) -> Predictor: diff --git a/gandy/models/models.py b/gandy/models/models.py index 884d506..f3c7c0c 100644 --- a/gandy/models/models.py +++ b/gandy/models/models.py @@ -29,7 +29,7 @@ class NotImplimented(Exception): Args: inst (object): the class instance that raises this exception - caller (str): method name + caller (str): method name """ def __init__(self, inst, caller): @@ -96,7 +96,7 @@ def check(self, Returns: Xs (ndarray): the formated X data (optional) Ys (ndarray): the formated Y data if present - """ + """ if hasattr(Xs, 'shape'): pass else: From d62b71c6096acab60ebde4b1e9497d48b92817f5 Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Sun, 14 Mar 2021 11:06:29 -0700 Subject: [PATCH 94/99] Debugging nan loss --- examples/gan_demo.ipynb | 437 --------------------------- gandy/models/dcgan.py | 25 +- gandy/tests/test_models/test_gans.py | 44 +-- 3 files changed, 21 insertions(+), 485 deletions(-) delete mode 100644 examples/gan_demo.ipynb diff --git a/examples/gan_demo.ipynb b/examples/gan_demo.ipynb deleted file mode 100644 index 72bbe8f..0000000 --- a/examples/gan_demo.ipynb +++ /dev/null @@ -1,437 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#

Demo of GANs

" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "WARNING:root:This caffe2 python run does not have GPU support. Will run in CPU only mode.\n" - ] - } - ], - "source": [ - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "import sklearn.datasets\n", - "import sklearn.model_selection\n", - "import sklearn.preprocessing\n", - "import deepchem\n", - "\n", - "from gandy.models import gans" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'2.5.0.dev'" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "deepchem.__version__" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#

A regression task, using the boston dataset

" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "##

Get Data

" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "# load data\n", - "Xs, Ys = sklearn.datasets.load_boston(return_X_y=True)\n", - "Xs_train, Xs_test, Ys_train, Ys_test = sklearn.model_selection.train_test_split(Xs, Ys, train_size = 0.8)\n", - "\n", - "# normalize\n", - "x_norm = sklearn.preprocessing.Normalizer()\n", - "Xs_train = x_norm.fit_transform(Xs_train)\n", - "Xs_test = x_norm.transform(Xs_test)\n", - "\n", - "# scale the ys\n", - "SCALING_FACTOR = np.max(Ys_train)\n", - "Ys_train = Ys_train/SCALING_FACTOR\n", - "Ys_test = Ys_test/SCALING_FACTOR" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(13,)\n" - ] - } - ], - "source": [ - "xshape = (Xs_train.shape[1],) # remove zero dimension\n", - "print(xshape)\n", - "yshape = (1,) # regression" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "##

Initialize Model

" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "###

Hyperparam options!

\n", - "\n", - "### Below are the defaults for\n", - "\n", - "#### GAN stuff:\n", - "- ```n_classes=yshape[0]```\n", - "- ```noise_shape=(10,)```\n", - "\n", - "\n", - "### *ALL HYPERPARMS BELOW MUST START WITH*\n", - "```generator_``` or ```discriminator_```\n", - "\n", - "#### Network architecture:\n", - "\n", - "- ```layer_dimensions=[128]```\n", - "- ```dropout=0.05```\n", - "\n", - "\n", - "#### Layer kwargs:\n", - "\n", - "- ```activation='relu'```\n", - "- ```use_bias=True```\n", - "- ```kernel_initializer=\"glorot_uniform\"```\n", - "- ```bias_initializer=\"zeros\"```\n", - "- ```kernel_regularizer='l2'```\n", - "- ```bias_regularizer=None```\n", - "- ```activity_regularizer=None```\n", - "- ```kernel_constraint=None```\n", - "- ```bias_constraint=None```\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "C:\\Users\\saman\\Downloads\\Anaconda\\lib\\site-packages\\gandy\\models\\dcgan.py:96: UserWarning: Incorrect key learning_rate. Must start with generator_ or discriminator_\n", - " Must start with generator_ or discriminator_\")\n" - ] - } - ], - "source": [ - "\"\"\"\n", - "Specifying n_classes yields a cGAN (contional GAN),\n", - "whereas a normal GAN is instanciated if n_classes is not specified.\n", - "For our uncertainty estimator, we always want a cGAN, so always set n_classes.\n", - "\"\"\"\n", - "# According the Lee and Seok, flip x and y:\n", - "# todo why specifying learning rate make non nan loss?\n", - "GAN = gans.GAN(xshape=yshape, yshape=xshape, n_classes=13, noise_shape=(5,), learning_rate=1e-4)\n", - "\n", - "# A normal data generation call:\n", - "# GAN = gans.GAN(xshape=xshape, yshape=yshape, n_classes=1, noise_shape=(5,), learning_rate=1e-4)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "##

Train!

" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "###

Hyperparam options

\n", - "\n", - "#### Training:\n", - "- ```batches=50```\n", - "\n", - " (Number of batches of bootstrapped data, e.g., epochs, to train on)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Ending global_step 999: generator average loss 0.69536, discriminator average loss 1.38287\n", - "Ending global_step 1999: generator average loss 0.696301, discriminator average loss 1.38107\n", - "Ending global_step 2999: generator average loss 0.700762, discriminator average loss 1.37573\n", - "Ending global_step 3999: generator average loss 0.70827, discriminator average loss 1.37029\n", - "Ending global_step 4999: generator average loss 0.718148, discriminator average loss 1.36587\n", - "Ending global_step 5999: generator average loss 0.731029, discriminator average loss 1.36072\n", - "Ending global_step 6999: generator average loss 0.744675, discriminator average loss 1.35707\n", - "Ending global_step 7999: generator average loss 0.760214, discriminator average loss 1.35337\n", - "Ending global_step 8999: generator average loss 0.779749, discriminator average loss 1.35111\n", - "Ending global_step 9999: generator average loss 0.792294, discriminator average loss 1.34851\n", - "Ending global_step 10999: generator average loss 0.80483, discriminator average loss 1.34579\n", - "Ending global_step 11999: generator average loss 0.827481, discriminator average loss 1.3447\n", - "Ending global_step 12999: generator average loss 0.83564, discriminator average loss 1.34294\n", - "Ending global_step 13999: generator average loss 0.856997, discriminator average loss 1.3411\n", - "Ending global_step 14999: generator average loss 0.875456, discriminator average loss 1.34052\n", - "Ending global_step 15999: generator average loss 0.891027, discriminator average loss 1.33849\n", - "Ending global_step 16999: generator average loss 0.906972, discriminator average loss 1.33875\n", - "Ending global_step 17999: generator average loss 0.925623, discriminator average loss 1.33755\n", - "Ending global_step 18999: generator average loss 0.93856, discriminator average loss 1.33633\n", - "Ending global_step 19999: generator average loss 0.959721, discriminator average loss 1.33743\n", - "Ending global_step 20999: generator average loss 0.972299, discriminator average loss 1.3351\n", - "Ending global_step 21999: generator average loss 0.995185, discriminator average loss 1.33613\n", - "Ending global_step 22999: generator average loss 1.01666, discriminator average loss 1.33549\n", - "Ending global_step 23999: generator average loss 1.0242, discriminator average loss 1.33608\n", - "Ending global_step 24999: generator average loss 1.02922, discriminator average loss 1.33904\n", - "Ending global_step 25999: generator average loss 1.04247, discriminator average loss 1.34015\n", - "Ending global_step 26999: generator average loss 1.06492, discriminator average loss 1.34133\n", - "Ending global_step 27999: generator average loss 1.06534, discriminator average loss 1.34221\n", - "Ending global_step 28999: generator average loss 1.08166, discriminator average loss 1.34247\n", - "Ending global_step 29999: generator average loss 1.0789, discriminator average loss 1.34523\n", - "Ending global_step 30999: generator average loss 1.09527, discriminator average loss 1.34513\n", - "Ending global_step 31999: generator average loss 1.11206, discriminator average loss 1.34491\n", - "Ending global_step 32999: generator average loss 1.10704, discriminator average loss 1.34602\n", - "Ending global_step 33999: generator average loss 1.13349, discriminator average loss 1.34522\n", - "Ending global_step 34999: generator average loss 1.13885, discriminator average loss 1.34669\n", - "Ending global_step 35999: generator average loss 1.15736, discriminator average loss 1.34602\n", - "Ending global_step 36999: generator average loss 1.1358, discriminator average loss 1.34578\n", - "Ending global_step 37999: generator average loss 1.14593, discriminator average loss 1.34663\n", - "Ending global_step 38999: generator average loss 1.16952, discriminator average loss 1.34695\n", - "Ending global_step 39999: generator average loss 1.17907, discriminator average loss 1.34748\n", - "Ending global_step 40999: generator average loss 1.15456, discriminator average loss 1.34795\n", - "Ending global_step 41999: generator average loss 1.17172, discriminator average loss 1.34837\n", - "Ending global_step 42999: generator average loss 1.19077, discriminator average loss 1.34858\n", - "Ending global_step 43999: generator average loss 1.18499, discriminator average loss 1.34921\n", - "Ending global_step 44999: generator average loss 1.19698, discriminator average loss 1.34782\n", - "Ending global_step 45999: generator average loss 1.19372, discriminator average loss 1.34842\n", - "Ending global_step 46999: generator average loss 1.20151, discriminator average loss 1.34818\n", - "Ending global_step 47999: generator average loss 1.21182, discriminator average loss 1.34853\n", - "Ending global_step 48999: generator average loss 1.23156, discriminator average loss 1.34689\n", - "Ending global_step 49999: generator average loss 1.22769, discriminator average loss 1.34798\n", - "TIMING: model fitting took 475.032 s\n" - ] - } - ], - "source": [ - "# A normal data generation call:\n", - "# GAN.train(Xs_train, Ys_train, batches=50000)\n", - "\n", - "# Flipping x and y:\n", - "GAN.train(Ys_train, Xs_train, batches=50000)" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "100" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "GAN._model.batch_size # this is automaticaally baked into deepchem" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "##

Predict

" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "###

Hyperparam options

\n", - "\n", - "#### Prediction:\n", - "- ```num_predictions=100```\n", - "\n", - " (Number of predictions to make in order to sample uncertainties.)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "# Because this is a conditional GAN, MUST specify Ys or a warning will be thrown\n", - "\n", - "# A normal data generation call:\n", - "# preds, uncs, flags = GAN.predict(Xs_test, uc_threshold = 0.01, Ys=Ys_test)\n", - "\n", - "# Flipping x and y:\n", - "# preds, uncs, flags = GAN.predict(Ys_test, uc_threshold = 0.01, Ys=Xs_test)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "##

Results

" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.015745554\n" - ] - } - ], - "source": [ - "preds, uncs = GAN._predict(Ys_test, Xs_test, num_predictions=500)\n", - "thresh = 0.02\n", - "flags = uncs > thresh\n", - "print(np.average(uncs))" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Text(0.5, 1.0, 'Certain and uncertain predictions, boston data')" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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Tz6Zs846ixkzZnD++efn+RUiVIf/+q//yvkFQURERMqX0lqkQPfffz8NGzbk6KOP5pRTTmHOnDlceumlvPrqq4BvSV+4cCG/+tWvaNasGdu2bct5HHnkkXTt2pW33347X7k33XRTnsA8np07d/L3v/+dSy+9lNTU1DzbaNmyJSeccELONrKzs3n++edp3749N9xwQ76yqlQp/mlfv379nHSRcAsWLKBdu3Z07tw5Tx0PHDjA+eefz0cffcTevXsBqFOnDlu2bOGjjz4qdn2Kqk6dOnz55Zd88cUX5VYHERERyaWW8xJSEi3UyWrYsNF9UZcAACAASURBVGH07dsXM+PII4+kTZs2eW7W3Lp1K9u3b+ftt9+O2eVhtIC4TZs2ha7DunXryM7OZvbs2cyePTvqMscffzwA27ZtIzMzkwsvvLDUhplv1aoVVatWzTd9zZo17N27N27Xj9u2bePYY4/lwQcf5Le//S09evSgadOm9OrVi4svvpgrrriClJSUUql3pClTpjBo0CA6duzI8ccfzznnnEPv3r3p3bt3iXyJERERkcQoOJcCtW7dmvPOOy/mfOccAOeddx533313ocstbKt5+DYGDhzIkCFDoi5zxBFH5Fm2tAJziF135xwdO3bk0UcfjbluKHA/66yz+Pbbb3nrrbdYvHgxixcvZtGiRTzwwAN89NFHBfZWc+jQoaK/gMBll13Gxo0beeONN1iyZAnvvvsus2fPpkePHrz77rtl9iVBREREPAXnUmwNGzakbt267Nq1K24QXxwnnHACZsaBAwcK3EbDhg2pV69eoe4BKOkAvnXr1mzdupVzzz23UC3PNWvW5PLLL+fyyy8H/MioI0eOZPbs2Tk9ptSvX5+MjIx860b2ghNLQa+xfv36DBw4kIEDB+Kc4/e//z1//vOfee211+jbt2+htiEiIiIlQ79bS7FVqVKFAQMGsGzZMl588cWoy0Tr6jARRx11FBdddBEvv/wyS5cuzTffOcfWrVtz6tOvXz+++uqrqCkwoZZ18MExEDX4LYrBgwfz448/xmw5/89//pPz/7Zt2/LNP+200/LVp02bNnzyySfs2bMnZ1pmZmZOl5MFifUas7Ky2LFjR55pZsapp54adXkREREpfWo5lxIxadIk/vd//5crr7ySK6+8kq5du5KSksKmTZt444036Ny5c4H9hxfkqaeeonv37vzqV79i8ODBnHrqqWRnZ7N+/Xpee+01Bg8enNNbywMPPMD777/P9ddfz9tvv0337t1xzrFq1SoOHTqU03Vi165dmTZtGiNGjODiiy+mevXqnHnmmRx33HFFquNtt93GO++8w1133cX777/PueeeS+3atdm8eTPvvfceqampLF68GID27dvTtWtXzjzzTJo2bZrT5WFKSgpXX311Tpk333wzAwcO5Nxzz2XQoEHs2LGDmTNn0qJFi5wBm+I5/fTTqVKlCpMmTSIzM5MjjzyS4447jrZt29KkSRMuvfRSTj31VI4++mg2bNjAU089Rb169ejdu3eR9oGIiIgUg3NOj+DRuXNnV5CvvvqqwGUqisWLFzvAPfTQQ4Va/pdffnETJ050HTp0cKmpqa5mzZquXbt27vrrr3dLly7NWW7u3LkOcIsXL45azpAhQ5w/NfPbunWru/POO13r1q1djRo1XJ06dVyHDh3crbfe6r788ss8y2ZmZrq77rrLtWrVylWvXt3Vr1/fde/e3b3wwgs5y2RlZbk77rjDNWvWzFWpUsUBbu7cuXFfZ4sWLVzPnj1jzj948KB77LHHXJcuXVxaWppLS0tzJ5xwguvfv7976623cpb74x//6Hr06OEaNmzoUlJS3DHHHOOuuOIKt3Llynxl/vnPf3bNmzd3KSkprl27dm727NlR92OsfTtv3jzXvn17V716dQe4IUOGuP3797vf//737vTTT3f169d3KSkprkWLFu6aa65xX3/9ddx94Fzlei+IiEgFs2CBczVqOAfOtWjhn5cxYIWLEo+aC/uJv7Lr0qWLW7FiRdxl1qxZkzOQi0hlpveCiIgclhYuhGHDICxdlLQ0mDEDBgwos2qY2UrnXJfI6co5FxEREZHKY+zYvIE5+Odjx5ZPfSIoOBcRERGRymPz5sSmlzEF5yIiIiJSeTRvntj0MqbgXEREREQqj0mTfI55uLQ0Pz0JKDgXERERkcpjwAB/82eNGv55ixZlfjNoPOrnXEREREQqlwEDYOZM/396erlWJZJazkVEREREkoSCcxERERGRJKHgXEREREQkSSg4FxERERFJEgrOk0y/l16g30svlHc1RERERKQcKDgXEREREUkSCs5FRERERJJEUgbnZnaPmf3VzNabmTOzjUUsZ7CZrTKzvWb2HzObZWYNS7i6Ugays7OZPHky7dq1IzU1lWOPPZY77riDX375pVTK+Prrrxk3bhxdu3alYcOG1KpVi06dOjFp0qSEtikiIiJJKj096fo4hyQNzoEHgXOBb4HMohRgZqOB+cBO4DbgaeBqIN3MjiyhekoZGT16NLfffjsnnngijz/+OH379mXq1Kn07t2b7OzsEi9jzpw5TJ48mVatWjFu3Dgeeugh2rZty7333ku3bt3Yu3dvabxMERERqeSSdYTQVs659QBm9gVQM5GVzawB8ACwHPi1cy4rmL4ceB0frD9YojWWItmzZw/vv/8+3bt3p27dulGX+fLLL3n88cfp06cPL730Us704447jltvvZXnn3+e/v37x91OomVcccUV3HPPPdSpUydn2vDhw2ndujWTJk1i9uzZ3HzzzUV92SIiIiJRJWXLeSgwL4bfAmnA46HAPCj3b8B6YGAxyy8Vr61bw6off+DTLd/Rfe4MXlu3pryrlGPJkiVccsklNGzYkKpVq2JmeR49evQodFlff/01U6ZM4YILLqB+/fr07t2bbdu2xVz+ueeewznHqFGj8ky/4YYbSEtLY8GCBQVuM9EyunTpkicwD7nqqqsA+OKLLwrcpoiIiEiikrXlvLhOD/5+EmXeUqCfmdV0zv0cPmPlypWYWcxCnXMlV8MIr61bw5j33uZAlv8u8f3u3Yx5720ALmvbvtS2Wxjz58/n2muvpUmTJowcOZKjjjqKl19+mfT0dOrWrUvPnj05//zzY66/d+9eFi9ezD//+U/eeOMN1q/3371OOukkbr31Vi666CKOO+64mOsvX76cKlWqcMYZZ+SZnpqaSqdOnVi+fHmBr6EkygD47rvvAGjUqFGhlhcREZHktGPHDp577jmGDx8eN/4raxU1OG8a/N0SZd4WwIJlvi6zGhXgoY8/ZO+hQ3mm7T10iIc+/rBcg/P169czfPhw2rVrx0cffUS9evUAn+Jx0kknsWnTJhYtWkRaWlqe9fbt28fMmTN54403SE9PZ9++fRx55JGce+653HXXXVx88cUce+yxharD999/T4MGDahRo0a+ec2aNePjjz/mwIEDpKSklGoZWVlZTJw4kWrVqhWYRiMiIiLJKTs7m2eeeYbf/e53bN++ne7du9OxY8fyrlaOpExrKQGhSHF/lHn7IpbJ0blzZ5xzMR+l6YfduxOaXlYmT56cE2iHAnOA6tWr06tXLw4cOMCmTZvyrffjjz9y66238uabb5Kdnc2dd97J999/z+uvv87w4cMLHZiDz0uPFlSDb/kOLVPaZYwaNYqlS5cyceJE2rZtW1C1RUREJMmsWrWK7t27c80119C6dWtWrlyZVIE5VNzgPBRlRYvGUiOWSQpNatVKaHpZef311znhhBPo1q1bvnn79/vvPjVr5r9ft3Hjxjz22GNccMEFVKlShYcffpgmTZrQu3dvnnzySTZs2FDoOqSlpeVsK9K+fftylinNMu677z6mTZvGsGHDuOeeewpTbREREUkSO3bs4JZbbqFLly588803zJ07lw8//JBOnTqVd9XyqajB+ffB32ZR5jUDXNgySeGubj04olreLKMjqlXjrm6Fv9GypO3YsYPNmzdzyimnRJ2/bNkyGjduHLUVPDU1NaflPCMjg7///e9cc801fPXVV4wcOZLjjz+edu3aMXr0aN5++20ORaT0hGvatCnbtm2LGlxv2bKFBg0axE1HKW4Z48eP54EHHuCaa65h+vTpcbcjIiIiySM7O5u5c+fSpk0bnnzySUaMGMHXX3/N0KFDqVIlOcPg5KxV8YXu7jsryrwzgXWRN4OWt8vatufBX/+GlKpVAWhaqxYP/vo35ZpvvmvXLoCoQeuyZctYu3YtV155ZYHlHHHEEVx88cVMmzaNb7/9lrVr1/Loo4/SvHlznnrqKS644AI2btwYc/3TTz+d7Oxsli1blmf6vn37WL16NV26dCmwDkUtY8KECUyYMIHBgwcza9aspLphRERERGILpbBce+21OSksjz/+eMyum5NGvBzrZHgAXwAb48xvDrQDqodNa4hPW/kUqBo2vTe+1fzeaGV17tzZFeSrr74qcJniuPrF593VLz5fqtsorP3797vU1FTXtGlTt2fPnpzpGRkZrmPHjq527druu+++K9Y2fv75Z/f666+7zMzMmMv861//cmbm+vTpk2f61KlTHeCeffbZnGkHDhxwa9ascZs2bSpyGSETJkxwgBs0aJDLysoqysur0Er7vSAiIlIUGRkZbuTIka5KlSquYcOGbu7cudE/x3v29I9yAqxwUeLRpOytxcwGAS2Cpw2BFDO7N3i+yTn3bNjizwA9geOAjQDOua1mdh/wMPCumT2HT2e5A1gLTCn1F1EBpKSkcOONN/LYY49xzjnn0L9/fzIyMpg9ezaZmZm88sorNGsWLXPIp8RMmVL43Xz22WfHnNexY0dGjhzJtGnT6NOnDxdddBFr1qxh6tSp9OzZM0/PKVu2bKF9+/b07NmT9LAheRMpA+CJJ57g/vvvp3nz5px33nksWrQoz/xGjRrF7T5SREREylZ2djbz58/n7rvvZvv27YwYMYI//OEPyd9SHilaxF7eDyAd38Id7ZEeY9mWUcoZCnyO76HlJ2AOcHSs7arlPL8DBw64MWPGuObNm7vq1au7xo0bu8GDB7t169bFXW/Dhg2xjl/Ux7///e+45R06dMg9/PDDrk2bNi4lJcU1bdrUjR492u3evTvqdntG+SZc2DKcc27IkCFx6xut/MpGLeciIpIsVq5c6bp27eoA161bN7dq1aqCV0rSlnNzpdxF4OGkS5cubsWKFXGXWbNmDe3bl14eeL+XXgDgucuvKrVtiJSE0n4viIiIFCQzM5P77ruPp556iqOOOoqHHnqIQYMGFe5mz1CL+o4dpVvJGMxspXMu301vSZnWIiIiIiISS4VJYYlCwXmSUYu5iIiISGyrVq1ixIgRLF26lG7duvH2228nZX/lRVVRu1IUERERkQokMzOTm2++mS5duvDtt98yb968pB1IqDjUci4iIiIiSStaCsvEiROpV69eeVetVCg4FxEREZGktGrVKkaOHMknn3xSIVNYolFai4iIiIgklfAUlm+++Ya5c+dWyBSWaBSci4iIiEhSyM7OZu7cubRt25annnqKESNG8PXXXzN06NDCdY9YWAsXwq5dsHMntGzpnycJpbWIiIiISLkrsxSWhQth2DAIjfWzaZN/DjBgQMlvL0FqORcRERGRchOZwlLqvbCMHQt79uSdtmePn54E1HIuIiIiImUusheWkSNHMnHixNIfSGjTpsSmlzG1nCebXr38Q0RERKSC+uyzzzj77LO59tpradOmDStXrmTq1KllM8Jn1aqJTS9jCs5FREREpExkZmYycuRITj/9dNavX8/8+fPLvheWrKzEppcxBeciIiIiUqpCvbC0adOG6dOnc/PNN7Nu3ToGDx6MmZVtZVq0SGx6GVNwLoeF7OxsJk+eTLt27UhNTeXYY4/ljjvu4JdffimVMr7++mvGjRtH165dadiwIbVq1aJTp05MmjQpoW2KiIhUduEpLG3btuWzzz7jscceK5sUlmgmTYK0tLzT0tL89CSg4FwOC6NHj+b222/nxBNP5PHHH6dv375MnTqV3r17k52dXeJlzJkzh8mTJ9OqVSvGjRvHQw89RNu2bbn33nvp1q0be/fuLY2XKSIiUmFEprCEemE55ZRTyrdiAwbAjBkQarFv0cI/T4JuFAFwzukRPDp37uwK8tVXXxW4TLH07Okfh7mff/7ZrVu3rkTK+uKLL5yZuT59+uSZPnXqVAe4hQsXlngZy5cvdzt27MhXztixYx3gHn/88SK8koql1N8LIiJyWMrKynKzZ892DRo0cFWqVHG33nqry8zMLO9q5Venjn+UE2CFixKPquU8mSxcCEuXwpIlSTda1ZIlS7jkkkto2LAhVatWxczyPHr06JFn+a1bt9K2bVvOOOMMpk6dyk8//VTkbT/33HM45xg1alSe6TfccANpaWksWLCgxMvo0qULderUyVfOVVddBcAXX3yR6MsQERGp8EIpLNddd11ypLAchhScJ4vQaFX79/vnodGqkiBAnz9/Pueeey6rV69m5MiRTJkyhV5Bd49169blsssu4+qrr86zTpMmTZg6dSpVq1bltttuo1mzZlx00UU899xz7Ins+L8Ay5cvp0qVKpxxxhl5pqemptKpUyeWL19eJmUAfPfddwA0atSokLUXERGp+EIpLF26dMlJYfnggw/KP4XlcBStOb2yPso1raVFC+f8QLJ5Hy1alM72Cunbb791qamp7sQTT3QZGRk50w8cOOBat27tUlJS3C+//BK3jG+++cZNnDjRtWvXzgGuZs2abvDgwe7tt992WVlZBdahQ4cO7uijj446r2/fvg5w+/fvL/UyDh065Lp27eqqVavm1q5dW2C9KzqltYiIyGGTwhKN0lokrs2bE5teRiZPnsy+ffuYOXMm9erVy5levXp1evXqxYEDB9hUwIharVq14r777mPNmjWsXLmSYcOG8d577/Gb3/yGY445psBeV/bs2UONGjWizktNTc1ZJp6SKGPUqFEsXbqUiRMn0rZt27jLioiIVHRKYSkdCs6TRfPmiU0vI6+//jonnHAC3bp1yzdvf5CCU7NmzUKXd9ppp/HII4+wdOlSLrnkEn744QceffRRtm7dGnOdtLS0nG1F2rdvX84y8RS3jPvuu49p06YxbNgw7rnnnrjbEhERqcgiU1hCAwkphaVkKDhPFknY5+aOHTvYvHlzzDfbsmXLaNy4Mccee2yhy5s9eza//vWvadGiBW+++SYXXnghCxcu5Jhjjom5XtOmTdm2bVvU4HrLli00aNCAlJSUuNsuThnjx4/ngQce4JprrmH69OkFvEoREZGKKTs7mzlz5uQMJHTLLbeU30BCJaFTJ/9IMgrOk0Woz81Q6kUS9Lm5a9cugKhB67Jly1i7di1XXnll3DL27dvHiy++SJ8+fWjcuDHXX389O3fu5NFHH2XLli3885//pH///lSrVi1mGaeffjrZ2dksW7YsX9mrV6+mS5cuBb6WopYxYcIEJkyYwODBg5k1a9bhefEREREpJqWwlB0F58lkwADo2hV69oSNG8u9M/zGjRuTmprKkiVL8gy6k5mZyfXXX0/t2rX53e9+F3Xd3bt3M3ToUBo1akTfvn1ZtWoVd9xxB2vWrGHFihXcdtttHH300YWqx1VXXYWZMWXKlDzTZ86cyZ49exgQtp8OHjzI2rVr2RyRq59IGSETJ05k/PjxDBo0iLlz51Klit4uIiJSuSiFpezFbq6USi8lJYUbb7yRxx57jHPOOYf+/fuTkZHB7NmzyczM5JVXXqFZs2ZR192+fTuvvfYaV111FQMHDqRHjx5FbnXu2LEjI0eOZNq0afTp04eLLrqINWvWMHXqVHr27En//v1zlt2yZQvt27enZ8+epKenF6kMgCeeeIL777+f5s2bc95557Fo0aI88xs1asT5559fpNcjIiKS7LKzs5k3bx533303GRkZ3HLLLUyYMKFitZSHxQnJRMG5xPXQQw9x5JFHsmDBAu68806OOuoofvOb3zB27FjatGkTc71mzZrx448/xuwhJVFTpkyhZcuWzJgxg3/84x80aNCAW265hYkTJxa6RTuRMkL9nm/evJkhQ4bkK6tnz54KzkVEpEL67LPPGDFiBJ9++ilnn302TzzxhFrKy5D5bhYFoEuXLm7FihVxl1mzZg3t27cvvUoEg/sk67c5kZBSfy+IiEiZysjI4N5772X69Ok0bNiQP//5z4fvzZ6HATNb6ZzLd9ObkmhFREREKrFQLyxt27bl6aefzumFZciQIQrMy4HSWpKNWsxFRESkjCiFJfmo5VxERESkksnIyGDEiBF06dKFDRs28Mwzz6gXliSh4FxERESkkoiVwjJo0CClsCQJBeciIpJcevXKvTleRErM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8S7ES5TJjxUJ2l1TQ8fVa9WFJUBN5aJlRXdERCRfqDiXvJUok9+vrJzrTqjAbd2Kcw42b+bK0o4seOD/1IUlQE3homVFd0REJJ8o1pIHFLeILRybGPHKi9TU1VEamjE/+4gjufPOOxk3bhzbdu6k/ILzefvhR+POlOvnm77KHj0ZOfelBtGWYruoVtEdERHJJyrOJafCcYKaujpOeWhK/ax4WHQmf968eRx/0Q9Yvnw55557Lpx1Jm2/8Q1FWLIk3hekYipam0p0R0RECoOKc8mZeHECoEHxN63/paxZs4YBAwYwffp0Dj/8cObMmcN5552Xk3E3NcV+0XK8i4uLKbojIiKFQ5lzyZlkvcoBdu3axZ133knXrl3ru7AsW7ZMhbkERusNiIhIPtHMueRMsjjBq6++ytChQ6mqquK8885jwoQJHHHEEY05RGkCmkJ0R0RECoeKc0nZgID7hceLE3y9dRsuu+wyZsyYoQhLAkH/fSRSjHGWSMUe3RERkcKhWIs0EK/neDbEihM0d44PHn6UZ555RhEWERERaXI0cy450yBOUFsLW7aw9uk59GpXysRly9SvXERERJocFedSL1lbw2yoaLs/26c9yScLF3H44YczbeJEzZSnIBd/V8VOcRYREckHirUIkHyVxKCXN4/swrL6b2/zzUt+oAhLirSipQSpMaNsIiKSnIpzARK3NQy6GJw3bx7HH388lZWV9OrViw9WrGDpjCe1kFCKUmlBKSIiIoVJxbkAidsaBlUMhhcSOv3009m+fTtz5szh2WefVXtEn7SipQQl6N+IiYhI5lScCxB/NcT2JSUZF4OREZZnnnmGm266SRGWDCT6uxJJleJRIiL5ScW5AIlXScykGHz11Vc57rjjqKyspHfv3ixbtoyxY8cqwpIBrWgpQVA8SkQkP6k4F8Brazi+T19aNmsGQGlJCeP79KVfWXlaxeCaNWu47LLL6NOnDzt37uTZZ59lzpw5irAEINHflUiqFI8SEclPaqUo9eKtkuhnefNdu3YxceJExo0bR21tLePGjWP48OHsu+++jfMmmgitaCmZirdCr+JRIiK5Zc65XI8hb1RUVLjFixfnehh5K9ly8a+++ipDhw6lqqqK888/nwkTJnD44Yc35hBFJEXhzHlktKV18+b6LYyISCMxs3eccxXR2xVrkYzFirDMnj1bhblIHlM8SkQkP+VlcW5m+5jZr8xshZntMLNPzewuM9svhWOPNrObzexNM9tkZtVm9q6ZjUrleEldTU0Nd9xxB2VlZcyePbu+C8u5556b0Xm1KIpI4+hXVs4J7dpzcodDeX3QYBXmIiJ5IF8z53cD1wJPA3cB5aHHJ5jZGc653QmO/TEwBJgDTAV2Ab2B3wKXmFl359z2bA6+WEXGWebOncvQoUNZsWJFgwhLsuiLiIiIiMSXd8W5mR0LXAPMcs71j9j+EXAPcBnweIJTPAX8zjn3VcS2+83sA2AUcDUwOfCBNxFr1qxh2LBhzJgxgyOOOILnnnuOc845J9fDEpE06Yu0iEh+ycdYywDAgAlR2x8AtgGXJzrYObc4qjAPC+ckvpnxCJug6AjLuHHjWLZsWYPCPIjVBrVioYiIiDRleTdzDpwI7Ab+FrnRObfDzN4NPZ+OQ0P3GzIYW5MUL8ISKd5qg0DKOdYgziEiIiJSyPJx5rwU+Mw5tzPGc2uAQ8yspZ8TmlkzYAxQS4JIzDvvvIOZxb01NatXr+bSSy/ljDPOoKamhueeey5uF5YgVhvUioUiIiLS1OVjcd4GiFWYA+yI2MePCUB3YIxzbmW6A2sqwhGWrl27MmfOnJgRlmhBrDaoFQtFRESkqcvH4nwb0CrOc/tG7JMSM/sNMBSY4pz7XaJ9u3XrhnMu7q0pmDt3LscddxzDhw+nT58+LF++nDFjxiRd4TPeqkmFH5UAABKZSURBVIJ+VhsM4hwiIiIihSwfi/O1eNGVWAV6B7zIS00qJzKzm4AbgYeAnwU2wiIUHWHxu5BQZY+etG7e8BKG1s2bU9mjZ8pjCOIcIiIiIoUsH4vzt/HGdVLkRjPbFzgeWJzKScxsLDAWeAT4iWsqU98+xYuw+F1IKIjVBrVioYiIiDR1+ditZQYwEvglEHkl4E/xsuZTwxvMrAvQwjm3IvIEZjYGuAl4FBiUZNGiJuvVV19l6NChVFVVxe3C4ke/snKmv78ESL93chDnEBERESlUeVecO+eWmtm9wFAzmwX8mT0rhL5Gw24rc4FOeH3RATCzIcA44BPgFeCHUZ1WNjjnXs7qm8hzq1evZtiwYTzxxBOBLySkglpEREQkfXlXnIf8ElgFDAbOAT4DJuF1W0k2Cx7ug94ReDjG868BTbI4r6mpYcKECdx8883U1dUxbtw4hg8fnvRiz8amAl9ERESaKlMUe4+Kigq3eHFKkfaCk8pCQiIiIiLSOMzsHedcRfT2fLwgVALkZyEhEREREcktFedFqqamhttvv93XQkIiIiIiklv5mjmXDLzyyitcc801irCIiIiIFBjNnBeRcITlzDPPVIRFREREpACpOC8C0RGWm2++WREWERERkQKkWEuBU4RFREREpHho5rxArV69mksuuUQRFhEREZEiouK8wNTU1HDbbbfRtWtXnn32WUVYRERERIqIYi0F5JVXXmHo0KGsXLmS8847j4kTJ2qmXERERKSIaOa8AER2Ydm1axfPPfccc+bMUWEuIiIiUmRUnOcxLSQkIiIi0rQo1pKn5s6dy9ChQ9WFRURERKQJ0cx5ngl3YTnjjDPUhUVERESkiVFxnifUhUVEREREFGvJAy+//DLXXHMNK1eupF+/ftx9992aKRcRERFpglSc59iSJUvo27cvXbp04fnnn+fss8/O9ZBEREREJEdUnOfYt7/9bZ544gnOO+889t1331wPR0RERERySMV5Hrj44otzPQQRERERyQO6IFREREREJE+oOBcRERERyRMqzkVERERE8oSKcxERERGRPKHiXEREREQkT6g4FxERERHJEyrORURERETyhIpzEREREZE8oeJcRERERCRPqDiXhMwMM8v1MKQA6bMj6dDnRtKhz42kI18/NyrORURERETyhIpzEREREZE8oeJcRERERCRPqDgXEREREckTKs5FRERERPKEinMRERERkTyh4lxEREREJE+Ycy7XY8gbZrYJ+DjX4xARERGRotfJOfe16I0qzkVERERE8oRiLSIiIiIieULFuYiIiIhInlBxLiIiIiKSJ1Sci4iIiIjkCRXnTZCZ7WNmvzKzFWa2w8w+NbO7zGy/FI492sxuNrM3zWyTmVWb2btmNiqV46WwZfLZiXGuNmb2kZk5M5ucjfFKfgjic2NmB5vZnWb2r9A5NpnZPDPrmc2xS+5k+rkxs7ZmNtLMlob+X/WZmS0ys6vMzLI9fskNM7vBzJ40sw9D/39ZleZ5rjCzf5jZdjPbYGZ/NLO9OqtkQ/PGeBHJO3cD1wJPA3cB5aHHJ5jZGc653QmO/TEwBJgDTAV2Ab2B3wKXmFl359z2bA5eciqTz060m4FDgh+i5KGMPjdm1gmYD7QF/g/4J3AA8G2gQ/aGLTmW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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "# figure\n", - "fig, ax = plt.subplots(figsize=(12,8))\n", - "\n", - "# plotting predicted vs true\n", - "# scatter\n", - "ax.errorbar(Ys_test.reshape(-1,1)[flags == False],\n", - " preds[flags == False], yerr = uncs[flags == False], ls='none',\n", - " c=plt.cm.viridis(0.5), marker='o', label=f'$\\sigma \\leq {thresh}$')\n", - "ax.errorbar(Ys_test.reshape(-1,1)[flags == True],\n", - " preds[flags == True], yerr = uncs[flags == True], ls='none',\n", - " c='r', marker='o', label=f'$\\sigma > {thresh}$')\n", - "min_y, max_y = np.min(Ys_test.reshape(-1,1)), np.max(Ys_test.reshape(-1,1))\n", - "# perfect results\n", - "x = np.linspace(min_y, max_y, 100)\n", - "ax.plot(x, x, 'k-', label= \"Perfect results\")\n", - "\n", - "# axes labels formatting\n", - "ax.set_xlabel('True target', fontsize=24)\n", - "ax.set_ylabel('Predicted target', fontsize=24)\n", - "\n", - "# tick formatting\n", - "plt.setp(ax.get_xticklabels(), fontsize=18)\n", - "plt.setp(ax.get_yticklabels(), fontsize=18)\n", - "ax.tick_params(direction='in', width=2, length=8)\n", - "\n", - "# legend & title\n", - "plt.legend(fontsize=18)\n", - "plt.title('Certain and uncertain predictions, boston data', size=24)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/gandy/models/dcgan.py b/gandy/models/dcgan.py index 9481d47..d15e9be 100644 --- a/gandy/models/dcgan.py +++ b/gandy/models/dcgan.py @@ -17,7 +17,8 @@ # deep learning imports import deepchem import tensorflow as tf -from tensorflow.keras.layers import Concatenate, Dense, Dropout, Input, LeakyReLU +from tensorflow.keras.layers import Concatenate, Dense, Input +from tensorflow.keras.layers import Dropout, LeakyReLU # typing imports from typing import Tuple, Type @@ -57,8 +58,8 @@ def __init__(self, xshape, yshape, noise_shape, **kwargs): # base hyperparameters for generator and discirminator - Base_hyperparams = dict(layer_dimensions=[32, 32], - dropout=0.1, + Base_hyperparams = dict(layer_dimensions=[128], + dropout=0.05, activation='relu', use_bias=True, kernel_initializer="glorot_uniform", @@ -165,9 +166,7 @@ def create_generator(self): gen = Dropout(dropout)(gen) # generator outputs - final_layer_kwargs = layer_kwargs.copy() - final_layer_kwargs.update(activation='sigmoid') - gen = Dense(self.yshape[0], **final_layer_kwargs)(gen) + gen = Dense(self.yshape[0], **layer_kwargs)(gen) # final construction of Keras model generator = tf.keras.Model(inputs=[noise_in], @@ -227,20 +226,16 @@ def create_discriminator(self): # every other kwarg is for the layers layer_kwargs = {key: kwargs[key] for key in kwargs.keys() - {'layer_dimensions', 'dropout'}} - # removing activation to implemetn LeakyReLU - layer_kwargs.update(activation=None) # construct input data_in = Input(shape=self.yshape) # build first layer of network discrim = Dense(layer_dimensions[0], **layer_kwargs)(data_in) - discrim = LeakyReLU()(discrim) # adding dropout to the weights discrim = Dropout(dropout)(discrim) # build subsequent layers for layer_dim in layer_dimensions[1:]: discrim = Dense(layer_dim, **layer_kwargs)(discrim) - discrim = LeakyReLU()(discrim) discrim = Dropout(dropout)(discrim) # To maintain the interpretation of a probability, @@ -488,9 +483,7 @@ def create_generator(self): gen = Dropout(dropout)(gen) # generator outputs - final_layer_kwargs = layer_kwargs.copy() - final_layer_kwargs.update(activation='sigmoid') - gen = Dense(self.yshape[0], **final_layer_kwargs)(gen) + gen = Dense(self.yshape[0], **layer_kwargs)(gen) # final construction of Keras model generator = tf.keras.Model(inputs=[noise_in, conditional_in], @@ -551,7 +544,7 @@ def create_discriminator(self): layer_kwargs = {key: kwargs[key] for key in kwargs.keys() - {'layer_dimensions', 'dropout'}} # removing activation to implemetn LeakyReLU - layer_kwargs.update(activation=None) + # layer_kwargs.update(activation=None) # construct input data_in = Input(shape=self.yshape) @@ -560,13 +553,13 @@ def create_discriminator(self): # build first layer of network discrim = Dense(layer_dimensions[0], **layer_kwargs)(discrim_input) - discrim = LeakyReLU()(discrim) + # discrim = LeakyReLU()(discrim) # adding dropout to the weights discrim = Dropout(dropout)(discrim) # build subsequent layers for layer_dim in layer_dimensions[1:]: discrim = Dense(layer_dim, **layer_kwargs)(discrim) - discrim = LeakyReLU()(discrim) + # discrim = LeakyReLU()(discrim) discrim = Dropout(dropout)(discrim) # To maintain the interpretation of a probability, diff --git a/gandy/tests/test_models/test_gans.py b/gandy/tests/test_models/test_gans.py index 980f86f..0a76d8c 100644 --- a/gandy/tests/test_models/test_gans.py +++ b/gandy/tests/test_models/test_gans.py @@ -26,7 +26,7 @@ def test__build(self): # CHECK (normal) GAN # create gan instance subject = gans.GAN(xshape=(4,), yshape=(2,)) - kwargs = dict(noise_shape=(5,)) + kwargs = dict(noise_shape=(5,), conditional=False) subject._build(**kwargs) # created mocked functions subject._model.create_generator = unittest.mock.MagicMock( @@ -45,7 +45,7 @@ def test__build(self): # create gan instance subject = gans.CondGAN(xshape=(4,), yshape=(2, 4)) self.assertTrue(issubclass(gans.CondGAN, gans.GAN)) - kwargs = dict(noise_shape=(5,), n_classes=4) + kwargs = dict(noise_shape=(5,)) subject._build(**kwargs) # assert create generator function called subject._model.create_generator.assert_called_once_with(kwargs) @@ -124,34 +124,17 @@ def test_iterbacthes(self, mocked__build): # check NOT one hot encoded Ys Xs = 'Xs' Ys = 'Ys' - subject = gans.GAN(xshape=(4,), yshape=(2,), n_classes=10) - subject.generate_data = mock.MagicMock( - name='generate_data', return_value=('classes', 'points')) - kwargs = dict(bacthes=1, batch_size=5) - with mock.patch('deepchem.metrics.to_one_hot', - return_value='one_hot_classes') as mocked_one_hot: - result = list(subject.iterbatches(Xs, Ys, **kwargs)) - subject.generate_data.assert_called_with(Xs, Ys, 5) - expected_result = {subject._model.data_inputs[0]: 'points', - subject._model.conditional_inputs[0]: - 'classes'} - self.assertEqual(expected_result, result) - # check one hot encoded Ys - Xs = 'Xs' - Ys = [[0, 1], [1, 0], [1, 0]] - subject = gans.GAN(xshape=(4,), yshape=(2,), n_classes=10) + subject = gans.GAN(xshape=(4,), yshape=(2,)) subject.generate_data = mock.MagicMock( name='generate_data', return_value=('classes', 'points')) - kwargs = dict(bacthes=1, batch_size=5) - with mock.patch('deepchem.metrics.to_one_hot', - return_value='one_hot_classes') as mocked_one_hot: - result = list(subject.iterbacthes(Xs, Ys, **kwargs)) - subject.generate_data.assert_called_with(Xs, Ys, 5) - mocked_one_hot.assert_called_with('classes', 10) - expected_result = {subject._model.data_inputs[0]: 'points', - subject._model.conditional_inputs[0]: - 'one_hot_classes'} - self.assertEqual(expected_result, result) + kwargs = dict(bacthes=1) + result = list(subject.iterbatches(Xs, Ys, **kwargs)) + subject.generate_data.assert_called_with(Xs, Ys, + subject.model.batch_size) + expected_result = {subject._model.data_inputs[0]: 'classes', + subject._model.conditional_inputs[0]: + 'points'} + self.assertEqual(expected_result, result) return @unittest.mock.patch('gandy.models.gans.GAN._build', return_value='Model') @@ -163,7 +146,7 @@ def test_generate_data(self, mocked__build): """ Xs = ['x1', 'x2', 'x3'] Ys = ['y1', 'y2', 'y3'] - subject = gans.GAN(xshape=(4,), yshape=(2,), n_classes=1) + subject = gans.GAN(xshape=(4,), yshape=(2,)) classes, points = subject.generate_data(Xs, Ys, 5) self.assertEqual(len(classes), 5) self.assertEqual(len(points), 5) @@ -181,9 +164,6 @@ def test_save(self, mocked__build): subject._model.save = mock.MagicMock(name='save') subject.save('path') subject._model.save.assert_called_with('path') - # test h5 save - subject.save('test_model.h5') - subject._model.save.assert_called_with('test_model.h5') return @unittest.mock.patch('tf.keras.models.load_model', return_value='Model') From 3ab53e813175f1c11086f98944c1a3add578f960 Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Sun, 14 Mar 2021 15:07:11 -0700 Subject: [PATCH 95/99] Fixed all the unit test errors/ fails :) --- gandy/models/gans.py | 11 ++- gandy/tests/test_models/test_gans.py | 113 ++++++++++----------------- 2 files changed, 46 insertions(+), 78 deletions(-) diff --git a/gandy/models/gans.py b/gandy/models/gans.py index 1e86553..d820e5e 100644 --- a/gandy/models/gans.py +++ b/gandy/models/gans.py @@ -231,7 +231,6 @@ def save(self, filename: str, **kwargs): """ # save model # filename could be a path or end with .h5 - print(f"Saving model as {filename}") self._model.save(filename) return None @@ -250,16 +249,16 @@ def load(cls, filename: str, **kwargs): """ # call Keras.load function if filename.endswith('.h5'): - model = tf.keras.model.load_model(filename, compile=False) + model = tf.keras.models.load_model(filename, compile=False) else: path_to_model = filename - model = tf.keras.model.load_model(path_to_model) + model = tf.keras.models.load_model(path_to_model) # get x and y shape - xshape = None - yshape = None + xshape = model.input_shape[1:] + yshape = model.layers[-1].get_config()['event_shape'] # instantiate instance = cls.__new__(cls) instance._xshape = xshape instance._yshape = yshape instance._model = model - return model + return instance diff --git a/gandy/tests/test_models/test_gans.py b/gandy/tests/test_models/test_gans.py index 0a76d8c..3866882 100644 --- a/gandy/tests/test_models/test_gans.py +++ b/gandy/tests/test_models/test_gans.py @@ -6,6 +6,8 @@ import gandy.models.gans as gans import gandy.models.models +import numpy as np + class TestGAN(unittest.TestCase): """Test GAN class.""" @@ -23,38 +25,13 @@ def test__build(self): This checks both functions are called. It also checks that both generator and discriminator are attributes with type == Keras model. """ - # CHECK (normal) GAN - # create gan instance - subject = gans.GAN(xshape=(4,), yshape=(2,)) - kwargs = dict(noise_shape=(5,), conditional=False) - subject._build(**kwargs) - # created mocked functions - subject._model.create_generator = unittest.mock.MagicMock( - name='create_generator') - subject._model.create_discriminator = unittest.mock.MagicMock( - name='create_discriminator') - # assert create generator function called - subject._model.create_generator.assert_called_once_with(kwargs) - # assert create discriminator function called - subject._model.create_discriminator.assert_called_once_with(kwargs) - # check attributes - self.assertTrue(subject.conditional, False) - self.assertTrue(subject.noise_shape, (5,)) - # CHECK Conditional GAN # create gan instance - subject = gans.CondGAN(xshape=(4,), yshape=(2, 4)) - self.assertTrue(issubclass(gans.CondGAN, gans.GAN)) + subject = gans.GAN(xshape=(10,), yshape=(1,)) kwargs = dict(noise_shape=(5,)) subject._build(**kwargs) - # assert create generator function called - subject._model.create_generator.assert_called_once_with(kwargs) - # assert create discriminator function called - subject._model.create_discriminator.assert_called_once_with(kwargs) # check attributes self.assertTrue(subject.conditional, True) - self.assertTrue(subject.noise_shape, (5,)) - self.assertTrue(subject.n_classes, 4) return def test__train(self): @@ -68,18 +45,20 @@ def test__train(self): Xs = 'Xs' Ys = 'Ys' subject = gans.GAN(xshape=(4,), yshape=(2,)) - subject.iterbacthes = mock.MagicMock(name='iterbatches', + subject.iterbatches = mock.MagicMock(name='iterbatches', return_value="Batch1") - subject._model.fit_gan = mock.MagicMock(name='fit_gan') - kwargs = dict(option='x1') - subject._train(Xs, Ys, **kwargs) + subject._model.fit_gan = mock.MagicMock(name='fit_gan', + return_value='losses') + kwargs = dict(batches=100) + losses = subject._train(Xs, Ys, **kwargs) # assert fit_gan was called - subject.iterbacthes.assert_called_with(Xs, Ys, **kwargs) + subject.iterbatches.assert_called_with(Xs, Ys, 100) subject._model.fit_gan.assert_called_with("Batch1") + self.assertEqual(losses, 'losses') return - @unittest.mock.patch('gandy.models.gans.GAN._build', return_value='Model') + @unittest.mock.patch('gandy.models.gans.GAN._build') def test__predict(self, mocked__build): """ Test predict function. @@ -88,33 +67,21 @@ def test__predict(self, mocked__build): This checks predictions and uncertainties are the appropriate shape and the appropriate deepchem calls are made. """ - Xs = 'Xs' - # CHECK (normal) GAN - subject = gans.GAN(xshape=(4,), yshape=(2,)) - subject.predict_gan_generator = mock.MagicMock( - name='predict_gan_generator', return_value='generated_points') - preds, ucs = subject._predict(Xs) - subject._model.predict_gan_generator.assert_called_with(None) - self.assertEqual(preds, 'generated_points') - self.assertEqual(ucs, None) - # CHECK Conditional GAN - Ys = 'Ys' - subject = gans.GAN(xshape=(4,), yshape=(2, 3), n_classes=3) + Xs = 'Xs' + subject = gans.GAN(xshape=(4,), yshape=(2, 3)) + subject.conditional = True # normally set in build subject._model.predict_gan_generator = mock.MagicMock( - name='predict_gan_generator', return_value='generated_points') - with mock.patch('deepchem.metrics.to_one_hot', - return_value=[10]) as mocked_one_hot: - preds, ucs = subject._predict(Xs, Ys=Ys) - mocked_one_hot.assert_called_with(Ys, 3) - subject._model.predict_gan_generator.assert_called_with( - conditional_inputs=[10]) - self.assertEqual(preds, 'generated_points') - self.assertEqual(ucs, None) + name='predict_gan_generator', return_value=np.array([[1]])) + preds, ucs = subject._predict(Xs) + subject._model.predict_gan_generator.assert_called_with( + conditional_inputs=[Xs]) + self.assertEqual(preds, np.array([1])) + self.assertEqual(ucs, np.array([0])) return - @unittest.mock.patch('gandy.models.gans.GAN._build', return_value='Model') - def test_iterbacthes(self, mocked__build): + @unittest.mock.patch('gandy.models.gans.GAN._build') + def test_iterbatches(self, mocked__build): """ Test iterbacthes function. @@ -125,16 +92,17 @@ def test_iterbacthes(self, mocked__build): Xs = 'Xs' Ys = 'Ys' subject = gans.GAN(xshape=(4,), yshape=(2,)) + subject._model.batch_size = mock.MagicMock(name='batch_size', + return_value=100) subject.generate_data = mock.MagicMock( - name='generate_data', return_value=('classes', 'points')) - kwargs = dict(bacthes=1) + name='generate_data', return_value=['classes', 'points']) + kwargs = dict(batches=1) result = list(subject.iterbatches(Xs, Ys, **kwargs)) subject.generate_data.assert_called_with(Xs, Ys, - subject.model.batch_size) - expected_result = {subject._model.data_inputs[0]: 'classes', - subject._model.conditional_inputs[0]: - 'points'} - self.assertEqual(expected_result, result) + subject._model.batch_size) + data = result[0] + for key in data.keys(): + self.assertTrue(data[key] in ['classes', 'points']) return @unittest.mock.patch('gandy.models.gans.GAN._build', return_value='Model') @@ -144,36 +112,37 @@ def test_generate_data(self, mocked__build): The generate_data function creates batches of boostrapped data. """ - Xs = ['x1', 'x2', 'x3'] - Ys = ['y1', 'y2', 'y3'] - subject = gans.GAN(xshape=(4,), yshape=(2,)) + Xs = np.array([[1], [2], [3]]) + Ys = np.array([2, 2, 1]) + subject = gans.GAN(xshape=(3,), yshape=(1,)) classes, points = subject.generate_data(Xs, Ys, 5) self.assertEqual(len(classes), 5) self.assertEqual(len(points), 5) return - @unittest.mock.patch('gandy.models.gans.GAN._build', return_value='Model') - def test_save(self, mocked__build): + def test_save(self): """ Test save function. This checks that a file is written with the appropriate name. """ # test path save - subject = gans.GAN(xshape=(4,), yshape=(2,), n_classes=10) + subject = gans.GAN(xshape=(4,), yshape=(2,)) subject._model.save = mock.MagicMock(name='save') subject.save('path') subject._model.save.assert_called_with('path') return - @unittest.mock.patch('tf.keras.models.load_model', return_value='Model') - def test_load(self, mocked_load): + def test_load(self,): """ Test load function. This checks that a Keras model instance is returned. """ # test load - subject = gans.GAN.load('test_model.h5') - self.assertEqaul(subject, 'Model') + with unittest.mock.patch('tensorflow.keras.models.load_model')\ + as mocked_load: + subject = gandy.models.gans.GAN.load('filename') + self.assertTrue(isinstance(subject, gandy.models.gans.GAN)) + mocked_load.assert_called_with('filename') return From 0d43ef9931833572d1e310881d27c78a21961dfb Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Sun, 14 Mar 2021 17:06:48 -0700 Subject: [PATCH 96/99] Fixed test and load functions --- gandy/models/dcgan.py | 4 ++-- gandy/models/gans.py | 9 ++++++--- gandy/tests/test_models/test_gans.py | 4 ++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/gandy/models/dcgan.py b/gandy/models/dcgan.py index d15e9be..0969205 100644 --- a/gandy/models/dcgan.py +++ b/gandy/models/dcgan.py @@ -95,8 +95,8 @@ def __init__(self, xshape, yshape, noise_shape, **kwargs): warnings.warn(f"Incorrect key {key}. Must be in\ {Base_hyperparams.keys()}") else: - warnings.warn(f"Incorrect key {key}.\ - Must start with generator_ or discriminator_") + warnings.warn(f"{key} must start with generator_ or discriminator_" + + f"\nPassing {key} as deepchem model init kwargs.") # Deepchem init function + class atributes. super(DCGAN, self).__init__(**kwargs) diff --git a/gandy/models/gans.py b/gandy/models/gans.py index d820e5e..7ff3878 100644 --- a/gandy/models/gans.py +++ b/gandy/models/gans.py @@ -231,7 +231,7 @@ def save(self, filename: str, **kwargs): """ # save model # filename could be a path or end with .h5 - self._model.save(filename) + self._model.model.save(filename) return None @classmethod @@ -254,11 +254,14 @@ def load(cls, filename: str, **kwargs): path_to_model = filename model = tf.keras.models.load_model(path_to_model) # get x and y shape - xshape = model.input_shape[1:] - yshape = model.layers[-1].get_config()['event_shape'] + input_shapes = model.input_shape + xshape = input_shapes[2] + yshape = input_shapes[1] + noise_shape = input_shapes[0] # instantiate instance = cls.__new__(cls) instance._xshape = xshape instance._yshape = yshape instance._model = model + instance._model.noise_shape = noise_shape return instance diff --git a/gandy/tests/test_models/test_gans.py b/gandy/tests/test_models/test_gans.py index 3866882..c78f0ac 100644 --- a/gandy/tests/test_models/test_gans.py +++ b/gandy/tests/test_models/test_gans.py @@ -128,9 +128,9 @@ def test_save(self): """ # test path save subject = gans.GAN(xshape=(4,), yshape=(2,)) - subject._model.save = mock.MagicMock(name='save') + subject._model.model.save = mock.MagicMock(name='save') subject.save('path') - subject._model.save.assert_called_with('path') + subject._model.model.save.assert_called_with('path') return def test_load(self,): From fd5ce24eb8f201c2ca9f7aa4e9b8c520f7fa3151 Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Sun, 14 Mar 2021 17:29:34 -0700 Subject: [PATCH 97/99] New + better demo --- examples/gan_demo.ipynb | 580 ++++++++++++++++++++++++++ gandy/models/dcgan.py | 2 +- gandy/tests/test_models/test_dcgan.py | 47 --- 3 files changed, 581 insertions(+), 48 deletions(-) create mode 100644 examples/gan_demo.ipynb delete mode 100644 gandy/tests/test_models/test_dcgan.py diff --git a/examples/gan_demo.ipynb b/examples/gan_demo.ipynb new file mode 100644 index 0000000..d437c89 --- /dev/null +++ b/examples/gan_demo.ipynb @@ -0,0 +1,580 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#

Demo of GANs

" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:This caffe2 python run does not have GPU support. Will run in CPU only mode.\n" + ] + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import sklearn.datasets\n", + "import sklearn.model_selection\n", + "import sklearn.preprocessing\n", + "import deepchem\n", + "\n", + "from gandy.models import gans" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'2.5.0.dev'" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "deepchem.__version__" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#

A regression task, using the boston dataset

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##

Get Data

" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "# load data\n", + "Xs, Ys = sklearn.datasets.load_boston(return_X_y=True)\n", + "Xs_train, Xs_test, Ys_train, Ys_test = sklearn.model_selection.train_test_split(Xs, Ys,\n", + " train_size=0.8,\n", + " random_state=42)\n", + "\n", + "# normalize x\n", + "x_norm = sklearn.preprocessing.Normalizer()\n", + "Xs_train = x_norm.fit_transform(Xs_train)\n", + "Xs_test = x_norm.transform(Xs_test)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# normalizing y to get standardize uncertainties\n", + "y_norm = sklearn.preprocessing.Normalizer()\n", + "Ys_train = y_norm.fit_transform(Ys_train.reshape(1, -1))\n", + "Ys_test = y_norm.transform(Ys_test.reshape(1, -1))\n", + "\n", + "# transposing to get batch as zeroth dimension\n", + "Ys_train = Ys_train.T\n", + "Ys_test = Ys_test.T" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(13,)\n", + "(1,)\n" + ] + } + ], + "source": [ + "xshape = (Xs_train.shape[1],) # remove zero dimension\n", + "print(xshape)\n", + "yshape = (Ys_train[1].shape) # regression\n", + "print(yshape)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##

Initialize Model

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "###

Hyperparam options!

\n", + "\n", + "### Below are the defaults for\n", + "\n", + "#### GAN stuff:\n", + "- ```noise_shape=(10,)```\n", + "- ```conditional=true```\n", + "\n", + "\n", + "### *ALL HYPERPARMS BELOW MUST START WITH*\n", + "```generator_``` or ```discriminator_```\n", + "\n", + "#### Network architecture:\n", + "\n", + "- ```layer_dimensions=[128]```\n", + "- ```dropout=0.05```\n", + "\n", + "\n", + "#### Layer kwargs:\n", + "\n", + "- ```activation='relu'```\n", + "- ```use_bias=True```\n", + "- ```kernel_initializer=\"glorot_uniform\"```\n", + "- ```bias_initializer=\"zeros\"```\n", + "- ```kernel_regularizer='l2'```\n", + "- ```bias_regularizer=None```\n", + "- ```activity_regularizer=None```\n", + "- ```kernel_constraint=None```\n", + "- ```bias_constraint=None```\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Users\\saman\\Downloads\\Anaconda\\lib\\site-packages\\gandy\\models\\dcgan.py:99: UserWarning: learning_rate must start with generator_ or discriminator_\n", + "Passing learning_rate as deepchem model init kwargs.\n", + " f\"\\nPassing {key} as deepchem model init kwargs.\")\n" + ] + } + ], + "source": [ + "# todo why specifying learning rate make non nan loss\n", + "GAN = gans.GAN(xshape=xshape, yshape=yshape, noise_shape=(5,), learning_rate=1e-4)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##

Train!

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "###

Hyperparam options

\n", + "\n", + "#### Training:\n", + "- ```batches=50```\n", + "\n", + " (Number of batches of bootstrapped data, e.g., epochs, to train on)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "100" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "GAN._model.batch_size # this is automaticaally baked into deepchem" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Step 1000: \tAvg gen loss 0.6946533406972886, \tAvg discrim loss 1.384789663553238\n", + "Step 2000: \tAvg gen loss 0.6952816368341446, \tAvg discrim loss 1.3823101232051849\n", + "Step 3000: \tAvg gen loss 0.6960971853733062, \tAvg discrim loss 1.380865407705307\n", + "Step 4000: \tAvg gen loss 0.6949829128980637, \tAvg discrim loss 1.3828670566082\n", + "Step 5000: \tAvg gen loss 0.6936914792060852, \tAvg discrim loss 1.3853883373737335\n", + "Step 6000: \tAvg gen loss 0.693521221101284, \tAvg discrim loss 1.3858618046045303\n", + "Step 7000: \tAvg gen loss 0.6935025429725648, \tAvg discrim loss 1.3856645780801773\n", + "Step 8000: \tAvg gen loss 0.6935849062204361, \tAvg discrim loss 1.3855630407333375\n", + "Step 9000: \tAvg gen loss 0.6935890994668007, \tAvg discrim loss 1.385524563074112\n", + "Step 10000: \tAvg gen loss 0.6936377281546593, \tAvg discrim loss 1.385346347093582\n", + "Step 11000: \tAvg gen loss 0.6938541138768196, \tAvg discrim loss 1.385112920641899\n", + "Step 12000: \tAvg gen loss 0.6938354486823082, \tAvg discrim loss 1.384942196726799\n", + "Step 13000: \tAvg gen loss 0.6939847853183746, \tAvg discrim loss 1.3849611324071884\n", + "Step 14000: \tAvg gen loss 0.694022923886776, \tAvg discrim loss 1.3849232378005982\n", + "Step 15000: \tAvg gen loss 0.6939718227982521, \tAvg discrim loss 1.384895178079605\n", + "Step 16000: \tAvg gen loss 0.6938923109173775, \tAvg discrim loss 1.384940111041069\n", + "Step 17000: \tAvg gen loss 0.6939016106128693, \tAvg discrim loss 1.385041773915291\n", + "Step 18000: \tAvg gen loss 0.6939402292370797, \tAvg discrim loss 1.3852565339803695\n", + "Step 19000: \tAvg gen loss 0.6934576245546341, \tAvg discrim loss 1.3854440643787385\n", + "Step 20000: \tAvg gen loss 0.6937300187349319, \tAvg discrim loss 1.3854314596652986\n", + "Step 21000: \tAvg gen loss 0.6935716012716293, \tAvg discrim loss 1.3857012275457383\n", + "Step 22000: \tAvg gen loss 0.6935019016265869, \tAvg discrim loss 1.3858024929761887\n", + "Step 23000: \tAvg gen loss 0.6933977078795434, \tAvg discrim loss 1.3859471516609192\n", + "Step 24000: \tAvg gen loss 0.6931718240380287, \tAvg discrim loss 1.3860991570949555\n", + "Step 25000: \tAvg gen loss 0.6934327245950699, \tAvg discrim loss 1.3862461247444153\n", + "Step 26000: \tAvg gen loss 0.6932429926395416, \tAvg discrim loss 1.3862048870325088\n", + "Step 27000: \tAvg gen loss 0.6931959349513054, \tAvg discrim loss 1.3862611467838288\n", + "Step 28000: \tAvg gen loss 0.693244632601738, \tAvg discrim loss 1.3863033655881882\n", + "Step 29000: \tAvg gen loss 0.6934658588767052, \tAvg discrim loss 1.3862594746351242\n", + "Step 30000: \tAvg gen loss 0.6935401089191436, \tAvg discrim loss 1.3862267524003982\n", + "Step 31000: \tAvg gen loss 0.6932165855169297, \tAvg discrim loss 1.3862918070554733\n", + "Step 32000: \tAvg gen loss 0.6931548373699188, \tAvg discrim loss 1.3861615583896636\n", + "Step 33000: \tAvg gen loss 0.6935629423856735, \tAvg discrim loss 1.3862318274974823\n", + "Step 34000: \tAvg gen loss 0.6932820871472358, \tAvg discrim loss 1.3862072378396988\n", + "Step 35000: \tAvg gen loss 0.693261498093605, \tAvg discrim loss 1.386176702260971\n", + "Step 36000: \tAvg gen loss 0.6934829040169715, \tAvg discrim loss 1.3861383835077286\n", + "Step 37000: \tAvg gen loss 0.6933314707279206, \tAvg discrim loss 1.3861590344905854\n", + "Step 38000: \tAvg gen loss 0.6935650395154953, \tAvg discrim loss 1.386151852965355\n", + "Step 39000: \tAvg gen loss 0.6935194611549378, \tAvg discrim loss 1.3860945677757264\n", + "Step 40000: \tAvg gen loss 0.6933920715451241, \tAvg discrim loss 1.3860829125642777\n", + "Step 41000: \tAvg gen loss 0.6933604261279106, \tAvg discrim loss 1.3861441333293916\n", + "Step 42000: \tAvg gen loss 0.6933467952013016, \tAvg discrim loss 1.3860709898471832\n", + "Step 43000: \tAvg gen loss 0.6934700159430504, \tAvg discrim loss 1.386030709028244\n", + "Step 44000: \tAvg gen loss 0.6934939726591111, \tAvg discrim loss 1.3860569614171983\n", + "Step 45000: \tAvg gen loss 0.6935677703022957, \tAvg discrim loss 1.385985896587372\n", + "Step 46000: \tAvg gen loss 0.6932731596827507, \tAvg discrim loss 1.3859024583101271\n", + "Step 47000: \tAvg gen loss 0.693531041443348, \tAvg discrim loss 1.3860208741426467\n", + "Step 48000: \tAvg gen loss 0.6933853635191918, \tAvg discrim loss 1.3859323072433472\n", + "Step 49000: \tAvg gen loss 0.6933485987186432, \tAvg discrim loss 1.385881119132042\n", + "Step 50000: \tAvg gen loss 0.6933808777332305, \tAvg discrim loss 1.3859679154157638\n", + "TIMING: model fitting took 532.391 s\n" + ] + } + ], + "source": [ + "GAN.train(Xs_train, Ys_train, batches=50000)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "losses = []\n", + "for key in GAN.sessions.keys():\n", + " losses.append(GAN.sessions[key])\n", + " # only get one training session losses\n", + " break\n", + "losses = np.array(losses[0])" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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jh49yoABA9i5cye33HILnTp14rfffmPw4MFkZGQUuf/69es555xzaNy4MXfffTeNGjVi3759zJs3jxUrVtCnTx/3efr378/q1au5/PLLufXWW8nLy2PZsmV8//33XHXVVR7HdR3z0UcfJT09nfj4eF+nd1u2bBnfffcdN910E9dffz2ff/45Dz74INHR0UycOJHk5GSeeOIJNm3axCuvvML111/PjBkzSvQzW7x4MQMHDiQiIoLbb7+dxo0b89133/HAAw+wYsUKPv7YWm49NzeXc845h927d3PbbbfRrl07jh07xsqVK5kzZw7Dhw8H4KmnnuKxxx7j4osv5pZbbiE8PJytW7fy7bffkpWVRURE8E3uKoreyZUKQX2GdGbC2B8Bay27zIxsjyDDub6hq0tl1z6tqd84kUP7jpGaks7i2evpO6To4M5p/YqdHN5vLR5fOynWoxFKIO27newO+tYt3+E3ONy5eb97PmSTFvWIivb8peqad7htwz7ycvNZs3Q7Pc/0LC1ds2Qbx46kA5DUIMHvuf46vD9fT5xL5olsTmrd0G/GrDQS6sRy+sAOnD6wYC7j8dQMtq7bS+2kOPcyHWWhRdtGxCfGuEt+XfyV3voTHh7G4Et6MOAv3Ti8/xj1GyeWuIuqUlVJeFgYdWNiOHTCs1GXzjmsOVq9ErBJfqXactc/i73P2LFj2bZtG++++y433HADALfddhv33HMPL7/8cpH7T5s2jRMnTvDJJ5/Qq1cvv+MeeughVq9ezbhx47jpJs9lvPPzC69A16VLFz766KOg38eqVatYsGABvXv3BmDUqFG0aNGCv//979xxxx288sorHuNffPFF1q9fT/v27YM+h8vdd99NVlYWCxYsoGvXrgDccccdXHnllUyaNImRI0dy9tlns2bNGtavX8+zzz7L/fff7/d4U6ZMoWPHjnz77bce2//73/8W+9oqit7RlQpBzVs24KTW1vIRWZk5HvMLAY/5Zq7gMDw8jLMvPdW9vSSlpfMd5Zq9z+pEeK3CZZO+eDal8b+A/bYNjk6lbX0Ha84SyVWLCpfOzHOUvfYd0tlvYFOnXjxPv3cjl48exCOvXee342pZi68dwym9WpVpYAhWJ9bOPZMLbQ803zCQ8PAwGjZN0sBQhZQGPkpLNXOoqquvv/6aRo0acf3113tsf+CBB4LaPzHRmjv+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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "gen_loss = losses[0]\n", + "discrim_loss = losses[1]\n", + "n = len(gen_loss)\n", + "batches = np.arange(n)*1000\n", + "\n", + "fig, axes = plt.subplots(figsize=(14,5), ncols=2)\n", + "plt.subplots_adjust(wspace=0.4)\n", + "\n", + "axes[0].plot(batches, gen_loss, c=plt.cm.viridis(0.1),\n", + " linewidth=3, label='gen loss')\n", + "axes[1].plot(batches, discrim_loss, c=plt.cm.viridis(0.5),\n", + " linewidth=3, label='discrim loss')\n", + "\n", + "for ax in axes:\n", + " ax.set_xlabel('Step', fontsize=24)\n", + " ax.set_ylabel('Loss', fontsize=24)\n", + " \n", + " plt.setp(ax.get_xticklabels(), fontsize=18)\n", + " plt.setp(ax.get_yticklabels(), fontsize=18)\n", + " ax.tick_params(direction='in', width=2, length=8)\n", + " \n", + " ax.legend(fontsize=18)\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##

Predict

" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "###

Hyperparam options

\n", + "\n", + "#### Prediction:\n", + "- ```num_predictions=100```\n", + "\n", + " (Number of predictions to make in order to sample uncertainties.)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.0009118369731974477\n" + ] + } + ], + "source": [ + "uc_threshold = 0.01\n", + "preds, uncs, flags = GAN.predict(Xs_test, uc_threshold = uc_threshold)\n", + "print(np.average(uncs))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##

Results

" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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kpJCxYoVadGiBQ899FDUPFAURVGys2TJElq2bMmYMWO47777WLVqVUIbNbSbWClUjDF8//33AFSpUgWwhuAll1zC5s2b+e1vf0uzZs3YunUrL7/8MhdffDHLly+nbt262eQMGTKEo0ePcuedd1K+fHkaNmzIlClTGD9+PIsWLWLKlCkAnHXWWQBs3ryZtm3bkpGRwR133EH9+vX5/vvveeWVV1iwYAHLly+nQoUKAOzevZt27dqxevVqbrrpJu655x6OHz/OypUr+eSTT7j11lt59NFHyczMzJYWwCWXXJJrHmzevJnLL7+cbt260bVrV/bv3w/A0aNHufbaa1myZAm9evXivvvuY8+ePUyYMIFLL72Uzz//nAsvvBCAp59+mieeeILOnTtz9913U7JkSTZu3MjMmTM5cuQIpUuXzs9tAqBLly5s3bqV8ePHM3ToUJo2bQpA/fr1ARgwYACvv/46vXv35v777+f48eN89913zJ8/P99pK4qinAocPnyYYcOG8cILL1C7dm3mzZvH5Zdfnmi11BhMFIMHD2bVqlWJViMbLVu2ZPTo0fmScfDgQbZv344xhq1btzJ27Fj+9a9/0aZNGxo2bAjAE088wYYNG1i6dCnnnXdeVty+ffvSokULhg0blqPF7dChQ6xcuZKUlJQst0svvZS5c+eyaNEievbsmS387373O44ePcrKlSupVatWlnu3bt1o06YNo0aNyhr7N3ToUFavXs1f/vIX+vfvn01OZmYmAFdddRVTp04NTCs3Nm7cyIQJE+jXr18293HjxpGens7f//53rrnmmiz3e++9l+bNmzNkyJCslrwZM2bQtGlTZs6cmU3Gc889F5cu0Tj33HNp27Yt48eP56qrrsrWyurp8Ktf/SqrhVRRFEWJnRUrVtC7d2/WrFnDnXfeyQsvvED58uUTrRag3cRKATNs2DCqVq3KmWeeyXnnncfrr7/Or3/9az788EPAthROnTqVyy67jJo1a7J9+/aso2zZsrRp04Y5c+bkkHvPPfdkMwSjsWfPHj755BN+/etfk5ycnC2N1NRUGjRokJVGZmYm77zzDk2bNuXOO+/MIatEifw/IpUrV87qfvXz1ltv0aRJE1q1apVNx4yMDK666ioWL17MoUOHAKhQoQJbtmxh8eLF+dYnr1SoUIHVq1fzzTffJEwHRVGUE42MjAyGDRvGxRdfzO7du5k1axbjx48vNoYgaMtgwshvC1xxpX///nTr1g0RoWzZsjRq1CjbOIht27axY8cO5syZE3EJmCADrFGjRjHr8O2335KZmcnEiROZOHFiYJh69eoBsH37dnbt2sW1115baFum1a9fn5IlS+ZwX7t2LYcOHYq6FM727dupXbs2zz77LDfeeCPt27enRo0apKWl0alTJ2666SaSkpIKRe9wRo8eTa9evWjRogX16tWjY8eOdO7cmc6dOxeI0awoinKy8fXXX9OnTx9WrlxJr169eOmll6hUqVKi1cqBGoNKgdKwYUOuvPLKiP7GGACuvPJKHn744Zjlxtoq6E+jZ8+e9OnTJzDMaaedli1sYe6dG0l3YwwtWrRg5MiREeN6hmLbtm1Zv349s2fPZsGCBSxYsIC3336bp59+msWLF+c68PjYsWN5vwDHDTfcwKZNm5g1axYLFy5k7ty5TJw4kfbt2zN37twiM0oVRVGKO8eOHeP5559n2LBhVKpUiRkzZnDjjTcmWq2IqDGoFClVq1alYsWK7N27N6rRmB8aNGiAiJCRkZFrGlWrVqVSpUoxjd8saIOxYcOGbNu2jcsvvzymlrVy5crRtWtXunbtCtidVwYMGMDEiROzZvRWrlw5cHmC8FnakcjtGitXrkzPnj3p2bMnxhgeeeQR/vSnP/HRRx/RrVu3mNJQFEU5mfn222/p06cPX375JTfddBOvvPJK1gTK4or27ShFSokSJejRowfLli3j/fffDwwTtPRLPJxxxhlcd911TJ8+naVLl+bwN8awbdu2LH1uu+021qxZE9il7LUcgjXGgAJbC6p379789NNPEVsG/esybt++PYf/BRdckEOfRo0a8cUXX2Stwwiwa9eurCV4ciPSNR4/fpzdu3dncxMRzj///MDwiqIopxqZmZmMGjWKli1b8t133zFt2jTee++9Ym8IgrYMKgngmWee4R//+Ac333wzN998M23atCEpKYkffviBWbNm0apVq1zX78uNV155hXbt2nHZZZfRu3dvzj//fDIzM9mwYQMfffQRvXv3zppN/PTTTzN//nz69evHnDlzaNeuHcYYVq5cybFjx7KWkmnTpg3jxo3j3nvvpVOnTpQuXZqLL76Ys88+O086Dho0iM8++4yHHnqI+fPnc/nll1O+fHk2b97MvHnzSE5OZsGC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pk+fzjPPPMOSJUvUECxEdMzgCY62CCqKoignC7t372bQoEG8+eabnHfeecyePTvikmVKwaEtg4qiKIqiJJzZs2fTokULpk6dymOPPcayZcvUECwi1BhUFEVRFCVh7Nu3j7vuuotrr72W008/nS+++II//OEPurh/EaLGoKIoiqIoCSE9PZ1zzz2XCRMmMGTIEP75z39y0UUX5R5RKVDUGFQURVEUpUg5ePAggwYNomPHjpQqVYpFixbx/PPPk5ycnGjVTkl0AomiKIqiKEXGkiVL6Nu3L9999x333Xcfzz33HGXLlk20Wqc02jKoKIqiKEqhc/jwYR5++GHat2/PkSNHmDdvHmPHjlVDsBigLYOKoiiKohQqK1asoHfv3qxZs4Z+/frx4osvUr58+USrpTi0ZVBRFEVRlEIhIyODYcOGcfHFF7N7925mzZrFhAkT1BAsZmjL4IlOWpo9p6cnUgtFURRFycbXX39N7969WbVqFb169eKll16iUqVKiVZLCUBbBhVFURRFKTCOHTvGiBEjaNWqFT/++CMzZszgzTffVEOwGKMtg4qiKIqiFAjr1q2jT58+LFu2jJtuuomXX36ZqlWrJlotJRe0ZVA56cjMzGTUqFE0adKE5ORkateuzYMPPsiBAwcKTcaIESPo1q0b9erVQ0RITU0toKtRFEUp/mRmZjJy5EhatmzJ999/z7Rp03jvvffUEDxBUGNQOem4//77eeCBBzjnnHMYO3Ys3bp1Y8yYMXTu3JnMzMxCkTF06FDmz59P/fr1tStEUZRTivXr15OWlsaDDz7I1VdfzerVq7n11lsRkUSrpsSIdhMrCefAgQNs2bKFRo0a5VvW6tWrGTt2LF26dOGDDz7Icj/77LMZOHAg77zzDt27dy9wGevXr6devXoANG/enP379+f7WhRFUYozxhheffVVHnroIUqWLMnkyZPp3bu3GoEnINoyeCIzdSosXQoLF0Jqqv1fDFi4cCHXX389VatWpWTJkohItqN9+/bZwm/bto3GjRvTunVrxowZwy+//JLntKdNm4YxhsGDB2dzv/POO0lJSeGtt94qFBmeIagoinIqsHnzZq6++mruvfdeLrnkEr755hv69OmjhuAJihqDJypTp0L//nDkiP3/ww/2f4INwjfeeIPLL7+cVatWMWDAAEaPHk2aW/6mYsWK3HDDDdx6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UuxkZGQwZcoUGjVqxN69e3n33XdZvHixEkFxRJXBECtXrhy/+MUvOHbsGJcuXXI7HJFCFRERQVRUFLfeeitly5Z1OxyRYuX777+ne/fubNiwgYceeohZs2YRHR3tdlhSBCkZLARVqlRRB5eIiASFtZZZs2YxdOhQjDH85S9/oUePHmpUlHxTMigiIlJEHD58mGeeeYaVK1fywAMPEB8fz2233eZ2WFLEac6giIhImLPWMn/+fOrVq8fGjRt5++23Wb16tRJBCQpVBkVERMLYTz/9RJ8+fUhISOCXv/wlc+fO5c4773Q7LClGVBkUEREJUx988AF169ZlxYoVTJo0ic8//1yJoASdkkEREZEw83//93888cQTdOnShTvuuIPt27czZMgQLd4uIaFkUEREJIz87W9/o27dunz00UeMGTOGzZs3U6dOHbfDkmJMcwZFRETCwKlTpxg4cCDz58+nYcOGrFy5koYNG7odlpQAqgyKiIi4bOXKldSrV48FCxbw0ksvsXXrViWCUmiUDIqIiLjk7Nmz9OnTh1//+tdUrlyZf/zjH7zyyitERka6HZqUIEoGRUREXLBhwwYaNGjArFmzGDZsGF999RXNmjVzOywpgZQMioiIFKLz588zcOBA2rZtS+nSpdm4cSPjx4+nXLlybocmJZQaSERERArJl19+SVxcHP/617/o378/r732GhUrVnQ7LCnhVBkUEREJsYsXLzJixAjatGlDamoqa9euZcqUKUoEJSyoMigiIhJCiYmJxMbGsmfPHnr16sXEiROpXLmy22GJZFJlUEREJARSU1N5+eWXadGiBadOneKzzz5j5syZSgQl7KgyKCIiEmQ7d+4kLi6O7du38/TTTzN58mSuueYat8MS8UuVQRERkSBJS0vjtddeo2nTphw5coSPP/6Y+fPnKxGUsKbKoIiISBDs27ePuLg4tm7dSufOnZk2bRrVqlVzOyyRXKkyKCIiUgAZGRm88cYbNG7cmG+//Zb333+fDz74QImgFBmqDIqIiOTTgQMH6N69Oxs3buSRRx5h5syZVK9e3e2wRBxRZVBERMShjIwMpk2bRsOGDdmxYwdz584lISFBiaAUSaoMioiIOPDDDz/Qs2dP1qxZQ4cOHZg9ezY333yz22GJ5JsqgyIiInlgrSU+Pp769evz5ZdfMn36dFasWKFEUIo8VQZFRERy8eOPP9KrVy8+/fRT7rvvPuLj47njjjvcDkskKBxVBo0xscaYzg7O/60xJtZ5WCIiIu6z1rJw4ULq1q3L2rVrefPNN1m3bp0SQSlWnA4TzwXedHD+JCDe4TNERERcl5ycTOfOnXnqqaeoXbs2SUlJDBw4kFKlNMNKipf8/BdtQny+iIiIqz766CPq1q3LsmXL+POf/8ymTZuoXbu222GJhESo5wxWBS6G+BkiIiJBcfLkSfr378/ChQtp3Lgx69ato169em6HJRJSIat1G2N+C1QBDoXqGSIiIsHy2WefUa9ePT744AP+9Kc/sWXLFiWCUiLkWBk0xgwEBmY7fL0x5rucLsOTBFYBLPBRgSIUEREJoTNnzjBkyBBmz55NvXr1+OSTT2jSpInbYYkUmtyGiasCNbJ8bYGIbMcCuQy8B7yan8BERERCbe3atfTo0YPDhw/z/PPP86c//YmyZcu6HZZIocotGZwLbPB+boB1wEmgUw7XZABngH9Za8/nJyhjTCk8Fck+eBLPZOADYJS1NiUP148EmgBNgduBQ9baGgHOnQvEBbhVZ2vtYofhi4hImEtJSWHEiBFMnTqVWrVq8cUXX9CyZUu3wxJxRY7JoLX2EFnm/BljfgB+stZ+HuK43gAGAB/jWZ6mjvfrxsaY9tbajFyuH4snaf0KT3UzL572c2xrHq8VEZEiYtOmTXTr1o3vvvuOwYMH87//+79UqFDB7bBEXOOomzhQdS2YjDF1gf7AR9baTlmOHwSmAE8AC3O5TU1r7Xfe63YBlXJ7rrX23XwHLSIiYe/ChQu8+OKLvPHGG9SoUYMNGzZw7733uh2WiOsK1E1sjPmFMaaZMSaY/zc9iWdIOvvi1rOA88D/5HYDXyLohPGo7B2iFhGRYmTLli00adKE119/nb59+7Jjxw4lgiJe+Up8jDFdjDE7gKPAFjxzCbO+X9UYs9oYs8YYE+Xw9s3xzDu8YojWWnsRSPK+Hwqnva8L3thb5HZBYmIixpiALxERcdelS5d44YUXaN26NSkpKaxevZpp06ZRqVKuA0YiJYbjZNAY82c8w7T1gFQ8HcZXZD7W2lPAMaAt8KjDR0QDJ6y1l/y8dwSoZoyJdBp3Do7hmaP4LPAbPPMNmwEbjTHtg/gcEREpRNu3b6d58+a89tprdOvWjZ07d9K+vX6si2TnKBk0xnQAhuPpFn4cz1y85ACnz8OTJP7GYUwVAH+JIPxnN5OgzfS11j5vrR1irV1grV1qrR0N3INnaZx3crq2adOmWGsDvkREpPBdvnyZV155hXvuuYfk5GSWLVvG7NmzqVKlituhiYQlp5XB5/BUAodZaxdba9NzOPdL77lOV+48DwRa5KlclnNCxlr7LzxL2dxpjKkVymeJiEjw7N69m1atWvHyyy/z+OOPs3v3bjp27Oh2WCJhzWky6JtHl1s3L971AE8D1R0+4yieoWB/CeFNeIaQUx3eMz++936sVgjPEhGRAkhPT2f8+PE0adKEQ4cOsXjxYhYsWMC1117rdmgiYc9pMlgVOONgMekIh/cH+CeeuO7JetAYUw5oBGzLxz3z47+8H38qpOeJiEg+fPPNN8TExDBixAgefvhhdu/eTadOOe2NICJZOU0GTwKVjTG5ztkzxtwOROFp0HBiEZ7h5UHZjvfCM1dwQZZn1DTG3OXw/lljrOhNMrMfbwx0BvZaaw/k9/4iIhI6GRkZTJkyhUaNGrF3717effddlixZwg033OB2aCJFiqNFp/Es99LR+/ogl3OHej9udPIAa+1OY8xU4DljzEfAZ/xnB5LPuXKIei1wG9m6mY0xT3uPA1wPRBpjXvR+fcha+1fv5/8FLDfGLAX+BaQADYEeQDrQ20nsIiJSOL7//nu6d+/Ohg0beOihh5g1axbR0dFuhyVSJDlNBv8CPAKMNcZs8W5XdwVjTAQwEuiHp8I3PR9xDcIzZ6838DBwAngLz97EuW1FB9ATuC/bsVe9Hz8HfMngMWANniVwngLKAz/iqU6+Zq3dl4/YRUQkRKy1zJo1i6FDh2KM4S9/+Qs9evTQ2q4iBWCcLoFijHkX6Ar8DCzFM5xaERgI3I0nWYzGU62bZq19LpgBh5NmzZrZbdsKawqjiEjJdvjwYZ555hlWrlxJu3btmD17NrfddlvuF4oIAMaYRGtts+zHnVYGAbrhWVuwP9Dde8wCk33PwrODyOvAiHzcX0REJJO1lr/+9a8MGDCAy5cvM3XqVPr27UupUto9VCQYHCeD1to0YLB3Xl8c0Aq4EU8zyk941hecpyFWEREpqGPHjtG3b18SEhJo06YNc+bM4c4773Q7LJFiJT+VQQCstd8CLwUxFhERkUwffPAB/fr149y5c0ycOJFBgwYREZGfFctEJCeqsYuISFg5ceIEXbp0oUuXLtxxxx1s376doUOHKhEUCRElgyIiEjYSEhKoW7cuH3/8MWPGjGHz5s3UqVPH7bBEijVHw8TGmHiH978EnAL2AmuttUccXi8iIiXAqVOnGDhwIPPnz6dhw4asWrWKhg0buh2WSIngdM5gN+/HrOvRZF/cKft7vq8zjDGLgAHW2pMOnysiIsXUypUr6dmzJ8eOHeOll17ixRdfJDIy0u2wREoMp8ngaKAs0BfPPsXfAZuAo973bwTaADXxrEM4Hc8Wck29x58E7jLG/NJae6nA0YuISJF19uxZ/vCHPzBz5kzq1KnD0qVLadbsqiXQRCTEnCaDfwbWAxFAF2vth/5OMsZ0AuLxJIDtrbWXjTGtgGVAY6APMCXfUYuISJG2YcMGunfvzqFDhxg2bBivvPIK5cpdtVW8iBQCpw0kI4EWQJ9AiSCAtXYJnoQvBhjuPfYlMATP0HHnfEUrIiJF2vnz5xk4cCBt27aldOnSbNy4kfHjxysRFHGR02SwC5AKBEwEs/gQTwNJ1yzHluDZneRuh88VEZEibvPmzTRq1IgpU6bQv39/kpKS+OUvf+l2WCIlntNk8DbgorU2PbcTvedcBGpkOZaCp7u4osPniohIEXXx4kVGjBhBTEwMqamprFu3jilTplCxov4pEAkHTpPBs0BlY0yuiz4ZY+4GqgApWY6V8h5TN7GISAmQmJhI06ZNGT9+PD179mTnzp20bdvW7bBEJAunyeAGPHP+ZhtjKgc6yRgTBczCs6zM+ixv1cDTfHLY4XNFRKQISU1N5eWXX6ZFixacPn2a5cuXM3PmTKKiotwOTUSycdpN/CfgETxNJPuNMTOAL4Afve/7lpbpBVTHM0w8Osv1XbwfP89nvCIiEuZ27txJbGwsSUlJPP3000yePJlrrrnG7bBEJABHyaC1dq8x5lHgPeAXwEsBTjV4hoK7Wmv3ZDl+AhjjvV5ERIqRtLQ0JkyYwMsvv8w111zD0qVLeeyxx9wOS0Ry4bQyiLV2jTHmLmAA8Bs8ncG+4eYMYA/wMfCWtfZEtmtnFSxcEREJR/v27SMuLo6tW7fSuXNnpk2bRrVq1dwOS0TywHEyCGCt/T/gZeBlY0wkcA3eaqC1NjWI8YmISBhLT09n8uTJ/PGPf6RChQq8//77dOnSJfcLRSRsOEoGjTEDvJ8uttYeBfAmfz8FOzAREQlvBw4coHv37mzcuJFHH32UGTNmUL16dbfDEhGHnHYTvwFMxDP3T0RESqCMjAymTZtGgwYN2LFjB3PnzmXp0qVKBEWKKKfDxCeA0hoKFhEpmX744Qd69uzJmjVr6NChA3/5y1+45ZZb3A5LRArAaWXwK6CKMeb6UAQjIiLhyVpLfHw89evX58svv2T69OmsWLFCiaBIMeA0GZzivSbQkjIiIlLM/PjjjzzyyCP07NmTxo0bs2PHDvr06YMxxu3QRCQIHCWD1trlwB+AvsaYvxpjGoYmLBERcZu1loULF1K3bl3WrVvHm2++ybp167jjjjvcDk1EgshpN/F33k/TgK5AV2PMBeD/gPQAl1lrbc38hygiIoXt+PHj9OvXjyVLltCyZUvmzZtHrVq13A5LRELAaQNJDT/HKnhfgViHzxARERd99NFH9O3bl9OnTzNu3DiGDh1KRESE22GJSIg4TQbbhiQKERFx3cmTJ+nfvz8LFy6kSZMmrFu3jnr16rkdloiEmNO9iT8PVSAiIuKezz77jGeeeYbk5GRGjx7NyJEjKVOmjNthiUghcNpNLCIixcjp06fp2bMnDz/8MNdddx1btmxh1KhRSgRFShAlgyIiJdTatWtp0KABc+fOZeTIkWzbto0mTZq4HZaIFDKncwYzGWNuA1oB0UBFIOCCU9baV/L7HBERCa5z584xYsQIpk2bRu3atfniiy9o2bKl22GJiEscJ4PGmGhgBvBQXk7H002sZFBEJAxs3LiRbt26cfDgQQYPHsyYMWMoX76822GJiIucrjNYBfgcuAPPPsWbgceAC8AS4BdASyDK+/6nwQxWRETy58KFC7z44ou88cYb1KhRgw0bNnDvvfe6HZaIhAGnlcHBQE1gK/Bra+0pY0wGcNpaGwtgjKkAvAg8D6RZa3sFM2AREXFm69atxMXFsW/fPvr27cuECROoVKmS22GJSJhw2kDyKJ5h32HW2lP+TrDWnrfWvgBMAnoYY54qYIwiIpIPly5d4o9//COtWrXi3LlzrFq1infeeUeJoIhcwWkyWBPIwDM8nFWkn3PHeT+qMigiUsiSkpK45557GDt2LHFxcezatYtf/epXboclImHIaTJYGjhjrc26D3EKUNkYc0U3sbX2BHAKqF+wEEVEJK8uX77Mq6++SvPmzTl+/DjLli0jPj6eKlWquB2aiIQpp8ngEaCqMSZrJfAwEAHUznqiMaY8UJWc9y0WEZEg2b17N61atWLUqFF07tyZXbt20bFjR7fDEpEw5zQZ/Mb78Y4sx770fuyb7dxBeJaWOZCPuEREJI/S09MZP348TZo04dChQyxevJiFCxdy3XXXuR2aiBQBTpPBT/EkeL/Jcuwd78f+xphPjTFjjDF/A/4XT7PJvIKHKSIi/nzzzTfExMQwYsQIOnbsyO7du+nUqZPbYYlIEeI0GfwYz3qCma1o1tp/AiPwJH4P4llSpiOepPFjPF3FIiISRBkZGUyePJlGjRqxd+9e3n33XRYvXswNN9zgdmgiUsQ4WmfQWnsM6Ozn+ERjzGdAJ+Bm4DSw2lq7OihRiohIpoMHD9KjRw82bNjAQw89xKxZs4iOjnY7LBEpovK9N3F21to9wJ5g3U9ERK5krWXWrFkMHToUYwyzZ8+me/fuZFvMQUTEEafb0d0LpFpr/5HH8+8Byllr/56f4ERExOPw4cM888wzrFy5kvbt2zN79mxuvfVWt8MSkWLAaWVwA/AjcFMez18E3JKP54iICJ5q4Pz58xk4cCCXL19m6tQn2TG9AAAgAElEQVSp9O3bl1KlnE75FhHxLz9JmtPxCI1fiIjkw7Fjx+jduzfLli0jJiaGOXPmULNmTbfDEpFiJtS/WkYBqSF+hohIsfPBBx9Qt25dVq1axaRJk1i/fr0SQREJiZAlg975gtfi2bVERETy4MSJE3Tp0oUuXbpQs2ZNtm/fzpAhQ4iIiHA7NBEppnIcJjbGxAFx2Q5fa4xZl9NleLahuxvP2oPLCxShiEgJkZCQQO/evfn5558ZM2YMw4cPp3RpTbkWkdDK7adMDeD+bMci/RwL5O/AKEcRiYiUMKdOnWLgwIHMnz+fRo0asXr1aho0aOB2WCJSQuSWDC4Fvvd+boB4PAtKD8rhmgzgDLDbWvttQQMUESnOVq5cSc+ePTl27BgvvfQSL774IpGRkW6HJSIlSI7JoLX2a+Br39fGmHjggrVW+w2LiBTA2bNn+cMf/sDMmTO5++67Wbp0Kc2aNXM7LBEpgRw1kFhrS1lrteeRiEgBrF+/ngYNGjBr1iyGDx9OYmKiEkERcY1WLRURKSTnz59n4MCBPPDAA5QuXZpNmzYxbtw4ypUr53ZoIlKCKRkUESkEmzdvplGjRkyZMoX+/fvz9ddf07p1a7fDEhFRMigiEkoXL15k+PDhxMTEkJqayrp165gyZQoVKlRwOzQREUB7BouIhExiYiKxsbHs2bOH3r17M3HiRKKiotwOS0TkCqoMiogEWWpqKqNGjaJFixacPn2a5cuXM2PGDCWCIhKWVBkUEQmiHTt2EBcXR1JSErGxsbz55ptcc801boclIhKQKoMiIkGQlpbG2LFjadasGUePHmXp0qXMmzdPiaCIhD1VBkVECmjfvn3ExcWxdetWHn/8caZOnUq1atXcDktEJE9UGRQRyaf09HRef/11GjduzLfffsv777/PokWLlAiKSJESsDLo3XouGKy1tmeQ7iUiEhYOHDhAt27d2LRpE48++igzZsygevXqboclIuJYTsPE3QALGD/v2SyfZ38/+3sWUDIoIsVCRkYG06dPZ9iwYZQpU4a5c+cSGxuLMf5+VIqIhL+cksHRAY5HAv2AKsAh4O/AETyJ343AvUAN4BQwHbgUpFhFRFz1ww8/0LNnT9asWUOHDh2YPXs2N998s9thiYgUSMBk0Fp7VTJojIkE1nuve9pau8DftcaYJ4GZQAzQLjihioi4w1rLnDlzGDx4MOnp6UyfPp3evXurGigixYLTBpLngZbAs4ESQQBr7XvAs8AvgeH5D09ExF1Hjx6lY8eO9OzZk8aNG7Nz50769OmjRFBEig2nyeCTQCrwXh7OfR/PEHFXp0GJiLjNWsvChQupV68e69evZ/Lkyaxbt47bb7/d7dBERILKaTJ4G3DRWpue24nW2jTgovcaEZEi4/jx4/zud7/jqaeeonbt2iQlJTFgwABKldJqXCJS/Dj9yXYWqGyMqZfbicaY+niaTM7mJzARETcsWbKEevXq8cknnzBu3Dg2bdpErVq13A5LRCRknCaD6/B0DccbYwLusWSMqQrMxrOszLr8hyciUjhOnjzJU089xe9+9ztuueUWEhMTGT58OBEREW6HJiISUk63o3sZeARoCuw3xszEs7TMUe/70XiWlukFXA+c914jIhK2Pv30U3r16kVycjKjR49m5MiRlClTxu2wREQKhaNk0Fr7jTHmIeBDPMneSO8rOwMcBx631v6rwFGKiITA6dOnGTJkCPHx8ZlDw02aNHE7LBGRQuV4NrS19u9AbTwVv51ABp7kz3g/3wm8BNzlPVdEJOysWbOG+vXr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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# figure\n", + "fig, ax = plt.subplots(figsize=(10,8))\n", + "\n", + "# plotting predicted vs true\n", + "# scatter\n", + "ax.errorbar(Ys_test.reshape(-1,1)[np.invert(flags)], preds[np.invert(flags)],\n", + " yerr = uncs[np.invert(flags)]*10, ls='none',\n", + " c=plt.cm.viridis(0.5), marker='o',\n", + " label=f'$\\sigma \\leq {uc_threshold}$')\n", + "\n", + "ax.errorbar(Ys_test.reshape(-1,1)[flags], preds[flags],\n", + " yerr = uncs[flags]*10, ls='none',\n", + " c='r', marker='o',\n", + " label=f'$\\sigma > {uc_threshold}$')\n", + "\n", + "min_y, max_y = np.min(Ys_test.reshape(-1,1)), np.max(Ys_test.reshape(-1,1))\n", + "# perfect results\n", + "x = np.linspace(min_y, max_y, 100)\n", + "ax.plot(x, x, 'k-', label= \"Perfect results\")\n", + "\n", + "# axes labels formatting\n", + "ax.set_xlabel('True target', fontsize=24)\n", + "ax.set_ylabel('Predicted target', fontsize=24)\n", + "\n", + "# tick formatting\n", + "plt.setp(ax.get_xticklabels(), fontsize=18)\n", + "plt.setp(ax.get_yticklabels(), fontsize=18)\n", + "ax.tick_params(direction='in', width=2, length=8)\n", + "\n", + "# legend & title\n", + "plt.legend(fontsize=18)\n", + "plt.title('Uncertain predictions on boston data\\nAll uncertainties x10 for display purposes', size=24)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
\n", + "" + ], + "text/plain": [ + "alt.Chart(...)" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import altair as alt\n", + "import pandas as pd\n", + "\n", + "df = pd.DataFrame({'preds': preds.squeeze(), 'true': Ys_test.squeeze(), 'uncs': uncs.squeeze()})\n", + "\n", + "alt.Chart(df).mark_point().encode(\n", + " x='true',\n", + " y='preds',\n", + " size='uncs:Q',\n", + " color='uncs:Q'\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "GAN.save('test_gan.h5')" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "GAN = gans.GAN.load('test_gan.h5')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/gandy/models/dcgan.py b/gandy/models/dcgan.py index 0969205..084e7c9 100644 --- a/gandy/models/dcgan.py +++ b/gandy/models/dcgan.py @@ -18,7 +18,7 @@ import deepchem import tensorflow as tf from tensorflow.keras.layers import Concatenate, Dense, Input -from tensorflow.keras.layers import Dropout, LeakyReLU +from tensorflow.keras.layers import Dropout # typing imports from typing import Tuple, Type diff --git a/gandy/tests/test_models/test_dcgan.py b/gandy/tests/test_models/test_dcgan.py deleted file mode 100644 index 29f347d..0000000 --- a/gandy/tests/test_models/test_dcgan.py +++ /dev/null @@ -1,47 +0,0 @@ -"""Testing functions for deepchem GAN class.""" - -# import numpy as np -import unittest -# import unittest.mock - -# import deepchem - -# import gandy.models.dcgan as dcgan - - -class TestGAN(unittest.TestCase): - """Test Deepchem GAN class.""" - - def test_create_generator(self): - """ - Test create generator function. - - The create generator function uses kwargs to create a Keras model. - This checks that the model compiles. - """ - # conditional_model = dcgan.CondDCGAN(xshape, yshape, noise_shape, - # n_classes=n_classes, **kwargs) - - # model = dcgan.DCGAN(self.xshape, self.yshape, noise_shape, **kwargs) - return - - def test_create_discriminator(self): - """ - Test create discriminator function. - - The create discriminator function uses kwargs to create a Keras model. - This checks that the model compiles. - """ - return - - def test_get_noise_input_shape(self): - """Test get_noise_input_shape function.""" - return - - def test_get_data_input_shapes(self): - """Test get_data_input_shapes function.""" - return - - def test_get_conditional_input_shapes(self): - """Test get_conditional_input_shapes function.""" - return From 7d983e347f533d802f2278e4bf1b412c903cadc9 Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Sun, 14 Mar 2021 17:37:52 -0700 Subject: [PATCH 98/99] Fixing PEP8 stuff --- gandy/models/dcgan.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gandy/models/dcgan.py b/gandy/models/dcgan.py index 084e7c9..72cfc0c 100644 --- a/gandy/models/dcgan.py +++ b/gandy/models/dcgan.py @@ -95,8 +95,9 @@ def __init__(self, xshape, yshape, noise_shape, **kwargs): warnings.warn(f"Incorrect key {key}. Must be in\ {Base_hyperparams.keys()}") else: - warnings.warn(f"{key} must start with generator_ or discriminator_" + - f"\nPassing {key} as deepchem model init kwargs.") + warnings.warn(f"{key} must start with generator_ or " + + f"discriminator_\nPassing {key} as deepchem" + + " model init kwargs.") # Deepchem init function + class atributes. super(DCGAN, self).__init__(**kwargs) From d50f2bc79c54be836f00a88d1254c1dd5388847d Mon Sep 17 00:00:00 2001 From: Samantha Tetef Date: Sun, 14 Mar 2021 19:24:00 -0700 Subject: [PATCH 99/99] Adjusted normalization to account for the nearly zero reconstucted y values --- examples/gan_demo.ipynb | 184 ++++++++++++++++++++++------------------ 1 file changed, 103 insertions(+), 81 deletions(-) diff --git a/examples/gan_demo.ipynb b/examples/gan_demo.ipynb index d437c89..f8b9eaa 100644 --- a/examples/gan_demo.ipynb +++ b/examples/gan_demo.ipynb @@ -89,20 +89,31 @@ "metadata": {}, "outputs": [], "source": [ - "# normalizing y to get standardize uncertainties\n", - "y_norm = sklearn.preprocessing.Normalizer()\n", - "Ys_train = y_norm.fit_transform(Ys_train.reshape(1, -1))\n", - "Ys_test = y_norm.transform(Ys_test.reshape(1, -1))\n", + "# # normalizing y to get standardize uncertainties\n", + "# y_norm = sklearn.preprocessing.Normalizer()\n", + "# Ys_train = y_norm.fit_transform(Ys_train.reshape(1, -1))\n", + "# Ys_test = y_norm.transform(Ys_test.reshape(1, -1))\n", "\n", - "# transposing to get batch as zeroth dimension\n", - "Ys_train = Ys_train.T\n", - "Ys_test = Ys_test.T" + "# # transposing to get batch as zeroth dimension\n", + "# Ys_train = Ys_train.T\n", + "# Ys_test = Ys_test.T" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, + "outputs": [], + "source": [ + "SCALE = np.max(Ys_train)\n", + "Ys_train = Ys_train.reshape(-1, 1)/SCALE\n", + "Ys_test = Ys_test.reshape(-1, 1)/SCALE" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, "outputs": [ { "name": "stdout", @@ -164,7 +175,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -179,7 +190,7 @@ ], "source": [ "# todo why specifying learning rate make non nan loss\n", - "GAN = gans.GAN(xshape=xshape, yshape=yshape, noise_shape=(5,), learning_rate=1e-4)" + "GAN = gans.GAN(xshape=xshape, yshape=yshape, noise_shape=(5,), learning_rate=5e-5)" ] }, { @@ -203,7 +214,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -212,7 +223,7 @@ "100" ] }, - "execution_count": 7, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -223,7 +234,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": { "scrolled": true }, @@ -232,57 +243,57 @@ "name": "stdout", "output_type": "stream", "text": [ - "Step 1000: \tAvg gen loss 0.6946533406972886, \tAvg discrim loss 1.384789663553238\n", - "Step 2000: \tAvg gen loss 0.6952816368341446, \tAvg discrim loss 1.3823101232051849\n", - "Step 3000: \tAvg gen loss 0.6960971853733062, \tAvg discrim loss 1.380865407705307\n", - "Step 4000: \tAvg gen loss 0.6949829128980637, \tAvg discrim loss 1.3828670566082\n", - "Step 5000: \tAvg gen loss 0.6936914792060852, \tAvg discrim loss 1.3853883373737335\n", - "Step 6000: \tAvg gen loss 0.693521221101284, \tAvg discrim loss 1.3858618046045303\n", - "Step 7000: \tAvg gen loss 0.6935025429725648, \tAvg discrim loss 1.3856645780801773\n", - "Step 8000: \tAvg gen loss 0.6935849062204361, \tAvg discrim loss 1.3855630407333375\n", - "Step 9000: \tAvg gen loss 0.6935890994668007, \tAvg discrim loss 1.385524563074112\n", - "Step 10000: \tAvg gen loss 0.6936377281546593, \tAvg discrim loss 1.385346347093582\n", - "Step 11000: \tAvg gen loss 0.6938541138768196, \tAvg discrim loss 1.385112920641899\n", - "Step 12000: \tAvg gen loss 0.6938354486823082, \tAvg discrim loss 1.384942196726799\n", - "Step 13000: \tAvg gen loss 0.6939847853183746, \tAvg discrim loss 1.3849611324071884\n", - "Step 14000: \tAvg gen loss 0.694022923886776, \tAvg discrim loss 1.3849232378005982\n", - "Step 15000: \tAvg gen loss 0.6939718227982521, \tAvg discrim loss 1.384895178079605\n", - "Step 16000: \tAvg gen loss 0.6938923109173775, \tAvg discrim loss 1.384940111041069\n", - "Step 17000: \tAvg gen loss 0.6939016106128693, \tAvg discrim loss 1.385041773915291\n", - "Step 18000: \tAvg gen loss 0.6939402292370797, \tAvg discrim loss 1.3852565339803695\n", - "Step 19000: \tAvg gen loss 0.6934576245546341, \tAvg discrim loss 1.3854440643787385\n", - "Step 20000: \tAvg gen loss 0.6937300187349319, \tAvg discrim loss 1.3854314596652986\n", - "Step 21000: \tAvg gen loss 0.6935716012716293, \tAvg discrim loss 1.3857012275457383\n", - "Step 22000: \tAvg gen loss 0.6935019016265869, \tAvg discrim loss 1.3858024929761887\n", - "Step 23000: \tAvg gen loss 0.6933977078795434, \tAvg discrim loss 1.3859471516609192\n", - "Step 24000: \tAvg gen loss 0.6931718240380287, \tAvg discrim loss 1.3860991570949555\n", - "Step 25000: \tAvg gen loss 0.6934327245950699, \tAvg discrim loss 1.3862461247444153\n", - "Step 26000: \tAvg gen loss 0.6932429926395416, \tAvg discrim loss 1.3862048870325088\n", - "Step 27000: \tAvg gen loss 0.6931959349513054, \tAvg discrim loss 1.3862611467838288\n", - "Step 28000: \tAvg gen loss 0.693244632601738, \tAvg discrim loss 1.3863033655881882\n", - "Step 29000: \tAvg gen loss 0.6934658588767052, \tAvg discrim loss 1.3862594746351242\n", - "Step 30000: \tAvg gen loss 0.6935401089191436, \tAvg discrim loss 1.3862267524003982\n", - "Step 31000: \tAvg gen loss 0.6932165855169297, \tAvg discrim loss 1.3862918070554733\n", - "Step 32000: \tAvg gen loss 0.6931548373699188, \tAvg discrim loss 1.3861615583896636\n", - "Step 33000: \tAvg gen loss 0.6935629423856735, \tAvg discrim loss 1.3862318274974823\n", - "Step 34000: \tAvg gen loss 0.6932820871472358, \tAvg discrim loss 1.3862072378396988\n", - "Step 35000: \tAvg gen loss 0.693261498093605, \tAvg discrim loss 1.386176702260971\n", - "Step 36000: \tAvg gen loss 0.6934829040169715, \tAvg discrim loss 1.3861383835077286\n", - "Step 37000: \tAvg gen loss 0.6933314707279206, \tAvg discrim loss 1.3861590344905854\n", - "Step 38000: \tAvg gen loss 0.6935650395154953, \tAvg discrim loss 1.386151852965355\n", - "Step 39000: \tAvg gen loss 0.6935194611549378, \tAvg discrim loss 1.3860945677757264\n", - "Step 40000: \tAvg gen loss 0.6933920715451241, \tAvg discrim loss 1.3860829125642777\n", - "Step 41000: \tAvg gen loss 0.6933604261279106, \tAvg discrim loss 1.3861441333293916\n", - "Step 42000: \tAvg gen loss 0.6933467952013016, \tAvg discrim loss 1.3860709898471832\n", - "Step 43000: \tAvg gen loss 0.6934700159430504, \tAvg discrim loss 1.386030709028244\n", - "Step 44000: \tAvg gen loss 0.6934939726591111, \tAvg discrim loss 1.3860569614171983\n", - "Step 45000: \tAvg gen loss 0.6935677703022957, \tAvg discrim loss 1.385985896587372\n", - "Step 46000: \tAvg gen loss 0.6932731596827507, \tAvg discrim loss 1.3859024583101271\n", - "Step 47000: \tAvg gen loss 0.693531041443348, \tAvg discrim loss 1.3860208741426467\n", - "Step 48000: \tAvg gen loss 0.6933853635191918, \tAvg discrim loss 1.3859323072433472\n", - "Step 49000: \tAvg gen loss 0.6933485987186432, \tAvg discrim loss 1.385881119132042\n", - "Step 50000: \tAvg gen loss 0.6933808777332305, \tAvg discrim loss 1.3859679154157638\n", - "TIMING: model fitting took 532.391 s\n" + "Step 1000: \tAvg gen loss 0.7005653661489487, \tAvg discrim loss 1.383721974015236\n", + "Step 2000: \tAvg gen loss 0.693137822508812, \tAvg discrim loss 1.3862176498174668\n", + "Step 3000: \tAvg gen loss 0.6937075527310371, \tAvg discrim loss 1.3853041270971298\n", + "Step 4000: \tAvg gen loss 0.6936965492963791, \tAvg discrim loss 1.3848481284379959\n", + "Step 5000: \tAvg gen loss 0.6945666595101356, \tAvg discrim loss 1.3845019186735152\n", + "Step 6000: \tAvg gen loss 0.6943578138947487, \tAvg discrim loss 1.3843441988229752\n", + "Step 7000: \tAvg gen loss 0.6943096024394035, \tAvg discrim loss 1.38392722427845\n", + "Step 8000: \tAvg gen loss 0.6946856787204743, \tAvg discrim loss 1.3837505439519882\n", + "Step 9000: \tAvg gen loss 0.6947433259487152, \tAvg discrim loss 1.3833782320022583\n", + "Step 10000: \tAvg gen loss 0.6951587910056114, \tAvg discrim loss 1.3828121030330658\n", + "Step 11000: \tAvg gen loss 0.6951612323522568, \tAvg discrim loss 1.3824316955804825\n", + "Step 12000: \tAvg gen loss 0.6956279197335243, \tAvg discrim loss 1.3817327207326888\n", + "Step 13000: \tAvg gen loss 0.695861194729805, \tAvg discrim loss 1.3813711644411086\n", + "Step 14000: \tAvg gen loss 0.6961504740715027, \tAvg discrim loss 1.3807270587682725\n", + "Step 15000: \tAvg gen loss 0.6967483221292495, \tAvg discrim loss 1.3799260572195053\n", + "Step 16000: \tAvg gen loss 0.6969651114344597, \tAvg discrim loss 1.3793615789413451\n", + "Step 17000: \tAvg gen loss 0.697314099431038, \tAvg discrim loss 1.3787989648580552\n", + "Step 18000: \tAvg gen loss 0.6978005281686783, \tAvg discrim loss 1.3780804442167283\n", + "Step 19000: \tAvg gen loss 0.6979357293248176, \tAvg discrim loss 1.377498877286911\n", + "Step 20000: \tAvg gen loss 0.6984333593249321, \tAvg discrim loss 1.3769132609367372\n", + "Step 21000: \tAvg gen loss 0.6985346401929855, \tAvg discrim loss 1.3762412637472152\n", + "Step 22000: \tAvg gen loss 0.6992717251181603, \tAvg discrim loss 1.3755210417509078\n", + "Step 23000: \tAvg gen loss 0.6991877008676529, \tAvg discrim loss 1.3753143258094787\n", + "Step 24000: \tAvg gen loss 0.6997933309674264, \tAvg discrim loss 1.3747382740974425\n", + "Step 25000: \tAvg gen loss 0.7001692517995834, \tAvg discrim loss 1.3744965431690217\n", + "Step 26000: \tAvg gen loss 0.7004035224318504, \tAvg discrim loss 1.3736837873458863\n", + "Step 27000: \tAvg gen loss 0.7008414195775986, \tAvg discrim loss 1.373176022052765\n", + "Step 28000: \tAvg gen loss 0.7012799869179726, \tAvg discrim loss 1.3726479399204254\n", + "Step 29000: \tAvg gen loss 0.7014802560210228, \tAvg discrim loss 1.3720180530548096\n", + "Step 30000: \tAvg gen loss 0.7021726654171944, \tAvg discrim loss 1.3714327815771103\n", + "Step 31000: \tAvg gen loss 0.7029320755004883, \tAvg discrim loss 1.370236884713173\n", + "Step 32000: \tAvg gen loss 0.7035190064907074, \tAvg discrim loss 1.36951764857769\n", + "Step 33000: \tAvg gen loss 0.7040885691642761, \tAvg discrim loss 1.368706019282341\n", + "Step 34000: \tAvg gen loss 0.7047167440056801, \tAvg discrim loss 1.368376741528511\n", + "Step 35000: \tAvg gen loss 0.7052603252530097, \tAvg discrim loss 1.3671231594085693\n", + "Step 36000: \tAvg gen loss 0.7056709294319152, \tAvg discrim loss 1.3665645232200623\n", + "Step 37000: \tAvg gen loss 0.706165294289589, \tAvg discrim loss 1.3659570508003236\n", + "Step 38000: \tAvg gen loss 0.7068880394697189, \tAvg discrim loss 1.3653373029232025\n", + "Step 39000: \tAvg gen loss 0.7073743197917938, \tAvg discrim loss 1.3648085827827454\n", + "Step 40000: \tAvg gen loss 0.7077153416275979, \tAvg discrim loss 1.3645117000341416\n", + "Step 41000: \tAvg gen loss 0.7083846154808998, \tAvg discrim loss 1.3639180800914765\n", + "Step 42000: \tAvg gen loss 0.7082982023954392, \tAvg discrim loss 1.3636782252788544\n", + "Step 43000: \tAvg gen loss 0.7090365720391274, \tAvg discrim loss 1.3633358459472655\n", + "Step 44000: \tAvg gen loss 0.7093083293437957, \tAvg discrim loss 1.3631585198640823\n", + "Step 45000: \tAvg gen loss 0.7092540643215179, \tAvg discrim loss 1.3637568624019623\n", + "Step 46000: \tAvg gen loss 0.7097676104903221, \tAvg discrim loss 1.363769258737564\n", + "Step 47000: \tAvg gen loss 0.7096816403865814, \tAvg discrim loss 1.3640771505832672\n", + "Step 48000: \tAvg gen loss 0.70963180154562, \tAvg discrim loss 1.3642490416765214\n", + "Step 49000: \tAvg gen loss 0.70898525172472, \tAvg discrim loss 1.3646650342941284\n", + "Step 50000: \tAvg gen loss 0.7094133262634277, \tAvg discrim loss 1.3658406114578248\n", + "TIMING: model fitting took 564.859 s\n" ] } ], @@ -292,7 +303,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -306,12 +317,12 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "metadata": {}, "outputs": [ { "data": { - "image/png": "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jh49yoABA9i5cye33HILnTp14rfffmPw4MFkZGQUuf/69es555xzaNy4MXfffTeNGjVi3759zJs3jxUrVtCnTx/3efr378/q1au5/PLLufXWW8nLy2PZsmV8//33XHXVVR7HdR3z0UcfJT09nfj4eF+nd1u2bBnfffcdN910E9dffz2ff/45Dz74INHR0UycOJHk5GSeeOIJNm3axCuvvML111/PjBkzSvQzW7x4MQMHDiQiIoLbb7+dxo0b89133/HAAw+wYsUKPv7YWm49NzeXc845h927d3PbbbfRrl07jh07xsqVK5kzZw7Dhw8H4KmnnuKxxx7j4osv5pZbbiE8PJytW7fy7bffkpWVRURE8E3uKoreyZUKQX2GdGbC2B8Bay27zIxsjyDDub6hq0tl1z6tqd84kUP7jpGaks7i2evpO6To4M5p/YqdHN5vLR5fOynWoxFKIO27newO+tYt3+E3ONy5eb97PmSTFvWIivb8peqad7htwz7ycvNZs3Q7Pc/0LC1ds2Qbx46kA5DUIMHvuf46vD9fT5xL5olsTmrd0G/GrDQS6sRy+sAOnD6wYC7j8dQMtq7bS+2kOPcyHWWhRdtGxCfGuEt+XfyV3voTHh7G4Et6MOAv3Ti8/xj1GyeWuIuqUlVJeFgYdWNiOHTCs1GXzjmsOVq9ErBJfqXactc/i73P2LFj2bZtG++++y433HADALfddhv33HMPL7/8cpH7T5s2jRMnTvDJJ5/Qq1cvv+MeeughVq9ezbhx47jpJs9lvPPzC69A16VLFz766KOg38eqVatYsGABvXv3BmDUqFG0aNGCv//979xxxx288sorHuNffPFF1q9fT/v27YM+h8vdd99NVlYWCxYsoGvXrgDccccdXHnllUyaNImRI0dy9tlns2bNGtavX8+zzz7L/fff7/d4U6ZMoWPHjnz77bce2//73/8W+9oqit7RlQpBzVs24KTW1vIRWZk5HvMLAY/5Zq7gMDw8jLMvPdW9vSSlpfMd5Zq9z+pEeK3CZZO+eDal8b+A/bYNjk6lbX0Ha84SyVWLCpfOzHOUvfYd0tlvYFOnXjxPv3cjl48exCOvXee342pZi68dwym9WpVpYAhWJ9bOPZMLbQ803zCQ8PAwGjZN0sBQhZQGPkpLNXOoqquvv/6aRo0acf3113tsf+CBB4LaPzHRmjv+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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -370,23 +381,34 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "0.0009118369731974477\n" + "0.07762911075762674\n" ] } ], "source": [ - "uc_threshold = 0.01\n", + "uc_threshold = 0.05\n", "preds, uncs, flags = GAN.predict(Xs_test, uc_threshold = uc_threshold)\n", "print(np.average(uncs))" ] }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "# inverses transform y data\n", + "preds = preds*SCALE\n", + "Ys_test = Ys_test*SCALE" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -396,12 +418,12 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 14, "metadata": {}, "outputs": [ { "data": { - "image/png": "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kpJCxYoVadGiBQ899FDUPFAURVGys2TJElq2bMmYMWO47777WLVqVUIbNbSbWClUjDF8//33AFSpUgWwhuAll1zC5s2b+e1vf0uzZs3YunUrL7/8MhdffDHLly+nbt262eQMGTKEo0ePcuedd1K+fHkaNmzIlClTGD9+PIsWLWLKlCkAnHXWWQBs3ryZtm3bkpGRwR133EH9+vX5/vvveeWVV1iwYAHLly+nQoUKAOzevZt27dqxevVqbrrpJu655x6OHz/OypUr+eSTT7j11lt59NFHyczMzJYWwCWXXJJrHmzevJnLL7+cbt260bVrV/bv3w/A0aNHufbaa1myZAm9evXivvvuY8+ePUyYMIFLL72Uzz//nAsvvBCAp59+mieeeILOnTtz9913U7JkSTZu3MjMmTM5cuQIpUuXzs9tAqBLly5s3bqV8ePHM3ToUJo2bQpA/fr1ARgwYACvv/46vXv35v777+f48eN89913zJ8/P99pK4qinAocPnyYYcOG8cILL1C7dm3mzZvH5Zdfnmi11BhMFIMHD2bVqlWJViMbLVu2ZPTo0fmScfDgQbZv344xhq1btzJ27Fj+9a9/0aZNGxo2bAjAE088wYYNG1i6dCnnnXdeVty+ffvSokULhg0blqPF7dChQ6xcuZKUlJQst0svvZS5c+eyaNEievbsmS387373O44ePcrKlSupVatWlnu3bt1o06YNo0aNyhr7N3ToUFavXs1f/vIX+vfvn01OZmYmAFdddRVTp04NTCs3Nm7cyIQJE+jXr18293HjxpGens7f//53rrnmmiz3e++9l+bNmzNkyJCslrwZM2bQtGlTZs6cmU3Gc889F5cu0Tj33HNp27Yt48eP56qrrsrWyurp8Ktf/SqrhVRRFEWJnRUrVtC7d2/WrFnDnXfeyQsvvED58uUTrRag3cRKATNs2DCqVq3KmWeeyXnnncfrr7/Or3/9az788EPAthROnTqVyy67jJo1a7J9+/aso2zZsrRp04Y5c+bkkHvPPfdkMwSjsWfPHj755BN+/etfk5ycnC2N1NRUGjRokJVGZmYm77zzDk2bNuXOO+/MIatEifw/IpUrV87qfvXz1ltv0aRJE1q1apVNx4yMDK666ioWL17MoUOHAKhQoQJbtmxh8eLF+dYnr1SoUIHVq1fzzTffJEwHRVGUE42MjAyGDRvGxRdfzO7du5k1axbjx48vNoYgaMtgwshvC1xxpX///nTr1g0RoWzZsjRq1CjbOIht27axY8cO5syZE3EJmCADrFGjRjHr8O2335KZmcnEiROZOHFiYJh69eoBsH37dnbt2sW1115baFum1a9fn5IlS+ZwX7t2LYcOHYq6FM727dupXbs2zz77LDfeeCPt27enRo0apKWl0alTJ2666SaSkpIKRe9wRo8eTa9evWjRogX16tWjY8eOdO7cmc6dOxeI0awoinKy8fXXX9OnTx9WrlxJr169eOmll6hUqVKi1cqBGoNKgdKwYUOuvPLKiP7GGACuvPJKHn744Zjlxtoq6E+jZ8+e9OnTJzDMaaedli1sYe6dG0l3YwwtWrRg5MiREeN6hmLbtm1Zv349s2fPZsGCBSxYsIC3336bp59+msWLF+c68PjYsWN5vwDHDTfcwKZNm5g1axYLFy5k7ty5TJw4kfbt2zN37twiM0oVRVGKO8eOHeP5559n2LBhVKpUiRkzZnDjjTcmWq2IqDGoFClVq1alYsWK7N27N6rRmB8aNGiAiJCRkZFrGlWrVqVSpUoxjd8saIOxYcOGbNu2jcsvvzymlrVy5crRtWtXunbtCtidVwYMGMDEiROzZvRWrlw5cHmC8FnakcjtGitXrkzPnj3p2bMnxhgeeeQR/vSnP/HRRx/RrVu3mNJQFEU5mfn222/p06cPX375JTfddBOvvPJK1gTK4or27ShFSokSJejRowfLli3j/fffDwwTtPRLPJxxxhlcd911TJ8+naVLl+bwN8awbdu2LH1uu+021qxZE9il7LUcgjXGgAJbC6p379789NNPEVsG/esybt++PYf/BRdckEOfRo0a8cUXX2Stwwiwa9eurCV4ciPSNR4/fpzdu3dncxMRzj///MDwiqIopxqZmZmMGjWKli1b8t133zFt2jTee++9Ym8IgrYMKgngmWee4R//+Ac333wzN998M23atCEpKYkffviBWbNm0apVq1zX78uNV155hXbt2nHZZZfRu3dvzj//fDIzM9mwYQMfffQRvXv3zppN/PTTTzN//nz69evHnDlzaNeuHcYYVq5cybFjx7KWkmnTpg3jxo3j3nvvpVOnTpQuXZqLL76Ys88+O086Dho0iM8++4yHHnqI+fPnc/nll1O+fHk2b97MvHnzSE5OZsGC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pk+fzjPPPMOSJUvUECxEdMzgCY62CCqKoignC7t372bQoEG8+eabnHfeecyePTvikmVKwaEtg4qiKIqiJJzZs2fTokULpk6dymOPPcayZcvUECwi1BhUFEVRFCVh7Nu3j7vuuotrr72W008/nS+++II//OEPurh/EaLGoKIoiqIoCSE9PZ1zzz2XCRMmMGTIEP75z39y0UUX5R5RKVDUGFQURVEUpUg5ePAggwYNomPHjpQqVYpFixbx/PPPk5ycnGjVTkl0AomiKIqiKEXGkiVL6Nu3L9999x333Xcfzz33HGXLlk20Wqc02jKoKIqiKEqhc/jwYR5++GHat2/PkSNHmDdvHmPHjlVDsBigLYOKoiiKohQqK1asoHfv3qxZs4Z+/frx4osvUr58+USrpTi0ZVBRFEVRlEIhIyODYcOGcfHFF7N7925mzZrFhAkT1BAsZmjL4IlOWpo9p6cnUgtFURRFycbXX39N7969WbVqFb169eKll16iUqVKiVZLCUBbBhVFURRFKTCOHTvGiBEjaNWqFT/++CMzZszgzTffVEOwGKMtg4qiKIqiFAjr1q2jT58+LFu2jJtuuomXX36ZqlWrJlotJRe0ZVA56cjMzGTUqFE0adKE5ORkateuzYMPPsiBAwcKTcaIESPo1q0b9erVQ0RITU0toKtRFEUp/mRmZjJy5EhatmzJ999/z7Rp03jvvffUEDxBUGNQOem4//77eeCBBzjnnHMYO3Ys3bp1Y8yYMXTu3JnMzMxCkTF06FDmz59P/fr1tStEUZRTivXr15OWlsaDDz7I1VdfzerVq7n11lsRkUSrpsSIdhMrCefAgQNs2bKFRo0a5VvW6tWrGTt2LF26dOGDDz7Icj/77LMZOHAg77zzDt27dy9wGevXr6devXoANG/enP379+f7WhRFUYozxhheffVVHnroIUqWLMnkyZPp3bu3GoEnINoyeCIzdSosXQoLF0Jqqv1fDFi4cCHXX389VatWpWTJkohItqN9+/bZwm/bto3GjRvTunVrxowZwy+//JLntKdNm4YxhsGDB2dzv/POO0lJSeGtt94qFBmeIagoinIqsHnzZq6++mruvfdeLrnkEr755hv69OmjhuAJihqDJypTp0L//nDkiP3/ww/2f4INwjfeeIPLL7+cVatWMWDAAEaPHk2aW/6mYsWK3HDDDdx6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UuxkZGQwZcoUGjVqxN69e3n33XdZvHixEkFxRJXBECtXrhy/+MUvOHbsGJcuXXI7HJFCFRERQVRUFLfeeitly5Z1OxyRYuX777+ne/fubNiwgYceeohZs2YRHR3tdlhSBCkZLARVqlRRB5eIiASFtZZZs2YxdOhQjDH85S9/oUePHmpUlHxTMigiIlJEHD58mGeeeYaVK1fywAMPEB8fz2233eZ2WFLEac6giIhImLPWMn/+fOrVq8fGjRt5++23Wb16tRJBCQpVBkVERMLYTz/9RJ8+fUhISOCXv/wlc+fO5c4773Q7LClGVBkUEREJUx988AF169ZlxYoVTJo0ic8//1yJoASdkkEREZEw83//93888cQTdOnShTvuuIPt27czZMgQLd4uIaFkUEREJIz87W9/o27dunz00UeMGTOGzZs3U6dOHbfDkmJMcwZFRETCwKlTpxg4cCDz58+nYcOGrFy5koYNG7odlpQAqgyKiIi4bOXKldSrV48FCxbw0ksvsXXrViWCUmiUDIqIiLjk7Nmz9OnTh1//+tdUrlyZf/zjH7zyyitERka6HZqUIEoGRUREXLBhwwYaNGjArFmzGDZsGF999RXNmjVzOywpgZQMioiIFKLz588zcOBA2rZtS+nSpdm4cSPjx4+nXLlybocmJZQaSERERArJl19+SVxcHP/617/o378/r732GhUrVnQ7LCnhVBkUEREJsYsXLzJixAjatGlDamoqa9euZcqUKUoEJSyoMigiIhJCiYmJxMbGsmfPHnr16sXEiROpXLmy22GJZFJlUEREJARSU1N5+eWXadGiBadOneKzzz5j5syZSgQl7KgyKCIiEmQ7d+4kLi6O7du38/TTTzN58mSuueYat8MS8UuVQRERkSBJS0vjtddeo2nTphw5coSPP/6Y+fPnKxGUsKbKoIiISBDs27ePuLg4tm7dSufOnZk2bRrVqlVzOyyRXKkyKCIiUgAZGRm88cYbNG7cmG+//Zb333+fDz74QImgFBmqDIqIiOTTgQMH6N69Oxs3buSRRx5h5syZVK9e3e2wRBxRZVBERMShjIwMpk2bRsOGDdmxYwdz584lISFBiaAUSaoMioiIOPDDDz/Qs2dP1qxZQ4cOHZg9ezY333yz22GJ5JsqgyIiInlgrSU+Pp769evz5ZdfMn36dFasWKFEUIo8VQZF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MoX+/fvz9ddf07p1a7fDEhFRMigiEkoXL15k+PDhxMTEkJqayrp165gyZQoVKlRwOzQREUB7BouIhExiYiKxsbHs2bOH3r17M3HiRKKiotwOS0TkCqoMiogEWWpqKqNGjaJFixacPn2a5cuXM2PGDCWCIhKWVBkUEQmiHTt2EBcXR1JSErGxsbz55ptcc801boclIhKQKoMiIkGQlpbG2LFjadasGUePHmXp0qXMmzdPiaCIhD1VBkVECmjfvn3ExcWxdetWHn/8caZOnUq1atXcDktEJE9UGRQRyaf09HRef/11GjduzLfffsv777/PokWLlAiKSJESsDLo3XouGKy1tmeQ7iUiEhYOHDhAt27d2LRpE48++igzZsygevXqboclIuJYTsPE3QALGD/v2SyfZ38/+3sWUDIoIsVCRkYG06dPZ9iwYZQpU4a5c+cSGxuLMf5+VIqIhL+cksHRAY5HAv2AKsAh4O/AETyJ343AvUAN4BQwHbgUpFhFRFz1ww8/0LNnT9asWUOHDh2YPXs2N998s9thiYgUSMBk0Fp7VTJojIkE1nuve9pau8DftcaYJ4GZQAzQLjihioi4w1rLnDlzGDx4MOnp6UyfPp3evXurGigixYLTBpLngZbAs4ESQQBr7XvAs8AvgeH5D09ExF1Hjx6lY8eO9OzZk8aNG7Nz50769OmjRFBEig2nyeCTQCrwXh7OfR/PEHFXp0GJiLjNWsvChQupV68e69evZ/Lkyaxbt47bb7/d7dBERILKaTJ4G3DRWpue24nW2jTgovcaEZEi4/jx4/zud7/jqaeeonbt2iQlJTFgwABKldJqXCJS/Dj9yXYWqGyMqZfbicaY+niaTM7mJzARETcsWbKEevXq8cknnzBu3Dg2bdpErVq13A5LRCRknCaD6/B0DccbYwLusWSMqQrMxrOszLr8hyciUjhOnjzJU089xe9+9ztuueUWEhMTGT58OBEREW6HJiISUk63o3sZeARoCuw3xszEs7TMUe/70XiWlukFXA+c914jIhK2Pv30U3r16kVycjKjR49m5MiRlClTxu2wREQKhaNk0Fr7jTHmIeBDPMneSO8rOwMcBx631v6rwFGKiITA6dOnGTJkCPHx8ZlDw02aNHE7LBGRQuV4NrS19u9AbTwVv51ABp7kz3g/3wm8BNzlPVdEJOysWbOG+vXr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\n", + "image/png": "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WrZsSVZWFs2bN2f48OF8//33ReTcsmUL48ePp3PnzmRnZ3PQQQfRs2dPXnjhBQByc3N56qmnCuUlIkybNi3p9bdu3Zrc3FyWLl3KWWedRYMGDTjmmGMK/PPy8rj77rs56qijyM7OpmHDhvTr14+lS5cWSkdVmThxIscccwz16tWjfv36dOzYkZ/85CeFHn4RYdiwYUXkSGU+3IQJE7jqqqsA6NOnT8E1Bunt2rWLCRMm0LFjR3JycmjYsCFdunRh3LhxScvAMAzDKMyqVavo2bMnkyZNYtSoUfz973/PqN3Zyr5qNZ77gV8D5wJ34YZdwQ2vxpPtzzsi/IwUUFW++OILAJo0aQI4Je6UU05hzZo1XH311Rx11FF8++23PPLII5x00kksWbKEVq1aFUpn7Nix7N69m2uvvZb69evTvn17nnnmGR577DEWLFjAM888A8AhhxwCwJo1azj55JPJz8/nJz/5CW3btuWLL77g0UcfZe7cuSxZsoQGDdwI+6ZNm+jZsycff/wxF110ETfccAN79+5l6dKlvPbaa1x66aWMHz+effv2FcoL4JRTTim2DNasWUPfvn25+OKLufDCC9m2zVm/2b17N2effTbvvfceV155JT/72c/YvHkzU6ZM4dRTT+Xvf/873bp1A+DOO+/kV7/6Ff369eP666+nZs2arFq1ildffZW8vLwyaQAGDhzIt99+y2OPPcatt95K586dAWjbti0AN954I0888QRDhgxhzJgx7N27l88//5x33nmn1HkbhmHsL8ycObPgo3nGjBkMGDAgwxJVMUVOVXeLyDdAE+/0jT9HDZ8GblHDrqVi9OjRLFu2rPiAFchxxx3HxIkTS5XGjh07WLduHarKt99+y0MPPcS//vUvevToQfv27QH41a9+xcqVK1m4cCHHHntsQdxhw4bRpUsXfv3rXxfp6dq5cydLly4lJyfWOXrqqafy1ltvsWDBAgYPHlwo/IgRI9i9ezdLly7lsMMOK3C/+OKL6dGjBw8++GDBwoVbb72Vjz/+mD/96U8MHz68UDr79u0D4IwzzuC5556LzKs4Vq1axZQpU7jmmmsKuT/88MPMmzePN954g7POOqvA/ac//SlHH300Y8eOLehBmzlzJp07d+bVV18tlMZvf/vbtGRJxjHHHMPJJ5/MY489xhlnnFGodzOQ4cc//nFBz6RhGIaROnl5edx8881MnjyZ7t278+KLL3LEEUdkWiygEg+tRiEi2cBhQDBh6z+4YdWTI4L38OclFSBateDXv/41TZs25eCDD+bYY4/liSee4Pzzz2fWrFmA66F77rnnOO2002jRogXr1q0rOOrUqUOPHj2YM2dOkXRvuOGGQkpcMjZv3sxrr73G+eefT3Z2dqE8WrduTbt27Qry2LdvHy+88AKdO3fm2muvLZJWjRqlr96NGzcu+PoK8+yzz9KpUye6du1aSMb8/HzOOOMM3n33XXbu3AlAgwYNWLt2Le+++26p5SkpDRo04OOPP+ajjz7KmAyGYRhVkVWrVtGrVy8mT57MqFGjePfddyuNEgeVtEdORA5S1fURXr/ByfwXcGZGROQvwEAROVZV/+Xj1wWuAT7HLZooU0rb81VZGT58OBdffDEiQp06dejQoUOhhQg//PAD69evZ86cOQnNhEQpTx06dEhZhk8//ZR9+/YxdepUpk6dGhmmTZs2AKxbt46NGzdy9tlnl9tq47Zt21KzZs0i7itWrGDnzp1JzaWsW7eOww8/nLvvvpsLLriAXr160bx5c3Jzczn33HO56KKLyMrKKhe545k4cSJXXnklXbp0oU2bNvTp04d+/frRr1+/MlF4DcMwqiOVcSg1nkqpyAG/FJEewFxgDVAXOAfoA7wPPBQK+z/A6cAcEXkQ2ILb2aEFcK7a6oWUad++PT/60Y8S+gdF+aMf/Yhbbrkl5XRT7Y0L5zF48GCGDh0aGebAAw8sFLY8TcYkkl1V6dKlCw888EDCuIGSd/LJJ/Pll1/y5ptvMnfuXObOncuf//xn7rzzTt59991iV+3u2bOn5Bfg6d+/P6tXr2b27NnMnz+ft956i6lTp9KrVy/eeuutClMoDcMwqgL5+fmMGzeuUg6lxlNZFbl5wJE4syMHAXtxvWvjgQdUNViRiqp+ISKnAr8FfgFkAR8CZ9v2XGVL06ZNadiwIVu2bEmq8JWGdu3aISLk5+cXm0fTpk1p1KhRSvMVy1rZa9++PT/88AN9+/ZNqUerbt26XHjhhVx44YWA29HixhtvZOrUqQUrRxs3bsyGDRuKxI1fDZyI4q6xcePGDB48mMGDB6Oq/OIXv+C+++7jlVde4eKLL04pD8MwjOrOqlWrGDRoEIsXL2bUqFHcd999lfpjt1KOqajqK6p6lqq2UNVsVa2jqsep6t1hJS4UfoWq9lfVhqqao6o9TYkre2rUqMEVV1zBokWLePnllyPDRJkHSYeDDjqIc845hxkzZrBw4cIi/qrKDz/8UCDPZZddxvLlyyOHYcOdsXXrOtODUYpSSRgyZAjfffddwh65sN29devWFfE/4YQTisjToUMH/vnPfxbY2QPYuHFjgZmW4kh0jXv37mXTpk2F3ESE448/PjK8YRjG/srMmTM5/vjj+eyzz5gxYwYTJ06s1EocVN4eOaOSctddd/GPf/yDSy65hEsuuYQePXqQlZXFV199xezZs+natWux9tmK49FHH6Vnz56cdtppDBkyhOOPP559+/axcuVKXnnlFYYMGVKwavXOO+/knXfe4ZprrmHOnDn07NkTVWXp0qXs2bOnwNxIjx49ePjhh/npT3/KueeeS61atTjppJNK3FU+atQo/va3vzFu3Djeeecd+vbtS/369VmzZg1vv/022dnZzJ07F4DOnTvTo0cPTjrpJJo3b15gJiQrK4tLL720IM2f/exnDB48mL59+3LllVeyadMmpkyZQqtWrQqMJyeje/fu1KhRg7vuuouNGzdSp04djjjiCDp27EizZs04//zzOf744zn44INZtWoVjz76KI0aNaJfv34lKgPDMIzqQn5+PjfffDOTJk2q9EOpRUhmGLe6Hl27dtXiWL58ebFhqgtz585VQO+///6Uwm/fvl3vuOMOPfroozU7O1vr1q2rnTp10muuuUYXLlxYEO7JJ59UQOfOnRuZztChQ9VVwaL88MMPOnbsWG3fvr3Wrl1bGzRooEcffbSOHDlSP/7440JhN27cqOPGjdO2bdtqrVq1tHHjxtqzZ0998cUXC8Ls3btXb7rpJm3RooXWqFFDAX3yySeTXmerVq20d+/eCf13796tkyZN0m7dumlOTo7m5ORou3bt9PLLL9c333yzINw999yjvXr10qZNm2pWVpYedthhetFFF+kHH3xQJM377rtPW7ZsqVlZWdqpUyedOnVqZDkmKttp06Zp586dtVatWgro0KFDNS8vT3/xi19o9+7dtXHjxpqVlaWtWrXSq666Sj/77LOkZRCwPz0PhmHsX6xcuVK7d++ugI4aNUrz8vIyLVIRgCWaQKcR3Q/XAnTr1k2XLElulWTFihUFRlUNY3/HngfDMKoj4VWpTz75ZKVclQog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/nlUldGjRxdyv/baa8nJyeHZZ58t1zTy8/PZtm1byYQ3DMOoJsQPpf7jH/8wJS6KSvIeN0UuEzz3HAwfDnl57v9XX7n/GVbmnnrqKfr27cuyZcu48cYbmThxIrnePErDhg3p378/l156aaE4zZo1Y/LkydSsWZNRo0bRokULzjnnHJ5//nl27NiRVv6LFy+mRo0anHjiiYXcs7OzOe6441i8eHG5pfHyyy+Tk5NDvXr1OPjggxkxYgSbN29OS37DMIyqzqxZszjhhBP47LPPmD59OpMmTaJ27dqZFqvyUZne46q63x1du3bV4li+fHmxYUpMq1aqUPRo1ar88iyGL7/8UrOzs/XII4/UDRs2FLjn5+dr+/btNSsrS7dv3540jS+++ELvuOMO7dSpkwJat25dHTJkiM6ZM0f37t1brAxHH320HnzwwZF+F198sQKal5dX5mmceOKJev/99+vMmTP1qaee0kGDBimgXbp00a1btxYr9/5AuT4PhmFknLy8PB01apQC2q1bN125cmWmRarcVPB7HFiiCXQa65HLBGvWpOdeATz44IPs2rWLKVOm0KhRowL3WrVqkZubS35+Pl999VXSNNq2bcttt93GihUr+OCDDxg+fDhvv/02Z555Jocddlixq0937NiR8MsvOzu7IEwySpLG+++/z9ixY7ngggsYMmQIL7zwAnfddRf/+c9/mDRpUtL8DMMwqjpRq1LNwG8xVKL3uClymaBly/TcK4BXX32Vdu3accoppxTxy/Ndx+mY4zjhhBP4/e9/z8KFCznvvPP49ttveeCBB/jhhx8SxsnJySnIK55du3YVhElGWaQBMG7cOLKysnj99deLDWsYhlFVCQ+lzpgxw4ZSU6USvcdNkcsEd90F8cpETo5zzwCbNm1izZo1HHvssZH+ixYt4tBDD+Xwww9POb2pU6dy+umn06pVK9544w3OPvtsnnvuOQ477LCE8Zo3b866desiFbG1a9fSpEkTsrKykuZdFmmA64kM0jIMw6huBAZ+BwwYQLt27Vi6dCkDBgzItFhVh0r0HjdFLhNccQU89hgEXz2tWrn/V1yREXG2bNkCEKngLFq0iE8++YRLLrkkaRq7du3i5ZdfZuDAgRx66KFcc801bN68mQceeIC1a9fy17/+lcsvv5wDDkhs8aZ79+7s27ePRYsWFUl72bJldOvW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\n", "text/plain": [ "
" ] @@ -419,12 +441,12 @@ "# plotting predicted vs true\n", "# scatter\n", "ax.errorbar(Ys_test.reshape(-1,1)[np.invert(flags)], preds[np.invert(flags)],\n", - " yerr = uncs[np.invert(flags)]*10, ls='none',\n", + " yerr = 10*uncs[np.invert(flags)], ls='none',\n", " c=plt.cm.viridis(0.5), marker='o',\n", " label=f'$\\sigma \\leq {uc_threshold}$')\n", "\n", "ax.errorbar(Ys_test.reshape(-1,1)[flags], preds[flags],\n", - " yerr = uncs[flags]*10, ls='none',\n", + " yerr = 10*uncs[flags], ls='none',\n", " c='r', marker='o',\n", " label=f'$\\sigma > {uc_threshold}$')\n", "\n", @@ -450,19 +472,19 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", - "
\n", + "
\n", "" ], "text/plain": [ "alt.Chart(...)" ] }, - "execution_count": 13, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -532,7 +554,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -541,7 +563,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [