From 518c78769dd1c9611d6227d8259e4f948996b94f Mon Sep 17 00:00:00 2001 From: Niklas Date: Tue, 6 Feb 2024 15:11:29 +0100 Subject: [PATCH] Added androidhelper (#524) --- .../utils/helpers/plugins/androidhelper.py | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 fedn/fedn/utils/helpers/plugins/androidhelper.py diff --git a/fedn/fedn/utils/helpers/plugins/androidhelper.py b/fedn/fedn/utils/helpers/plugins/androidhelper.py new file mode 100644 index 000000000..c01e2a053 --- /dev/null +++ b/fedn/fedn/utils/helpers/plugins/androidhelper.py @@ -0,0 +1,107 @@ +import os +import struct +import tempfile + +import numpy as np + +from .helperbase import HelperBase + + +class Helper(HelperBase): + """FEDn helper class for android json model weights.""" + + def __init__(self): + """Initialize helper.""" + self.name = "androidhelper" + super().__init__() + + # function to calculate an incremental weighted average of the weights + + def increment_average( + self, model, model_next, num_examples, total_examples + ): + """Incremental weighted average of model weights. + + :param model: Current model weights. + :type model: list of numpy arrays. + :param model_next: New model weights. + :type model_next: list of numpy arrays. + :param num_examples: Number of examples in new model. + :type num_examples: int + :param total_examples: Total number of examples. + :type total_examples: int + :return: Incremental weighted average of model weights. + :rtype: list of numpy arrays. + """ + # Incremental weighted average + w = num_examples / total_examples + + return (1 - w) * model + w * model_next + + # function to calculate an incremental weighted average of the weights using numpy.add + def increment_average_add( + self, model, model_next, num_examples, total_examples + ): + """Incremental weighted average of model weights. + + :param model: Current model weights. + :type model: list of numpy arrays. + :param model_next: New model weights. + :type model_next: list of numpy arrays. + :param num_examples: Number of examples in new model. + :type num_examples: int + :param total_examples: Total number of examples. + :type total_examples: int + :return: Incremental weighted average of model weights. + :rtype: list of numpy arrays. + """ + # Incremental weighted average + w = np.add( + model, + num_examples + * (np.array(model_next) - np.array(model)) + / total_examples, + ) + return w + + def save(self, weights, path=None): + """Serialize weights to file. The serialized model must be a single binary object. + + :param weights: weights in json format. + :param path: Path to file. + :return: Path to file. + """ + if not path: + path = self.get_tmp_path() + + byte_array = struct.pack("f"*len(weights), *weights) + with open(path, "wb") as file: + file.write(byte_array) + + return path + + def load(self, fh): + """Load weights from file or filelike. + + :param fh: file path, filehandle, filelike. + :return: List of weights in json format. + """ + print("in android helper load") + if isinstance(fh, str): + with open(fh, "rb") as file: + byte_data = file.read() + else: + byte_data = fh.read() + + weights = np.array(struct.unpack(f'{len(byte_data) // 4}f', byte_data)) + + return weights + + def get_tmp_path(self): + """Return a temporary output path compatible with save_model, load_model. + + :return: Path to file. + """ + fd, path = tempfile.mkstemp(suffix="") + os.close(fd) + return path