From e81b88e0e1a32595718234fb11663e9d6d93f15c Mon Sep 17 00:00:00 2001 From: Charles Dickens Date: Sat, 27 Apr 2024 15:37:55 -0700 Subject: [PATCH] Style and signatures of deep models. --- psl-python/pslpython/deeppsl/model.py | 115 ++++++++++-------- psl-python/pslpython/deeppsl/server.py | 30 ++--- .../models/deeppsl/sign/pytorch_model.py | 16 +-- .../models/deeppsl/sign/scikit_learn_model.py | 12 +- .../models/deeppsl/sign/tensorflow_model.py | 24 ++-- 5 files changed, 108 insertions(+), 89 deletions(-) diff --git a/psl-python/pslpython/deeppsl/model.py b/psl-python/pslpython/deeppsl/model.py index 27b525bab..452c64780 100644 --- a/psl-python/pslpython/deeppsl/model.py +++ b/psl-python/pslpython/deeppsl/model.py @@ -42,46 +42,59 @@ def __init__(self): Higher-level methods that are passed nicely-formatted data for implementing classes to extend. """ - def internal_init(self, application, options = {}): + def internal_init(self, application, options): """ Initialize the model. :param application: The application that is using the model from ["inference", "learning"]. :param options: Additional options for the model provided by the user in the configuration file. + :return: """ raise NotImplementedError("internal_init") - def internal_fit(self, data, gradients, options = {}): + def internal_fit(self, data, gradients, options): + """ + Use the data and gradients to update the model. + :param data: The data used to compute the gradients. + :param gradients: The gradients computed from the data. + :param options: Additional options for the model provided by the user in the configuration file. + :return: + """ raise NotImplementedError("internal_fit") - def internal_next_batch(self, options = {}): - # Default to doing nothing. + def internal_next_batch(self, options): + """ + Prepare the next batch of data for training. + Default to doing nothing. + :param options: + :return: + """ return - def internal_train_mode(self, options = {}): + def internal_train_mode(self, options): """ Set the model to training mode. Training mode is identified by the _train flag being true. - :param options: + :param options: Additional options for the model provided by the user in the configuration file. """ self._train = True return - def internal_eval_mode(self, options = {}): + def internal_eval_mode(self, options): """ - Set the model to evaluation mode. Evaluation mode is identified by the _train flag being fals. - :param options: + Set the model to evaluation mode. Evaluation mode is identified by the _train flag being false. + :param options: Additional options for the model provided by the user in the configuration file. """ self._train = False return - def internal_epoch_start(self, options = {}): + def internal_epoch_start(self, options): # Default to doing nothing. return - def internal_epoch_end(self, options = {}): + def internal_epoch_end(self, options): # Default to doing nothing. return - def internal_is_epoch_complete(self, options = {}): + def internal_is_epoch_complete(self, options): """ Default to assuming that the epoch is complete. This is equivalent to assuming a single batch per epoch Override this method if your model needs to do something special to determine if the epoch is complete. @@ -91,43 +104,49 @@ def internal_is_epoch_complete(self, options = {}): """ return True - def internal_predict(self, data, options = {}): + def internal_predict(self, data, options): + """ + Use the data to make predictions. + :param data: The data to make predictions with. + :param options: Additional options for the model provided by the user in the configuration file. + :return: + """ raise NotImplementedError("internal_predict") - def internal_eval(self, data, options = {}): + def internal_eval(self, data, options): """ Default to assuming that the model does have an evaluator and return 0.0. Override this method if your model has an evaluator. If you do override this method, you must return a dictionary with a key, "loss", that maps to a float where lower is better. - :param data: - :param options: + :param data: The data to evaluate the model with. + :param options: Additional options for the model provided by the user in the configuration file. :return: A float indicating the loss. Higher is worse. """ return 0.0 - def internal_save(self, options = {}): + def internal_save(self, options): raise NotImplementedError("internal_save") """ Low-level methods that take care of moving around data. """ - def init_weight(self, shared_memory_path, application, options = {}): + def init_weight(self, shared_memory_path, application, options): raise NotImplementedError("init_weight") - def fit_weight(self, options = {}): + def fit_weight(self, options): raise NotImplementedError("fit_weight") - def predict_weight(self, options = {}): + def predict_weight(self, options): raise NotImplementedError("predict_weight") - def predict_weight_learn(self, options = {}): + def predict_weight_learn(self, options): raise NotImplementedError("predict_weight") - def eval_weight(self, options = {}): + def eval_weight(self, options): raise NotImplementedError("eval_weight") - def init_predicate(self, shared_memory_path, application, options = {}): + def init_predicate(self, shared_memory_path, application, options): """ Initialize the model. :param shared_memory_path: The path to the shared memory file. @@ -152,9 +171,9 @@ def init_predicate(self, shared_memory_path, application, options = {}): self._data = numpy.array(self._data) - return self.internal_init(application, options = options) + return self.internal_init(application, options) - def fit_predicate(self, options = {}): + def fit_predicate(self, options): self._shared_buffer.seek(0) count = self._read_int() @@ -163,30 +182,30 @@ def fit_predicate(self, options = {}): data = numpy.array([self._data[index] for index in entity_indexes]) - return self.internal_fit(data, gradients, options = options) + return self.internal_fit(data, gradients, options) - def train_mode(self, options = {}): - return self.internal_train_mode(options = options) + def train_mode(self, options): + return self.internal_train_mode(options) - def eval_mode(self, options = {}): - return self.internal_eval_mode(options = options) + def eval_mode(self, options): + return self.internal_eval_mode(options) - def next_batch(self, options = {}): - return self.internal_next_batch(options = options) + def next_batch(self, options): + return self.internal_next_batch(options) - def epoch_start(self, options = {}): - return self.internal_epoch_start(options = options) + def epoch_start(self, options): + return self.internal_epoch_start(options) - def epoch_end(self, options = {}): - return self.internal_epoch_end(options = options) + def epoch_end(self, options): + return self.internal_epoch_end(options) - def is_epoch_complete(self, options = {}): - return self.internal_is_epoch_complete(options = options) + def is_epoch_complete(self, options): + return self.internal_is_epoch_complete(options) - def predict_predicate(self, options = {}): - self._predict_predicate(options = options) + def predict_predicate(self, options): + self._predict_predicate(options) - def _predict_predicate(self, options = {}): + def _predict_predicate(self, options): self._shared_buffer.seek(0) count = self._read_int() @@ -194,17 +213,17 @@ def _predict_predicate(self, options = {}): data = numpy.array([self._data[index] for index in entity_indexes]) - predictions, response = self.internal_predict(data, options=options) + predictions, response = self.internal_predict(data, options) self._shared_buffer.seek(0) self._write_int(int(options['class-size']) * len(predictions)) - predictions = numpy.array(predictions, dtype = '>f4', copy = False) - self._shared_buffer.write(predictions.tobytes(order = 'C')) + predictions = numpy.array(predictions, dtype='>f4', copy=False) + self._shared_buffer.write(predictions.tobytes(order='C')) return response - def eval_predicate(self, options = {}): + def eval_predicate(self, options): self._shared_buffer.seek(0) count = self._read_int() @@ -212,10 +231,10 @@ def eval_predicate(self, options = {}): data = numpy.array([self._data[index] for index in entity_indexes]) - return self.internal_eval(data, options=options) + return self.internal_eval(data, options) - def save(self, options = {}): - return self.internal_save(options=options) + def save(self, options): + return self.internal_save(options) """ Helper methods. diff --git a/psl-python/pslpython/deeppsl/server.py b/psl-python/pslpython/deeppsl/server.py index 6e6116b24..6436d000e 100644 --- a/psl-python/pslpython/deeppsl/server.py +++ b/psl-python/pslpython/deeppsl/server.py @@ -107,9 +107,9 @@ def _init(self, request): self._model = self._load_model(os.path.join(options['relative-dir'], options['model-path'])) if deep_model == 'DeepModelPredicate': - return self._model.init_predicate(shared_memory_path, application, options=options) + return self._model.init_predicate(shared_memory_path, application, options) elif deep_model == 'DeepModelWeight': - return self._model.init_weight(shared_memory_path, application, options=options) + return self._model.init_weight(shared_memory_path, application, options) else: raise ValueError("Unknown deep model type in init: '%s'." % (deep_model,)) @@ -118,38 +118,38 @@ def _fit(self, request): options = request.get('options', {}) if deep_model == 'DeepModelPredicate': - return self._model.fit_predicate(options=options) + return self._model.fit_predicate(options) elif deep_model == 'DeepModelWeight': - return self._model.fit_weight(options=options) + return self._model.fit_weight(options) else: raise ValueError("Unknown deep model type in fit: '%s'." % (deep_model,)) def _train_mode(self, request): - return self._model.train_mode(options=request.get('options', {})) + return self._model.train_mode(request.get('options', {})) def _eval_mode(self, request): - return self._model.eval_mode(options=request.get('options', {})) + return self._model.eval_mode(request.get('options', {})) def _next_batch(self, request): - return self._model.next_batch(options=request.get('options', {})) + return self._model.next_batch(request.get('options', {})) def _epoch_start(self, request): - return self._model.epoch_start(options=request.get('options', {})) + return self._model.epoch_start(request.get('options', {})) def _epoch_end(self, request): - return self._model.epoch_end(options=request.get('options', {})) + return self._model.epoch_end(request.get('options', {})) def _is_epoch_complete(self, request): - return self._model.is_epoch_complete(options=request.get('options', {})) + return self._model.is_epoch_complete(request.get('options', {})) def _predict(self, request): deep_model = request['deep_model'] options = request.get('options', {}) if deep_model == 'DeepModelPredicate': - return self._model.predict_predicate(options=options) + return self._model.predict_predicate(options) elif deep_model == 'DeepModelWeight': - return self._model.predict_weight(options=options) + return self._model.predict_weight(options) else: raise ValueError("Unknown deep model type in predict: '%s'." % (deep_model,)) @@ -158,15 +158,15 @@ def _eval(self, request): options = request.get('options', {}) if deep_model == 'DeepModelPredicate': - return self._model.eval_predicate(options=options) + return self._model.eval_predicate(options) elif deep_model == 'DeepModelWeight': - return self._model.eval_weight(options=options) + return self._model.eval_weight(options) else: raise ValueError("Unknown deep model type in eval: '%s'." % (deep_model,)) def _save(self, request): options = request.get('options', {}) - return self._model.save(options=options) + return self._model.save(options) def _close(self): if self._model is not None: diff --git a/psl-python/tests/resources/models/deeppsl/sign/pytorch_model.py b/psl-python/tests/resources/models/deeppsl/sign/pytorch_model.py index a6e26c694..2c6dfd4dd 100755 --- a/psl-python/tests/resources/models/deeppsl/sign/pytorch_model.py +++ b/psl-python/tests/resources/models/deeppsl/sign/pytorch_model.py @@ -34,7 +34,7 @@ def __init__(self): self._loss = None self._optimizer = None - def internal_init(self, application, options = {}): + def internal_init(self, application, options): class SignPytorchNetwork(torch.nn.Module): def __init__(self, input_size, output_size): _import() @@ -54,7 +54,7 @@ def forward(self, x): return {} - def internal_fit(self, data, gradients, options = {}, verbose=0): + def internal_fit(self, data, gradients, options, verbose=0): features = torch.FloatTensor(data[0]) labels = torch.FloatTensor(data[1]) @@ -68,24 +68,24 @@ def internal_fit(self, data, gradients, options = {}, verbose=0): self._optimizer.step() return {} - def internal_predict(self, data, options = {}, verbose=0): + def internal_predict(self, data, options, verbose=0): features = torch.FloatTensor(data[0]) predictions = self._model(features) return predictions, {} - def internal_eval(self, data, options = {}): - predictions, _ = self.internal_predict(data, options=options) + def internal_eval(self, data, options): + predictions, _ = self.internal_predict(data, options) results = {'loss': self._loss(self._model(torch.FloatTensor(data[0])), torch.FloatTensor(data[1])).item(), 'metrics': calculate_metrics(predictions.detach().numpy(), data[1], self._metrics)} return results - def internal_save(self, options = {}): + def internal_save(self, options): torch.save(self._model.state_dict(), options['save_path']) return {} - def load(self, options = {}): - self.internal_init(None, options=options) + def load(self, options): + self.internal_init(None, options) self._model.load_state_dict(torch.load(options['load_path'])) return {} diff --git a/psl-python/tests/resources/models/deeppsl/sign/scikit_learn_model.py b/psl-python/tests/resources/models/deeppsl/sign/scikit_learn_model.py index 45f1d6321..39c0d1420 100755 --- a/psl-python/tests/resources/models/deeppsl/sign/scikit_learn_model.py +++ b/psl-python/tests/resources/models/deeppsl/sign/scikit_learn_model.py @@ -32,25 +32,25 @@ def __init__(self): self._model = None self._metrics = ['categorical_accuracy'] - def internal_init(self, application, options = {}): + def internal_init(self, application, options): self._model = tree.DecisionTreeClassifier() return {} - def internal_fit(self, data, gradients, options = {}): + def internal_fit(self, data, gradients, options): self._model.fit(numpy.array(data[0]), numpy.array(data[1])) return {} - def internal_predict(self, data, options = {}): + def internal_predict(self, data, options): predictions = self._model.predict(data[0]) return predictions, {} - def internal_eval(self, data, options = {}): - predictions, _ = self.internal_predict(data); + def internal_eval(self, data, options): + predictions, _ = self.internal_predict(data, options) results = {'metrics': calculate_metrics(predictions, data[1], self._metrics)} return results - def internal_save(self, options = {}): + def internal_save(self, options): return {} diff --git a/psl-python/tests/resources/models/deeppsl/sign/tensorflow_model.py b/psl-python/tests/resources/models/deeppsl/sign/tensorflow_model.py index abe61f4ab..9b158d73b 100755 --- a/psl-python/tests/resources/models/deeppsl/sign/tensorflow_model.py +++ b/psl-python/tests/resources/models/deeppsl/sign/tensorflow_model.py @@ -32,7 +32,7 @@ def __init__(self): self._model = None self._metrics = ['categorical_accuracy'] - def internal_init(self, application, options = {}): + def internal_init(self, application, options): layers = [ tensorflow.keras.layers.Input((int(options['input_shape']), )), tensorflow.keras.layers.Dense(int(options['output_shape']), activation = 'softmax'), @@ -41,25 +41,25 @@ def internal_init(self, application, options = {}): model = tensorflow.keras.Sequential(layers) model.compile( - optimizer = tensorflow.keras.optimizers.Adam(learning_rate = float(options['learning_rate'])), - loss = tensorflow.keras.losses.CategoricalCrossentropy(from_logits = False), - metrics = self._metrics + optimizer=tensorflow.keras.optimizers.Adam(learning_rate=float(options['learning_rate'])), + loss=tensorflow.keras.losses.CategoricalCrossentropy(from_logits=False), + metrics=self._metrics ) self._model = model return {} - def internal_fit(self, data, gradients, options = {}): + def internal_fit(self, data, gradients, options): data = self._prepare_data(data) - self._model.fit(data[0], data[1], epochs = int(options['epochs']), verbose=0) + self._model.fit(data[0], data[1], epochs=int(options['epochs']), verbose=0) return {} - def internal_predict(self, data, options = {}): + def internal_predict(self, data, options): data = self._prepare_data(data) predictions = self._model.predict(numpy.array(data[0]), verbose=0) return predictions, {} - def internal_eval(self, data, options = {}): + def internal_eval(self, data, options): data = self._prepare_data(data) predictions, _ = self.internal_predict(data, options=options) results = {'loss': self._model.compiled_loss(tensorflow.constant(predictions, dtype=tensorflow.float32), tensorflow.constant(data[1], dtype=tensorflow.float32)), @@ -67,14 +67,14 @@ def internal_eval(self, data, options = {}): return results - def internal_save(self, options = {}): + def internal_save(self, options): if 'save_path' not in options: return {} - self._model.save(options['save_path'], save_format = 'tf') + self._model.save(options['save_path'], save_format='tf') return {} - def load(self, options = {}): + def load(self, options): self._model = tensorflow.keras.models.load_model(options['load_path']) return {} @@ -82,7 +82,7 @@ def _prepare_data(self, data): if len(data) == 2: return data - return [numpy.asarray(data[:, :-1]), numpy.asarray([[1, 0] if label == 0 else [0, 1] for label in data[:,-1]])] + return numpy.array([numpy.asarray(data[:, :-1]), numpy.asarray([[1, 0] if label == 0 else [0, 1] for label in data[:, -1]])]) def calculate_metrics(y_pred, y_truth, metrics):