From f428ac56faf8bb5805864ad13a403683cb6128b5 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sun, 20 Oct 2024 10:33:34 -0400 Subject: [PATCH 01/99] readme --- baselines/fedht/README.md | 99 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 baselines/fedht/README.md diff --git a/baselines/fedht/README.md b/baselines/fedht/README.md new file mode 100644 index 000000000000..c9455870e7c9 --- /dev/null +++ b/baselines/fedht/README.md @@ -0,0 +1,99 @@ +--- +title: Federated nonconvex sparse learning +url: https://arxiv.org/abs/2101.00052 +labels: [non-iid, image classification, sparsity] +dataset: [MNIST] +--- + +# Federated nonconvex sparse learning + +> Note: If you use this Flower baseline in your work, please remember to cite the original authors of the paper, as well as Flower. + +**Paper:** [https://arxiv.org/abs/2101.00052](https://arxiv.org/abs/2101.00052) + +**Authors:** Qianqian Tong, Guannan Liang, Tan Zhu, Jinbo Bi + +**Abstract:** Nonconvex sparse learning plays an essential role in many areas, such as signal processing and deep network compression. Iterative hard thresholding (IHT) methods are the state-of-the-art for nonconvex sparse learning due to their capability of recovering true support and scalability with large datasets. Theoretical analysis of IHT is currently based on centralized IID data. In realistic large-scale situations, however, data are distributed, hardly IID, and private to local edge computing devices. It is thus necessary to examine the property of IHT in federated settings, which update in parallel on local devices and communicate with a central server only once in a while without sharing local data. In this paper, we propose two IHT methods: Federated Hard Thresholding (Fed-HT) and Federated Iterative Hard Thresholding (FedIter-HT). We prove that both algorithms enjoy a linear convergence rate and have strong guarantees to recover the optimal sparse estimator, similar to traditional IHT methods, but now with decentralized non-IID data. Empirical results demonstrate that the Fed-HT and FedIter-HT outperform their competitor - a distributed IHT, in terms of decreasing the objective values with lower requirements on communication rounds and bandwidth. + +# Environment Setup + +Create a new Python environment using [pyenv](https://github.com/pyenv/pyenv) and [virtualenv plugin](https://github.com/pyenv/pyenv-virtualenv). Alternatively, use a Python environment manager of your choice like [`venv`](https://docs.python.org/3/library/venv.html). Then install the project: + +```bash +# Create the environment +pyenv virtualenv 3.10.11 fedht-3.10.11 + +# Activate it +pyenv activate fedht-3.10.11 + +# Then install the project +cd fedht && pip install -e . +``` + +## About this baseline + +The purpose of this baseline is 1) implement the federated aggregation strategies introduced in Tong et. al. 2020, and 2) showcase the aggregation strategies with the datasets included in the paper. The two strategies introduced include Fed-HT and FedIter-HT. Fed-HT and FedIter-HT both apply hardthresholding (restricted by the hardthresholding parameter $\tau$) following the aggregation step. FedIter-HT, additionally, applies hardthresholding to each client model prior to aggregation. We also include FedAvg and Distributed-IHT , i.e., Fed-HT with `num_local_epochs` set to 1. + +Two federated classification models are implemented, the first using the well-known MNIST dataset (with 10 clients) and the second using a simulated dataset (with 25 clients). + +| Dataset | Model | Features | Classes | +| ------------------| ---------------------------------|----------|---------| +| `MNIST` | `Multinomial Regression` |724 | 10 | +| `Simulation II` | `Logistic Regression` |1000 | 2 | + +The data generation procedure for the simulated dataset matches that of Simulation II in Tong et. al. For both datasets, the initial global model parameters are all set to 0. Additionally, the MNIST data is heterogeneous, with only two out of the ten classes included in each client dataset. + +**Contributors:** Chancellor Johnstone + +**Training Hyperparameters:** The hyperparameters can be found in `conf/base_.yaml` files. The configuration files can be adjusted inline when calling the function from the terminal. The hardthresholding parameter can be adjusted through the `num_keep`. FedIter-HT can be implemented by setting `iterht=True`, but the default sets `iterht=False`. + +| Description | Default Value (MNIST) | +| --------------------- | ----------------------------------- | +| `num_clients` | `10` | +| `num_rounds` | `100` | +| `batch_size` | `50` | +| `num_local_epochs` | `10` | +| `num_keep` | `500` | +| `learning_rate` | `0.0005` | +| `weight_decay` | `0.000` | +| `client resources` | `{'num_cpus': 2}` | +| `iterht` | `False` | + +| Description | Default Value (Simulation II) | +| --------------------- | ----------------------------------- | +| `num_clients` | `25` | +| `num_rounds` | `100` | +| `batch_size` | `50` | +| `num_local_epochs` | `5` | +| `num_keep` | `200` | +| `learning_rate` | `0.0001` | +| `weight_decay` | `0.000` | +| `client resources` | `{'num_cpus': 2}` | +| `iterht` | `False` | + +We note that in the current implementation, only weights (and not biases) of the model(s) are subject to hardthresholding; this practice aligns with sparse model literature. Additionally, the `num_keep` hardthresholding parameter is enforced at the output layer level, as opposed to constraining the number of parameters across the entire model. Specifically, for a fully connected layer with $i$ inputs and $j$ outputs, the $j$-th output's parameters are constrained by `num_keep`. + +## Install dependencies + +```bash +pip install . +``` + +## Expected Results +### MNIST (`num_keep` = 500) +``` +python main.py --config-name base_mnist agg=fedavg num_keep=500 num_local_epochs=10 learning_rate=0.00005 +python main.py --config-name base_mnist agg=fedht num_keep=500 num_local_epochs=10 learning_rate=0.00005 +python main.py --config-name base_mnist agg=fedht iterht=True num_keep=500 num_local_epochs=10 learning_rate=0.00005 +python main.py --config-name base_mnist agg=fedht num_keep=500 num_local_epochs=1 learning_rate=0.00005 +``` + + +### Simulation II (`num_keep` = 200) +``` +python main.py -config-name base_simII agg=fedavg num_local_epochs=5 learning_rate=0.01 +python main.py -config-name base_simII agg=fedht num_local_epochs=5 learning_rate=0.01 +python main.py -config-name base_simII agg=fedht iterht=True num_local_epochs=5 learning_rate=0.01 +python main.py -config-name base_simII agg=fedht num_local_epochs=1 learning_rate=0.01 +``` + From 6c9a7962ac22caec1bc54b9a3fb76ff1f697546d Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sun, 20 Oct 2024 10:34:47 -0400 Subject: [PATCH 02/99] all files --- baselines/fedht/.gitignore | 323 +++++++++++++ baselines/fedht/fedht/__init__.py | 23 + baselines/fedht/fedht/aggregate.py | 448 +++++++++++++++++++ baselines/fedht/fedht/client.py | 158 +++++++ baselines/fedht/fedht/conf/base_mnist.yaml | 43 ++ baselines/fedht/fedht/conf/base_simII.yaml | 43 ++ baselines/fedht/fedht/fedht.py | 289 ++++++++++++ baselines/fedht/fedht/gen_sim_data.py | 35 ++ baselines/fedht/fedht/loss_results_mnist.png | Bin 0 -> 39211 bytes baselines/fedht/fedht/loss_results_simII.png | Bin 0 -> 48561 bytes baselines/fedht/fedht/main.py | 133 ++++++ baselines/fedht/fedht/model.py | 74 +++ baselines/fedht/fedht/server.py | 25 ++ baselines/fedht/fedht/utils.py | 68 +++ baselines/fedht/pyproject.toml | 50 +++ 15 files changed, 1712 insertions(+) create mode 100644 baselines/fedht/.gitignore create mode 100644 baselines/fedht/fedht/__init__.py create mode 100644 baselines/fedht/fedht/aggregate.py create mode 100644 baselines/fedht/fedht/client.py create mode 100644 baselines/fedht/fedht/conf/base_mnist.yaml create mode 100644 baselines/fedht/fedht/conf/base_simII.yaml create mode 100644 baselines/fedht/fedht/fedht.py create mode 100644 baselines/fedht/fedht/gen_sim_data.py create mode 100644 baselines/fedht/fedht/loss_results_mnist.png create mode 100644 baselines/fedht/fedht/loss_results_simII.png create mode 100644 baselines/fedht/fedht/main.py create mode 100644 baselines/fedht/fedht/model.py create mode 100644 baselines/fedht/fedht/server.py create mode 100644 baselines/fedht/fedht/utils.py create mode 100644 baselines/fedht/pyproject.toml diff --git a/baselines/fedht/.gitignore b/baselines/fedht/.gitignore new file mode 100644 index 000000000000..2afed68957b2 --- /dev/null +++ b/baselines/fedht/.gitignore @@ -0,0 +1,323 @@ +<<<<<<< HEAD +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ +======= +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ +>>>>>>> master diff --git a/baselines/fedht/fedht/__init__.py b/baselines/fedht/fedht/__init__.py new file mode 100644 index 000000000000..a9b0f112d5d4 --- /dev/null +++ b/baselines/fedht/fedht/__init__.py @@ -0,0 +1,23 @@ +# Copyright 2020 Flower Labs GmbH. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""Contains the strategy abstraction and different implementations.""" + +from flwr.server.strategy.fedavg import FedAvg as FedAvg +from .fedht import FedHT as FedHT + +__all__ = [ + "FedAvg", + "FedHT" +] diff --git a/baselines/fedht/fedht/aggregate.py b/baselines/fedht/fedht/aggregate.py new file mode 100644 index 000000000000..75bd92054fb4 --- /dev/null +++ b/baselines/fedht/fedht/aggregate.py @@ -0,0 +1,448 @@ +# Copyright 2020 Flower Labs GmbH. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""Aggregation functions for strategy implementations.""" +# mypy: disallow_untyped_calls=False + +from functools import reduce +from typing import Any, Callable, List, Tuple +import numpy as np +from flwr.common import FitRes, NDArray, NDArrays, parameters_to_ndarrays +from flwr.server.client_proxy import ClientProxy + +def aggregate(results: List[Tuple[NDArrays, int]]) -> NDArrays: + """Compute weighted average.""" + # Calculate the total number of examples used during training + num_examples_total = sum(num_examples for (_, num_examples) in results) + + # Create a list of weights, each multiplied by the related number of examples + weighted_weights = [ + [layer * num_examples for layer in weights] for weights, num_examples in results + ] + + # Compute average weights of each layer + weights_prime: NDArrays = [ + reduce(np.add, layer_updates) / num_examples_total + for layer_updates in zip(*weighted_weights) + ] + return weights_prime + + +def aggregate_inplace(results: List[Tuple[ClientProxy, FitRes]]) -> NDArrays: + """Compute in-place weighted average.""" + # Count total examples + num_examples_total = sum(fit_res.num_examples for (_, fit_res) in results) + + # Compute scaling factors for each result + scaling_factors = [ + fit_res.num_examples / num_examples_total for _, fit_res in results + ] + + # Let's do in-place aggregation + # Get first result, then add up each other + params = [ + scaling_factors[0] * x for x in parameters_to_ndarrays(results[0][1].parameters) + ] + for i, (_, fit_res) in enumerate(results[1:]): + res = ( + scaling_factors[i + 1] * x + for x in parameters_to_ndarrays(fit_res.parameters) + ) + params = [reduce(np.add, layer_updates) for layer_updates in zip(params, res)] + + return params + + +def aggregate_median(results: List[Tuple[NDArrays, int]]) -> NDArrays: + """Compute median.""" + # Create a list of weights and ignore the number of examples + weights = [weights for weights, _ in results] + + # Compute median weight of each layer + median_w: NDArrays = [ + np.median(np.asarray(layer), axis=0) for layer in zip(*weights) + ] + return median_w + + +def aggregate_krum( + results: List[Tuple[NDArrays, int]], num_malicious: int, to_keep: int +) -> NDArrays: + """Choose one parameter vector according to the Krum function. + + If to_keep is not None, then MultiKrum is applied. + """ + # Create a list of weights and ignore the number of examples + weights = [weights for weights, _ in results] + + # Compute distances between vectors + distance_matrix = _compute_distances(weights) + + # For each client, take the n-f-2 closest parameters vectors + num_closest = max(1, len(weights) - num_malicious - 2) + closest_indices = [] + for distance in distance_matrix: + closest_indices.append( + np.argsort(distance)[1 : num_closest + 1].tolist() # noqa: E203 + ) + + # Compute the score for each client, that is the sum of the distances + # of the n-f-2 closest parameters vectors + scores = [ + np.sum(distance_matrix[i, closest_indices[i]]) + for i in range(len(distance_matrix)) + ] + + if to_keep > 0: + # Choose to_keep clients and return their average (MultiKrum) + best_indices = np.argsort(scores)[::-1][len(scores) - to_keep :] # noqa: E203 + best_results = [results[i] for i in best_indices] + return aggregate(best_results) + + # Return the model parameters that minimize the score (Krum) + return weights[np.argmin(scores)] + + +# pylint: disable=too-many-locals +def aggregate_bulyan( + results: List[Tuple[NDArrays, int]], + num_malicious: int, + aggregation_rule: Callable, # type: ignore + **aggregation_rule_kwargs: Any, +) -> NDArrays: + """Perform Bulyan aggregation. + + Parameters + ---------- + results: List[Tuple[NDArrays, int]] + Weights and number of samples for each of the client. + num_malicious: int + The maximum number of malicious clients. + aggregation_rule: Callable + Byzantine resilient aggregation rule used as the first step of the Bulyan + aggregation_rule_kwargs: Any + The arguments to the aggregation rule. + + Returns + ------- + aggregated_parameters: NDArrays + Aggregated parameters according to the Bulyan strategy. + """ + byzantine_resilient_single_ret_model_aggregation = [aggregate_krum] + # also GeoMed (but not implemented yet) + byzantine_resilient_many_return_models_aggregation = [] # type: ignore + # Brute, Medoid (but not implemented yet) + + num_clients = len(results) + if num_clients < 4 * num_malicious + 3: + raise ValueError( + "The Bulyan aggregation requires then number of clients to be greater or " + "equal to the 4 * num_malicious + 3. This is the assumption of this method." + "It is needed to ensure that the method reduces the attacker's leeway to " + "the one proved in the paper." + ) + selected_models_set: List[Tuple[NDArrays, int]] = [] + + theta = len(results) - 2 * num_malicious + beta = theta - 2 * num_malicious + + for _ in range(theta): + best_model = aggregation_rule( + results=results, num_malicious=num_malicious, **aggregation_rule_kwargs + ) + list_of_weights = [weights for weights, num_samples in results] + # This group gives exact result + if aggregation_rule in byzantine_resilient_single_ret_model_aggregation: + best_idx = _find_reference_weights(best_model, list_of_weights) + # This group requires finding the closest model to the returned one + # (weights distance wise) + elif aggregation_rule in byzantine_resilient_many_return_models_aggregation: + # when different aggregation strategies available + # write a function to find the closest model + raise NotImplementedError( + "aggregate_bulyan currently does not support the aggregation rules that" + " return many models as results. " + "Such aggregation rules are currently not available in Flower." + ) + else: + raise ValueError( + "The given aggregation rule is not added as Byzantine resilient. " + "Please choose from Byzantine resilient rules." + ) + + selected_models_set.append(results[best_idx]) + + # remove idx from tracker and weights_results + results.pop(best_idx) + + # Compute median parameter vector across selected_models_set + median_vect = aggregate_median(selected_models_set) + + # Take the averaged beta parameters of the closest distance to the median + # (coordinate-wise) + parameters_aggregated = _aggregate_n_closest_weights( + median_vect, selected_models_set, beta_closest=beta + ) + return parameters_aggregated + + +def weighted_loss_avg(results: List[Tuple[int, float]]) -> float: + """Aggregate evaluation results obtained from multiple clients.""" + num_total_evaluation_examples = sum(num_examples for (num_examples, _) in results) + weighted_losses = [num_examples * loss for num_examples, loss in results] + return sum(weighted_losses) / num_total_evaluation_examples + + +def aggregate_qffl( + parameters: NDArrays, deltas: List[NDArrays], hs_fll: List[NDArrays] +) -> NDArrays: + """Compute weighted average based on Q-FFL paper.""" + demominator: float = np.sum(np.asarray(hs_fll)) + scaled_deltas = [] + for client_delta in deltas: + scaled_deltas.append([layer * 1.0 / demominator for layer in client_delta]) + updates = [] + for i in range(len(deltas[0])): + tmp = scaled_deltas[0][i] + for j in range(1, len(deltas)): + tmp += scaled_deltas[j][i] + updates.append(tmp) + new_parameters = [(u - v) * 1.0 for u, v in zip(parameters, updates)] + return new_parameters + + +def _compute_distances(weights: List[NDArrays]) -> NDArray: + """Compute distances between vectors. + + Input: weights - list of weights vectors + Output: distances - matrix distance_matrix of squared distances between the vectors + """ + flat_w = np.array([np.concatenate(p, axis=None).ravel() for p in weights]) + distance_matrix = np.zeros((len(weights), len(weights))) + for i, flat_w_i in enumerate(flat_w): + for j, flat_w_j in enumerate(flat_w): + delta = flat_w_i - flat_w_j + norm = np.linalg.norm(delta) + distance_matrix[i, j] = norm**2 + return distance_matrix + + +def _trim_mean(array: NDArray, proportiontocut: float) -> NDArray: + """Compute trimmed mean along axis=0. + + It is based on the scipy implementation. + + https://docs.scipy.org/doc/scipy/reference/generated/ + scipy.stats.trim_mean.html. + """ + axis = 0 + nobs = array.shape[axis] + lowercut = int(proportiontocut * nobs) + uppercut = nobs - lowercut + if lowercut > uppercut: + raise ValueError("Proportion too big.") + + atmp = np.partition(array, (lowercut, uppercut - 1), axis) + + slice_list = [slice(None)] * atmp.ndim + slice_list[axis] = slice(lowercut, uppercut) + result: NDArray = np.mean(atmp[tuple(slice_list)], axis=axis) + return result + + +def aggregate_trimmed_avg( + results: List[Tuple[NDArrays, int]], proportiontocut: float +) -> NDArrays: + """Compute trimmed average.""" + # Create a list of weights and ignore the number of examples + weights = [weights for weights, _ in results] + + trimmed_w: NDArrays = [ + _trim_mean(np.asarray(layer), proportiontocut=proportiontocut) + for layer in zip(*weights) + ] + + return trimmed_w + + +def _check_weights_equality(weights1: NDArrays, weights2: NDArrays) -> bool: + """Check if weights are the same.""" + if len(weights1) != len(weights2): + return False + return all( + np.array_equal(layer_weights1, layer_weights2) + for layer_weights1, layer_weights2 in zip(weights1, weights2) + ) + + +def _find_reference_weights( + reference_weights: NDArrays, list_of_weights: List[NDArrays] +) -> int: + """Find the reference weights by looping through the `list_of_weights`. + + Raise Error if the reference weights is not found. + + Parameters + ---------- + reference_weights: NDArrays + Weights that will be searched for. + list_of_weights: List[NDArrays] + List of weights that will be searched through. + + Returns + ------- + index: int + The index of `reference_weights` in the `list_of_weights`. + + Raises + ------ + ValueError + If `reference_weights` is not found in `list_of_weights`. + """ + for idx, weights in enumerate(list_of_weights): + if _check_weights_equality(reference_weights, weights): + return idx + raise ValueError("The reference weights not found in list_of_weights.") + + +def _aggregate_n_closest_weights( + reference_weights: NDArrays, results: List[Tuple[NDArrays, int]], beta_closest: int +) -> NDArrays: + """Calculate element-wise mean of the `N` closest values. + + Note, each i-th coordinate of the result weight is the average of the beta_closest + -ith coordinates to the reference weights + + + Parameters + ---------- + reference_weights: NDArrays + The weights from which the distances will be computed + results: List[Tuple[NDArrays, int]] + The weights from models + beta_closest: int + The number of the closest distance weights that will be averaged + + Returns + ------- + aggregated_weights: NDArrays + Averaged (element-wise) beta weights that have the closest distance to + reference weights + """ + list_of_weights = [weights for weights, num_examples in results] + aggregated_weights = [] + + for layer_id, layer_weights in enumerate(reference_weights): + other_weights_layer_list = [] + for other_w in list_of_weights: + other_weights_layer = other_w[layer_id] + other_weights_layer_list.append(other_weights_layer) + other_weights_layer_np = np.array(other_weights_layer_list) + diff_np = np.abs(layer_weights - other_weights_layer_np) + # Create indices of the smallest differences + # We do not need the exact order but just the beta closest weights + # therefore np.argpartition is used instead of np.argsort + indices = np.argpartition(diff_np, kth=beta_closest - 1, axis=0) + # Take the weights (coordinate-wise) corresponding to the beta of the + # closest distances + beta_closest_weights = np.take_along_axis( + other_weights_layer_np, indices=indices, axis=0 + )[:beta_closest] + aggregated_weights.append(np.mean(beta_closest_weights, axis=0)) + return aggregated_weights + +# calls hardthreshold function for each list element in weights_all +def hardthreshold_list(weights_all, num_keep: int) -> NDArrays: + + params = [hardthreshold(each, num_keep) for each in weights_all] + return params + +# hardthreshold function applied to array +def hardthreshold(weights_prime, num_keep: int) -> NDArrays: + + # check for len of array + val_len = weights_prime.size + + # intercepts not hardthresholded + if val_len > 1: + if num_keep > val_len: + params = weights_prime + print("The number of parameters kept is greater than the length of the vector. All parameters will be kept.") + else: + # Compute the magnitudes + magnitudes = np.abs(weights_prime) + + # Get the k-th largest value in the vector + threshold = np.partition(magnitudes, -num_keep)[-num_keep] + + # Create a new vector where values below the threshold are set to zero + params = np.where(magnitudes >= threshold, weights_prime, 0) + + else: + params = weights_prime + + return params + +def aggregate_hardthreshold( + results: List[Tuple[NDArrays, int]], num_keep: int, iterht: bool) -> NDArrays: + """ + Applies hard thresholding to keep only the k largest weights in a client-weight vector. Fed-HT (Fed-IterHT) can be + found at https://arxiv.org/abs/2101.00052 + """ + if num_keep <= 0: + raise ValueError("k must be a positive integer.") + + """Compute weighted average.""" + # Calculate the total number of examples used during training + num_examples_total = sum(num_examples for (_, num_examples) in results) + + green = '\033[92m' + reset = '\033[0m' + + # check for iterht=True; set in cfg + if iterht: + print(f"{green}INFO {reset}:\t\tUsing Fed-IterHT for model aggregation with threshold = ", num_keep) + + # apply across all models within each client + + # 'results' is a collection of tuples of the form (params, num_obs) + # we want to adjust the 'params' portion of that tuple + # i: iterates through clients + # ignoring second element in results[i] skips over number of observations + # j: iterates through all layers of a model + # k: iterates through the slices of each layer + for i in range(len(results)): + for j in range(len(results[i][0])): + for k in range(len(results[i][0][j])): + results[i][0][j][k] = hardthreshold(results[i][0][j][k], num_keep) + + weighted_weights1 = [ + [layer * num_examples for layer in weights] for weights, num_examples in results + ] + weighted_weights2 = weighted_weights1 + + else: + print(f"{green}INFO {reset}:\t\tUsing Fed-HT for model aggregation with threshold = ", num_keep) + + weighted_weights1 = [ + [layer * num_examples for layer in weights] for weights, num_examples in results + ] + weighted_weights2 = weighted_weights1 + + hold = [reduce(np.add, layer_updates) / num_examples_total for layer_updates in zip(*weighted_weights2)] + params = [hardthreshold_list(layer_updates, num_keep) for layer_updates in hold] + + result: NDArrays = params + + return result diff --git a/baselines/fedht/fedht/client.py b/baselines/fedht/fedht/client.py new file mode 100644 index 000000000000..bfab727815d2 --- /dev/null +++ b/baselines/fedht/fedht/client.py @@ -0,0 +1,158 @@ +from fedht.model import train, test +from flwr.common import Context +from torch.utils.data import DataLoader +from collections import OrderedDict +import torch +from flwr.client import Client, NumPyClient +from flwr.common import Context +from typing import cast +from fedht.utils import MyDataset +from omegaconf import DictConfig + +# SimII client +class SimIIClient(NumPyClient): + def __init__(self, + trainloader, + testloader, + model, + num_obs, + num_features, + num_classes, + cfg: DictConfig) -> None: + + self.trainloader = trainloader + self.testloader = testloader + self.model = model + self.num_obs = num_obs + self.num_features = num_features + self.num_classes = num_classes + self.cfg = cfg + + # get parameters from existing model + def get_parameters(self, config): + return [val.cpu().numpy() for _, val in self.model.state_dict().items()] + + def fit(self, parameters, config): + + # set model parameters + params_dict = zip(self.model.state_dict().keys(), parameters) + state_dict = OrderedDict({k: torch.Tensor(v) for k, v in params_dict}) + self.model.load_state_dict(state_dict, strict=True) + + # train model + self.model.train() + + #training for local epochs defined by config + train(self.model, self.trainloader, self.cfg) + + return self.get_parameters(self.model), self.num_obs, {} + + def evaluate(self, parameters, config): + + # set model parameters + params_dict = zip(self.model.state_dict().keys(), parameters) + state_dict = OrderedDict({k: torch.Tensor(v) for k, v in params_dict}) + self.model.load_state_dict(state_dict, strict=True) + + # need to change from log_loss to torch.loss and change other metrics + loss, accuracy = test(self.model, self.trainloader) + + return loss, self.num_obs, {"accuracy": accuracy} + +# client fn for input into simulation +def generate_client_fn_simII(dataset, + num_features, + num_classes, + model, + cfg: DictConfig): + + # def client_fn(cid: int): + def client_fn(context: Context) -> Client: + + # Get node_config value to fetch partition_id + partition_id = cast(int, context.node_config["partition-id"]) + + # Load the partition data + X_train, y_train = dataset + num_obs = X_train.shape[1] + test_dataset = train_dataset = MyDataset(X_train[int(partition_id),:,:], y_train[:,int(partition_id)]) + trainloader = DataLoader(train_dataset, batch_size=cfg.batch_size, shuffle=True) + testloader = DataLoader(test_dataset, batch_size=cfg.batch_size, shuffle=True) + + return SimIIClient(trainloader, testloader, model, num_obs, num_features, num_classes, cfg).to_client() + + return client_fn + +# MNIST client +class MnistClient(NumPyClient): + def __init__(self, + trainloader, + testloader, + model, + num_obs, + num_features, + num_classes, + cfg: DictConfig) -> None: + + self.trainloader = trainloader + self.testloader = testloader + self.model = model + self.num_obs = num_obs + self.num_features = num_features + self.num_classes = num_classes + self.cfg = cfg + + # get parameters from existing model + def get_parameters(self, config): + return [val.cpu().numpy() for _, val in self.model.state_dict().items()] + + def fit(self, parameters, config): + + # set model parameters + params_dict = zip(self.model.state_dict().keys(), parameters) + state_dict = OrderedDict({k: torch.Tensor(v) for k, v in params_dict}) + self.model.load_state_dict(state_dict, strict=True) + + # train model + self.model.train() + + #training for local epochs defined by config + train(self.model, self.trainloader, self.cfg) + + return self.get_parameters(self.model), self.num_obs, {} + + def evaluate(self, parameters, config): + + # set model parameters + params_dict = zip(self.model.state_dict().keys(), parameters) + state_dict = OrderedDict({k: torch.Tensor(v) for k, v in params_dict}) + self.model.load_state_dict(state_dict, strict=True) + + # need to change from log_loss to torch.loss and change other metrics + loss, accuracy = test(self.model, self.trainloader) + + return loss, self.num_obs, {"accuracy": accuracy} + +# client fn for input into simulation +def generate_client_fn_mnist(dataset, + num_features, + num_classes, + model, + cfg: DictConfig): + + # def client_fn(cid: int): + def client_fn(context: Context) -> Client: + + # Get node_config value to fetch partition_id + partition_id = cast(int, context.node_config["partition-id"]) + + # Load the partition data + train_dataset = dataset.load_partition(int(partition_id), "train").with_format("numpy") + num_obs = train_dataset.num_rows + test_dataset = dataset.load_partition(int(partition_id), "train").with_format("numpy") + trainloader = DataLoader(train_dataset, batch_size=cfg.batch_size, shuffle=True) + testloader = DataLoader(test_dataset, batch_size=cfg.batch_size, shuffle=True) + + return MnistClient(trainloader, testloader, model, num_obs, num_features, num_classes, cfg).to_client() + + return client_fn \ No newline at end of file diff --git a/baselines/fedht/fedht/conf/base_mnist.yaml b/baselines/fedht/fedht/conf/base_mnist.yaml new file mode 100644 index 000000000000..fc81461dc9ce --- /dev/null +++ b/baselines/fedht/fedht/conf/base_mnist.yaml @@ -0,0 +1,43 @@ +--- +num_clients: 10 # total number of clients +num_local_epochs: 10 # number of local epochs +batch_size: 50 +num_rounds: 100 +learning_rate: 0.003 +weight_decay: .000 +momentum: 0.5 +num_keep: 500 +agg: fedht +iterht: False +data: mnist + +client_resources: + num_cpus: 7 + +use_cuda: false +specified_device: null # the ID of cuda device, if null, then use defaults torch.device("cuda") + +dataset: + name: mnist + split: sample + num_classes: 10 + seed: 42 + num_clients: ${num_clients} + fraction: 0.8 + +fit_config: + drop_client: false + epochs: ${num_local_epochs} + batch_size: ${batch_size} + learning_rate: ${learning_rate} + weight_decay: ${weight_decay} + +strategy: + fraction_fit: 1.0 + fraction_evaluate: 1.0 + min_fit_clients: 2 + min_evaluate_clients: 2 + min_available_clients: 2 + evaluate_fn: null + on_fit_config_fn: null + on_evaluate_config_fn: null diff --git a/baselines/fedht/fedht/conf/base_simII.yaml b/baselines/fedht/fedht/conf/base_simII.yaml new file mode 100644 index 000000000000..6530c58e30db --- /dev/null +++ b/baselines/fedht/fedht/conf/base_simII.yaml @@ -0,0 +1,43 @@ +--- +num_clients: 25 # total number of clients +num_local_epochs: 10 # number of local epochs +batch_size: 50 +num_rounds: 100 +learning_rate: 0.0001 +weight_decay: .0000 +momentum: 0.5 +num_keep: 200 +agg: fedht +iterht: False +data: simII + +client_resources: + num_cpus: 4 + +use_cuda: false +specified_device: null # the ID of cuda device, if null, then use defaults torch.device("cuda") + +dataset: + name: mnist + split: sample + num_classes: 10 + seed: 42 + num_clients: ${num_clients} + fraction: 0.8 + +fit_config: + drop_client: false + epochs: ${num_local_epochs} + batch_size: ${batch_size} + learning_rate: ${learning_rate} + weight_decay: ${weight_decay} + +strategy: + fraction_fit: 1.0 + fraction_evaluate: 1.0 + min_fit_clients: 2 + min_evaluate_clients: 2 + min_available_clients: 2 + evaluate_fn: null + on_fit_config_fn: null + on_evaluate_config_fn: null diff --git a/baselines/fedht/fedht/fedht.py b/baselines/fedht/fedht/fedht.py new file mode 100644 index 000000000000..c75be5275e9f --- /dev/null +++ b/baselines/fedht/fedht/fedht.py @@ -0,0 +1,289 @@ +# Copyright 2020 Flower Labs GmbH. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""Federated Hardthresholding (FedHT) +""" + + +from logging import WARNING +from typing import Callable, Dict, List, Optional, Tuple, Union +import numpy as np + +from flwr.common import ( + EvaluateIns, + EvaluateRes, + FitIns, + FitRes, + MetricsAggregationFn, + NDArrays, + Parameters, + Scalar, + ndarrays_to_parameters, + parameters_to_ndarrays, +) +from flwr.common.logger import log +from flwr.server.client_manager import ClientManager +from flwr.server.client_proxy import ClientProxy + +# from flwr.server.strategy.aggregate import aggregate, aggregate_inplace, weighted_loss_avg +#from flwr.server.strategy.aggregate import aggregate_inplace, weighted_loss_avg, aggregate_hardthreshold +from fedht.aggregate import aggregate_inplace, weighted_loss_avg, aggregate_hardthreshold +from flwr.server.strategy.strategy import Strategy + +WARNING_MIN_AVAILABLE_CLIENTS_TOO_LOW = """ +Setting `min_available_clients` lower than `min_fit_clients` or +`min_evaluate_clients` can cause the server to fail when there are too few clients +connected to the server. `min_available_clients` must be set to a value larger +than or equal to the values of `min_fit_clients` and `min_evaluate_clients`. +""" + + +# pylint: disable=line-too-long +class FedHT(Strategy): + """Federated Hardthreshold strategy. + + Implementation based on https://arxiv.org/abs/1602.05629 + + Parameters + ---------- + fraction_fit : float, optional + Fraction of clients used during training. In case `min_fit_clients` + is larger than `fraction_fit * available_clients`, `min_fit_clients` + will still be sampled. Defaults to 1.0. + fraction_evaluate : float, optional + Fraction of clients used during validation. In case `min_evaluate_clients` + is larger than `fraction_evaluate * available_clients`, + `min_evaluate_clients` will still be sampled. Defaults to 1.0. + num_keep : int, number of parameters to keep different from 0. Defaults to 5. + iterht : boolean, if true, utilizes the Fed-IterHT strategy. Defaults to False. + min_fit_clients : int, optional + Minimum number of clients used during training. Defaults to 2. + min_evaluate_clients : int, optional + Minimum number of clients used during validation. Defaults to 2. + min_available_clients : int, optional + Minimum number of total clients in the system. Defaults to 2. + evaluate_fn : Optional[Callable[[int, NDArrays, Dict[str, Scalar]],Optional[Tuple[float, Dict[str, Scalar]]]]] + Optional function used for validation. Defaults to None. + on_fit_config_fn : Callable[[int], Dict[str, Scalar]], optional + Function used to configure training. Defaults to None. + on_evaluate_config_fn : Callable[[int], Dict[str, Scalar]], optional + Function used to configure validation. Defaults to None. + accept_failures : bool, optional + Whether or not accept rounds containing failures. Defaults to True. + initial_parameters : Parameters, optional + Initial global model parameters. + fit_metrics_aggregation_fn : Optional[MetricsAggregationFn] + Metrics aggregation function, optional. + evaluate_metrics_aggregation_fn : Optional[MetricsAggregationFn] + Metrics aggregation function, optional. + inplace : bool (default: True) + Enable (True) or disable (False) in-place aggregation of model updates. + """ + + # pylint: disable=too-many-arguments,too-many-instance-attributes, line-too-long + def __init__( + self, + *, + fraction_fit: float = 1.0, + fraction_evaluate: float = 1.0, + num_keep: int = 5, + iterht: bool = False, + min_fit_clients: int = 2, + min_evaluate_clients: int = 2, + min_available_clients: int = 2, + evaluate_fn: Optional[ + Callable[ + [int, NDArrays, Dict[str, Scalar]], + Optional[Tuple[float, Dict[str, Scalar]]], + ] + ] = None, + on_fit_config_fn: Optional[Callable[[int], Dict[str, Scalar]]] = None, + on_evaluate_config_fn: Optional[Callable[[int], Dict[str, Scalar]]] = None, + accept_failures: bool = True, + initial_parameters: Optional[Parameters] = None, + fit_metrics_aggregation_fn: Optional[MetricsAggregationFn] = None, + evaluate_metrics_aggregation_fn: Optional[MetricsAggregationFn] = None, + inplace: bool = True, + ) -> None: + super().__init__() + + if ( + min_fit_clients > min_available_clients + or min_evaluate_clients > min_available_clients + ): + log(WARNING, WARNING_MIN_AVAILABLE_CLIENTS_TOO_LOW) + + self.fraction_fit = fraction_fit + self.fraction_evaluate = fraction_evaluate + self.num_keep = num_keep + self.iterht = iterht + self.min_fit_clients = min_fit_clients + self.min_evaluate_clients = min_evaluate_clients + self.min_available_clients = min_available_clients + self.evaluate_fn = evaluate_fn + self.on_fit_config_fn = on_fit_config_fn + self.on_evaluate_config_fn = on_evaluate_config_fn + self.accept_failures = accept_failures + self.initial_parameters = initial_parameters + self.fit_metrics_aggregation_fn = fit_metrics_aggregation_fn + self.evaluate_metrics_aggregation_fn = evaluate_metrics_aggregation_fn + self.inplace = inplace + + def __repr__(self) -> str: + """Compute a string representation of the strategy.""" + rep = f"FedAvg(accept_failures={self.accept_failures})" + return rep + + def num_fit_clients(self, num_available_clients: int) -> Tuple[int, int]: + """Return the sample size and the required number of available clients.""" + num_clients = int(num_available_clients * self.fraction_fit) + return max(num_clients, self.min_fit_clients), self.min_available_clients + + def num_evaluation_clients(self, num_available_clients: int) -> Tuple[int, int]: + """Use a fraction of available clients for evaluation.""" + num_clients = int(num_available_clients * self.fraction_evaluate) + return max(num_clients, self.min_evaluate_clients), self.min_available_clients + + def initialize_parameters( + self, client_manager: ClientManager + ) -> Optional[Parameters]: + """Initialize global model parameters.""" + initial_parameters = self.initial_parameters + self.initial_parameters = None # Don't keep initial parameters in memory + return initial_parameters + + def evaluate( + self, server_round: int, parameters: Parameters + ) -> Optional[Tuple[float, Dict[str, Scalar]]]: + """Evaluate model parameters using an evaluation function.""" + if self.evaluate_fn is None: + # No evaluation function provided + return None + parameters_ndarrays = parameters_to_ndarrays(parameters) + eval_res = self.evaluate_fn(server_round, parameters_ndarrays, {}) + if eval_res is None: + return None + loss, metrics = eval_res + return loss, metrics + + def configure_fit( + self, server_round: int, parameters: Parameters, client_manager: ClientManager + ) -> List[Tuple[ClientProxy, FitIns]]: + """Configure the next round of training.""" + config = {} + if self.on_fit_config_fn is not None: + # Custom fit config function provided + config = self.on_fit_config_fn(server_round) + fit_ins = FitIns(parameters, config) + + # Sample clients + sample_size, min_num_clients = self.num_fit_clients( + client_manager.num_available() + ) + clients = client_manager.sample( + num_clients=sample_size, min_num_clients=min_num_clients + ) + + # Return client/config pairs + return [(client, fit_ins) for client in clients] + + def configure_evaluate( + self, server_round: int, parameters: Parameters, client_manager: ClientManager + ) -> List[Tuple[ClientProxy, EvaluateIns]]: + """Configure the next round of evaluation.""" + # Do not configure federated evaluation if fraction eval is 0. + if self.fraction_evaluate == 0.0: + return [] + + # Parameters and config + config = {} + if self.on_evaluate_config_fn is not None: + # Custom evaluation config function provided + config = self.on_evaluate_config_fn(server_round) + evaluate_ins = EvaluateIns(parameters, config) + + # Sample clients + sample_size, min_num_clients = self.num_evaluation_clients( + client_manager.num_available() + ) + clients = client_manager.sample( + num_clients=sample_size, min_num_clients=min_num_clients + ) + + # Return client/config pairs + return [(client, evaluate_ins) for client in clients] + + def aggregate_fit( + self, + server_round: int, + results: List[Tuple[ClientProxy, FitRes]], + failures: List[Union[Tuple[ClientProxy, FitRes], BaseException]], + ) -> Tuple[Optional[Parameters], Dict[str, Scalar]]: + """Aggregate fit results using weighted average.""" + if not results: + return None, {} + # Do not aggregate if there are failures and failures are not accepted + if not self.accept_failures and failures: + return None, {} + + # no in-place aggregation for FedHT + weights_results = [ + (parameters_to_ndarrays(fit_res.parameters), fit_res.num_examples) + for _, fit_res in results + ] + + # use hardthresholding + aggregated_ndarrays = aggregate_hardthreshold(weights_results, self.num_keep, self.iterht) + parameters_aggregated = ndarrays_to_parameters(aggregated_ndarrays) + + # Aggregate custom metrics if aggregation fn was provided + metrics_aggregated = {} + if self.fit_metrics_aggregation_fn: + fit_metrics = [(res.num_examples, res.metrics) for _, res in results] + metrics_aggregated = self.fit_metrics_aggregation_fn(fit_metrics) + elif server_round == 1: # Only log this warning once + log(WARNING, "No fit_metrics_aggregation_fn provided") + + return parameters_aggregated, metrics_aggregated + + def aggregate_evaluate( + self, + server_round: int, + results: List[Tuple[ClientProxy, EvaluateRes]], + failures: List[Union[Tuple[ClientProxy, EvaluateRes], BaseException]], + ) -> Tuple[Optional[float], Dict[str, Scalar]]: + """Aggregate evaluation losses using weighted average.""" + if not results: + return None, {} + # Do not aggregate if there are failures and failures are not accepted + if not self.accept_failures and failures: + return None, {} + + # Aggregate loss + loss_aggregated = weighted_loss_avg( + [ + (evaluate_res.num_examples, evaluate_res.loss) + for _, evaluate_res in results + ] + ) + + # Aggregate custom metrics if aggregation fn was provided + metrics_aggregated = {} + if self.evaluate_metrics_aggregation_fn: + eval_metrics = [(res.num_examples, res.metrics) for _, res in results] + metrics_aggregated = self.evaluate_metrics_aggregation_fn(eval_metrics) + elif server_round == 1: # Only log this warning once + log(WARNING, "No evaluate_metrics_aggregation_fn provided") + + return loss_aggregated, metrics_aggregated diff --git a/baselines/fedht/fedht/gen_sim_data.py b/baselines/fedht/fedht/gen_sim_data.py new file mode 100644 index 000000000000..e07954568f21 --- /dev/null +++ b/baselines/fedht/fedht/gen_sim_data.py @@ -0,0 +1,35 @@ +import numpy as np + +def sim_data(ni: int, num_clients: int, num_features: int, alpha=1, beta=1): + + #generate client-based model coefs + u = np.random.normal(0, alpha, num_clients) + x = np.zeros((num_features,num_clients)) + x[0:99,:] = np.random.multivariate_normal(u, np.diag(np.ones(num_clients)), 99) + + #generate observations + ivec = np.arange(1,num_features+1) + vari = np.diag(1/(ivec**1.2)) + + B = np.random.normal(0, beta, num_features) + v = np.random.multivariate_normal(B, np.diag(np.ones(num_features)), num_clients) + + error = np.random.multivariate_normal(u,np.diag(np.ones(num_clients)), ni) + z = np.zeros((num_clients, ni, num_features)) + y = np.zeros((ni, num_clients)) + + # (num_clients, ni, num_feaures) + for i in range(z.shape[0]): + z[i,:,:] = np.random.multivariate_normal(v[i], vari, ni) + hold = np.matmul(z[i,:,:],x[:,i]) + error[:,i] + y[:,i] = np.exp(hold) / (1+np.exp(hold)) + + for j in range(num_clients): + top_indices = np.argpartition(y[:,j], -100)[-100:] + mask = np.zeros(y[:,j].shape, dtype=bool) + mask[top_indices] = True + y[mask,j] = 1 + y[~mask,j] = 0 + + #might need to adjust; vague data generating process + return z, y diff --git a/baselines/fedht/fedht/loss_results_mnist.png b/baselines/fedht/fedht/loss_results_mnist.png new file mode 100644 index 0000000000000000000000000000000000000000..8533d713fcd030390d4efc76d7d656c29941192d GIT binary patch literal 39211 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%0AT%Qhr1)2QVr=+f%BWsZK^b#n z^GWL1*C=$U(qr%KY_4n)xG`cg2=_X#4dcV`0EfB8>-b}hr2OapZ}K-GgPAJe@Di$p z;uDxUDvdtgP_hv5N1Rq*VtKHZ#h|jlwCV>W^5yK{DJvV>yePx}8r?Visu`G7|1~~k zfR0g{`s2>tZ&hV60Sj~DSNSaZG%WXp9Rq)C+4Bt^;)ma;nr*yb6?A0}55@8Rs4uDW zI)05c_R)VGfk_!gtZI;Z#{fMe`X^w%cvkKH!ll7vbc zgox_}JsC;CK=8!z?61q;y%k9qx$v064--Kh|2iQEN>7P^fI$7pm9RD-1jJPtZ}UhW zxp%bKTyam{wZEe~5RMs@tcl!mqjub?*?O0hvCP|oj{V{y3C*5w=WEC)nHT29) z{eZ&>$bkY~vjK=NkRb9l4CuYJk!UCr%BoXva44$kE190r`?Y6{thFbwQG%Y?+}|Gy z?Pqtk8N=uNcnOoF2^Ri;8|~MJD@X9ze?H`bc_2VMBHoZ>Wn}}gDGib<9m9#>Fe*0{WPB+vj=iJKvRz>&bb40Op3u$2aYvo!_aBw7|9$u=A1E_&G$8E#`}cit zU}L(!{U#BhvyQhMyzO8QGx{oJ>qhDc20a8`eYa~~$&&wM3Hh-G&`%SVFe z0K}}P>&G}b7a&ierS)~84yYtR(;zeqY!%RKL_|ao{Q^Q6B{h02!V{xDx0Ca;JcleE zm9DJRm2>zT`s><9Hl32RyQ7D%cEhaz_hhoSct}uVhM?5-0 z3b(no2{#@nEBWt-+zTn1b|3ZZew_5Qod4bVj$@WmK;Zs#{c8CjIx>4!BIx~AQbXb{ z33C6wDIYPXHi*HbMZ}ALu;IB@{*j>!xfC%l`g$Os(>mVNMne-&>oP#=8|GdNudbBPSa<)KB;IhkVVU$@<;Z+(tVCBdF_)Cv#fK4Py zeDrsBO~m!%%R@bU?6In~sJ81Y`I{MHZ(^E|pYpf`KQ(LmN7}y&3ng&A<(8e|Pfcwk zjjq7+@3MSgE%crGJ+w4wpz& zu7T;`0+f7^#I6V{{y(19Sqvs;08>g1Yl4shO@rIlfTl}fFsBQ#RZ2sCs*qsbg&V|) ztKe>vgASOo?5*(Tu1Sk5H-QYLjJz>R^H`gi;NMv$wzc6PT3f^$v^=I=$oV5AI)@;3h-rc_W%6pPzeJ1WCG3Ww zcdKDi_b`AnMN;I&ia&Z(Wi!B_`VeTT1db|~0=@y9l*5ZgTph%RgA5k_1hluHw{J;d zsv_XqG?ddD$m!`UnTJyJ!>QUbvuJq~WZ$-`iSMXoFD+f_>ESfOUg*3u^uBetgoqqh z!_=J-7cTjhV3!!q0szMa5ZZbUz+Q5A9Gp&}-fn%TyyD~So2-6oy`^n5IN2kNZn^>3 z?;;FJbl#CydwUk@l5?AB$o=ZeVctVWuG8?E=_6Fcb(Zf)&Y6&q-}(w6A(Dc)7D3~G zGqPp8Kl#bnP-e1fwDN;IdCM9?eSHC6vF3S-duny-(5%c;Qo$U}g*4|P0ml?`N(C#5kv2_@a&<`c zaBYOKrkGGn<&g0a1eN{+SV+msUVnB-O-;ShmlIOc`h%$BcVrRTATR#2_Zq<0%dFmZ z%s~R!S$nKPvc~Z)+T_QF`2ol_>%Uwvf_DdXXrK1_O!#gjU3O0t!3P3v zJP0GSvtN0g_J;af#k;UG<>S;RloM5|hYAVwHTBD5p4kQeu0m?n>;MySD9qsN@hI=% zy&qwSdk+>000|x=;}uP3p0X@jNN*V!W(~X5x%0@Gv^#SmEnpm;#1;gmv;h{ z6OympM34V!-V3Rn4yI_L63kSl-gg`ONdM-}Kr?hbB8%uTk|tyy0W1-ifK_JQMJ3Co zQrriPO|O=>{kNR>zaDMFb;2|YCsz7lvVhOp#~TGa@goo>h^I7xd|1;MtHJXB#95u z1c7ouTic=dj^TH^sr70Xtk)FjK1(!NbvJ$Zz@_dkSBw>zPJli5?|#Aef(?U?fGeP! zAah;u^75+oy$}Pvm&a9WMQ1PYX8}!G_}J*Dl3M-RYrJf7zvcd;86ppf3M^q^eKyI( z+1dL*p;5@$2Ef>hcx=xi^oTced#%n;DSWC{7&Bk+LkJm*>KT*<&l1?U|1p`E@gz|+ zBot>cSf}i>Oh?0&!Ud*>bi9p5^yPsBnN7bPO)_z zMmwZQ$Uw(k^98}WFe7Y?kTm{c-R5IXt}M<={k4(b(!`@5gWVm<+HYEvx6;(le zR+&y_Rm%3acr9}+(K+-2pMF*(LdFSQWXn2k=E1nRDu|c&Y$s)*)-A41kt!uBI`h~U z<7T0)4h8t0qp`3Yo!Bi8Fh0(C$zvA0Iy7WdgoT{`e!@|~=;wQW%n1HLa0VRMINJC; zlgML6kl}rhM1EW0Vy*-&63!r=?!VQ!?RH=diCd{V{{I-5M6M4sFJYI}z>*T1)D2j` zUvQX-S5k^x#u;X03J>e1$lcWAf<>OSBi72{62nd1s@+^U@Nu7ZMk=FRH_){^ zw2`Tw*7fn)q+R0~9H|wB;P*V}z6$o@Y07P+`d%?FV@z1nd(w@|ds`}Ao27btB|P={ zO!mo%jQ`XY^_oXjIoWmNG9hhC=+!cNy1k8)@7xijAtLUxJ=CK$s_OIzUbyALrGPE~ zdJ>#*jD}+Yag2|Y?ZxF#N(u!7kJ-%xKTS;rAI?mRTye+tdlPg$=C7>uI>m#ETis>L zqmS=^)7cV1`kHxwvO}%PkH@rd9lS;Za-HUdVq=?7Y%3fzBX7~L@?TTQDQRu0sr8@{z5EO z)T(6`C?ie`tEmM#jexyD_TkyZow-1VHywK66%F&I7W(SP;YBj}j$?=13O#IDM%~Gl zK26>y(>nPL4ast*XC8bM)y@{GQtZe)Uf@Kwq#GZ^G2BJdZKU~f!v0VK!LCxblJ1~QR$09jRAz}S)X(!# z6cP!e^x@YcbBOHaF~b%Y=OfE?{LN3Zwo$T4NE;0BBVBYzst^8ZAgA~W1{3ju`TUQc z6q0C1h8F_a?=)2((PxtjxZ3iONevh*tMCOcyf2+Suo?JTr zlr8DE7P~E-?bWVD(y$G*BE5Xk80IiY5sK*|NkAzLjf`5MlLD>g2B(q%w9-X_N2sZe z7e*^G3ho-b{2V1sJr`KCQOwOBo`$V;77aYek+zg73kcA;v#=mXa)}MOO-yKW^^n_D z(qp@6Wn?62Z_f##2tfXv|5OOZ`%&M|D;EkVt z{k>;1@wwb$e`Tjib?*E_8i6&?wTso?-vA1VF$>O97O0Ad>;cSeUk2kFP6?@BIhyv&$Jazw+##t7o0FS6 z7LJ|)OD%d?US@!JlrXwa;edc~LQ;TghGJ5twKj_4Sb&J=MpIY_W31ZOwg0#qS$9(< z6u73#L+{^aVXNd`<+DU>ZC9+{?@#d;KP4(IRwLOOdM_QVjC_@y;enVRm>zSGXImtY zr2qEb$M3pb;aBgI?hQF15hv5ar<$S(I{$|C@1zDo129d<1nh4@f<6c4JEUa4dwc<1 z)HGa?epwwqbD~?Ox*;&&kYMN5DOQp-_nq3%Xt9?iPu0-{oC}N4uOkKJ1=H2%?@9p* zIUpKrBa}w)?+q4G4<9D6@W#Bh9?R6?s&)>$7d@4=g*ybs;uFqnjK z@Nbj*Zt`6%xO$0G@e1%mAw1c$0r|XO&lq_=OU(Ndmu!)ntuP4yKf(xk{{#$%1VAKU ztjzxU1?b)V`4rGgBz0HQN`iYli-GFM*vDd9!FX_A8UuNxi_Z`K%|pn~i1ZwQXA21* zqu@}f?7??fFJ1_UxGQfL*l)HAdg&d}7lkIU4V`64+OH?#K1YU-R6X3G^x^)AFL0Fz zo|{s+w+;@r&QDOz-g9V8uTq3#)xZZzCdO8a*eOz#jzZWZ5iaM%&DX#P&@7u|0Fi;- zz$Zcq!FS{|qDDz_O$$j_U*T2jF^P(lVXtSzpJv9DPo8YVseRS=EqYEs4KJNATvBFw zHZHY|3vY&rz>b%nFd06N= zle~ZLux4ld^24bJh9WXEvp1Oc%u#L8@9FKEKu1TVDNkL#p{CkKPxtxX%Aj|N5zZDq z&gr6lDa)RwJcNF}eJRb`u}thj^wZ2_`T{0V(~!Cn-m}v5)hkC+t`3JQVHLNpa* z{uNphLR6qTIzx~d&NYG78k^u>n%jb`Eq`k7RX5l5roeG8iT=^hj~Bd@CWq?_yn97 zUQ;{tgPk3T0p6T=J~&mnv)CO0gB`@7MaWd)Zq~s81ZSaV#VKiNX^-cA*y}}MpD(0$ zd|%8+e$%Fl2C*k&*97wI)YS1~Pqq5WzN7PT~%NV=Ks68`ryL|3_W-Wg7&- zc9;8Zw<;y1E97gQp`552q}c0;6n+i9a{r&Mi+u$@! z#yKgysMV>;G?v35eCra32*(SYp0rKOs`>R!3(MRx4LO@Ev07OH$>!vd!&z0nle5PIk!{^{m$`{wD`YC@&TsO z`e4z3@Y`SiUYVknN>LAojhtZF+@g`x1jMHbpxc2Xj~q@=0mlN~rr=jlU{>D-$y7+* zm>wZ7GV%)z#kmzJ!Y}9S{+)e9%{hwFsLGl=iL@khEHSy*vo>0=ZE5T5WF}?7i`ExU zGFYp^jBv90>VwV6b{58xXG@jx|0NC=;Hm1F9Z%)G#3Y&pZ6C=$gY_5y0pVC+W3c#A zz~+=RZfjOqs=w1Hu{^3l9A6fVNa!28t%TxVJ-dL5Lr%mwbptesZ{P^hJJ?Qq>Zboq zk;1OZunklVS*Qu=);WN;~t;@&<*l*7VuKFQoS@JoernMz+(LlaazQf0t3m*iX4&)S~@yA z@OZQ)XvKXLA>U4X9Wm3u_Wu2MKaeEse=iV|xTJp#4-GYd`3%uO6mr%m!RD@zf`WP9 z%R|@nJTG#7JD!hjX}^1$O>e1E_W!n?OUpOvBfHjd zXW`WuWWq04fl_~6sgj#o9WLpF1Ea+1!Ek^SY&SEAIlxhLbQHwZL5f9m#Ow|*X68UD zar0zME&g9$`_kuqL^t?8#I}uKk-9@ZNvJo;*Ho-TzLL9rWxUe={Onf?7WG?pZPj;b zJDu{XBZBzI%XR+8Td=N;X#3a|8C=_fm#>(E^Pjo_|7jf=;qUwB#{56Nk_GIhV8COJ zpgAe0i^xGL8aX1K(HZ)3VPIu;ME^lS&R#l;TFoWO4>p;bEwx=jViAnZxR2W7sFHXj z2l7|9^?k{!%#p_U47%C!>S_m2@i}X8m(^0ZjGv23gI5Lt-FqYe-!~XA{1-yd7 znvBd5uz=RmHk>nqx01ll!?UxqL&v~C!rVYzeGf_B#Jpk0FOA*2bIH*UyUzAc_F?e! z!YLo)KIzcXN4rxDr`Q~CPnSkx+~s{~77b`DvLpYA5WL_ZD(LWV@E;sODgk>x7(mIr zVkfL}CV-8iVVuJoZ@z*ajOpp8;&MHW?+2}f zK*+yziiz~>3X`lo%6S)*EM z{j$aPlotFi?E;wCJ zqbgRz)6!tj&;DtQdXkIu`AujsmF4AzP_TbU)1cj+^+O6Oi+d4*I6ThOz=`FP3O=@cDGg5vWmXa)8O13?d)|x{xz0 zaMmwLm}>h274@&rGlTQMMx2&V=FjT+P%P{pILC`DhXW^T=s1ZW*Zkifumw1Et{}rF zyuuq!he4CbWv-9|a9y(j3qn*c~n zxvp+kJM@zxu*r=CS`VaGX+XJ*uJDF>;bzqL=xY!LoYvg@^F3)v_@}QDX3g6nZ8h7c z!F|fR$4j#-MfrhA!Xy9rm|u;(eMt%N3Se4u&|wElo|5{(=`+N(KR<4BQwnT}WMwtz zq2iOn=zTPsb>Md&9M^OX`X`}@yML?7- zAf=Sj-K|m^5TqNFl$34|X^?KDl}@Emy1TpHxzGE2c*gk0mvhc=9B}VntTor1_kG>p zN}hV^xqmjrsMoJG(w7hXGT(tNDEQbcS|RYiUxEu?4-Eu{Gcd6M!onPgjKB}$1oO(b z8>R3GQJpyA)W-pnbxobOWm6XK=P9bMKH^=l2{7v`Am1Q<_KeeMF;pm5)fnDv;a|}Y z2^)VckCifm)bg3b-C3P?9oG-m(5EZjQy%P&piid+wpy%i4PxyscrnXOA58^omZ2V6 z_Vl%aNysC)R=w3KpHX?4K5uBcNMN6H0}3c1T|Ch*~~+q@H4Zea);{ zCQm+ovgi7pq)pMdBd*Q3>hiosL2jTpP4hkvy>QPfw>9{ZWT-2}C4nZ^*_lXA@$5#U z-Gpkcqw2Nk#tC$B0eHwBX=5&Qc@=VSnPo-?pvJr244%zY<7Em*tg7-uB8;AXTeQzk zO{u8#H#FPhh}g9~D=g?9b}ncA*LbBu{pPc65rp>9A4UrA`5V*iUiX=Jqx^22AJLAE zHMItBZ&Whz2BW@YCVa`qP(+|)k$gA?IPC2GRh(!lc#}7$VGwY`FTtoc`ZkHO&Ev+x zhUO-(|M@8+I6Kw?=m;nn+~=AlH7oxa{2l zS76+*P$$je9cLrg1)AQ-|6K|EgSb|5P`^UC2i_hSo#!a$5Tc=|aVWAAzvGvNtIGsv z6vT4#7(ADio!-E0Y+E|;Tp?<#v1^*p3BEm9J&>M72v=4S0S_Cr3Sd{eUIcCslpz-l z1syIbrlEAK(Z6p!wg`F(w6>R9?r+|j^f>#lH9a6Vbg?>wPipi> z322GABm}8U?RT&^9T=Lm;g?=Q(-tNCfsyk7(49byfZGT!m9Ys0=}KhU{rwg6#_k@~ zQNM(G@+xuM_9*P@*Y&$dBa$WID+PDOq+04h(XhL8%f-b`FF$_&b8j~MQjlFy>gnl$ zzc>!iKL#$)sEb9IMU&qrdN6yo)zpO@YCIG4@9pW0{$_e38svWv!$^P3DrXYidg&7z zy?PvkS#uCMoHBAx0+Wfa_D$8xSQg$AE<%$X|HS|oSOA)!bObZr^~Tm#fW_T<`ie;! z3a(f|YR{K`(<=l6p}n9)(fvXCN#^MRupYhk4t!GFKOJtQ+L|6HsHI~n=icZ`&d{cE z*vi!u($iy6!voxafXnoy+wqBUdEe2LR7`A8d*{DvP>%TB5j^$;TLK{v;6Q(EWo?aG z&jIZwjlA~s6G#D($zS}SL5$C)duqxZgdyZY&o8oB^-C_uw~*DmT|Q^VD# z@^a0nY&qJ;!~0q8?l4TsEu5)$L)KsPB&1r+c!w5O_|m$|hk9J{8lExVMs@gr=qZvZ z9rxx~BWnOPq5u)H&rx0!y=bdEuNF7umCrdHep`7kQ*Jw8GKE5ru{Kv zpWfS_Fmab zdeVjCgCSUj1wBFNUU14hoR7WGmA5!!)T4j&er`xPU=SAAufVf|Q@#OQJ8+VF!uezu zOcK_sgAd?8jW)MMZ(En!WMsQ2$S!;m@Ca2Bv9tUqFYy&CciMW%m!m;>4d3G|pK)fU zIHkd!ZBb-rL8{z#XRP!yt4j~Xf!qt|;f2}y;Q{5fI_-7*hdJdbJ`Rq%ob(^sY**6> z%O7b>|H1;L^^P8wh={?me72?vE>A}?^``{YdBQ<-!sVedgRQoNm@|k%|1*6{Up!uv?~3t*0i2ow2rWb%@hu_2Q6HB3^U#;%H`+ zQ$_3OxOnxXrTS+83nI>fL! zo^kqMd@P4Yt5Q{|Gm$^8b`M~GPfKBkGJz_I`}`b~&n7Uv(FAE)`QJ**1kRGm|Haw^ z`I#9?8cs?>^M1cXsT2nPX0D;p9!U2Mu~BT9y}f}lV9eoi_54;C0LJd>N%PO2<$`Xq z0OTtuh+kaGtKiVdl{GcX1Ey5YV-TPI-CZ0iJ3E?-CLzfHUS7`O=lZcxlkuJeaa>-R z&TQb7Mh_PPbuNB!8h`I7f+Hm}?rf?tmh&MgE2|78r|^KV$lHRwu9%te%Bj|0XFF~2 znIp_w{4Za|59dZbZ2O})P+=MB(GX0j^60B6@YKfR5gqQP61(%CGlQveOy9p>{ijii z9FJnn6v%oo)t7mGm82S5hk@$2`;OO(_~D@}+Dx*E{y%>30h{EpjV*nKo93=kZDL&& z*auSid_U#oQM3+!{is=yCP9P<{X{U7=iqlVIk0`ChOoc91a!e_wZTti+Pi9Mh|kET zPq}e`5xmwi`k*X^&MDB8ImFCMTT9a;?+py6UBcBm)s`bN8 zYREq>kICw%Z>-ZU{rw@6G3$ltWd-VM*Pd}%AWiRbaaw&{?TQ)995IP|lCwC3N3Be> z#+;J=C{D%pbdx3=zEPepsG`iEaj=N9k)Mvy&sCv4^=(IhVk&K;rd-Mb2kUp4QO}^7 zoSB)7R^2$uQb2vLR)y%@dM9p+Tv?@b|I>=A8m&sMo}c&a*;3NdG8g)2i2##`oFZa= zbI9xdk7XuPgZ$(oG5pW2oo>;+%;Aep5}6+kas#(XgL3TTR^l7;)DS%_Vo42Z95PzP z!UH9SNN(@{1EMNO)s^k#vkRp?cJGu0@`-Uy~Jjpy}f38gjD zoN0a-J=^kDQ#MEji6+yvj_`yY;p|x6XFs6(NWyN71|!1z^E$09fn0e8AAR>(M$kRk z&3ht%H9E`VE*6&yFyhN z0f)oyD_7CFwekqFYu8q5m9la}69dVHrpj~+@84^T{&W)oTk$iCAcyySd#g~3lgpZH9}{UaMmiH03r}}HW-fH+n8FNu!uaA86P#{=;}Oi6KYE$vTKD~Itpl7UU{76-l(4_7 z%{1)&{>E(h(a8lZ5g|T)0WP_<=ntD$aGz28O-f42Fen=~pbs|)t-5!kf}jtv09Tk% z(E0{&6!SN@D=6bXRZfhO?g1}DTC2U`s(yg?5=C^$F{O3SHkC`MS1K-c-3z{5F8bM_ zS8d>tlTcPrWKC~a*h5}tfd?)Za7Rq{mY6p$>g_GM8~uowXQ!B1UJsD650)+VAbNV) z7x|wjNe;^9YQ3Ava@et0de9c~+XjwL?6 z$Z?M=Q_Zu(_~-`_y6C8jH|Jp`c|GC;Y&SF$4nIw|MPKO4zrKIH<u)3E`xesu06vn1!ypup6 z5p-}fU1KQud4QpN`%jt2r9#K_ngrkv;wx;jRzsuz#b^8b$GW>WTqSS2KR9tu<`fyo zh2`v1&QRmW)%Ym3JWa81`X`D5vMIx5D+36)XR+=vzquWb^etAEVJ;4LV;Y!bWncg# zcSo5@Re>Cb@-U9W`Z8seej;(*#QCMcOU=ui6GZ*1yvm|vQks)h=YO?1^_M^Sdc?jc zj_}r~(mA|8kYh><10?h7oHZ(e356`#4=LEN3Z;02ocuwLe`c=!?-xw!Rg^H}Rg2&J zNeG5DtT#zX!plc&J-um6L+0;bZdB&RWYY1j{knYHkLo(7FFv&?Z(UBgGUJ?2b}P3$ z4suVW%}3=g3N9Nkc-7ct)~56ZKp)z#S{VKEUgn#_m-nVz_i#}Z@kBm{5YWudY;5#E z?+@m3U*VLs7~R1{WH$c23FMj7mG=FXZ-uyIl=8eq3rPfQGM&7ny#?I{xo*R$qWF<; zx=MT|lOP6S=H#lhRH-X`qKcrDYrN_MGCsXu{$Ot)3#oW;b~Ka@rHHWg;w6WMqKvxh zN_hMYUSrvEfyeS}G-b{R+m-O+m-nyE(>Ap#xVG*EmbFgk$*SDht3zv*== zZ^~h&8F`hjHMG^)AtCeNDKb(M;}T8~Pj2R_4i!Cii4J#C9-<(V z&?=4t*?U3||5U>31W7}VmOr7<-$dFHBWz-w-{m@gv(&S@T1v@r$Ba+Xk*;dEe0cwp z*tp$#tmHo`>LH;G?{~f5#rrN#vLrL3In?sNd6859L#O&%w9MZc6-NB6Z&n87IO9M4 zkTgw!WH5zAJh>Xq|HVd6Oims#>b~3Yqa}g>mnz#mRIRZ2)6L=(080kzUY-~qxKx%X z?xo8nemt(e@3=(Hi>*ndX58Dh!G{jQ>0-Yn6eE~E=asMh)3~s4uv1z4 zN6L-^GqnTD%J(V~UpdGAiWI*=BEv%k75|cQxSxG+Icknzo?gr(R0-pfmHn?lw){J{ zLaorxvOz`9{lFPvH@v$ry_Prp;&PRhMTzO8Yvi*7sJe>a*nkrH)4Csjgj4<>K-8fX z=+*@Q{2HF5oDR>2=*zkVe9kg`S$TqnM-z1>bfW#M zWCB;3@%8pBg^?q^KmF)D+z}&2nbOFYDc{6o2s2-{g{-=_+%DJbOL)Dv^^a|@)Mk|F#dXCvnUO-S~Qfk6haL?qCVGW z@Wj^~*{;s#{|wwF8`Mzud#F(Qxu{r?+MRHq_pQ#oLIIa4_%Evbpgq{1$>cl!q znnN=^dPsWniBmHd^bLRvmHE>Ns=TTnkPGx z(^5w?l!Xs?CZARXYa$p(SXHGj<_5WjIyC&t7-~)`4Mc|KW0c`g6w8?_q%H}*mcxyq zIc@~{)s~LUA@?@+o6VZPeEy^W~V?6lWICUC>^c=s#m=nNdiz$L)b}B#ya4} z*!VFj=5!y%Y4pFjCa8C|ZM-MMJFb1fo#T9bZSPqyw~ehOnLyP=pU}*S&0Z_Ls3K50 zERiI2RDgEEx~j`pPu7F0cT*0R;ebaHJ=(sj$nmiRA92Q>IQKuN17E=SM!OREZ(3hzam7stt*`f6{kZB49Zd-Cx=_z-) z$%t$Zo5Qqcyx+FnlT)>K%5aY?EG(e$6HtoM?g6bomX1D*X>813NJj>1z-Y>bG*MT7 z(s8o670epwcLlYFUO=V>7cP^`^;@@C^l02q#AIf6YWn3zi_t@9RLV!#G-DYD9m1b7 z4;B{EH0WaArFhuWfSO-lhnGSL2vbck0_h(d|Aopj05j>1f##|SeRprJ11(olNb88f z%6PG;s8}p~jny%wgQ%UO(6|>W{*k-88Au9huGc+b4!aDnrOfx@ZZ#A}b9`5p2%Zm; zO@<&0jrVN%pJ{M*mr_=@@{ME=yyaY10YROxo1)ZhAFtoc1~LbjufV*7@^7K|D8zBh zA`D`e=gX8ix*kixABM=&)VR;)!BAn6f8`!^Y}AuG{4jb^FYLEEM6Li3 z5#^qQ*Rk4mg%Azp5!7$`WE3KuE*9<9;Mt?K;(U8`ol442k-Dhtb=?|{N?`%gfRw}6 z{bEoQnhi#$nWk2oiSevGgLT@UyoPq60)9A#h3(b&qWqy(VWH-agA|WJ?+&y&=cYOr zWZC!7P$X%AHu>(N%vr>-QD?gDQL~SHL87Fm*`TMe&!0;(B9Kytqu~Y7T#xev&p$bh zTs&9D;Eex%`iHms)ix0en>wBAKY9mql~xq$Z-G80j<4i~Od{shWgb!sx_f;>+zz76 z%5cOIt(J<(jp79DgY!YSUUEu`q;6v3n_(qfoHC=|^wev^Ipy{u!m~nP##b-JfYW5% zM3)o-JL?rX#;@=20*D(&-`ED7*5`3KX1y*^MGeh2zw^jkkEq>3UJm7Z-7Rs$RN>E) z=g<>~Cw(t@qP@^VZTN`SI=f}y7M?eq$RJBs*|Q#B#~~vF2LpBs|N- zIvjc!Yk9Dr^AH^1qZ1hG8%-S@xAms(^sl|m4YOHNl}O6ZuNfPAxigzfo9N|5pa;E8 z8IUU;JL9I{i;v_}j%O<Ln*L<%1lhWb%L4UhOn@$XNY13muAJ_?-Ityhm10b!v0m=legqC)4HN@C)#L#{>k`$ zdhsDK3!O}Mdfo;5!H@xdP0fKy)}yDT72sDMeEqB8Qt_hf@>1(Si_la{^hTLQ-s11l z3e}^Y_?+>^0-Yg~zaoCh8(W19xr-x$Zpx+aMZJ;impiWwFHrSC>lnpN!;7xek1NE#T^=f;;s)t;jT@rm|SUvV%$H z%^@oYio|tl@3sahL=fL0|90wAVOcnN%V9IyO-Flh@MRJo6c5FH#%6gKwvNutKX8ovt$_j~;#{x3vVJw~=~T`HEm9!ps@SX~R;U8uSg zyA`54)it2M*VB2vk_FQD`1sku2Z-x?`=+Hn_rsUB4Oq0*FD~P`6x{eiX;iSr8?IVq zFTZK1gT^pd4v0TU<<-3ff=dorKWx62&vUnsj$H_!)+6 zI8CxH16X4lLX`Zut_9-c$VUrPU7kM{TEF~QTz$uST@(M}vR*vQILus|SZq1GZ=3BI z#nBO^h|lfZ)z%4tmf5ZpT09zWkOu#Krd}aEVQxOy{`*afa*x7(&#>yybHly@aT4Q> zZ%BM@b9tFBeJOd?F&t{>1m+e>>lyFvLQiTcO7>O^M_zp6d{LRQ*+_+%@xNn~trQ*> zoUWe9;LiulC7iDJCogJ?X=sChGt5DD`SGn4kNwj7#%fdKQg@unoeMZ|n7_`*V>KW9 zhD0Cvu28u)VlXFeCl$~gH}pquZMf+bZBu6y@;SZ%f2mQ9i{;ilvj>d86j6Cd@p-H@ zD+9%lPg0-oH(KlOW$LWR@+l8l0XJ?Nful5Ts{H8wG+?u#t6ORyMd)H&W}Np z8HF_ccKqmMJ@G6LK7!Ox{nj1w$XLi`D{Jf;X*YDpnUe%R#U|4 zTUri2ewx0}H53cV#jgtYZC>36{Ufv23vYivpWaUzGRD8twC~VA;zAl}NOckTU^+Gg z#A`18*cyFahP$C!d0&|JC8q;|tMqE|QrpqGsrxtc_xCzO%>7L-*@&S1M)B_6WKR(J zYP}jqT%#6d*t6HySwyb`imcoGdBlU@%;|S?zs9#q-xavUUX4eif1Tg=u)^`Wj5@Qa zIMSdXF%dO+$6}#cLB~LU(A#o&d6tXO-yso<$F3ro&mVq!@uzGAaK7i-h8p7wbb6>^ zWXTZ1;3-`D5kC8+%BjskV4m%xAp)6ODa1S-!QEfhZkjv&@$3j@dTVIV(oBlou^F3$ zEywX#UusqNLp^Nel2w`rM7eYfI}&#`Ycb(EXHP)E#o2rWF*OUwrneY$7(HQTjq=^1 zHuq|4!;t!V#ZyWYIx0!_6@byCDZjohwSL#x^esAYbi|OhgJG z!^8o~>4pgFk!j^VrEZ=1!sr!DJXLgIT~qn5*FHA_p+jD8!_!7f^XlyCa0sF* zY)zwl@&hD@5i)bjUTOR4BVGUuPXFsM5$kA0zpxa!^y?>)xQ9S=G%|c9?bXmUaNP#= z!#u7Kr#XKzZekB&u0JvSDoOI+v-COU|1W#2=`9rQCR6nSaL!@^QtgAg`LDaccXs#; z542>+pR^v?CmOosO2#-tb-6@$N!zR3=CXMz>8rEmGpAbJsa&k2d$vL563?by=;waX zIF*vmU7`41miOV6LL4+=P=Z^Zf43Fx*<9PaIhl5PzWV5SX*)w~~vXk8T218+PA)+rIQEXou8^BIsNz;D$Dax6mr_h4QD z%T+K~*)l|9>FaV7L}aqFMt)8=`SuJ)51X%51vhA3wzRbfp$wsCACKbP%^}=N<@5G9 zqG^OIUz-<^@-_6%n$QE0v<8X~;KH|8wTDMFOd#pVDr1U7cAw|vK(h>1;O(@5hNNTb zftW;R+W;Zi%a?@*b*L-Ql0{~GaHfnrp`6CQ@vg|7YvAxFm- zp)|irXo&`cZ=i-S2yAovv-gcI1dL9eJippTS6v%U{af_0{U&yQu8PbSGRi$S-e}B#UMs&Q3cu*bVy)r=OVvpS# zT>7s2Zk4S>pPZ1`%u3uk{Xol^)2t}A@14-Nxr`Urf@uWyVY<}TilO*ZO6txCFB{5P z43Rzy4!54O$u6arp=cT?j1ur5Q2M}8L5Ps z8^iH)u5B4cLjw6@V;{4aR+&w^fY&~P3sEB6Wtc1<_yj||Z`<=hjp4VRt ze?q^doH@5yz-gPEEGIM+fiRoxlIYr6%8;_N-8BE4xpMb8SHSPIgGIatFHKLLXE9nQ zO|6gbnF0KS5a2mrg!xeNfj&Zp31zumY0Ki>u!$cCm9se*Q~6ewT^W63F=Tm?+IvSl zm=k2J4h}~+Nobjn)%L&q=~!QN3sFKzpaF37UV;r3l`v9VfeYTduV3$F$|eQG#HiIS zfu6l(co^)1px%sd!;CF4CzwkrunD0;8X3w{FMj$oXf-~qzrFn;h7o(&cG-W$Hi6IP zy}QM}nKXHWHzXIRhmd<2?#xM4xz znFJYP1cU_5VYgn5TG^q4u5uc#>}Pg8KRkz94zTyh+PAH%YiaJm!VI0|T1=0o(yzu< zd^MC8&$EuEwBEp?i;sd8K`PT{01e3l7au12!tknr_w3qqX`dh}Xc1Trs3c90(7dJI zT3x-3Z~Oya;@iN-($X46F)-}NhJhsH=bbr0A%}u^tM|V!VQ^l)F3SIJeM0@DLy~Dd zrLidK=dpX=QEiZ8N9civv?5{E+#;T!-Vtx*g(dozE>oPTZpwgzvE=R~i=Mcr$eTfL z+mv;Yra8k+Y}$8klYD|c4f=gTO=I;!HS{cmOkLzMV8YVljfMnV2IqSf6hII!o%Ha& zn^^1iam(9b9i281;g7l7@TG=v=2KKeSHM*uA|QF`sLVt0fs!Eq(f8~xdDqe4q5#%` z@Ghx;LLH@{O>~Aio*ipc9Wif`^&3M1kB5W=1R&6&+$os1h$oA00xn4rXi)h9#0YEG z?A#oRs{(|dQUm>VXbcsT(MKUYw?4sB*7mt4VJN=Y-QDVTJ!FPVdJF7e zoQ0Ru=&*m!xlg;D3!ht+efZVUip8wO46o{g#Ei$Ac$r$N;)N^KhwBtT0hlxB7daQX zz`}$l{y&$t&3@BwMVinfG-bmZnv$xOkG8rL^^~Qfx z1etm?5DmG{6$8)bvHJ^Q2C*>u+!gh#z=Tn1f!l$kP5r4>a~NeNspGm?vMNLGcaC#A z)p*qT`pw)pGN}_<)5t=t%54_FfZZ#^E9aS?TNbio;Y;%PAa(?sVRLQncUkx?plXE`h+PiR}!hz#awEc5@Nprwo{e4rPbR82CJkLw`16I9uSgosB-*{J-;yMA|0(#n0O^F zoFTdDs*>xOKq(!6gH<(~LQ;N@?(#rwHonrwUv7VXv#XcmZO;PYA68XF_8j=GB{ulW zoj_k3*`*OOy{(_@$Itywm|n!+Kj5Ast%SF3ut|@3Oxz7T1>eEm z`6)D*0>uvR?lk5rTB;U~!L8t>!q_}|TGEp*i(>IgEK+pM^eVGtq~zl8+&_zc=9^HS zfe0B6=~zY5cQZq5EN2YF%mVdO`p@S<`GbuOHa8M!mL8p@ulMc>=?RH9LuU-oM?N~RFgG}EBwTKi>LRmXHvyA z8&1WpL1vN_Ib#)EAUy&E$LGo^t1L0gp?y_ls zlNPxz!h1MJP{Foj1 z`79}={5SvIo<^<(1SVk@*cZIBYnPmGwcD{ebabI(GU_}keQ@QGMY58M@whx7AV6|A zQ3UEUfNv2{P)I7jxib6AAhl@+VIU!h0R9#p-kie@cAe?GLj3`z;}vEVnQ}X`T8k`d zDTiMzy*@t1q>9~jcMWYKGqiiH=k0p1J@v9_E6?A5=(-{WB2*d6cym9)`ExB+Eoaqb ziO^jE9>=YF9*L)3BxI^qV@KBCz58L=cN@NS!{L$7LVSj|&U?wq_o|+&dVf>BkTmwT z%H{W)2%2(zg}3@@J+$MVv_?#MjBq-0B?{&v(YJhb-|fV;JZdPS0`B5?q=^XlwjLxh zUXIJjC?P`A#B+AIeg}}Reb_^QF=M!u zRJY#nBL&4y%QbK)v!DM-PrGZ{A(ykMLe1?`$vqq`fa~5l$>%cW=ECi=xjxfb%33vA z1NaW3DqeLyeGAiIumca14ukUDJ=LB6j#F=GOrIm^E{++tnf8V@Zsm78jR!HMRH)wI zLzf5N*%qXP#hDPEThkU(_qb$GNv0kiR}3-vrWAB^bfEK-DzCw+>$0=AvttBX&*IsI zNjyd7c!SWtnvD1_q0~weZ*F7ZV9!r=T@g}{OM>T~*p}7buk;po^3?R6T}tWEu;3^H z;}Ey4`APVx+i*nnu=V?6ou!Z~>T}@><=`2!l2K#2wgb!R+G*Br^xun*Y9-s5?V1)| zChn=Vt9-0r{Hyrf#dO!WH#Do1OzW8jr!@m4=pBA&Cw^IHZ=Cacoylo;wYajP4MsBG zHpOj+%*XC? z2PO`i+IJ&KI#e|tEWKwuuQI6yJCy=M1Kp$}nq_7$6d5O_Vc-vlGKcL7?tVDmZ66&m z!xjnfC58tIU|m3=@&R|#F-<}w*=sk<6$}Sjfcp|@>*#bqY|G~~*T5{fRC5i-?HA># z>{`(eee+fk0#8GF^xF^VZcMRb*k68GaIQTQA5s1#iMgfrDZ`A8=N8Y@(#4j0re0~h zx0vtFQFX(Z^INlZ-d{A4eijYQ8%INZ&tA$K-d0Y!ad`QmH%H_ZoU%^7{UICd)0^5p zB8SqYBH^#Phn>J>>gD{04iTCWet0e% zD{XU~?3f0c;M-i^K&s+9J^~JIxlbu5HLp3F#e?YbSc9*!))2=@6`#5DXr3JPnhk6* zRi0aZm^;BOIMZ1I+6-);v`ew)aeGR9au&$N9Esq62*|JSbanE01rYaW26*0NEWg5B z3x<)B08v0e*TYe{Z@@;KU0;8M^%eEdxkI2b2)02X*}Q0q=RWZfUH(S}#n|@Q7V=Mx z@@(`JTm9c1>X=RKZ`P@u#MWvEpA+r9na$~tyd+k<;ZESa-KZ_gmA6JXGvxcaf ziS2Kfhg}Pn-J;TxpOc+5u+Bkgv5j@e8EFGp4JeRnIo~r&F;fsDU!;8s&Q56%JbAh# zm&zLb;eTRPy}0W>0-mGVZjBV6aV>(W9sQFK^bxtkKEeyB$z6uqRips-qDetYQFvmJ7Nm_T-&=DxyP=j%D4d` zn{Up3XK|eD2ky=92Bg1Oy#CInJ6fNun`DYrn(uk=O=HquC&npowR-}&m$33(~iuUqe5&^o3uDwrq8FOd$>KZLY8JYKZmkZyLzx55iFQmyNrkXrYx9 zZ-S3FZ}E??@ylmz<~Mw95>OFPG0RFNG1BzXIP$uL0b&G7R^Q(-@K4l&z@DW&Z`~>O z?94oo-!>df*tDR(6q((JQkMopfa@RJgAWc?-2a~ZHDU2KK8R|t#L`vdY z8vK;qo}K6K^EvZ1^H}uW@n#A$siCb$ERX!ZS|LXM!uT2{bNFzQ$OsK^yITF=E%<4P zuFr|^+?%;cZrg2z(%4fG83w>Dcyz-!Yy@w=V}Ah|Ik1Yprpe-`8ng3;w2SmOQ9ict z8~tVP@clna5WP5!T6M3~bGr$6qSc|?2gmagugy1X)bHX@q-Um*^Qkg=<#^}iX_{oX zI3S2*T~mj0(1osW7P|P*q6k&q6CBZ>Z(i`ttp>lHS@AJ%C{MEH?fhM9Vk2qHVNo_! z2s;Rgz(d8~Y4ktMaGBvNM}*1(oK3h_nzv&y7FP;e3U$GEcz-sz+~T#`LWA;XL3dgJ z{_r1eRo8^)kIzCYjA`EQRiE1e>Qv;Lg{i^kx~RI`>QdDsKo`eY3~w$nH21@B82ScxU_0PTP3r_!oYSi306W z56GYe25=72ytr{FP}W0`yanbVV?~t}+~241@hM*DJKi%0GZ0kOo+^=>W*=g}dR9@Z+uz>c5bBR8OdX^7+4_;@&Y ziPgCwk`U&=Qoy1_2`Qns3cMV^s>fow-T{1OJ{mV5KK_uTGX|QF**z{v7?` zzya<>^uhK=kn#WuJz}#fNU)g(ATIdHuJM3{5n||@dV53Qe7+5o#%}{FJZ`6DrtGgZ z=T@*l3)5zqd$ZO<4?&LZx^ojCUQ4~JOL0$SXfnZ3vlv?@0T4f?C zmKMMqQlk8gXo*QlrVw<5its7FLZRBB84n%0dk{nfKeaRIvV{^%xC7b$MqflKE_E(p z4fL)9=yu|Gl@JHRxg@2R8n&}l240%9GAIk-eXkda{jjyXi~sVVrSI&`GB%Y3RfKIf zWLI=VK42P#zjtnFsSPd|3@?e$PiVv?oasQR_WHA_AY=|>M>r< zHR+dl!+5oKPZt+_zCn`Bb+79m?46IIUWAuoR;Q)i2R(dH`35{7sAm~Gr!8Yb#jFOv z$SAg&5e6PrQAtTaxf=xK4Cblbfp(lAge3wsx*1$Qop2>hKtT2VLX5t;ItMHHRq&^< z`$OK&S7uxFb7|SbZJ{3a+;vJyTrXJOEHiqxXJp?KQ+`3fDT(%x^%I5yLAq!p)9u@8 zExy|%1pQ}3%DJgA*spQfluhYBcKN(hk|=R;_n;~*VQBJ>WE@+%aPA50?v9IlDkpr{ z5y!ifp79cyn|y$-50*{dickRzIy%TDcGic$m->t6-SIwM{sd(^+b;x;sBYYxxU2^6 z8Zq{XD4Q6DS88MEn|xJPQZ?+!_md9Y%2q%}pM#+3YQmpXDDOW6zJWcIkctYY5uTs* z&hOgQ;q&wF0J~t!?Xd~dio1{X(LR6v4E}t4a&n0**(6k|3D^pZLD^P;3Ta+Qu&kef zn^^VZ2YM0Ey=LntcHjQOoESJ7&DR8j9Tcg9VTKU5W;W14_a{OUy z|HRY6V8ZgmVN?4Bvo$De8QIxGVA%1a1gv1FWR3Nyh7_n$9s_$1xNc~~=L%&5D-)FV zTfSH4ub7R%{1Zs}l4c4!VOpQZ_O)C+B{^0UtB7@pdrT4Z#<)4IW(*HEhw_6pG`0to z)SsE(Lp;X+8O7=xO#psVJVW^wgt$M^lY}1V;73G8dV}hEqCO}H2RQRG>guBwjEt&z zB&B0PDSE%BuGwM$^z+!I z?HVBww3aT^VSRJleyLs^{wOW%c;=_+{>hSj8U|qJ&gw)I-aR4FUirck6A~V@moM0m zp7*z6+ycDG)5AH8v{MEXeY9P%7hO1Co9$OXw zq0ur69mdLnX5wRp)IktKW3?ZRuu8%AHq!G=zE_i<@r8MSFE17s`Q=L^^oFG%SG%xw z_cp*579kJ8!L~fnt0VnZ+++oAbMy4N#>8#M?XT_(+F2p)9##j)i_4jmN5Eh3!yRB9 z$hx|qY5~WmWDvg>f#DzEvD`%I|=LRjG^E;{-m24{HeDUzSE6I-Yh!zU#;TZ zqvUQr+z2UvX*Ca{1mHn4ShFFyo+2PNuvg#6CH7Jao=cniliquUf6$OK)q^YrpZbVGCaOe~D>KWM|28``AbUJfaTC87lmAmPNpK!=H7UwI+; zY#`>hS#?ph>pbyQz_<17ZBNUFp;Djk+BvkhfIY+V;R)W?b|nB+1J5FPq5}BqJlW6i zDJVK&jkZ{W+CUKU;*7xFP+x${b=@}&7zYpEc$#EW6 zc}p;0%fa#ZZh$?WCuDB)LmWKhaLE8sgBJh~sBndQOiZxKNEYhWVdCJR;L`Ad0FT`N zo1S{42L}52nqGee8x92|guIAgD$>7ZyRo`F4sP9QNN;{1DTPj(@+tb+^=mEG6OqK* zhwT42p@M2#^6iB6ED9D{PUn3S7=WW?L(utF?H(S0s_B`Sd?6Id8cfqr0H6X-QRxP$ zX4ixo;c~hMaU1?vSy_Rk$#knIGQ$xixtFCzr&DfcU$wX~F+BkiVd;fp}>g+u8`qgJQ>4+4ti!-rtaWhbTEI3#dJAbzG>Sxvy?or zho$y?xjp_iooE~*T!P)*KlvYS8kB2t!a@Y&Y9z^zq&3oLs3qV#R{V_)4{rv;9wfu6 z73=~~AF-*Em zD|lC~CqPCLOjWXgCc_U=CU=9yVF%-}UB(9}`H_e9rY!L!J&~Ej`!K!>EJ8&~@-MNgS(#*Ho&vsAecYB`5JJh~ zNSqGvoa)JoN2GCaMNUs2=@l3gcaU@a)9;tkr1s zj%*rkCJZe_W9ZGuV3>2w>$5bbi-Q$l-Nwc%gxaoedalh*F(whm=qUqZQ@NRQXLTFx zh~eBED7AE22Z(?drg&-F8w-!aM;8hBaRW$z2<6HuG10mE_Q*O@NE7)YVZ>nsv zHhG@7^FPvN$@doafAY%rBZ07gLUmOmr6qv2w-*y0dkBJ8$=%kxe38B3J+2{z9kFDA#B^i;t@0J zC<(1>@*tZhJNuWgYY|LM0!PcKhP%$8u(C)(7vbA4*eT$Yy10zQR47>;{4L;X+y{=} z!Te84y({xuezdmMjyvCIG&2pQ1n|pYqw;6)q>q6to41eeTC;&r=~49FR4jfnuMLJ* zQzQI&;KD!dzYx5N;q~zs|726HLK+4LnBD-fKUO-Ee~Z_6jD>di+r3=nk1@KRmQqHx z(0zSZcW$~I0-bfb?g;8Wp@;6%*97KXYbU0ev3XkwKfww5e{4@TBch`0wD!c{Y!(h> z&*Z~m;iS9P9m-Nc< zTDYXN)S?X%dOyJq0LA-D2)OYN4z63UYi@2vLk0E1cZc+a(L*`j}jmnY7S-*I#l% z`M1I@7b%mNcNZIY-<1cQJGvmKW0q#@V4^-Fc7}J5fez0m}_-Dj$1Z~eBZy+7C0<0C(nm3@jT*Q7(J2njwR<&-F&n*viXulXU9kCOxL=%Bh?+qv z7b;;BLUaAWi-yXC#DJ`2*dY`Y6e{K*)(_-^Pf=F}_6M>0S9Fpx8{a#rEE>BRO;k#XYbNnZ;g;U}kFs)vdESR%g5zjpWfQr}My)|1nTYQUasDk(MK zF+=7_fXPy@mYGg`Ui>EoS}q}dxOPyau)@Cg7IHEwZhVJ*qjs;C4|SA}jlGK!)I%5u zKs<0^z=H-;3m?ciTXacEZ0iJ0Bs~jDdxZdO+<-9ehcEr2l9&dN!N6qdX1_+o`NliH zJO7{C(L{Ac1_>8A**6A+B_6Bla$!XH^tsc7(|q&I_U;>jVn<9B>ie`dyjXe%tJUr) z>ZK*4+G!XQ9%r~p(l~%2)WT{D`lyh$U&JMh`4L_v+B^K)XyCai&WAn?DnJWG7Xg5_ z#a}j3Y;tn)^o$GwT3R_1lWevVbQ9n-rxqr~$9DoH%@N>*z)~%;aIv?428Efu;eC2~ zb3H(LrIZ@?UGIlmiNdh|tu*W+b+x2u*l2C*5&lPTesS>UYtu%`&v(rf)WItq%rib@ z`(L;$>oSa4p^M`1`hhR=bz%P!8N$Xt^9w4|ANAqf3Y!Xq;b<*+)9X}z0YH0>&X0eto^7 zx~e_2fu#*O*qJu8FR%i_# z*rK^jmly!c@&8BN@G*a(=l`W@c+3Gf0Te(6p3&Oe}`P)tyObwF4YDh(Q% zSjNyysW5o!jbE8+B%@wKTw(t4Y|C%oHkO|pIrNEp%KN7uwtE2(TJ8vW3+es97mj4Z z$cVJIW$b(ihf4@ZgEt=vzp;(3_V#JOsR9siV)*Ps%PE{2)D&j52e*o!!hL?T&>02k zG#eEdYos@ovUylDBC39;%`E?JsNgpJb}Q3;F|L;Oj$Q-aeR0$o^k8#h=EqiIu3zyv z|NlN=Li-^I2SdPrzsaj6t$i-kD=&}61KJP=kDvAl7z?RPjFt9R-U|89BEp(;Y z7`MNz5u^K+B=z4J&4`_Sdp@GboLsvW(`IqG?dRB-N00oU>Zh*f#mPSJM%V?2Is!;) zL?9g4J^f!QIs*gGv5`0aO-3jsgfXF`AYeaTVNvA5y}y$7itc!U3~Wq&@_%Z1*T>Lx zeyk2dnBhH^*OkN-rs&La&rd;6eDfaaNB#fBbNjUf+6f>1k$TQc^_;lk{m@&SMU#K( zmU$WwY#>uf2zehrbJ@IE?xN0m!t)%DaE$suvjt$@dxdy%<;WG)pU*7r@oG5G|DV#X zJetaOZ&PWaIYXnAd5+A9R5DMQDpN9)sgQXr(xAv3wjom3+U6mI-bhi@%Wh+bD3KwV zQ-<$)I%l1;zO%mb*Y~dFtrhm(&-2{(Z@7NfeOowz*Lsq{l7prgV{blCr`mQJ| zwQV8lq}7stcM^Xnf3#a1Lm({QVNtnq)22u2r0V zGc!`)#>2wFaP{iDC%fK|a?b9W@}uc}l|%X(;AJ!`vRMaEoEsq;$iUxFVk2s70^1R` zgs0!jh&|1K%j7P9Vt|;06>tz7Abb?-XyN(q3Wuu+g?e{Pg{|h#zBL`?x~-adu`XLi zKkoQmbGB8Y$SF~D?!#Gmfzi%wOH-WQznw31=<(ek8$T!K4^OOFT;w`z#&>(tjbv{N^1)Ix2U2}@XUk26 z1-ZG8Yp2MU)(s*P=IoSlF}vE#$HOC_%)`So(3OyyN`Y)-2HFB<|C(K=h1rnJq-+aK zT}FOpK$_7YcttF1$8*n=<_>ohW+o|kQOpBGEvmC6BqY{f(NPvLubjTr+)!Vi4N!Zq zD)loq>Uuqhqn#k}vyc8^TNii6&AsdeOXk5Q8U@IZO zx;4Y_c&I_7;-hwEG5%7ks|NFn7Oz{39j5HE;zf;yAE(8hd1{a&2sAoeeZQtM}T6B4LtL9Aa%nTG_j z2%k*LfEiY{@aqWbg>c-<$jqFFb;0~ZBd05z?%-I&z(lb;Y8~a?D>vX`Q9EHKw~U7u z4L#b9S)lH>;T|P<;a$4fz*JSwSbe8MX-4dRAtC0N*M`~WQ?y%_Ba@fYZuT4Tem1K; z;{TJYdTwu6hv}VgwsRdN>!^=5#6wmg((lrDMYCewJGA*`zV{AMKi&AtaDp)G>4<#&;0aPuMB&m<-HuxzA$gfr&T9^;okRm z(UEx%xvNDjxcz#M1UQ}9375Y|iIN9*A6^@rYrsT(rVwTs`22F;HJ2?5!{ln+p8&RB zPkeivJ#-;YU%yhc)bV^r75j^f6CAa_Hy-1%Gt-Ck>lmHgG404@nmN;v?>c(XO0BlT z%WCTc*w=l+@#X?kVQ?C{oHwzsd>OhML+a%g5V%$Ky)qoA^?8ZJKi<(e##b`{FuMs! z)7^O)&GL`Q$s+pK{(co`o(l*FV6C!EMl3~-2D_HHZA~KiP0q|@q3k~7)Nv2+5=mES zF%~7L{sR>z!g6!3&zCI{l9FiJg$c)R0OruNC2W_(Ax{C=WK|t-T}EXnIjzCWW~vLt zQX@)+=#gZa`@@G18SW(|-KIK}hSxr~s-se=F7&sJy`!G}q{KvZUtf6)ta#6cmGj^0 z=rBEL@?0SHo9x^enf;w)#CFvddu6vG3qV>Db}S-~wVvBj1t}%Y%@aM(Zo!3)=18mf zKGIB)>8Xp2IvO%RHLf4B>hT7WIWY&;|jOEfZg-Kf3O6;-w zKeY?oado}l-!C4q<3(K92Dzm#F=q2qUT&xLw<@(>ym+x8u%+mc8Fz4bGmVx2MX(rP zJ~$-KA?L=8sp3Omj6U(O0b)6D=_k3(NqQT=LnwpX0bXt13A`GRO=M)`roBNMNx89t zKQ^Tp+{SPN3UvoLSjxRZf+i;?1>5Do8N%jz?V(GcGbp-HTipi5evZ>6OXE<;x6 z!-Qc1js54kwBSR$EWQD8K@{&dAtDRMFLo{7Qxx~X%U)jP^q?uN$$K0 zAo7df-Ce9aEhwzvT~FAj(C19QhzJRV zor>;R#45~JV#i%Ewdg9H32MV+VlyXaD>m7bHbZxJw`;AdY={y-xv#(aN=$`0SXLum zs%Cz7j7ds5lm75wMb^WIpT@?_KTV>SH=axGhTkf*Y3s_w2A6QJbyMU$F3>1@(A^f0 zEOV_K6Q7tC?nf^b8Ak-=zfYD|wc4*vjVn!6V7i*M^Gs6}?HlL7d z#n?b7AO5uHp!bH^PxODZ)lCcA6*O2`g>9WsJ5oA2oO_mSgho5YkRq3v(B59#R8cV` zYm%KFGc4~hZBSLFBO4MD9$4FdDUM+3e|Z%Jz!Nmoqv&hqarQu6TiX@|6a#?@5D^>Q z=89hdHK_8i^BRyd&%|9 z`-z&kA(5KPgldFST{n$#_PFDRc1_d!I&b>>uLuoM%r8&c^t|Z!`EzK8MXm|0xQ3g% zZg%YQx9B6%`L5n~RTVQeJPMSvF1aAQ>T1juROmv*9(8HFTjL}IqL)XyW+Y-p6h)& zS%?u_c*C{wa<8aub^S$wi@qE89)2a1UmZOdh@P2nuf(w%ZzvCA*2rvk?^?y?&YNXjaqPuqEy#w3 z4m~VV-H^gv`Se?S-9zqYW=}J=)y44j)vuHF-HzkA8+KW}SZ!wE9~G~iY%RXC@BK+l z-i(EOwEMVyw=fS(oy@g!{WJMoYYS54_?# zY!zvz4vYwji8^a)_f!j#4HnKeEKKk~WZl2A0In`6wP?A2Fa+@$yuFLQH|7pQJGXD2 zltbOn`_1epKlD7?9W<$)7ZnvjF?wpFH$2`mw0)kKT>KfK#tw>$V{i%E)6uf9wF3VK z99}XaC2%{f{wV^L=1KLJ^TuhtEh-X(M}x{*z5{=nM6S@7D58r3sQVg6VTteb*>E!o zpZmM<(L(h26EuTG@HF2V-F99;MC9#rsVzJ_gMLEKh^{YIYQSONkixCkHn@|)m?W+A zl`pIQ`tEHc)qzJ+G6val$LdF`x^+l#2Ecv@`D}Dnle^jV@qQIC>MKm)K!1(FN!{uk zf0mlAZev?pvEHpreo{ug+`fHvvx&|Z zLhy-ulA8-UokVBd?QLzN;^V_Qo#MUPH17T54&M0Jjg%X7rzftNPFPj2HaaGTaqWf; z9+$O=Tc>aO`t@r(9fiuB8_(&rke1k@s15^AEvci?$~zi|C6nKVhK0Ey-!CjI+@AT^ zFSd2?Z!TBSJ^%cJXNP^(cDzJrXsGO%4KskwRY-tU<26;s^ZNz|`D(2+uKve|Y3MwC zcF9fFz6(t%uCSg!=5c#3OGGpDJOK2Iy74*k<$EIm8b%z)`pxA3^#_lp{8i$~tH|!S z`0w9Bmrnf4pT2%A7wHZG+vA^fRJYbCyhEL;b$6%Kj~_qML4qQTZXzfKiP9d!hG$AH zT+PoHBm8-Rk?vOTd_&PvRb35n_e^;AK_4EoAYFoyu!dUUe_z^pCsOa01F9Wm>ZI^S zSigGDcG?r7$UvZof*5rZeO4&zJ_cMs#fSn2s1@XT`|5TkD59BxC!3 zUOk1$3da5Zeo(uA1D$jY%qj5(Xbn5Z#MI0%FVz57155|P6ar)i6nzG_z4T|*S9sqz z{ok7LJ?5nnj-cqkh7SmtC;VPDF=pDNv>=|7lT#)^%=L-N9NaF&WNGJqJmz%7#Ep3# zoejTEHv>*@4l zq}G_IsJ^!xkUZnLq8}MlezIn+gaLi?F=OL+^N-M(w?Lrab*+W6|#f^Bt9GCEq8SR zHM8+TVY$4(#O$?Vxq(|dmT92WkKTZfb!?mT}efB@FJ?>D_0JI!+_pGwo-l| z9#MS%{%bIGVR#1xBTRwn!CTWXG>i>#Gzs*_7#LKjdw{0nLDedTv5x8;j-zU7H|tNw zGEc7kxzPY>1Vjg85d}U5F_Ji1DyS5;iL(W>i$bK3}`*)(BAfLCvfB~B{u7mv=cd*xH85x-*nM*CG zZk_0P#zLrIGr>RGn0LDKQ7qvJCTw0Ai=qOd#;&{;*f%zCSHmL6ZV>g;>9<(BNpX5J zEW~Vo+3W8IKJX~{-B7DOdH1C`&7Caq*?ZKhSLvJqtT_sCvWR$Th| zSrD4#S&ttp6nwSS#DV~8p{gB^m5Sg*MSx5fG@;~ysA(&7z_msZ3C#iO-^AB?JrrVw z1d<0Fa@V*^$~lF5c>>q{YDKC~>GlBVn5iUof$UK)fPjlEClf z<>i6skP9aCM;9Vm!CBk{3XM(O-R6qZ?~VbBJijs5xzk2E+33oZ5=@?xCV%{ZL{7 z$gnsP_J$}-6n!9Rghcwk>a2g|>i?c~F$Bl&|0&lp%oI>KBCi3qNEq_y@f{IKI&{$> zgI2LkZX|$N{{vK6LiuULArMTHKw9K4bKm)ACT>?gR!XxdPOdcsb;L$Af)b4KjXUP` zaXme~Tb7MDFviK5wzan9ZBbFtWe*RLnGp~eq)`?8_1g4_@gvn$jk;f5*@~}Lq$X5wlliLMHug{0YwPg^&TwQdIEntW612YI77=pi;Yb$ zgS1qtOEgb2=-6>!W@*0YH$8L| zukSn}<Xz3fp4Kt}^FlG<*z%VFNn; z47Y(MRA+6-Uxf3Cmtru&jkP$3(};``R1cq2FV515xhQz2Z4g<6h+YcWLTTa0N@B1H z-}6H*AMaxZ)jRncEH=WgG*WSvqo=2*Y`hz$zu7P+L`a7g z=v06Y<((~$K;sFYnhKCZ8+Q|o{1l(j!bVxw{!sI$ zh36Y5yBzZiky3~`<*(T>N3v!hvD&e@UjvTb0dGSfL_G5HNhX#7ae(Y5T7#BifQ=TM zdl9D6aPaXF0%1e*S5Z+R4mu6J28C2ad5>>6vKe87h9m;MlS+kDLVz5ETZ%AS_!BLZ2u2|1IgfO9 zb~e%JXGJhE5Xfpr5M)iSS(wy3n+T-^0cCL5TFRVJZRO%tl+9qWLmnEsP$dsG_-8?+ z@iM3Zdd!R#w|0GaVoWBcB1w|=5#6P8WAw+d1`goG6XDj@&aOwoSPpLt+}H?(XR-6H z^mtKo2NO|VUR5cVj~!)uw{Feg)lGFWd4%^JZeX`8A=B<2#Q3*hg(2AJH8YIuH@6%Z zE$fCchXW1ye-Ou^?Cp~O5;u!T3=sb=<8c) zppk>jK_5=bL_Og-hwc4n6%*A6aOvsk{;fn!fgPMY z?&ppQ!WthK%Lyl2gU~#wsjK&apv^>y>~w;4&QdyZh$o$1a`6~(p&pySsQXFhK)u)T z1ksIs=nRu9`B!~oV_U9~P}L-!i0HIb=;jE%6lL&I(Q`Dor6$Mlg*?AEC{3m&jk~T6 z;c9i3*?a!bPJNBelmkLSWaXKs^IdXFqN~9JSwPG`4>}}fNqdl0&LD5w*?kp>tTOj$ z;I6G4tT=d7>?X-(@ulfk)<*i1NYKfm*p#_QY6wCkxrUz zA)Ai8y3HzNT4>FL#?N->-2)VJY~M)0Xd0cQiF&LU8;)>VxyHoo6j?FSo&&K!W`ZS; zU4L9v^{aza9+*|Ay$*13T_s>OpA@2+Bbpr^&G^fhdO_mmE%;csA|gZ$7N@E{pa6nr z-9|ngk`-Kh769{A&vrQmyUbK$pA*d`;5w(Sl3PIDS%1mDfB)WphHCOAStb%0e;dLc zp!o3Lo}rtwlrd)~y#cqu2S1RAW)pV{{XpYvL;@6Ka4M^Bm`aAb+%@o>qHpZ=g-`0Q zS6m(_gZ5z=5SSu%GgPg#_j-T2x<92Dt!|!WZ7SF#AVujS(aj@fIz=AAl{RB@U4W@| zD~?o%)hupaF*hF4aoHY#8Dsccr#GFk`(>o zQ6#c`P60|6ezbex8YthWN%(2RHva$BCdBLfpHR|&lsJjYn}oHzGZRCL+xL JQ&g<3{SSBl0j>Z5 literal 0 HcmV?d00001 diff --git a/baselines/fedht/fedht/main.py b/baselines/fedht/fedht/main.py new file mode 100644 index 000000000000..060fad7de129 --- /dev/null +++ b/baselines/fedht/fedht/main.py @@ -0,0 +1,133 @@ +""" +Author: Chance Johnstone +Purpose: main function for fedht baseline Simulation II example from Tong et al 2020 + +Notes: +Code included in this baseline generated with the help of numerous +Flower, Python, and PyTorch resources. +""" + +from fedht.utils import sim_data, MyDataset +import flwr as fl +from fedht.client import generate_client_fn_simII, generate_client_fn_mnist +from fedht.server import get_evaluate_fn, fit_round +from fedht.fedht import FedHT +from fedht.model import LogisticRegression +from flwr_datasets import FederatedDataset +from flwr_datasets.partitioner import PathologicalPartitioner +import pickle +import random +from torch.utils.data import DataLoader +import numpy as np +from flwr.common import NDArrays, ndarrays_to_parameters +import hydra +from omegaconf import DictConfig + +@hydra.main(config_path="conf", config_name="base_mnist", version_base=None) +def main(cfg: DictConfig): + + # set seed + random.seed(2024) + + if cfg.data == 'mnist': + #set up mnist data + # set partitioner + partitioner = PathologicalPartitioner( + num_partitions=cfg.num_clients, + partition_by="label", + num_classes_per_partition=2, + class_assignment_mode = "first-deterministic" + ) + + # load MNIST data + num_features = 28 * 28 + num_classes = 10 + dataset = FederatedDataset(dataset="mnist", partitioners={"train": num_classes}) + test_dataset = dataset.load_split("test").with_format("numpy") + testloader = DataLoader(test_dataset, batch_size=cfg.batch_size, shuffle=False) + + # define model + model = LogisticRegression(num_features, num_classes) + + # set client function + client_fn = generate_client_fn_mnist(dataset, + num_features=num_features, + num_classes=num_classes, + model=model, + cfg=cfg) + + elif cfg.data == "simII": + + # simulate data from Simulation II in Tong et al + num_obs = 1000 + num_clients = cfg.num_clients + num_features = 1000 + num_classes = 2 + + dataset = sim_data(num_obs, num_clients, num_features, 1, 1) + X_test, y_test = sim_data(num_obs, 1, num_features, 1, 1) + test_dataset = MyDataset(X_test[0,:,:], y_test[:,0]) + testloader = DataLoader(test_dataset, batch_size=cfg.batch_size, shuffle=False) + + # define model + model = LogisticRegression(num_features, num_classes) + + # set client function + client_fn = generate_client_fn_simII(dataset, + num_features=num_features, + num_classes=num_classes, + model=model, + cfg=cfg) + + #initialize global model to all zeros + weights = np.zeros((num_classes,num_features)) + bias = np.zeros(num_classes) + init_params: NDArrays = (weights, bias) + init_params = ndarrays_to_parameters(init_params) + + # define strategy: fedht + strategy_fedht = FedHT(min_available_clients=cfg.strategy.min_available_clients, + num_keep=cfg.num_keep, + evaluate_fn=get_evaluate_fn(testloader, model), + on_fit_config_fn=fit_round, + iterht=cfg.iterht, + initial_parameters=init_params + ) + + # define strategy: fedavg + strategy_fedavg = fl.server.strategy.FedAvg(min_available_clients=cfg.strategy.min_available_clients, + evaluate_fn=get_evaluate_fn(testloader, model), + on_fit_config_fn=fit_round, + initial_parameters=init_params) + + if cfg.agg == "fedht": + strategy=strategy_fedht + elif cfg.agg == "fedavg": + strategy=strategy_fedavg + else: + print("Must select either fedht or fedavg for the aggregation strategy.") + + # # start simulation + random.seed(2025) + hist_mnist = fl.simulation.start_simulation( + client_fn=client_fn, + num_clients=cfg.num_clients, + config=fl.server.ServerConfig(num_rounds=cfg.num_rounds), + strategy=strategy, + client_resources={ + "num_cpus": cfg.client_resources.num_cpus + } + ) + + if cfg.iterht == True: + iterstr = "iter" + else: + iterstr = "" + + filename = "simII_" + cfg.agg + iterstr + "_local" + str(cfg.num_local_epochs) + "_lr" + str(cfg.learning_rate) + "_numkeep" + str(cfg.num_keep) + ".pkl" + + with open(filename, 'wb') as file: + pickle.dump(hist_mnist, file) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/baselines/fedht/fedht/model.py b/baselines/fedht/fedht/model.py new file mode 100644 index 000000000000..2e016f96e4d2 --- /dev/null +++ b/baselines/fedht/fedht/model.py @@ -0,0 +1,74 @@ +import torch +import torch.nn as nn +from torch.utils.data import DataLoader +import torch.optim as optim +import torch.optim as optim +from omegaconf import DictConfig + +# model code initially pulled from fedprox baseline +# generates multinomial logistic regression model via torch +class LogisticRegression(nn.Module): + + def __init__(self, num_features, num_classes: int) -> None: + super().__init__() + + # one single linear layer + self.linear = nn.Linear(num_features, num_classes) + + def forward(self, input_tensor: torch.Tensor) -> torch.Tensor: + # forward pass; sigmoid transform included in CBELoss criterion + output_tensor = self.linear(torch.flatten(input_tensor, 1)) + return output_tensor + +# define train function that will be called by each client to train the model +def train(model, + trainloader: DataLoader, + cfg: DictConfig) -> None: + + criterion = nn.CrossEntropyLoss() + optimizer = optim.SGD(model.parameters(), lr=cfg.learning_rate, weight_decay=cfg.weight_decay) + + # train + for epoch in range(cfg.num_local_epochs): + for i, data in enumerate(trainloader): + + inputs, labels = data["image"], data["label"] + + # Zero the gradients + optimizer.zero_grad() + + # Forward pass + model.train() + + loss = criterion(model(inputs.float()), labels.long()) + + # Backward pass and optimization + loss.backward() + optimizer.step() + +def test(model, testloader: DataLoader) -> None: + + criterion = nn.CrossEntropyLoss() + + #initialize + correct, total, loss = 0, 0, 0.0 + + # put into evlauate mode + model.eval() + with torch.no_grad(): + for i, data in enumerate(testloader): + + images, labels = data["image"], data["label"] + + outputs = model(images.float()) + total += labels.size(0) + loss += criterion(outputs, labels.long()).item() + _, predicted = torch.max(outputs.data, 1) + correct += (predicted == labels).sum().item() + + if len(testloader.dataset) == 0: + raise ValueError("Testloader can't be 0, exiting...") + + loss /= len(testloader) + accuracy = correct / total + return loss, accuracy \ No newline at end of file diff --git a/baselines/fedht/fedht/server.py b/baselines/fedht/fedht/server.py new file mode 100644 index 000000000000..f49dae942e2e --- /dev/null +++ b/baselines/fedht/fedht/server.py @@ -0,0 +1,25 @@ +from collections import OrderedDict +from fedht.model import test +from typing import Dict +import torch + +# send fit round for history +def fit_round(server_round: int) -> Dict: + """Send round number to client.""" + return {"server_round": server_round} + +def get_evaluate_fn(testloader, + model): + + # global evaluation + def evaluate(server_round, parameters, config): # type: ignore + + # set model parameters + params_dict = zip(model.state_dict().keys(), parameters) + state_dict = OrderedDict({k: torch.Tensor(v) for k, v in params_dict}) + model.load_state_dict(state_dict, strict=True) + + loss, accuracy = test(model, testloader) + return loss, {"accuracy": accuracy} + + return evaluate \ No newline at end of file diff --git a/baselines/fedht/fedht/utils.py b/baselines/fedht/fedht/utils.py new file mode 100644 index 000000000000..0040b18dfd11 --- /dev/null +++ b/baselines/fedht/fedht/utils.py @@ -0,0 +1,68 @@ +from torch.utils.data import Dataset +import numpy as np + +class MyDataset(Dataset): + def __init__(self, features, labels): + self.data = { + "image": features, + "label": labels + } + + def __len__(self): + return len(self.data["image"]) + + def __getitem__(self, idx): + return {key: value[idx] for key, value in self.data.items()} + +def partition_data(data, num_partitions): + # Calculate the size of each partition + X, y = data + partition_size = len(X) // num_partitions + # Create partitions + partitionsX = [X[i * partition_size:(i + 1) * partition_size] for i in range(num_partitions)] + partitionsy = [y[i * partition_size:(i + 1) * partition_size] for i in range(num_partitions)] + + # Handle any remaining items + if len(data) % num_partitions != 0: + # partitions[-1] = partitions[-1] + data[num_partitions * partition_size:] + partitionsX[-1] = np.vstack((partitionsX[-1], X[num_partitions * partition_size:])) + partitionsy[-1] = np.vstack((partitionsy[-1], y[num_partitions * partition_size:])) + + + return partitionsX, partitionsy + +import numpy as np + +def sim_data(ni: int, num_clients: int, num_features: int, alpha=1, beta=1): + + #generate client-based model coefs + u = np.random.normal(0, alpha, num_clients) + x = np.zeros((num_features,num_clients)) + x[0:99,:] = np.random.multivariate_normal(u, np.diag(np.ones(num_clients)), 99) + + #generate observations + ivec = np.arange(1,num_features+1) + vari = np.diag(1/(ivec**1.2)) + + B = np.random.normal(0, beta, num_features) + v = np.random.multivariate_normal(B, np.diag(np.ones(num_features)), num_clients) + + error = np.random.multivariate_normal(u,np.diag(np.ones(num_clients)), ni) + z = np.zeros((num_clients, ni, num_features)) + y = np.zeros((ni, num_clients)) + + # (num_clients, ni, num_feaures) + for i in range(z.shape[0]): + z[i,:,:] = np.random.multivariate_normal(v[i], vari, ni) + hold = np.matmul(z[i,:,:],x[:,i]) + error[:,i] + y[:,i] = np.exp(hold) / (1+np.exp(hold)) + + for j in range(num_clients): + top_indices = np.argpartition(y[:,j], -100)[-100:] + mask = np.zeros(y[:,j].shape, dtype=bool) + mask[top_indices] = True + y[mask,j] = 1 + y[~mask,j] = 0 + + #might need to adjust; vague data generating process + return z, y diff --git a/baselines/fedht/pyproject.toml b/baselines/fedht/pyproject.toml new file mode 100644 index 000000000000..95ff549bcd1e --- /dev/null +++ b/baselines/fedht/pyproject.toml @@ -0,0 +1,50 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "fedht" +version = "1.0.0" +description = "implementing FedHT" +license = "Apache-2.0" +dependencies = [ + "flwr==1.11.0", + "flwr[simulation]>=1.9.0,<2.0", + "flwr-datasets[vision]>=0.0.2,<1.0.0", + "scikit-learn>=1.1.1", + "numpy>=1.21.0", + "matplotlib==3.9.2", + "hydra-core==1.3.2", + "torch==2.4.1", + "torchvision>=0.19.1", + "numpy>=1.26.4" +] + +[project.optional-dependencies] +dev = [ + "isort==5.13.2", + "black==24.2.0", + "docformatter==1.7.5", + "mypy==1.8.0", + "pylint==3.2.6", + "flake8==5.0.4", + "pytest==6.2.4", + "pytest-watch==4.2.0", + "ruff==0.1.9", + "types-requests==2.31.0.20240125", +] + +[tool.hatch.build.targets.wheel] +packages = ["."] + +[tool.flwr.app] +publisher = [ + "The Flower Authors ", + "Chancellor Johnstone ", +] + +[tool.flwr.federations] +default = "local-simulation" + +[tool.flwr.federations.local-simulation] +options.num-supernodes = 10 From 2048d15805bb351abc91b2411eab0fd189c46a42 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sun, 20 Oct 2024 10:40:42 -0400 Subject: [PATCH 03/99] pyproject update --- baselines/fedht/pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/baselines/fedht/pyproject.toml b/baselines/fedht/pyproject.toml index 95ff549bcd1e..c79cd546a0b9 100644 --- a/baselines/fedht/pyproject.toml +++ b/baselines/fedht/pyproject.toml @@ -17,7 +17,8 @@ dependencies = [ "hydra-core==1.3.2", "torch==2.4.1", "torchvision>=0.19.1", - "numpy>=1.26.4" + "numpy>=1.26.4", + "poetry>=1.8.4" ] [project.optional-dependencies] From 55306963a8eeae77f0ed14a0b12213b3f890bf58 Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Sun, 20 Oct 2024 10:43:03 -0400 Subject: [PATCH 04/99] Update README.md --- baselines/fedht/README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/baselines/fedht/README.md b/baselines/fedht/README.md index c9455870e7c9..9e423c74f9cf 100644 --- a/baselines/fedht/README.md +++ b/baselines/fedht/README.md @@ -82,18 +82,18 @@ pip install . ## Expected Results ### MNIST (`num_keep` = 500) ``` -python main.py --config-name base_mnist agg=fedavg num_keep=500 num_local_epochs=10 learning_rate=0.00005 -python main.py --config-name base_mnist agg=fedht num_keep=500 num_local_epochs=10 learning_rate=0.00005 -python main.py --config-name base_mnist agg=fedht iterht=True num_keep=500 num_local_epochs=10 learning_rate=0.00005 -python main.py --config-name base_mnist agg=fedht num_keep=500 num_local_epochs=1 learning_rate=0.00005 +python fedht.main --config-name base_mnist agg=fedavg num_keep=500 num_local_epochs=10 learning_rate=0.00005 +python fedht.main --config-name base_mnist agg=fedht num_keep=500 num_local_epochs=10 learning_rate=0.00005 +python fedht.main --config-name base_mnist agg=fedht iterht=True num_keep=500 num_local_epochs=10 learning_rate=0.00005 +python fedht.main --config-name base_mnist agg=fedht num_keep=500 num_local_epochs=1 learning_rate=0.00005 ``` ### Simulation II (`num_keep` = 200) ``` -python main.py -config-name base_simII agg=fedavg num_local_epochs=5 learning_rate=0.01 -python main.py -config-name base_simII agg=fedht num_local_epochs=5 learning_rate=0.01 -python main.py -config-name base_simII agg=fedht iterht=True num_local_epochs=5 learning_rate=0.01 -python main.py -config-name base_simII agg=fedht num_local_epochs=1 learning_rate=0.01 +python fedht.main -config-name base_simII agg=fedavg num_local_epochs=5 learning_rate=0.01 +python fedht.main -config-name base_simII agg=fedht num_local_epochs=5 learning_rate=0.01 +python fedht.main -config-name base_simII agg=fedht iterht=True num_local_epochs=5 learning_rate=0.01 +python fedht.main -config-name base_simII agg=fedht num_local_epochs=1 learning_rate=0.01 ``` From 9cea33d01e3d04ef1d76ac55fa883ec0cba774b4 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sun, 20 Oct 2024 11:58:32 -0400 Subject: [PATCH 05/99] changes to pyproject --- baselines/fedht/pyproject.toml | 137 +++++++++++++++++++++++++-------- 1 file changed, 107 insertions(+), 30 deletions(-) diff --git a/baselines/fedht/pyproject.toml b/baselines/fedht/pyproject.toml index c79cd546a0b9..71732bbae55a 100644 --- a/baselines/fedht/pyproject.toml +++ b/baselines/fedht/pyproject.toml @@ -1,42 +1,119 @@ [build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" +requires = ["poetry-core>=1.4.0"] +build-backend = "poetry.masonry.api" -[project] +[tool.poetry] name = "fedht" version = "1.0.0" -description = "implementing FedHT" +description = "Federated nonconvex sparse learning" license = "Apache-2.0" -dependencies = [ - "flwr==1.11.0", - "flwr[simulation]>=1.9.0,<2.0", - "flwr-datasets[vision]>=0.0.2,<1.0.0", - "scikit-learn>=1.1.1", - "numpy>=1.21.0", - "matplotlib==3.9.2", - "hydra-core==1.3.2", - "torch==2.4.1", - "torchvision>=0.19.1", - "numpy>=1.26.4", - "poetry>=1.8.4" +authors = ["Chancellor Johnstone "] +readme = "README.md" +homepage = "https://flower.ai" +repository = "https://github.com/adap/flower" +documentation = "https://flower.ai" + +[tool.poetry.dependencies] +python = ">=3.10.0, <3.11.0" +flwr = { extras = ["simulation"], version = "1.9.0" } +flwr-datasets = { extras = ["vision"], version = ">=0.3.0" }, +scikit-learn>=1.1.1", +matplotlib = "3.9.2", +hydra-core = "1.3.2", +torch = "2.4.1", +torchvision = ">0.19.1", +numpy = ">1.26.4" ] -[project.optional-dependencies] -dev = [ - "isort==5.13.2", - "black==24.2.0", - "docformatter==1.7.5", - "mypy==1.8.0", - "pylint==3.2.6", - "flake8==5.0.4", - "pytest==6.2.4", - "pytest-watch==4.2.0", - "ruff==0.1.9", - "types-requests==2.31.0.20240125", +[tool.poetry.dev-dependencies] +isort = "==5.13.2" +black = "==24.2.0" +docformatter = "==1.7.5" +mypy = "==1.4.1" +pylint = "==2.8.2" +flake8 = "==3.9.2" +pytest = "==6.2.4" +pytest-watch = "==4.2.0" +ruff = "==0.0.272" +types-requests = "==2.27.7" +virtualenv = "==20.21.0" + +[tool.isort] +line_length = 88 +indent = " " +multi_line_output = 3 +include_trailing_comma = true +force_grid_wrap = 0 +use_parentheses = true + +[tool.black] +line-length = 88 +target-version = ["py38", "py39", "py310", "py311"] + +[tool.pytest.ini_options] +minversion = "6.2" +addopts = "-qq" +testpaths = ["flwr_baselines"] + +[tool.mypy] +ignore_missing_imports = true +strict = false +plugins = "numpy.typing.mypy_plugin" + +[tool.pylint."MESSAGES CONTROL"] +good-names = "i,j,k,_,x,y,X,Y" +signature-mutators = "hydra.main.main" + +[tool.pylint."TYPECHECK"] +generated-members = "numpy.*, torch.*, tensorflow.*" + +[[tool.mypy.overrides]] +module = ["importlib.metadata.*", "importlib_metadata.*"] +follow_imports = "skip" +follow_imports_for_stubs = true +disallow_untyped_calls = false + +[[tool.mypy.overrides]] +module = "torch.*" +follow_imports = "skip" +follow_imports_for_stubs = true + +[tool.docformatter] +wrap-summaries = 88 +wrap-descriptions = 88 + +[tool.ruff] +target-version = "py38" +line-length = 88 +select = ["D", "E", "F", "W", "B", "ISC", "C4"] +fixable = ["D", "E", "F", "W", "B", "ISC", "C4"] +ignore = ["B024", "B027"] +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".hg", + ".mypy_cache", + ".nox", + ".pants.d", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "venv", + "proto", ] -[tool.hatch.build.targets.wheel] -packages = ["."] +[tool.ruff.pydocstyle] +convention = "numpy" [tool.flwr.app] publisher = [ From ae7816382432c603f4d952773a14a5f77a1ab952 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sun, 20 Oct 2024 12:00:33 -0400 Subject: [PATCH 06/99] error fix --- baselines/fedht/pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/baselines/fedht/pyproject.toml b/baselines/fedht/pyproject.toml index 71732bbae55a..2a6578d668d0 100644 --- a/baselines/fedht/pyproject.toml +++ b/baselines/fedht/pyproject.toml @@ -23,7 +23,6 @@ hydra-core = "1.3.2", torch = "2.4.1", torchvision = ">0.19.1", numpy = ">1.26.4" -] [tool.poetry.dev-dependencies] isort = "==5.13.2" From ad6e5fbe840150d7436c5c427bd0242da9a1eb8f Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sun, 20 Oct 2024 12:18:30 -0400 Subject: [PATCH 07/99] error fix --- baselines/fedht/pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/baselines/fedht/pyproject.toml b/baselines/fedht/pyproject.toml index 2a6578d668d0..cc8cb0754c67 100644 --- a/baselines/fedht/pyproject.toml +++ b/baselines/fedht/pyproject.toml @@ -16,8 +16,8 @@ documentation = "https://flower.ai" [tool.poetry.dependencies] python = ">=3.10.0, <3.11.0" flwr = { extras = ["simulation"], version = "1.9.0" } -flwr-datasets = { extras = ["vision"], version = ">=0.3.0" }, -scikit-learn>=1.1.1", +flwr-datasets = { extras = ["vision"], version = "1.0.0" }, +scikit-learn =">=1.1.1", matplotlib = "3.9.2", hydra-core = "1.3.2", torch = "2.4.1", From c06afaf4344508bd5da0c3e480beac6c17faaf39 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sun, 20 Oct 2024 12:19:39 -0400 Subject: [PATCH 08/99] error fix --- baselines/fedht/pyproject.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/baselines/fedht/pyproject.toml b/baselines/fedht/pyproject.toml index cc8cb0754c67..464591a7f85e 100644 --- a/baselines/fedht/pyproject.toml +++ b/baselines/fedht/pyproject.toml @@ -16,12 +16,12 @@ documentation = "https://flower.ai" [tool.poetry.dependencies] python = ">=3.10.0, <3.11.0" flwr = { extras = ["simulation"], version = "1.9.0" } -flwr-datasets = { extras = ["vision"], version = "1.0.0" }, -scikit-learn =">=1.1.1", -matplotlib = "3.9.2", -hydra-core = "1.3.2", -torch = "2.4.1", -torchvision = ">0.19.1", +flwr-datasets = { extras = ["vision"], version = "1.0.0" } +scikit-learn =">=1.1.1" +matplotlib = "3.9.2" +hydra-core = "1.3.2" +torch = "2.4.1" +torchvision = ">0.19.1" numpy = ">1.26.4" [tool.poetry.dev-dependencies] From df7ec0fe8e90603279d7a917f8e54e1903398c6c Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sun, 20 Oct 2024 12:25:39 -0400 Subject: [PATCH 09/99] fix numpy dependency --- baselines/fedht/pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/baselines/fedht/pyproject.toml b/baselines/fedht/pyproject.toml index 464591a7f85e..3d8b23c3da49 100644 --- a/baselines/fedht/pyproject.toml +++ b/baselines/fedht/pyproject.toml @@ -15,14 +15,14 @@ documentation = "https://flower.ai" [tool.poetry.dependencies] python = ">=3.10.0, <3.11.0" -flwr = { extras = ["simulation"], version = "1.9.0" } +flwr = { extras = ["simulation"], version = "1.11.0" } flwr-datasets = { extras = ["vision"], version = "1.0.0" } scikit-learn =">=1.1.1" matplotlib = "3.9.2" hydra-core = "1.3.2" torch = "2.4.1" torchvision = ">0.19.1" -numpy = ">1.26.4" +numpy = ">=1.21.0, <2.0.0" [tool.poetry.dev-dependencies] isort = "==5.13.2" From b9ab7bf9035f53911b75353d200f7a7abd25d5b6 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sun, 20 Oct 2024 12:34:13 -0400 Subject: [PATCH 10/99] flwr datasets error --- baselines/fedht/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/baselines/fedht/pyproject.toml b/baselines/fedht/pyproject.toml index 3d8b23c3da49..7989bd84ea13 100644 --- a/baselines/fedht/pyproject.toml +++ b/baselines/fedht/pyproject.toml @@ -16,7 +16,7 @@ documentation = "https://flower.ai" [tool.poetry.dependencies] python = ">=3.10.0, <3.11.0" flwr = { extras = ["simulation"], version = "1.11.0" } -flwr-datasets = { extras = ["vision"], version = "1.0.0" } +flwr-datasets = { extras = ["vision"], version = "0.3.0" } scikit-learn =">=1.1.1" matplotlib = "3.9.2" hydra-core = "1.3.2" From 9b69e14002720c1f5f363d043267932a6af7ef6f Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sun, 20 Oct 2024 12:48:43 -0400 Subject: [PATCH 11/99] torchvision install fix --- baselines/fedht/pyproject.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/baselines/fedht/pyproject.toml b/baselines/fedht/pyproject.toml index 7989bd84ea13..60e747687a01 100644 --- a/baselines/fedht/pyproject.toml +++ b/baselines/fedht/pyproject.toml @@ -15,12 +15,12 @@ documentation = "https://flower.ai" [tool.poetry.dependencies] python = ">=3.10.0, <3.11.0" -flwr = { extras = ["simulation"], version = "1.11.0" } -flwr-datasets = { extras = ["vision"], version = "0.3.0" } +flwr = { extras = ["simulation"], version = ">1.11.0" } +flwr-datasets = { extras = ["vision"], version = ">0.3.0" } scikit-learn =">=1.1.1" -matplotlib = "3.9.2" +matplotlib = ">3.9.2" hydra-core = "1.3.2" -torch = "2.4.1" +torch = ">2.4.1" torchvision = ">0.19.1" numpy = ">=1.21.0, <2.0.0" From fd525cec88147eeacbe9a64c77d04d616e437331 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sun, 20 Oct 2024 12:54:45 -0400 Subject: [PATCH 12/99] dependencies --- baselines/fedht/pyproject.toml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/baselines/fedht/pyproject.toml b/baselines/fedht/pyproject.toml index 60e747687a01..0f1cbef7cef4 100644 --- a/baselines/fedht/pyproject.toml +++ b/baselines/fedht/pyproject.toml @@ -15,13 +15,13 @@ documentation = "https://flower.ai" [tool.poetry.dependencies] python = ">=3.10.0, <3.11.0" -flwr = { extras = ["simulation"], version = ">1.11.0" } -flwr-datasets = { extras = ["vision"], version = ">0.3.0" } +flwr = { extras = ["simulation"], version = ">=1.11.0" } +flwr-datasets = { extras = ["vision"], version = ">=0.3.0" } scikit-learn =">=1.1.1" -matplotlib = ">3.9.2" +matplotlib = ">=3.9.2" hydra-core = "1.3.2" -torch = ">2.4.1" -torchvision = ">0.19.1" +torch = ">=2.4.1" +torchvision = ">=0.19.1" numpy = ">=1.21.0, <2.0.0" [tool.poetry.dev-dependencies] From c2315cfe1ff03f8dabfe935dff6280eb930e6bee Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sun, 20 Oct 2024 12:59:41 -0400 Subject: [PATCH 13/99] deleting gen_sim_data --- baselines/fedht/fedht/gen_sim_data.py | 35 --------------------------- 1 file changed, 35 deletions(-) delete mode 100644 baselines/fedht/fedht/gen_sim_data.py diff --git a/baselines/fedht/fedht/gen_sim_data.py b/baselines/fedht/fedht/gen_sim_data.py deleted file mode 100644 index e07954568f21..000000000000 --- a/baselines/fedht/fedht/gen_sim_data.py +++ /dev/null @@ -1,35 +0,0 @@ -import numpy as np - -def sim_data(ni: int, num_clients: int, num_features: int, alpha=1, beta=1): - - #generate client-based model coefs - u = np.random.normal(0, alpha, num_clients) - x = np.zeros((num_features,num_clients)) - x[0:99,:] = np.random.multivariate_normal(u, np.diag(np.ones(num_clients)), 99) - - #generate observations - ivec = np.arange(1,num_features+1) - vari = np.diag(1/(ivec**1.2)) - - B = np.random.normal(0, beta, num_features) - v = np.random.multivariate_normal(B, np.diag(np.ones(num_features)), num_clients) - - error = np.random.multivariate_normal(u,np.diag(np.ones(num_clients)), ni) - z = np.zeros((num_clients, ni, num_features)) - y = np.zeros((ni, num_clients)) - - # (num_clients, ni, num_feaures) - for i in range(z.shape[0]): - z[i,:,:] = np.random.multivariate_normal(v[i], vari, ni) - hold = np.matmul(z[i,:,:],x[:,i]) + error[:,i] - y[:,i] = np.exp(hold) / (1+np.exp(hold)) - - for j in range(num_clients): - top_indices = np.argpartition(y[:,j], -100)[-100:] - mask = np.zeros(y[:,j].shape, dtype=bool) - mask[top_indices] = True - y[mask,j] = 1 - y[~mask,j] = 0 - - #might need to adjust; vague data generating process - return z, y From 289e39a024b1d40afbce417e3106635b2dd284b2 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sun, 20 Oct 2024 13:03:52 -0400 Subject: [PATCH 14/99] isort changes --- baselines/fedht/fedht/aggregate.py | 2 ++ baselines/fedht/fedht/client.py | 14 ++++++++------ baselines/fedht/fedht/fedht.py | 10 +++++++--- baselines/fedht/fedht/main.py | 23 +++++++++++++---------- baselines/fedht/fedht/model.py | 4 ++-- baselines/fedht/fedht/server.py | 5 ++++- baselines/fedht/fedht/utils.py | 4 +++- 7 files changed, 39 insertions(+), 23 deletions(-) diff --git a/baselines/fedht/fedht/aggregate.py b/baselines/fedht/fedht/aggregate.py index 75bd92054fb4..c2c6116629f4 100644 --- a/baselines/fedht/fedht/aggregate.py +++ b/baselines/fedht/fedht/aggregate.py @@ -17,10 +17,12 @@ from functools import reduce from typing import Any, Callable, List, Tuple + import numpy as np from flwr.common import FitRes, NDArray, NDArrays, parameters_to_ndarrays from flwr.server.client_proxy import ClientProxy + def aggregate(results: List[Tuple[NDArrays, int]]) -> NDArrays: """Compute weighted average.""" # Calculate the total number of examples used during training diff --git a/baselines/fedht/fedht/client.py b/baselines/fedht/fedht/client.py index bfab727815d2..9afd26bdd823 100644 --- a/baselines/fedht/fedht/client.py +++ b/baselines/fedht/fedht/client.py @@ -1,13 +1,15 @@ -from fedht.model import train, test -from flwr.common import Context -from torch.utils.data import DataLoader from collections import OrderedDict +from typing import cast + import torch -from flwr.client import Client, NumPyClient +from flwr.client import Client, NumPyClient from flwr.common import Context -from typing import cast -from fedht.utils import MyDataset from omegaconf import DictConfig +from torch.utils.data import DataLoader + +from fedht.model import test, train +from fedht.utils import MyDataset + # SimII client class SimIIClient(NumPyClient): diff --git a/baselines/fedht/fedht/fedht.py b/baselines/fedht/fedht/fedht.py index c75be5275e9f..e87b802fb074 100644 --- a/baselines/fedht/fedht/fedht.py +++ b/baselines/fedht/fedht/fedht.py @@ -18,8 +18,8 @@ from logging import WARNING from typing import Callable, Dict, List, Optional, Tuple, Union -import numpy as np +import numpy as np from flwr.common import ( EvaluateIns, EvaluateRes, @@ -35,11 +35,15 @@ from flwr.common.logger import log from flwr.server.client_manager import ClientManager from flwr.server.client_proxy import ClientProxy +from flwr.server.strategy.strategy import Strategy # from flwr.server.strategy.aggregate import aggregate, aggregate_inplace, weighted_loss_avg #from flwr.server.strategy.aggregate import aggregate_inplace, weighted_loss_avg, aggregate_hardthreshold -from fedht.aggregate import aggregate_inplace, weighted_loss_avg, aggregate_hardthreshold -from flwr.server.strategy.strategy import Strategy +from fedht.aggregate import ( + aggregate_hardthreshold, + aggregate_inplace, + weighted_loss_avg, +) WARNING_MIN_AVAILABLE_CLIENTS_TOO_LOW = """ Setting `min_available_clients` lower than `min_fit_clients` or diff --git a/baselines/fedht/fedht/main.py b/baselines/fedht/fedht/main.py index 060fad7de129..9329e57d4c39 100644 --- a/baselines/fedht/fedht/main.py +++ b/baselines/fedht/fedht/main.py @@ -7,21 +7,24 @@ Flower, Python, and PyTorch resources. """ -from fedht.utils import sim_data, MyDataset -import flwr as fl -from fedht.client import generate_client_fn_simII, generate_client_fn_mnist -from fedht.server import get_evaluate_fn, fit_round -from fedht.fedht import FedHT -from fedht.model import LogisticRegression -from flwr_datasets import FederatedDataset -from flwr_datasets.partitioner import PathologicalPartitioner import pickle import random -from torch.utils.data import DataLoader + +import flwr as fl +import hydra import numpy as np from flwr.common import NDArrays, ndarrays_to_parameters -import hydra +from flwr_datasets import FederatedDataset +from flwr_datasets.partitioner import PathologicalPartitioner from omegaconf import DictConfig +from torch.utils.data import DataLoader + +from fedht.client import generate_client_fn_mnist, generate_client_fn_simII +from fedht.fedht import FedHT +from fedht.model import LogisticRegression +from fedht.server import fit_round, get_evaluate_fn +from fedht.utils import MyDataset, sim_data + @hydra.main(config_path="conf", config_name="base_mnist", version_base=None) def main(cfg: DictConfig): diff --git a/baselines/fedht/fedht/model.py b/baselines/fedht/fedht/model.py index 2e016f96e4d2..03075d797cca 100644 --- a/baselines/fedht/fedht/model.py +++ b/baselines/fedht/fedht/model.py @@ -1,9 +1,9 @@ import torch import torch.nn as nn -from torch.utils.data import DataLoader -import torch.optim as optim import torch.optim as optim from omegaconf import DictConfig +from torch.utils.data import DataLoader + # model code initially pulled from fedprox baseline # generates multinomial logistic regression model via torch diff --git a/baselines/fedht/fedht/server.py b/baselines/fedht/fedht/server.py index f49dae942e2e..be69c090038f 100644 --- a/baselines/fedht/fedht/server.py +++ b/baselines/fedht/fedht/server.py @@ -1,8 +1,11 @@ from collections import OrderedDict -from fedht.model import test from typing import Dict + import torch +from fedht.model import test + + # send fit round for history def fit_round(server_round: int) -> Dict: """Send round number to client.""" diff --git a/baselines/fedht/fedht/utils.py b/baselines/fedht/fedht/utils.py index 0040b18dfd11..8ff6b5d97cea 100644 --- a/baselines/fedht/fedht/utils.py +++ b/baselines/fedht/fedht/utils.py @@ -1,5 +1,6 @@ -from torch.utils.data import Dataset import numpy as np +from torch.utils.data import Dataset + class MyDataset(Dataset): def __init__(self, features, labels): @@ -33,6 +34,7 @@ def partition_data(data, num_partitions): import numpy as np + def sim_data(ni: int, num_clients: int, num_features: int, alpha=1, beta=1): #generate client-based model coefs From ae7ba57072b20ebb913e8dae1c119af67f44c832 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sun, 20 Oct 2024 13:11:43 -0400 Subject: [PATCH 15/99] fixing init --- baselines/fedht/fedht/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/baselines/fedht/fedht/__init__.py b/baselines/fedht/fedht/__init__.py index a9b0f112d5d4..ab3ac5cfbfde 100644 --- a/baselines/fedht/fedht/__init__.py +++ b/baselines/fedht/fedht/__init__.py @@ -15,6 +15,7 @@ """Contains the strategy abstraction and different implementations.""" from flwr.server.strategy.fedavg import FedAvg as FedAvg + from .fedht import FedHT as FedHT __all__ = [ From 45cea9dc4a196271f19c9f59db4b7ef762bde25b Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sun, 20 Oct 2024 13:36:07 -0400 Subject: [PATCH 16/99] black reformat --- baselines/fedht/fedht/__init__.py | 5 +- baselines/fedht/fedht/client.py | 103 +++++++++++++++------------ baselines/fedht/fedht/fedht.py | 8 ++- baselines/fedht/fedht/main.py | 111 +++++++++++++++++------------- baselines/fedht/fedht/model.py | 32 +++++---- baselines/fedht/fedht/server.py | 6 +- baselines/fedht/fedht/utils.py | 58 +++++++++------- 7 files changed, 180 insertions(+), 143 deletions(-) diff --git a/baselines/fedht/fedht/__init__.py b/baselines/fedht/fedht/__init__.py index ab3ac5cfbfde..a76f09289842 100644 --- a/baselines/fedht/fedht/__init__.py +++ b/baselines/fedht/fedht/__init__.py @@ -18,7 +18,4 @@ from .fedht import FedHT as FedHT -__all__ = [ - "FedAvg", - "FedHT" -] +__all__ = ["FedAvg", "FedHT"] diff --git a/baselines/fedht/fedht/client.py b/baselines/fedht/fedht/client.py index 9afd26bdd823..d1c18a2b0717 100644 --- a/baselines/fedht/fedht/client.py +++ b/baselines/fedht/fedht/client.py @@ -13,14 +13,16 @@ # SimII client class SimIIClient(NumPyClient): - def __init__(self, - trainloader, - testloader, - model, - num_obs, - num_features, - num_classes, - cfg: DictConfig) -> None: + def __init__( + self, + trainloader, + testloader, + model, + num_obs, + num_features, + num_classes, + cfg: DictConfig, + ) -> None: self.trainloader = trainloader self.testloader = testloader @@ -33,7 +35,7 @@ def __init__(self, # get parameters from existing model def get_parameters(self, config): return [val.cpu().numpy() for _, val in self.model.state_dict().items()] - + def fit(self, parameters, config): # set model parameters @@ -44,13 +46,13 @@ def fit(self, parameters, config): # train model self.model.train() - #training for local epochs defined by config + # training for local epochs defined by config train(self.model, self.trainloader, self.cfg) return self.get_parameters(self.model), self.num_obs, {} - + def evaluate(self, parameters, config): - + # set model parameters params_dict = zip(self.model.state_dict().keys(), parameters) state_dict = OrderedDict({k: torch.Tensor(v) for k, v in params_dict}) @@ -60,13 +62,12 @@ def evaluate(self, parameters, config): loss, accuracy = test(self.model, self.trainloader) return loss, self.num_obs, {"accuracy": accuracy} - + + # client fn for input into simulation -def generate_client_fn_simII(dataset, - num_features, - num_classes, - model, - cfg: DictConfig): +def generate_client_fn_simII( + dataset, num_features, num_classes, model, cfg: DictConfig +): # def client_fn(cid: int): def client_fn(context: Context) -> Client: @@ -77,24 +78,31 @@ def client_fn(context: Context) -> Client: # Load the partition data X_train, y_train = dataset num_obs = X_train.shape[1] - test_dataset = train_dataset = MyDataset(X_train[int(partition_id),:,:], y_train[:,int(partition_id)]) + test_dataset = train_dataset = MyDataset( + X_train[int(partition_id), :, :], y_train[:, int(partition_id)] + ) trainloader = DataLoader(train_dataset, batch_size=cfg.batch_size, shuffle=True) testloader = DataLoader(test_dataset, batch_size=cfg.batch_size, shuffle=True) - return SimIIClient(trainloader, testloader, model, num_obs, num_features, num_classes, cfg).to_client() - + return SimIIClient( + trainloader, testloader, model, num_obs, num_features, num_classes, cfg + ).to_client() + return client_fn + # MNIST client class MnistClient(NumPyClient): - def __init__(self, - trainloader, - testloader, - model, - num_obs, - num_features, - num_classes, - cfg: DictConfig) -> None: + def __init__( + self, + trainloader, + testloader, + model, + num_obs, + num_features, + num_classes, + cfg: DictConfig, + ) -> None: self.trainloader = trainloader self.testloader = testloader @@ -107,7 +115,7 @@ def __init__(self, # get parameters from existing model def get_parameters(self, config): return [val.cpu().numpy() for _, val in self.model.state_dict().items()] - + def fit(self, parameters, config): # set model parameters @@ -118,13 +126,13 @@ def fit(self, parameters, config): # train model self.model.train() - #training for local epochs defined by config + # training for local epochs defined by config train(self.model, self.trainloader, self.cfg) return self.get_parameters(self.model), self.num_obs, {} - - def evaluate(self, parameters, config): - + + def evaluate(self, parameters, config): + # set model parameters params_dict = zip(self.model.state_dict().keys(), parameters) state_dict = OrderedDict({k: torch.Tensor(v) for k, v in params_dict}) @@ -134,13 +142,12 @@ def evaluate(self, parameters, config): loss, accuracy = test(self.model, self.trainloader) return loss, self.num_obs, {"accuracy": accuracy} - + + # client fn for input into simulation -def generate_client_fn_mnist(dataset, - num_features, - num_classes, - model, - cfg: DictConfig): +def generate_client_fn_mnist( + dataset, num_features, num_classes, model, cfg: DictConfig +): # def client_fn(cid: int): def client_fn(context: Context) -> Client: @@ -149,12 +156,18 @@ def client_fn(context: Context) -> Client: partition_id = cast(int, context.node_config["partition-id"]) # Load the partition data - train_dataset = dataset.load_partition(int(partition_id), "train").with_format("numpy") + train_dataset = dataset.load_partition(int(partition_id), "train").with_format( + "numpy" + ) num_obs = train_dataset.num_rows - test_dataset = dataset.load_partition(int(partition_id), "train").with_format("numpy") + test_dataset = dataset.load_partition(int(partition_id), "train").with_format( + "numpy" + ) trainloader = DataLoader(train_dataset, batch_size=cfg.batch_size, shuffle=True) testloader = DataLoader(test_dataset, batch_size=cfg.batch_size, shuffle=True) - return MnistClient(trainloader, testloader, model, num_obs, num_features, num_classes, cfg).to_client() - - return client_fn \ No newline at end of file + return MnistClient( + trainloader, testloader, model, num_obs, num_features, num_classes, cfg + ).to_client() + + return client_fn diff --git a/baselines/fedht/fedht/fedht.py b/baselines/fedht/fedht/fedht.py index e87b802fb074..cc174bfda2d4 100644 --- a/baselines/fedht/fedht/fedht.py +++ b/baselines/fedht/fedht/fedht.py @@ -38,7 +38,7 @@ from flwr.server.strategy.strategy import Strategy # from flwr.server.strategy.aggregate import aggregate, aggregate_inplace, weighted_loss_avg -#from flwr.server.strategy.aggregate import aggregate_inplace, weighted_loss_avg, aggregate_hardthreshold +# from flwr.server.strategy.aggregate import aggregate_inplace, weighted_loss_avg, aggregate_hardthreshold from fedht.aggregate import ( aggregate_hardthreshold, aggregate_inplace, @@ -174,7 +174,7 @@ def evaluate( if self.evaluate_fn is None: # No evaluation function provided return None - parameters_ndarrays = parameters_to_ndarrays(parameters) + parameters_ndarrays = parameters_to_ndarrays(parameters) eval_res = self.evaluate_fn(server_round, parameters_ndarrays, {}) if eval_res is None: return None @@ -248,7 +248,9 @@ def aggregate_fit( ] # use hardthresholding - aggregated_ndarrays = aggregate_hardthreshold(weights_results, self.num_keep, self.iterht) + aggregated_ndarrays = aggregate_hardthreshold( + weights_results, self.num_keep, self.iterht + ) parameters_aggregated = ndarrays_to_parameters(aggregated_ndarrays) # Aggregate custom metrics if aggregation fn was provided diff --git a/baselines/fedht/fedht/main.py b/baselines/fedht/fedht/main.py index 9329e57d4c39..e4420bd538cc 100644 --- a/baselines/fedht/fedht/main.py +++ b/baselines/fedht/fedht/main.py @@ -28,18 +28,18 @@ @hydra.main(config_path="conf", config_name="base_mnist", version_base=None) def main(cfg: DictConfig): - + # set seed random.seed(2024) - if cfg.data == 'mnist': - #set up mnist data + if cfg.data == "mnist": + # set up mnist data # set partitioner partitioner = PathologicalPartitioner( - num_partitions=cfg.num_clients, - partition_by="label", + num_partitions=cfg.num_clients, + partition_by="label", num_classes_per_partition=2, - class_assignment_mode = "first-deterministic" + class_assignment_mode="first-deterministic", ) # load MNIST data @@ -53,14 +53,16 @@ def main(cfg: DictConfig): model = LogisticRegression(num_features, num_classes) # set client function - client_fn = generate_client_fn_mnist(dataset, - num_features=num_features, - num_classes=num_classes, - model=model, - cfg=cfg) - + client_fn = generate_client_fn_mnist( + dataset, + num_features=num_features, + num_classes=num_classes, + model=model, + cfg=cfg, + ) + elif cfg.data == "simII": - + # simulate data from Simulation II in Tong et al num_obs = 1000 num_clients = cfg.num_clients @@ -69,57 +71,60 @@ def main(cfg: DictConfig): dataset = sim_data(num_obs, num_clients, num_features, 1, 1) X_test, y_test = sim_data(num_obs, 1, num_features, 1, 1) - test_dataset = MyDataset(X_test[0,:,:], y_test[:,0]) + test_dataset = MyDataset(X_test[0, :, :], y_test[:, 0]) testloader = DataLoader(test_dataset, batch_size=cfg.batch_size, shuffle=False) # define model model = LogisticRegression(num_features, num_classes) # set client function - client_fn = generate_client_fn_simII(dataset, - num_features=num_features, - num_classes=num_classes, - model=model, - cfg=cfg) - - #initialize global model to all zeros - weights = np.zeros((num_classes,num_features)) + client_fn = generate_client_fn_simII( + dataset, + num_features=num_features, + num_classes=num_classes, + model=model, + cfg=cfg, + ) + + # initialize global model to all zeros + weights = np.zeros((num_classes, num_features)) bias = np.zeros(num_classes) init_params: NDArrays = (weights, bias) init_params = ndarrays_to_parameters(init_params) # define strategy: fedht - strategy_fedht = FedHT(min_available_clients=cfg.strategy.min_available_clients, - num_keep=cfg.num_keep, - evaluate_fn=get_evaluate_fn(testloader, model), - on_fit_config_fn=fit_round, - iterht=cfg.iterht, - initial_parameters=init_params - ) + strategy_fedht = FedHT( + min_available_clients=cfg.strategy.min_available_clients, + num_keep=cfg.num_keep, + evaluate_fn=get_evaluate_fn(testloader, model), + on_fit_config_fn=fit_round, + iterht=cfg.iterht, + initial_parameters=init_params, + ) # define strategy: fedavg - strategy_fedavg = fl.server.strategy.FedAvg(min_available_clients=cfg.strategy.min_available_clients, - evaluate_fn=get_evaluate_fn(testloader, model), - on_fit_config_fn=fit_round, - initial_parameters=init_params) - + strategy_fedavg = fl.server.strategy.FedAvg( + min_available_clients=cfg.strategy.min_available_clients, + evaluate_fn=get_evaluate_fn(testloader, model), + on_fit_config_fn=fit_round, + initial_parameters=init_params, + ) + if cfg.agg == "fedht": - strategy=strategy_fedht + strategy = strategy_fedht elif cfg.agg == "fedavg": - strategy=strategy_fedavg + strategy = strategy_fedavg else: - print("Must select either fedht or fedavg for the aggregation strategy.") + print("Must select either fedht or fedavg for the aggregation strategy.") # # start simulation random.seed(2025) hist_mnist = fl.simulation.start_simulation( - client_fn=client_fn, - num_clients=cfg.num_clients, - config=fl.server.ServerConfig(num_rounds=cfg.num_rounds), - strategy=strategy, - client_resources={ - "num_cpus": cfg.client_resources.num_cpus - } + client_fn=client_fn, + num_clients=cfg.num_clients, + config=fl.server.ServerConfig(num_rounds=cfg.num_rounds), + strategy=strategy, + client_resources={"num_cpus": cfg.client_resources.num_cpus}, ) if cfg.iterht == True: @@ -127,10 +132,22 @@ def main(cfg: DictConfig): else: iterstr = "" - filename = "simII_" + cfg.agg + iterstr + "_local" + str(cfg.num_local_epochs) + "_lr" + str(cfg.learning_rate) + "_numkeep" + str(cfg.num_keep) + ".pkl" + filename = ( + "simII_" + + cfg.agg + + iterstr + + "_local" + + str(cfg.num_local_epochs) + + "_lr" + + str(cfg.learning_rate) + + "_numkeep" + + str(cfg.num_keep) + + ".pkl" + ) - with open(filename, 'wb') as file: + with open(filename, "wb") as file: pickle.dump(hist_mnist, file) + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/baselines/fedht/fedht/model.py b/baselines/fedht/fedht/model.py index 03075d797cca..ca7d3e162774 100644 --- a/baselines/fedht/fedht/model.py +++ b/baselines/fedht/fedht/model.py @@ -18,17 +18,18 @@ def __init__(self, num_features, num_classes: int) -> None: def forward(self, input_tensor: torch.Tensor) -> torch.Tensor: # forward pass; sigmoid transform included in CBELoss criterion output_tensor = self.linear(torch.flatten(input_tensor, 1)) - return output_tensor + return output_tensor + # define train function that will be called by each client to train the model -def train(model, - trainloader: DataLoader, - cfg: DictConfig) -> None: - +def train(model, trainloader: DataLoader, cfg: DictConfig) -> None: + criterion = nn.CrossEntropyLoss() - optimizer = optim.SGD(model.parameters(), lr=cfg.learning_rate, weight_decay=cfg.weight_decay) - - # train + optimizer = optim.SGD( + model.parameters(), lr=cfg.learning_rate, weight_decay=cfg.weight_decay + ) + + # train for epoch in range(cfg.num_local_epochs): for i, data in enumerate(trainloader): @@ -39,25 +40,26 @@ def train(model, # Forward pass model.train() - + loss = criterion(model(inputs.float()), labels.long()) # Backward pass and optimization loss.backward() optimizer.step() + def test(model, testloader: DataLoader) -> None: - + criterion = nn.CrossEntropyLoss() - #initialize + # initialize correct, total, loss = 0, 0, 0.0 # put into evlauate mode model.eval() with torch.no_grad(): for i, data in enumerate(testloader): - + images, labels = data["image"], data["label"] outputs = model(images.float()) @@ -65,10 +67,10 @@ def test(model, testloader: DataLoader) -> None: loss += criterion(outputs, labels.long()).item() _, predicted = torch.max(outputs.data, 1) correct += (predicted == labels).sum().item() - + if len(testloader.dataset) == 0: raise ValueError("Testloader can't be 0, exiting...") loss /= len(testloader) - accuracy = correct / total - return loss, accuracy \ No newline at end of file + accuracy = correct / total + return loss, accuracy diff --git a/baselines/fedht/fedht/server.py b/baselines/fedht/fedht/server.py index be69c090038f..85cf7e9cbf8d 100644 --- a/baselines/fedht/fedht/server.py +++ b/baselines/fedht/fedht/server.py @@ -11,8 +11,8 @@ def fit_round(server_round: int) -> Dict: """Send round number to client.""" return {"server_round": server_round} -def get_evaluate_fn(testloader, - model): + +def get_evaluate_fn(testloader, model): # global evaluation def evaluate(server_round, parameters, config): # type: ignore @@ -25,4 +25,4 @@ def evaluate(server_round, parameters, config): # type: ignore loss, accuracy = test(model, testloader) return loss, {"accuracy": accuracy} - return evaluate \ No newline at end of file + return evaluate diff --git a/baselines/fedht/fedht/utils.py b/baselines/fedht/fedht/utils.py index 8ff6b5d97cea..3fff9ba75d29 100644 --- a/baselines/fedht/fedht/utils.py +++ b/baselines/fedht/fedht/utils.py @@ -4,10 +4,7 @@ class MyDataset(Dataset): def __init__(self, features, labels): - self.data = { - "image": features, - "label": labels - } + self.data = {"image": features, "label": labels} def __len__(self): return len(self.data["image"]) @@ -15,56 +12,65 @@ def __len__(self): def __getitem__(self, idx): return {key: value[idx] for key, value in self.data.items()} + def partition_data(data, num_partitions): # Calculate the size of each partition X, y = data partition_size = len(X) // num_partitions # Create partitions - partitionsX = [X[i * partition_size:(i + 1) * partition_size] for i in range(num_partitions)] - partitionsy = [y[i * partition_size:(i + 1) * partition_size] for i in range(num_partitions)] + partitionsX = [ + X[i * partition_size : (i + 1) * partition_size] for i in range(num_partitions) + ] + partitionsy = [ + y[i * partition_size : (i + 1) * partition_size] for i in range(num_partitions) + ] # Handle any remaining items if len(data) % num_partitions != 0: # partitions[-1] = partitions[-1] + data[num_partitions * partition_size:] - partitionsX[-1] = np.vstack((partitionsX[-1], X[num_partitions * partition_size:])) - partitionsy[-1] = np.vstack((partitionsy[-1], y[num_partitions * partition_size:])) - - + partitionsX[-1] = np.vstack( + (partitionsX[-1], X[num_partitions * partition_size :]) + ) + partitionsy[-1] = np.vstack( + (partitionsy[-1], y[num_partitions * partition_size :]) + ) + return partitionsX, partitionsy + import numpy as np def sim_data(ni: int, num_clients: int, num_features: int, alpha=1, beta=1): - - #generate client-based model coefs + + # generate client-based model coefs u = np.random.normal(0, alpha, num_clients) - x = np.zeros((num_features,num_clients)) - x[0:99,:] = np.random.multivariate_normal(u, np.diag(np.ones(num_clients)), 99) + x = np.zeros((num_features, num_clients)) + x[0:99, :] = np.random.multivariate_normal(u, np.diag(np.ones(num_clients)), 99) - #generate observations - ivec = np.arange(1,num_features+1) - vari = np.diag(1/(ivec**1.2)) + # generate observations + ivec = np.arange(1, num_features + 1) + vari = np.diag(1 / (ivec**1.2)) B = np.random.normal(0, beta, num_features) v = np.random.multivariate_normal(B, np.diag(np.ones(num_features)), num_clients) - error = np.random.multivariate_normal(u,np.diag(np.ones(num_clients)), ni) + error = np.random.multivariate_normal(u, np.diag(np.ones(num_clients)), ni) z = np.zeros((num_clients, ni, num_features)) y = np.zeros((ni, num_clients)) # (num_clients, ni, num_feaures) for i in range(z.shape[0]): - z[i,:,:] = np.random.multivariate_normal(v[i], vari, ni) - hold = np.matmul(z[i,:,:],x[:,i]) + error[:,i] - y[:,i] = np.exp(hold) / (1+np.exp(hold)) + z[i, :, :] = np.random.multivariate_normal(v[i], vari, ni) + hold = np.matmul(z[i, :, :], x[:, i]) + error[:, i] + y[:, i] = np.exp(hold) / (1 + np.exp(hold)) for j in range(num_clients): - top_indices = np.argpartition(y[:,j], -100)[-100:] - mask = np.zeros(y[:,j].shape, dtype=bool) + top_indices = np.argpartition(y[:, j], -100)[-100:] + mask = np.zeros(y[:, j].shape, dtype=bool) mask[top_indices] = True - y[mask,j] = 1 - y[~mask,j] = 0 + y[mask, j] = 1 + y[~mask, j] = 0 - #might need to adjust; vague data generating process + # might need to adjust; vague data generating process return z, y From acc767109bb2278a6bc4327bf572708c8b2d08a1 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sun, 20 Oct 2024 13:42:32 -0400 Subject: [PATCH 17/99] static folder --- .../fedht/{fedht => _static}/loss_results_mnist.png | Bin .../fedht/{fedht => _static}/loss_results_simII.png | Bin 2 files changed, 0 insertions(+), 0 deletions(-) rename baselines/fedht/{fedht => _static}/loss_results_mnist.png (100%) rename baselines/fedht/{fedht => _static}/loss_results_simII.png (100%) diff --git a/baselines/fedht/fedht/loss_results_mnist.png b/baselines/fedht/_static/loss_results_mnist.png similarity index 100% rename from baselines/fedht/fedht/loss_results_mnist.png rename to baselines/fedht/_static/loss_results_mnist.png diff --git a/baselines/fedht/fedht/loss_results_simII.png b/baselines/fedht/_static/loss_results_simII.png similarity index 100% rename from baselines/fedht/fedht/loss_results_simII.png rename to baselines/fedht/_static/loss_results_simII.png From 0a080f04d55b7ddd51177c8367dcfb83eb5e74e3 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sun, 20 Oct 2024 13:48:19 -0400 Subject: [PATCH 18/99] agg reformat --- baselines/fedht/fedht/aggregate.py | 63 +++++++++++++++++++----------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/baselines/fedht/fedht/aggregate.py b/baselines/fedht/fedht/aggregate.py index c2c6116629f4..462dd7dade90 100644 --- a/baselines/fedht/fedht/aggregate.py +++ b/baselines/fedht/fedht/aggregate.py @@ -364,57 +364,66 @@ def _aggregate_n_closest_weights( aggregated_weights.append(np.mean(beta_closest_weights, axis=0)) return aggregated_weights + # calls hardthreshold function for each list element in weights_all def hardthreshold_list(weights_all, num_keep: int) -> NDArrays: params = [hardthreshold(each, num_keep) for each in weights_all] return params + # hardthreshold function applied to array def hardthreshold(weights_prime, num_keep: int) -> NDArrays: - + # check for len of array val_len = weights_prime.size - # intercepts not hardthresholded + # intercepts not hardthresholded if val_len > 1: if num_keep > val_len: params = weights_prime - print("The number of parameters kept is greater than the length of the vector. All parameters will be kept.") + print( + "The number of parameters kept is greater than the length of the vector. All parameters will be kept." + ) else: # Compute the magnitudes magnitudes = np.abs(weights_prime) - + # Get the k-th largest value in the vector threshold = np.partition(magnitudes, -num_keep)[-num_keep] - + # Create a new vector where values below the threshold are set to zero params = np.where(magnitudes >= threshold, weights_prime, 0) - else: + else: params = weights_prime return params - + + def aggregate_hardthreshold( - results: List[Tuple[NDArrays, int]], num_keep: int, iterht: bool) -> NDArrays: + results: List[Tuple[NDArrays, int]], num_keep: int, iterht: bool +) -> NDArrays: """ - Applies hard thresholding to keep only the k largest weights in a client-weight vector. Fed-HT (Fed-IterHT) can be + Applies hard thresholding to keep only the k largest weights in a client-weight vector. Fed-HT (Fed-IterHT) can be found at https://arxiv.org/abs/2101.00052 """ if num_keep <= 0: raise ValueError("k must be a positive integer.") - + """Compute weighted average.""" # Calculate the total number of examples used during training num_examples_total = sum(num_examples for (_, num_examples) in results) - - green = '\033[92m' - reset = '\033[0m' + + green = "\033[92m" + reset = "\033[0m" # check for iterht=True; set in cfg if iterht: - print(f"{green}INFO {reset}:\t\tUsing Fed-IterHT for model aggregation with threshold = ", num_keep) + print( + f"{green}INFO {reset}:\t\tUsing Fed-IterHT for model aggregation with threshold = ", + num_keep, + ) # apply across all models within each client @@ -428,23 +437,31 @@ def aggregate_hardthreshold( for j in range(len(results[i][0])): for k in range(len(results[i][0][j])): results[i][0][j][k] = hardthreshold(results[i][0][j][k], num_keep) - + weighted_weights1 = [ - [layer * num_examples for layer in weights] for weights, num_examples in results + [layer * num_examples for layer in weights] + for weights, num_examples in results ] weighted_weights2 = weighted_weights1 - else: - print(f"{green}INFO {reset}:\t\tUsing Fed-HT for model aggregation with threshold = ", num_keep) + else: + print( + f"{green}INFO {reset}:\t\tUsing Fed-HT for model aggregation with threshold = ", + num_keep, + ) weighted_weights1 = [ - [layer * num_examples for layer in weights] for weights, num_examples in results + [layer * num_examples for layer in weights] + for weights, num_examples in results ] weighted_weights2 = weighted_weights1 - - hold = [reduce(np.add, layer_updates) / num_examples_total for layer_updates in zip(*weighted_weights2)] + + hold = [ + reduce(np.add, layer_updates) / num_examples_total + for layer_updates in zip(*weighted_weights2) + ] params = [hardthreshold_list(layer_updates, num_keep) for layer_updates in hold] - + result: NDArrays = params - + return result From b0131a601e425d68fb4dcf69057250a14216acfe Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sun, 20 Oct 2024 15:06:19 -0400 Subject: [PATCH 19/99] docformatter changes --- baselines/fedht/fedht/aggregate.py | 8 +++++--- baselines/fedht/fedht/fedht.py | 9 ++++----- baselines/fedht/fedht/main.py | 3 ++- baselines/fedht/fedht/utils.py | 1 - 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/baselines/fedht/fedht/aggregate.py b/baselines/fedht/fedht/aggregate.py index 462dd7dade90..f011d1208d41 100644 --- a/baselines/fedht/fedht/aggregate.py +++ b/baselines/fedht/fedht/aggregate.py @@ -404,9 +404,11 @@ def hardthreshold(weights_prime, num_keep: int) -> NDArrays: def aggregate_hardthreshold( results: List[Tuple[NDArrays, int]], num_keep: int, iterht: bool ) -> NDArrays: - """ - Applies hard thresholding to keep only the k largest weights in a client-weight vector. Fed-HT (Fed-IterHT) can be - found at https://arxiv.org/abs/2101.00052 + """Applies hard thresholding to keep only the k largest weights in a + client-weight vector. + + Fed-HT (Fed-IterHT) can be found at + https://arxiv.org/abs/2101.00052 """ if num_keep <= 0: raise ValueError("k must be a positive integer.") diff --git a/baselines/fedht/fedht/fedht.py b/baselines/fedht/fedht/fedht.py index cc174bfda2d4..6a549e30c800 100644 --- a/baselines/fedht/fedht/fedht.py +++ b/baselines/fedht/fedht/fedht.py @@ -12,14 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Federated Hardthresholding (FedHT) -""" +"""Federated Hardthresholding (FedHT)""" from logging import WARNING from typing import Callable, Dict, List, Optional, Tuple, Union -import numpy as np from flwr.common import ( EvaluateIns, EvaluateRes, @@ -41,7 +39,6 @@ # from flwr.server.strategy.aggregate import aggregate_inplace, weighted_loss_avg, aggregate_hardthreshold from fedht.aggregate import ( aggregate_hardthreshold, - aggregate_inplace, weighted_loss_avg, ) @@ -150,7 +147,9 @@ def __repr__(self) -> str: return rep def num_fit_clients(self, num_available_clients: int) -> Tuple[int, int]: - """Return the sample size and the required number of available clients.""" + """Return the sample size and the required number of available + clients. + """ num_clients = int(num_available_clients * self.fraction_fit) return max(num_clients, self.min_fit_clients), self.min_available_clients diff --git a/baselines/fedht/fedht/main.py b/baselines/fedht/fedht/main.py index e4420bd538cc..df9eed61a73d 100644 --- a/baselines/fedht/fedht/main.py +++ b/baselines/fedht/fedht/main.py @@ -2,7 +2,8 @@ Author: Chance Johnstone Purpose: main function for fedht baseline Simulation II example from Tong et al 2020 -Notes: +Notes +----- Code included in this baseline generated with the help of numerous Flower, Python, and PyTorch resources. """ diff --git a/baselines/fedht/fedht/utils.py b/baselines/fedht/fedht/utils.py index 3fff9ba75d29..ec52a64cec7d 100644 --- a/baselines/fedht/fedht/utils.py +++ b/baselines/fedht/fedht/utils.py @@ -38,7 +38,6 @@ def partition_data(data, num_partitions): return partitionsX, partitionsy -import numpy as np def sim_data(ni: int, num_clients: int, num_features: int, alpha=1, beta=1): From 5e2a418ff365548b8209efd492d26b93f765cda8 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sun, 20 Oct 2024 15:32:55 -0400 Subject: [PATCH 20/99] formatting --- baselines/fedht/fedht/aggregate.py | 4 ++-- baselines/fedht/fedht/client.py | 14 +++++++++++--- baselines/fedht/fedht/fedht.py | 9 ++------- baselines/fedht/fedht/main.py | 9 ++++++++- baselines/fedht/fedht/server.py | 3 ++- baselines/fedht/fedht/utils.py | 10 +++++++--- 6 files changed, 32 insertions(+), 17 deletions(-) diff --git a/baselines/fedht/fedht/aggregate.py b/baselines/fedht/fedht/aggregate.py index f011d1208d41..0bab09510a31 100644 --- a/baselines/fedht/fedht/aggregate.py +++ b/baselines/fedht/fedht/aggregate.py @@ -404,8 +404,8 @@ def hardthreshold(weights_prime, num_keep: int) -> NDArrays: def aggregate_hardthreshold( results: List[Tuple[NDArrays, int]], num_keep: int, iterht: bool ) -> NDArrays: - """Applies hard thresholding to keep only the k largest weights in a - client-weight vector. + """Applies hard thresholding to keep only the k largest weights in a client-weight + vector. Fed-HT (Fed-IterHT) can be found at https://arxiv.org/abs/2101.00052 diff --git a/baselines/fedht/fedht/client.py b/baselines/fedht/fedht/client.py index d1c18a2b0717..a22c6b7abf81 100644 --- a/baselines/fedht/fedht/client.py +++ b/baselines/fedht/fedht/client.py @@ -23,6 +23,7 @@ def __init__( num_classes, cfg: DictConfig, ) -> None: + """SimII client for simulation II experimentation.""" self.trainloader = trainloader self.testloader = testloader @@ -37,7 +38,7 @@ def get_parameters(self, config): return [val.cpu().numpy() for _, val in self.model.state_dict().items()] def fit(self, parameters, config): - + """Fit model.""" # set model parameters params_dict = zip(self.model.state_dict().keys(), parameters) state_dict = OrderedDict({k: torch.Tensor(v) for k, v in params_dict}) @@ -52,7 +53,7 @@ def fit(self, parameters, config): return self.get_parameters(self.model), self.num_obs, {} def evaluate(self, parameters, config): - + """Evaluate model.""" # set model parameters params_dict = zip(self.model.state_dict().keys(), parameters) state_dict = OrderedDict({k: torch.Tensor(v) for k, v in params_dict}) @@ -68,9 +69,11 @@ def evaluate(self, parameters, config): def generate_client_fn_simII( dataset, num_features, num_classes, model, cfg: DictConfig ): + """Generates client function for simulated FL.""" # def client_fn(cid: int): def client_fn(context: Context) -> Client: + """Define client function for centralized metrics.""" # Get node_config value to fetch partition_id partition_id = cast(int, context.node_config["partition-id"]) @@ -103,6 +106,7 @@ def __init__( num_classes, cfg: DictConfig, ) -> None: + """MNIST client for MNIST experimentation.""" self.trainloader = trainloader self.testloader = testloader @@ -114,10 +118,11 @@ def __init__( # get parameters from existing model def get_parameters(self, config): + """Get parameters.""" return [val.cpu().numpy() for _, val in self.model.state_dict().items()] def fit(self, parameters, config): - + """Fit model.""" # set model parameters params_dict = zip(self.model.state_dict().keys(), parameters) state_dict = OrderedDict({k: torch.Tensor(v) for k, v in params_dict}) @@ -132,6 +137,7 @@ def fit(self, parameters, config): return self.get_parameters(self.model), self.num_obs, {} def evaluate(self, parameters, config): + """Evaluate model.""" # set model parameters params_dict = zip(self.model.state_dict().keys(), parameters) @@ -148,9 +154,11 @@ def evaluate(self, parameters, config): def generate_client_fn_mnist( dataset, num_features, num_classes, model, cfg: DictConfig ): + """Generate client function for simulated FL.""" # def client_fn(cid: int): def client_fn(context: Context) -> Client: + """Define client function for centralized metrics.""" # Get node_config value to fetch partition_id partition_id = cast(int, context.node_config["partition-id"]) diff --git a/baselines/fedht/fedht/fedht.py b/baselines/fedht/fedht/fedht.py index 6a549e30c800..c7cce34b8ec9 100644 --- a/baselines/fedht/fedht/fedht.py +++ b/baselines/fedht/fedht/fedht.py @@ -37,10 +37,7 @@ # from flwr.server.strategy.aggregate import aggregate, aggregate_inplace, weighted_loss_avg # from flwr.server.strategy.aggregate import aggregate_inplace, weighted_loss_avg, aggregate_hardthreshold -from fedht.aggregate import ( - aggregate_hardthreshold, - weighted_loss_avg, -) +from fedht.aggregate import aggregate_hardthreshold, weighted_loss_avg WARNING_MIN_AVAILABLE_CLIENTS_TOO_LOW = """ Setting `min_available_clients` lower than `min_fit_clients` or @@ -147,9 +144,7 @@ def __repr__(self) -> str: return rep def num_fit_clients(self, num_available_clients: int) -> Tuple[int, int]: - """Return the sample size and the required number of available - clients. - """ + """Return the sample size and the required number of available clients.""" num_clients = int(num_available_clients * self.fraction_fit) return max(num_clients, self.min_fit_clients), self.min_available_clients diff --git a/baselines/fedht/fedht/main.py b/baselines/fedht/fedht/main.py index df9eed61a73d..82671def3bbe 100644 --- a/baselines/fedht/fedht/main.py +++ b/baselines/fedht/fedht/main.py @@ -29,6 +29,13 @@ @hydra.main(config_path="conf", config_name="base_mnist", version_base=None) def main(cfg: DictConfig): + """Main file for fedht baseline + + Parameters + ---------- + cfg : DictConfig + Config file for federated baseline; read from fedht/conf + """ # set seed random.seed(2024) @@ -128,7 +135,7 @@ def main(cfg: DictConfig): client_resources={"num_cpus": cfg.client_resources.num_cpus}, ) - if cfg.iterht == True: + if cfg.iterht: iterstr = "iter" else: iterstr = "" diff --git a/baselines/fedht/fedht/server.py b/baselines/fedht/fedht/server.py index 85cf7e9cbf8d..2502652df8d8 100644 --- a/baselines/fedht/fedht/server.py +++ b/baselines/fedht/fedht/server.py @@ -13,10 +13,11 @@ def fit_round(server_round: int) -> Dict: def get_evaluate_fn(testloader, model): + """Get evaluate function for centralized metrics.""" # global evaluation def evaluate(server_round, parameters, config): # type: ignore - + """Define evaluate function for centralized metrics.""" # set model parameters params_dict = zip(model.state_dict().keys(), parameters) state_dict = OrderedDict({k: torch.Tensor(v) for k, v in params_dict}) diff --git a/baselines/fedht/fedht/utils.py b/baselines/fedht/fedht/utils.py index ec52a64cec7d..57aba1bf147d 100644 --- a/baselines/fedht/fedht/utils.py +++ b/baselines/fedht/fedht/utils.py @@ -3,17 +3,23 @@ class MyDataset(Dataset): + """Create an object with Dataset parent class for dataloader.""" + def __init__(self, features, labels): + """Initialize self with data object.""" self.data = {"image": features, "label": labels} def __len__(self): + """Returns length of features in data.""" return len(self.data["image"]) def __getitem__(self, idx): + """Assign key.""" return {key: value[idx] for key, value in self.data.items()} def partition_data(data, num_partitions): + """Partition data into clients.""" # Calculate the size of each partition X, y = data partition_size = len(X) // num_partitions @@ -38,10 +44,8 @@ def partition_data(data, num_partitions): return partitionsX, partitionsy - - def sim_data(ni: int, num_clients: int, num_features: int, alpha=1, beta=1): - + """Simulate data for simII.""" # generate client-based model coefs u = np.random.normal(0, alpha, num_clients) x = np.zeros((num_features, num_clients)) From 48130eb0a997cb3aa051c1c6c443c1bc38cf387c Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sun, 20 Oct 2024 15:53:45 -0400 Subject: [PATCH 21/99] formatting --- baselines/fedht/fedht/aggregate.py | 13 ++++++------- baselines/fedht/fedht/client.py | 14 ++++++++------ baselines/fedht/fedht/fedht.py | 7 +++---- baselines/fedht/fedht/main.py | 17 ++++------------- baselines/fedht/fedht/model.py | 15 ++++++++++----- baselines/fedht/fedht/server.py | 2 ++ baselines/fedht/fedht/utils.py | 4 +++- 7 files changed, 36 insertions(+), 36 deletions(-) diff --git a/baselines/fedht/fedht/aggregate.py b/baselines/fedht/fedht/aggregate.py index 0bab09510a31..ed7cb17551f7 100644 --- a/baselines/fedht/fedht/aggregate.py +++ b/baselines/fedht/fedht/aggregate.py @@ -367,14 +367,14 @@ def _aggregate_n_closest_weights( # calls hardthreshold function for each list element in weights_all def hardthreshold_list(weights_all, num_keep: int) -> NDArrays: - + """Call hardthreshold.""" params = [hardthreshold(each, num_keep) for each in weights_all] return params # hardthreshold function applied to array def hardthreshold(weights_prime, num_keep: int) -> NDArrays: - + """Perform hardthresholding on single array.""" # check for len of array val_len = weights_prime.size @@ -383,7 +383,7 @@ def hardthreshold(weights_prime, num_keep: int) -> NDArrays: if num_keep > val_len: params = weights_prime print( - "The number of parameters kept is greater than the length of the vector. All parameters will be kept." + "num_keep parameter greater than length of vector. All parameters kept." ) else: # Compute the magnitudes @@ -404,8 +404,7 @@ def hardthreshold(weights_prime, num_keep: int) -> NDArrays: def aggregate_hardthreshold( results: List[Tuple[NDArrays, int]], num_keep: int, iterht: bool ) -> NDArrays: - """Applies hard thresholding to keep only the k largest weights in a client-weight - vector. + """Apply hard thresholding to keep only the k largest weights. Fed-HT (Fed-IterHT) can be found at https://arxiv.org/abs/2101.00052 @@ -423,7 +422,7 @@ def aggregate_hardthreshold( # check for iterht=True; set in cfg if iterht: print( - f"{green}INFO {reset}:\t\tUsing Fed-IterHT for model aggregation with threshold = ", + f"{green}INFO {reset}:\t\tUsing Fed-IterHT with num_keep = ", num_keep, ) @@ -448,7 +447,7 @@ def aggregate_hardthreshold( else: print( - f"{green}INFO {reset}:\t\tUsing Fed-HT for model aggregation with threshold = ", + f"{green}INFO {reset}:\t\tUsing Fed-HT with num_keep = ", num_keep, ) diff --git a/baselines/fedht/fedht/client.py b/baselines/fedht/fedht/client.py index a22c6b7abf81..8f634f2ac1f2 100644 --- a/baselines/fedht/fedht/client.py +++ b/baselines/fedht/fedht/client.py @@ -1,3 +1,5 @@ +"""Generate client for fedht baseline.""" + from collections import OrderedDict from typing import cast @@ -13,6 +15,8 @@ # SimII client class SimIIClient(NumPyClient): + """Define SimIIClient class.""" + def __init__( self, trainloader, @@ -24,7 +28,6 @@ def __init__( cfg: DictConfig, ) -> None: """SimII client for simulation II experimentation.""" - self.trainloader = trainloader self.testloader = testloader self.model = model @@ -35,6 +38,7 @@ def __init__( # get parameters from existing model def get_parameters(self, config): + """Get parameters.""" return [val.cpu().numpy() for _, val in self.model.state_dict().items()] def fit(self, parameters, config): @@ -69,12 +73,11 @@ def evaluate(self, parameters, config): def generate_client_fn_simII( dataset, num_features, num_classes, model, cfg: DictConfig ): - """Generates client function for simulated FL.""" + """Generate client function for simulated FL.""" # def client_fn(cid: int): def client_fn(context: Context) -> Client: """Define client function for centralized metrics.""" - # Get node_config value to fetch partition_id partition_id = cast(int, context.node_config["partition-id"]) @@ -96,6 +99,8 @@ def client_fn(context: Context) -> Client: # MNIST client class MnistClient(NumPyClient): + """Define MnistClient class.""" + def __init__( self, trainloader, @@ -107,7 +112,6 @@ def __init__( cfg: DictConfig, ) -> None: """MNIST client for MNIST experimentation.""" - self.trainloader = trainloader self.testloader = testloader self.model = model @@ -138,7 +142,6 @@ def fit(self, parameters, config): def evaluate(self, parameters, config): """Evaluate model.""" - # set model parameters params_dict = zip(self.model.state_dict().keys(), parameters) state_dict = OrderedDict({k: torch.Tensor(v) for k, v in params_dict}) @@ -159,7 +162,6 @@ def generate_client_fn_mnist( # def client_fn(cid: int): def client_fn(context: Context) -> Client: """Define client function for centralized metrics.""" - # Get node_config value to fetch partition_id partition_id = cast(int, context.node_config["partition-id"]) diff --git a/baselines/fedht/fedht/fedht.py b/baselines/fedht/fedht/fedht.py index c7cce34b8ec9..550a1b869253 100644 --- a/baselines/fedht/fedht/fedht.py +++ b/baselines/fedht/fedht/fedht.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Federated Hardthresholding (FedHT)""" +"""Federated Hardthresholding (FedHT).""" from logging import WARNING @@ -35,8 +35,6 @@ from flwr.server.client_proxy import ClientProxy from flwr.server.strategy.strategy import Strategy -# from flwr.server.strategy.aggregate import aggregate, aggregate_inplace, weighted_loss_avg -# from flwr.server.strategy.aggregate import aggregate_inplace, weighted_loss_avg, aggregate_hardthreshold from fedht.aggregate import aggregate_hardthreshold, weighted_loss_avg WARNING_MIN_AVAILABLE_CLIENTS_TOO_LOW = """ @@ -71,7 +69,8 @@ class FedHT(Strategy): Minimum number of clients used during validation. Defaults to 2. min_available_clients : int, optional Minimum number of total clients in the system. Defaults to 2. - evaluate_fn : Optional[Callable[[int, NDArrays, Dict[str, Scalar]],Optional[Tuple[float, Dict[str, Scalar]]]]] + evaluate_fn : Optional[Callable[[int, NDArrays, Dict[str, Scalar]], + Optional[Tuple[float, Dict[str, Scalar]]]]] Optional function used for validation. Defaults to None. on_fit_config_fn : Callable[[int], Dict[str, Scalar]], optional Function used to configure training. Defaults to None. diff --git a/baselines/fedht/fedht/main.py b/baselines/fedht/fedht/main.py index 82671def3bbe..bb1274529a49 100644 --- a/baselines/fedht/fedht/main.py +++ b/baselines/fedht/fedht/main.py @@ -1,12 +1,4 @@ -""" -Author: Chance Johnstone -Purpose: main function for fedht baseline Simulation II example from Tong et al 2020 - -Notes ------ -Code included in this baseline generated with the help of numerous -Flower, Python, and PyTorch resources. -""" +"""Run main for fedht baseline.""" import pickle import random @@ -29,14 +21,13 @@ @hydra.main(config_path="conf", config_name="base_mnist", version_base=None) def main(cfg: DictConfig): - """Main file for fedht baseline + """Run main file for fedht baseline. Parameters ---------- cfg : DictConfig - Config file for federated baseline; read from fedht/conf + Config file for federated baseline; read from fedht/conf. """ - # set seed random.seed(2024) @@ -53,7 +44,7 @@ def main(cfg: DictConfig): # load MNIST data num_features = 28 * 28 num_classes = 10 - dataset = FederatedDataset(dataset="mnist", partitioners={"train": num_classes}) + dataset = FederatedDataset(dataset="mnist", partitioners={"train": partitioner}) test_dataset = dataset.load_split("test").with_format("numpy") testloader = DataLoader(test_dataset, batch_size=cfg.batch_size, shuffle=False) diff --git a/baselines/fedht/fedht/model.py b/baselines/fedht/fedht/model.py index ca7d3e162774..15a8c92c2e8d 100644 --- a/baselines/fedht/fedht/model.py +++ b/baselines/fedht/fedht/model.py @@ -1,3 +1,5 @@ +"""Define model for fedht baseline.""" + import torch import torch.nn as nn import torch.optim as optim @@ -8,14 +10,17 @@ # model code initially pulled from fedprox baseline # generates multinomial logistic regression model via torch class LogisticRegression(nn.Module): + """Define LogisticRegression class.""" def __init__(self, num_features, num_classes: int) -> None: + """Define model.""" super().__init__() # one single linear layer self.linear = nn.Linear(num_features, num_classes) def forward(self, input_tensor: torch.Tensor) -> torch.Tensor: + """Define forward pass.""" # forward pass; sigmoid transform included in CBELoss criterion output_tensor = self.linear(torch.flatten(input_tensor, 1)) return output_tensor @@ -23,15 +28,15 @@ def forward(self, input_tensor: torch.Tensor) -> torch.Tensor: # define train function that will be called by each client to train the model def train(model, trainloader: DataLoader, cfg: DictConfig) -> None: - + """Train model.""" criterion = nn.CrossEntropyLoss() optimizer = optim.SGD( model.parameters(), lr=cfg.learning_rate, weight_decay=cfg.weight_decay ) # train - for epoch in range(cfg.num_local_epochs): - for i, data in enumerate(trainloader): + for _epoch in range(cfg.num_local_epochs): + for _i, data in enumerate(trainloader): inputs, labels = data["image"], data["label"] @@ -49,7 +54,7 @@ def train(model, trainloader: DataLoader, cfg: DictConfig) -> None: def test(model, testloader: DataLoader) -> None: - + """Test model.""" criterion = nn.CrossEntropyLoss() # initialize @@ -58,7 +63,7 @@ def test(model, testloader: DataLoader) -> None: # put into evlauate mode model.eval() with torch.no_grad(): - for i, data in enumerate(testloader): + for _i, data in enumerate(testloader): images, labels = data["image"], data["label"] diff --git a/baselines/fedht/fedht/server.py b/baselines/fedht/fedht/server.py index 2502652df8d8..0e215cfc1c60 100644 --- a/baselines/fedht/fedht/server.py +++ b/baselines/fedht/fedht/server.py @@ -1,3 +1,5 @@ +"""Generate server for fedht baseline.""" + from collections import OrderedDict from typing import Dict diff --git a/baselines/fedht/fedht/utils.py b/baselines/fedht/fedht/utils.py index 57aba1bf147d..ad67623b5d92 100644 --- a/baselines/fedht/fedht/utils.py +++ b/baselines/fedht/fedht/utils.py @@ -1,3 +1,5 @@ +"""Execute utility functions for fedht baseline.""" + import numpy as np from torch.utils.data import Dataset @@ -10,7 +12,7 @@ def __init__(self, features, labels): self.data = {"image": features, "label": labels} def __len__(self): - """Returns length of features in data.""" + """Return length of features in data.""" return len(self.data["image"]) def __getitem__(self, idx): From 9053f49a0212223b22b2f4fa9ea6325d47f60eb8 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Mon, 21 Oct 2024 14:41:20 -0400 Subject: [PATCH 22/99] test errors --- baselines/fedht/fedht/aggregate.py | 7 +++---- baselines/fedht/fedht/main.py | 2 +- baselines/fedht/fedht/model.py | 1 + baselines/fedht/pyproject.toml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/baselines/fedht/fedht/aggregate.py b/baselines/fedht/fedht/aggregate.py index ed7cb17551f7..c54bef0b0c8d 100644 --- a/baselines/fedht/fedht/aggregate.py +++ b/baselines/fedht/fedht/aggregate.py @@ -368,7 +368,7 @@ def _aggregate_n_closest_weights( # calls hardthreshold function for each list element in weights_all def hardthreshold_list(weights_all, num_keep: int) -> NDArrays: """Call hardthreshold.""" - params = [hardthreshold(each, num_keep) for each in weights_all] + params: NDArrays = [hardthreshold(each, num_keep) for each in weights_all] return params @@ -461,8 +461,7 @@ def aggregate_hardthreshold( reduce(np.add, layer_updates) / num_examples_total for layer_updates in zip(*weighted_weights2) ] - params = [hardthreshold_list(layer_updates, num_keep) for layer_updates in hold] - - result: NDArrays = params + + result: NDArrays = [hardthreshold_list(layer_updates, num_keep) for layer_updates in hold] return result diff --git a/baselines/fedht/fedht/main.py b/baselines/fedht/fedht/main.py index bb1274529a49..0d27688990df 100644 --- a/baselines/fedht/fedht/main.py +++ b/baselines/fedht/fedht/main.py @@ -88,7 +88,7 @@ def main(cfg: DictConfig): # initialize global model to all zeros weights = np.zeros((num_classes, num_features)) bias = np.zeros(num_classes) - init_params: NDArrays = (weights, bias) + init_params: NDArrays = [weights, bias] init_params = ndarrays_to_parameters(init_params) # define strategy: fedht diff --git a/baselines/fedht/fedht/model.py b/baselines/fedht/fedht/model.py index 15a8c92c2e8d..3776caa034c5 100644 --- a/baselines/fedht/fedht/model.py +++ b/baselines/fedht/fedht/model.py @@ -78,4 +78,5 @@ def test(model, testloader: DataLoader) -> None: loss /= len(testloader) accuracy = correct / total + return loss, accuracy diff --git a/baselines/fedht/pyproject.toml b/baselines/fedht/pyproject.toml index 0f1cbef7cef4..c36345e2d41e 100644 --- a/baselines/fedht/pyproject.toml +++ b/baselines/fedht/pyproject.toml @@ -14,7 +14,7 @@ repository = "https://github.com/adap/flower" documentation = "https://flower.ai" [tool.poetry.dependencies] -python = ">=3.10.0, <3.11.0" +python = ">=3.10.0,<4.0" flwr = { extras = ["simulation"], version = ">=1.11.0" } flwr-datasets = { extras = ["vision"], version = ">=0.3.0" } scikit-learn =">=1.1.1" From 105622e48e338bd6291ecaad3f4a98d0e0b3d174 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Mon, 21 Oct 2024 15:56:53 -0400 Subject: [PATCH 23/99] format --- baselines/fedht/fedht/aggregate.py | 16 +++++++++++++--- baselines/fedht/fedht/main.py | 6 ++++-- baselines/fedht/fedht/model.py | 4 ++-- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/baselines/fedht/fedht/aggregate.py b/baselines/fedht/fedht/aggregate.py index c54bef0b0c8d..b6ef28718e3a 100644 --- a/baselines/fedht/fedht/aggregate.py +++ b/baselines/fedht/fedht/aggregate.py @@ -368,7 +368,11 @@ def _aggregate_n_closest_weights( # calls hardthreshold function for each list element in weights_all def hardthreshold_list(weights_all, num_keep: int) -> NDArrays: """Call hardthreshold.""" - params: NDArrays = [hardthreshold(each, num_keep) for each in weights_all] + params: NDArrays = [ + val + for sublist in (hardthreshold(each, num_keep) for each in weights_all) + for val in sublist + ] return params @@ -461,7 +465,13 @@ def aggregate_hardthreshold( reduce(np.add, layer_updates) / num_examples_total for layer_updates in zip(*weighted_weights2) ] - - result: NDArrays = [hardthreshold_list(layer_updates, num_keep) for layer_updates in hold] + + result: NDArrays = [ + val + for sublist in ( + hardthreshold_list(layer_updates, num_keep) for layer_updates in hold + ) + for val in sublist + ] return result diff --git a/baselines/fedht/fedht/main.py b/baselines/fedht/fedht/main.py index 0d27688990df..f2a1081d11a6 100644 --- a/baselines/fedht/fedht/main.py +++ b/baselines/fedht/fedht/main.py @@ -7,6 +7,7 @@ import hydra import numpy as np from flwr.common import NDArrays, ndarrays_to_parameters +from flwr.server.strategy.strategy import Strategy from flwr_datasets import FederatedDataset from flwr_datasets.partitioner import PathologicalPartitioner from omegaconf import DictConfig @@ -88,8 +89,8 @@ def main(cfg: DictConfig): # initialize global model to all zeros weights = np.zeros((num_classes, num_features)) bias = np.zeros(num_classes) - init_params: NDArrays = [weights, bias] - init_params = ndarrays_to_parameters(init_params) + init_params_arr: NDArrays = [weights, bias] + init_params = ndarrays_to_parameters(init_params_arr) # define strategy: fedht strategy_fedht = FedHT( @@ -109,6 +110,7 @@ def main(cfg: DictConfig): initial_parameters=init_params, ) + strategy: Strategy if cfg.agg == "fedht": strategy = strategy_fedht elif cfg.agg == "fedavg": diff --git a/baselines/fedht/fedht/model.py b/baselines/fedht/fedht/model.py index 3776caa034c5..fd53ceb1c479 100644 --- a/baselines/fedht/fedht/model.py +++ b/baselines/fedht/fedht/model.py @@ -53,7 +53,7 @@ def train(model, trainloader: DataLoader, cfg: DictConfig) -> None: optimizer.step() -def test(model, testloader: DataLoader) -> None: +def test(model, testloader: DataLoader): """Test model.""" criterion = nn.CrossEntropyLoss() @@ -78,5 +78,5 @@ def test(model, testloader: DataLoader) -> None: loss /= len(testloader) accuracy = correct / total - + return loss, accuracy From 29695ef602f0bf8050fa4981f410d4f5d33d433c Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Mon, 21 Oct 2024 15:57:32 -0400 Subject: [PATCH 24/99] Update README.md --- baselines/fedht/README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/baselines/fedht/README.md b/baselines/fedht/README.md index 9e423c74f9cf..172e7b5c58b6 100644 --- a/baselines/fedht/README.md +++ b/baselines/fedht/README.md @@ -82,18 +82,18 @@ pip install . ## Expected Results ### MNIST (`num_keep` = 500) ``` -python fedht.main --config-name base_mnist agg=fedavg num_keep=500 num_local_epochs=10 learning_rate=0.00005 -python fedht.main --config-name base_mnist agg=fedht num_keep=500 num_local_epochs=10 learning_rate=0.00005 -python fedht.main --config-name base_mnist agg=fedht iterht=True num_keep=500 num_local_epochs=10 learning_rate=0.00005 -python fedht.main --config-name base_mnist agg=fedht num_keep=500 num_local_epochs=1 learning_rate=0.00005 +python -m fedht.main --config-name base_mnist agg=fedavg num_keep=500 num_local_epochs=10 learning_rate=0.00005 +python -m fedht.main --config-name base_mnist agg=fedht num_keep=500 num_local_epochs=10 learning_rate=0.00005 +python -m fedht.main --config-name base_mnist agg=fedht iterht=True num_keep=500 num_local_epochs=10 learning_rate=0.00005 +python -m fedht.main --config-name base_mnist agg=fedht num_keep=500 num_local_epochs=1 learning_rate=0.00005 ``` ### Simulation II (`num_keep` = 200) ``` -python fedht.main -config-name base_simII agg=fedavg num_local_epochs=5 learning_rate=0.01 -python fedht.main -config-name base_simII agg=fedht num_local_epochs=5 learning_rate=0.01 -python fedht.main -config-name base_simII agg=fedht iterht=True num_local_epochs=5 learning_rate=0.01 -python fedht.main -config-name base_simII agg=fedht num_local_epochs=1 learning_rate=0.01 +python -m fedht.main -config-name base_simII agg=fedavg num_local_epochs=5 learning_rate=0.01 +python -m fedht.main -config-name base_simII agg=fedht num_local_epochs=5 learning_rate=0.01 +python -m fedht.main -config-name base_simII agg=fedht iterht=True num_local_epochs=5 learning_rate=0.01 +python -m fedht.main -config-name base_simII agg=fedht num_local_epochs=1 learning_rate=0.01 ``` From 0a70e0a0b0ecb0c4b444edc1fe6b8001961e5b27 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Mon, 21 Oct 2024 20:53:02 -0400 Subject: [PATCH 25/99] fixing linting issues --- baselines/fedht/fedht/aggregate.py | 9 ++++----- baselines/fedht/fedht/utils.py | 14 +++++++------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/baselines/fedht/fedht/aggregate.py b/baselines/fedht/fedht/aggregate.py index b6ef28718e3a..43dceaf94153 100644 --- a/baselines/fedht/fedht/aggregate.py +++ b/baselines/fedht/fedht/aggregate.py @@ -416,7 +416,6 @@ def aggregate_hardthreshold( if num_keep <= 0: raise ValueError("k must be a positive integer.") - """Compute weighted average.""" # Calculate the total number of examples used during training num_examples_total = sum(num_examples for (_, num_examples) in results) @@ -438,10 +437,10 @@ def aggregate_hardthreshold( # ignoring second element in results[i] skips over number of observations # j: iterates through all layers of a model # k: iterates through the slices of each layer - for i in range(len(results)): - for j in range(len(results[i][0])): - for k in range(len(results[i][0][j])): - results[i][0][j][k] = hardthreshold(results[i][0][j][k], num_keep) + for i, layer in enumerate(results): + for j, sub_layer in enumerate(layer[0]): + for k, weight in enumerate(sub_layer): + results[i][0][j][k] = hardthreshold(weight, num_keep) weighted_weights1 = [ [layer * num_examples for layer in weights] diff --git a/baselines/fedht/fedht/utils.py b/baselines/fedht/fedht/utils.py index ad67623b5d92..54232ccdd0ed 100644 --- a/baselines/fedht/fedht/utils.py +++ b/baselines/fedht/fedht/utils.py @@ -26,24 +26,24 @@ def partition_data(data, num_partitions): X, y = data partition_size = len(X) // num_partitions # Create partitions - partitionsX = [ + partitions_x = [ X[i * partition_size : (i + 1) * partition_size] for i in range(num_partitions) ] - partitionsy = [ + partitions_y = [ y[i * partition_size : (i + 1) * partition_size] for i in range(num_partitions) ] # Handle any remaining items if len(data) % num_partitions != 0: # partitions[-1] = partitions[-1] + data[num_partitions * partition_size:] - partitionsX[-1] = np.vstack( - (partitionsX[-1], X[num_partitions * partition_size :]) + partitions_x[-1] = np.vstack( + (partitions_x[-1], X[num_partitions * partition_size :]) ) - partitionsy[-1] = np.vstack( - (partitionsy[-1], y[num_partitions * partition_size :]) + partitions_y[-1] = np.vstack( + (partitions_y[-1], y[num_partitions * partition_size :]) ) - return partitionsX, partitionsy + return partitions_x, partitions_y def sim_data(ni: int, num_clients: int, num_features: int, alpha=1, beta=1): From ed1a621df26e782bb7eb6e457f11c57e975d34a8 Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Mon, 21 Oct 2024 21:02:31 -0400 Subject: [PATCH 26/99] Update README.md --- baselines/fedht/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/baselines/fedht/README.md b/baselines/fedht/README.md index 172e7b5c58b6..bda364834528 100644 --- a/baselines/fedht/README.md +++ b/baselines/fedht/README.md @@ -27,7 +27,7 @@ pyenv virtualenv 3.10.11 fedht-3.10.11 pyenv activate fedht-3.10.11 # Then install the project -cd fedht && pip install -e . +cd fedht && poetry install -e ``` ## About this baseline From fb1ce53bae520400a68a458e6ae21ea1d5d93081 Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Mon, 21 Oct 2024 21:02:51 -0400 Subject: [PATCH 27/99] Update README.md --- baselines/fedht/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/baselines/fedht/README.md b/baselines/fedht/README.md index bda364834528..2f35bf1d3ed9 100644 --- a/baselines/fedht/README.md +++ b/baselines/fedht/README.md @@ -27,7 +27,7 @@ pyenv virtualenv 3.10.11 fedht-3.10.11 pyenv activate fedht-3.10.11 # Then install the project -cd fedht && poetry install -e +cd fedht && poetry install ``` ## About this baseline From 62cba0fc273633c9a2e74efa482ac6e9ad117cab Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Mon, 21 Oct 2024 21:03:11 -0400 Subject: [PATCH 28/99] Update README.md --- baselines/fedht/README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/baselines/fedht/README.md b/baselines/fedht/README.md index 2f35bf1d3ed9..3f3ab027aca8 100644 --- a/baselines/fedht/README.md +++ b/baselines/fedht/README.md @@ -73,12 +73,6 @@ The data generation procedure for the simulated dataset matches that of Simulati We note that in the current implementation, only weights (and not biases) of the model(s) are subject to hardthresholding; this practice aligns with sparse model literature. Additionally, the `num_keep` hardthresholding parameter is enforced at the output layer level, as opposed to constraining the number of parameters across the entire model. Specifically, for a fully connected layer with $i$ inputs and $j$ outputs, the $j$-th output's parameters are constrained by `num_keep`. -## Install dependencies - -```bash -pip install . -``` - ## Expected Results ### MNIST (`num_keep` = 500) ``` From 56b928e63f5750820b2b85017f9a47be9425585f Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Mon, 21 Oct 2024 21:46:38 -0400 Subject: [PATCH 29/99] aggregate format --- baselines/fedht/fedht/aggregate.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/baselines/fedht/fedht/aggregate.py b/baselines/fedht/fedht/aggregate.py index 43dceaf94153..28dc63b804bf 100644 --- a/baselines/fedht/fedht/aggregate.py +++ b/baselines/fedht/fedht/aggregate.py @@ -368,7 +368,7 @@ def _aggregate_n_closest_weights( # calls hardthreshold function for each list element in weights_all def hardthreshold_list(weights_all, num_keep: int) -> NDArrays: """Call hardthreshold.""" - params: NDArrays = [ + params = [ val for sublist in (hardthreshold(each, num_keep) for each in weights_all) for val in sublist @@ -416,6 +416,7 @@ def aggregate_hardthreshold( if num_keep <= 0: raise ValueError("k must be a positive integer.") + """Compute weighted average.""" # Calculate the total number of examples used during training num_examples_total = sum(num_examples for (_, num_examples) in results) @@ -437,10 +438,10 @@ def aggregate_hardthreshold( # ignoring second element in results[i] skips over number of observations # j: iterates through all layers of a model # k: iterates through the slices of each layer - for i, layer in enumerate(results): - for j, sub_layer in enumerate(layer[0]): - for k, weight in enumerate(sub_layer): - results[i][0][j][k] = hardthreshold(weight, num_keep) + for i in range(len(results)): + for j in range(len(results[i][0])): + for k in range(len(results[i][0][j])): + results[i][0][j][k] = hardthreshold(results[i][0][j][k], num_keep) weighted_weights1 = [ [layer * num_examples for layer in weights] @@ -465,7 +466,7 @@ def aggregate_hardthreshold( for layer_updates in zip(*weighted_weights2) ] - result: NDArrays = [ + new_result: NDArrays = [ val for sublist in ( hardthreshold_list(layer_updates, num_keep) for layer_updates in hold @@ -473,4 +474,4 @@ def aggregate_hardthreshold( for val in sublist ] - return result + return new_result From 892e6b2d9db0c196f2eb23aa34552efb0464bd78 Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Mon, 21 Oct 2024 21:48:52 -0400 Subject: [PATCH 30/99] Update README.md --- baselines/fedht/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/baselines/fedht/README.md b/baselines/fedht/README.md index 3f3ab027aca8..4838ecea9353 100644 --- a/baselines/fedht/README.md +++ b/baselines/fedht/README.md @@ -81,7 +81,7 @@ python -m fedht.main --config-name base_mnist agg=fedht num_keep=500 num_local_e python -m fedht.main --config-name base_mnist agg=fedht iterht=True num_keep=500 num_local_epochs=10 learning_rate=0.00005 python -m fedht.main --config-name base_mnist agg=fedht num_keep=500 num_local_epochs=1 learning_rate=0.00005 ``` - + ### Simulation II (`num_keep` = 200) ``` @@ -90,4 +90,4 @@ python -m fedht.main -config-name base_simII agg=fedht num_local_epochs=5 learni python -m fedht.main -config-name base_simII agg=fedht iterht=True num_local_epochs=5 learning_rate=0.01 python -m fedht.main -config-name base_simII agg=fedht num_local_epochs=1 learning_rate=0.01 ``` - + From ed5da2b543ed42853a58e28cd69951bdaefac677 Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Thu, 31 Oct 2024 20:25:59 -0400 Subject: [PATCH 31/99] Apply suggestions from code review Co-authored-by: Javier --- baselines/fedht/README.md | 8 ++++---- baselines/fedht/fedht/__init__.py | 22 +--------------------- 2 files changed, 5 insertions(+), 25 deletions(-) diff --git a/baselines/fedht/README.md b/baselines/fedht/README.md index 4838ecea9353..95f1cb272230 100644 --- a/baselines/fedht/README.md +++ b/baselines/fedht/README.md @@ -85,9 +85,9 @@ python -m fedht.main --config-name base_mnist agg=fedht num_keep=500 num_local_e ### Simulation II (`num_keep` = 200) ``` -python -m fedht.main -config-name base_simII agg=fedavg num_local_epochs=5 learning_rate=0.01 -python -m fedht.main -config-name base_simII agg=fedht num_local_epochs=5 learning_rate=0.01 -python -m fedht.main -config-name base_simII agg=fedht iterht=True num_local_epochs=5 learning_rate=0.01 -python -m fedht.main -config-name base_simII agg=fedht num_local_epochs=1 learning_rate=0.01 +python -m fedht.main --config-name base_simII agg=fedavg num_local_epochs=5 learning_rate=0.01 +python -m fedht.main --config-name base_simII agg=fedht num_local_epochs=5 learning_rate=0.01 +python -m fedht.main --config-name base_simII agg=fedht iterht=True num_local_epochs=5 learning_rate=0.01 +python -m fedht.main --config-name base_simII agg=fedht num_local_epochs=1 learning_rate=0.01 ``` diff --git a/baselines/fedht/fedht/__init__.py b/baselines/fedht/fedht/__init__.py index a76f09289842..e64653acc7d1 100644 --- a/baselines/fedht/fedht/__init__.py +++ b/baselines/fedht/fedht/__init__.py @@ -1,21 +1 @@ -# Copyright 2020 Flower Labs GmbH. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== -"""Contains the strategy abstraction and different implementations.""" - -from flwr.server.strategy.fedavg import FedAvg as FedAvg - -from .fedht import FedHT as FedHT - -__all__ = ["FedAvg", "FedHT"] +"""FedHt package.""" From 31950481798a4fb4d93c4020885c20b918c04012 Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Fri, 1 Nov 2024 09:40:45 -0400 Subject: [PATCH 32/99] Update README.md --- baselines/fedht/README.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/baselines/fedht/README.md b/baselines/fedht/README.md index 95f1cb272230..7ef7e7cd22fb 100644 --- a/baselines/fedht/README.md +++ b/baselines/fedht/README.md @@ -17,7 +17,7 @@ dataset: [MNIST] # Environment Setup -Create a new Python environment using [pyenv](https://github.com/pyenv/pyenv) and [virtualenv plugin](https://github.com/pyenv/pyenv-virtualenv). Alternatively, use a Python environment manager of your choice like [`venv`](https://docs.python.org/3/library/venv.html). Then install the project: +Create a new Python environment using [pyenv](https://github.com/pyenv/pyenv) and [virtualenv plugin](https://github.com/pyenv/pyenv-virtualenv). ```bash # Create the environment @@ -25,7 +25,25 @@ pyenv virtualenv 3.10.11 fedht-3.10.11 # Activate it pyenv activate fedht-3.10.11 +``` + +Alternatively, use a Python environment manager of your choice like [`venv`](https://docs.python.org/3/library/venv.html). You can utilize [`venv`](https://docs.python.org/3/library/venv.html) with following: + +```bash +# Set local version of Python +pyenv local 3.10.11 +# Install local version of Python +pyenv install 3.10.11 + +# Activate environment +virtualenv venv +.\venv\Scripts\activate +``` + + Then install the project: + +```bash # Then install the project cd fedht && poetry install ``` From ffa7320b21d9e32f56be7c5b58705a32c2dd4c06 Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Fri, 1 Nov 2024 09:57:42 -0400 Subject: [PATCH 33/99] Update README.md --- baselines/fedht/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/baselines/fedht/README.md b/baselines/fedht/README.md index 7ef7e7cd22fb..b648ac2d6b7a 100644 --- a/baselines/fedht/README.md +++ b/baselines/fedht/README.md @@ -30,12 +30,12 @@ pyenv activate fedht-3.10.11 Alternatively, use a Python environment manager of your choice like [`venv`](https://docs.python.org/3/library/venv.html). You can utilize [`venv`](https://docs.python.org/3/library/venv.html) with following: ```bash -# Set local version of Python -pyenv local 3.10.11 - # Install local version of Python pyenv install 3.10.11 +# Set local version of Python +pyenv local 3.10.11 + # Activate environment virtualenv venv .\venv\Scripts\activate From d181d80361de61115f35887965a47ce9ba73d166 Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Fri, 1 Nov 2024 11:23:30 -0400 Subject: [PATCH 34/99] Update README.md --- baselines/fedht/README.md | 33 ++++++--------------------------- 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/baselines/fedht/README.md b/baselines/fedht/README.md index b648ac2d6b7a..5d47947b073d 100644 --- a/baselines/fedht/README.md +++ b/baselines/fedht/README.md @@ -17,35 +17,14 @@ dataset: [MNIST] # Environment Setup -Create a new Python environment using [pyenv](https://github.com/pyenv/pyenv) and [virtualenv plugin](https://github.com/pyenv/pyenv-virtualenv). - -```bash -# Create the environment -pyenv virtualenv 3.10.11 fedht-3.10.11 - -# Activate it -pyenv activate fedht-3.10.11 -``` - -Alternatively, use a Python environment manager of your choice like [`venv`](https://docs.python.org/3/library/venv.html). You can utilize [`venv`](https://docs.python.org/3/library/venv.html) with following: - -```bash -# Install local version of Python -pyenv install 3.10.11 - -# Set local version of Python -pyenv local 3.10.11 - -# Activate environment -virtualenv venv -.\venv\Scripts\activate -``` - - Then install the project: +Once inside the fedht folder (where the pyproject.toml is), you can install the project using [poetry][https://github.com/pyenv/pyenv](https://github.com/python-poetry/poetry), which automatically creates a virtual enviornment. ```bash -# Then install the project -cd fedht && poetry install +# Install the project +poetry install + +# Activate virtual environment +poetry shell ``` ## About this baseline From 0de04ef1bb248d87871606b78746bee69585cb81 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Fri, 1 Nov 2024 17:01:34 -0400 Subject: [PATCH 35/99] fixing bug on non iterable --- baselines/fedht/fedht/aggregate.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/baselines/fedht/fedht/aggregate.py b/baselines/fedht/fedht/aggregate.py index 28dc63b804bf..e990ff1a707f 100644 --- a/baselines/fedht/fedht/aggregate.py +++ b/baselines/fedht/fedht/aggregate.py @@ -368,12 +368,16 @@ def _aggregate_n_closest_weights( # calls hardthreshold function for each list element in weights_all def hardthreshold_list(weights_all, num_keep: int) -> NDArrays: """Call hardthreshold.""" + # params = [ + # val + # for sublist in (hardthreshold(each, num_keep) for each in weights_all) + # for val in sublist + # ] + params = [ - val - for sublist in (hardthreshold(each, num_keep) for each in weights_all) - for val in sublist + hardthreshold(each, num_keep) for each in weights_all ] - return params + return [params] # hardthreshold function applied to array @@ -402,7 +406,7 @@ def hardthreshold(weights_prime, num_keep: int) -> NDArrays: else: params = weights_prime - return params + return np.array(params) def aggregate_hardthreshold( From dc54d4cefb78aed56486f83e28958a7cdf178186 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Fri, 1 Nov 2024 17:06:38 -0400 Subject: [PATCH 36/99] comment clean up --- baselines/fedht/fedht/aggregate.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/baselines/fedht/fedht/aggregate.py b/baselines/fedht/fedht/aggregate.py index e990ff1a707f..015207059f9f 100644 --- a/baselines/fedht/fedht/aggregate.py +++ b/baselines/fedht/fedht/aggregate.py @@ -368,11 +368,6 @@ def _aggregate_n_closest_weights( # calls hardthreshold function for each list element in weights_all def hardthreshold_list(weights_all, num_keep: int) -> NDArrays: """Call hardthreshold.""" - # params = [ - # val - # for sublist in (hardthreshold(each, num_keep) for each in weights_all) - # for val in sublist - # ] params = [ hardthreshold(each, num_keep) for each in weights_all From 950970ea7ca04d897550fcb782064938ce9726ea Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Fri, 1 Nov 2024 19:29:03 -0400 Subject: [PATCH 37/99] adjusted aggregate --- baselines/fedht/fedht/aggregate.py | 345 +---------------------------- 1 file changed, 3 insertions(+), 342 deletions(-) diff --git a/baselines/fedht/fedht/aggregate.py b/baselines/fedht/fedht/aggregate.py index 015207059f9f..cbb303e20861 100644 --- a/baselines/fedht/fedht/aggregate.py +++ b/baselines/fedht/fedht/aggregate.py @@ -16,188 +16,10 @@ # mypy: disallow_untyped_calls=False from functools import reduce -from typing import Any, Callable, List, Tuple +from typing import List, Tuple import numpy as np -from flwr.common import FitRes, NDArray, NDArrays, parameters_to_ndarrays -from flwr.server.client_proxy import ClientProxy - - -def aggregate(results: List[Tuple[NDArrays, int]]) -> NDArrays: - """Compute weighted average.""" - # Calculate the total number of examples used during training - num_examples_total = sum(num_examples for (_, num_examples) in results) - - # Create a list of weights, each multiplied by the related number of examples - weighted_weights = [ - [layer * num_examples for layer in weights] for weights, num_examples in results - ] - - # Compute average weights of each layer - weights_prime: NDArrays = [ - reduce(np.add, layer_updates) / num_examples_total - for layer_updates in zip(*weighted_weights) - ] - return weights_prime - - -def aggregate_inplace(results: List[Tuple[ClientProxy, FitRes]]) -> NDArrays: - """Compute in-place weighted average.""" - # Count total examples - num_examples_total = sum(fit_res.num_examples for (_, fit_res) in results) - - # Compute scaling factors for each result - scaling_factors = [ - fit_res.num_examples / num_examples_total for _, fit_res in results - ] - - # Let's do in-place aggregation - # Get first result, then add up each other - params = [ - scaling_factors[0] * x for x in parameters_to_ndarrays(results[0][1].parameters) - ] - for i, (_, fit_res) in enumerate(results[1:]): - res = ( - scaling_factors[i + 1] * x - for x in parameters_to_ndarrays(fit_res.parameters) - ) - params = [reduce(np.add, layer_updates) for layer_updates in zip(params, res)] - - return params - - -def aggregate_median(results: List[Tuple[NDArrays, int]]) -> NDArrays: - """Compute median.""" - # Create a list of weights and ignore the number of examples - weights = [weights for weights, _ in results] - - # Compute median weight of each layer - median_w: NDArrays = [ - np.median(np.asarray(layer), axis=0) for layer in zip(*weights) - ] - return median_w - - -def aggregate_krum( - results: List[Tuple[NDArrays, int]], num_malicious: int, to_keep: int -) -> NDArrays: - """Choose one parameter vector according to the Krum function. - - If to_keep is not None, then MultiKrum is applied. - """ - # Create a list of weights and ignore the number of examples - weights = [weights for weights, _ in results] - - # Compute distances between vectors - distance_matrix = _compute_distances(weights) - - # For each client, take the n-f-2 closest parameters vectors - num_closest = max(1, len(weights) - num_malicious - 2) - closest_indices = [] - for distance in distance_matrix: - closest_indices.append( - np.argsort(distance)[1 : num_closest + 1].tolist() # noqa: E203 - ) - - # Compute the score for each client, that is the sum of the distances - # of the n-f-2 closest parameters vectors - scores = [ - np.sum(distance_matrix[i, closest_indices[i]]) - for i in range(len(distance_matrix)) - ] - - if to_keep > 0: - # Choose to_keep clients and return their average (MultiKrum) - best_indices = np.argsort(scores)[::-1][len(scores) - to_keep :] # noqa: E203 - best_results = [results[i] for i in best_indices] - return aggregate(best_results) - - # Return the model parameters that minimize the score (Krum) - return weights[np.argmin(scores)] - - -# pylint: disable=too-many-locals -def aggregate_bulyan( - results: List[Tuple[NDArrays, int]], - num_malicious: int, - aggregation_rule: Callable, # type: ignore - **aggregation_rule_kwargs: Any, -) -> NDArrays: - """Perform Bulyan aggregation. - - Parameters - ---------- - results: List[Tuple[NDArrays, int]] - Weights and number of samples for each of the client. - num_malicious: int - The maximum number of malicious clients. - aggregation_rule: Callable - Byzantine resilient aggregation rule used as the first step of the Bulyan - aggregation_rule_kwargs: Any - The arguments to the aggregation rule. - - Returns - ------- - aggregated_parameters: NDArrays - Aggregated parameters according to the Bulyan strategy. - """ - byzantine_resilient_single_ret_model_aggregation = [aggregate_krum] - # also GeoMed (but not implemented yet) - byzantine_resilient_many_return_models_aggregation = [] # type: ignore - # Brute, Medoid (but not implemented yet) - - num_clients = len(results) - if num_clients < 4 * num_malicious + 3: - raise ValueError( - "The Bulyan aggregation requires then number of clients to be greater or " - "equal to the 4 * num_malicious + 3. This is the assumption of this method." - "It is needed to ensure that the method reduces the attacker's leeway to " - "the one proved in the paper." - ) - selected_models_set: List[Tuple[NDArrays, int]] = [] - - theta = len(results) - 2 * num_malicious - beta = theta - 2 * num_malicious - - for _ in range(theta): - best_model = aggregation_rule( - results=results, num_malicious=num_malicious, **aggregation_rule_kwargs - ) - list_of_weights = [weights for weights, num_samples in results] - # This group gives exact result - if aggregation_rule in byzantine_resilient_single_ret_model_aggregation: - best_idx = _find_reference_weights(best_model, list_of_weights) - # This group requires finding the closest model to the returned one - # (weights distance wise) - elif aggregation_rule in byzantine_resilient_many_return_models_aggregation: - # when different aggregation strategies available - # write a function to find the closest model - raise NotImplementedError( - "aggregate_bulyan currently does not support the aggregation rules that" - " return many models as results. " - "Such aggregation rules are currently not available in Flower." - ) - else: - raise ValueError( - "The given aggregation rule is not added as Byzantine resilient. " - "Please choose from Byzantine resilient rules." - ) - - selected_models_set.append(results[best_idx]) - - # remove idx from tracker and weights_results - results.pop(best_idx) - - # Compute median parameter vector across selected_models_set - median_vect = aggregate_median(selected_models_set) - - # Take the averaged beta parameters of the closest distance to the median - # (coordinate-wise) - parameters_aggregated = _aggregate_n_closest_weights( - median_vect, selected_models_set, beta_closest=beta - ) - return parameters_aggregated - +from flwr.common import NDArrays def weighted_loss_avg(results: List[Tuple[int, float]]) -> float: """Aggregate evaluation results obtained from multiple clients.""" @@ -205,166 +27,6 @@ def weighted_loss_avg(results: List[Tuple[int, float]]) -> float: weighted_losses = [num_examples * loss for num_examples, loss in results] return sum(weighted_losses) / num_total_evaluation_examples - -def aggregate_qffl( - parameters: NDArrays, deltas: List[NDArrays], hs_fll: List[NDArrays] -) -> NDArrays: - """Compute weighted average based on Q-FFL paper.""" - demominator: float = np.sum(np.asarray(hs_fll)) - scaled_deltas = [] - for client_delta in deltas: - scaled_deltas.append([layer * 1.0 / demominator for layer in client_delta]) - updates = [] - for i in range(len(deltas[0])): - tmp = scaled_deltas[0][i] - for j in range(1, len(deltas)): - tmp += scaled_deltas[j][i] - updates.append(tmp) - new_parameters = [(u - v) * 1.0 for u, v in zip(parameters, updates)] - return new_parameters - - -def _compute_distances(weights: List[NDArrays]) -> NDArray: - """Compute distances between vectors. - - Input: weights - list of weights vectors - Output: distances - matrix distance_matrix of squared distances between the vectors - """ - flat_w = np.array([np.concatenate(p, axis=None).ravel() for p in weights]) - distance_matrix = np.zeros((len(weights), len(weights))) - for i, flat_w_i in enumerate(flat_w): - for j, flat_w_j in enumerate(flat_w): - delta = flat_w_i - flat_w_j - norm = np.linalg.norm(delta) - distance_matrix[i, j] = norm**2 - return distance_matrix - - -def _trim_mean(array: NDArray, proportiontocut: float) -> NDArray: - """Compute trimmed mean along axis=0. - - It is based on the scipy implementation. - - https://docs.scipy.org/doc/scipy/reference/generated/ - scipy.stats.trim_mean.html. - """ - axis = 0 - nobs = array.shape[axis] - lowercut = int(proportiontocut * nobs) - uppercut = nobs - lowercut - if lowercut > uppercut: - raise ValueError("Proportion too big.") - - atmp = np.partition(array, (lowercut, uppercut - 1), axis) - - slice_list = [slice(None)] * atmp.ndim - slice_list[axis] = slice(lowercut, uppercut) - result: NDArray = np.mean(atmp[tuple(slice_list)], axis=axis) - return result - - -def aggregate_trimmed_avg( - results: List[Tuple[NDArrays, int]], proportiontocut: float -) -> NDArrays: - """Compute trimmed average.""" - # Create a list of weights and ignore the number of examples - weights = [weights for weights, _ in results] - - trimmed_w: NDArrays = [ - _trim_mean(np.asarray(layer), proportiontocut=proportiontocut) - for layer in zip(*weights) - ] - - return trimmed_w - - -def _check_weights_equality(weights1: NDArrays, weights2: NDArrays) -> bool: - """Check if weights are the same.""" - if len(weights1) != len(weights2): - return False - return all( - np.array_equal(layer_weights1, layer_weights2) - for layer_weights1, layer_weights2 in zip(weights1, weights2) - ) - - -def _find_reference_weights( - reference_weights: NDArrays, list_of_weights: List[NDArrays] -) -> int: - """Find the reference weights by looping through the `list_of_weights`. - - Raise Error if the reference weights is not found. - - Parameters - ---------- - reference_weights: NDArrays - Weights that will be searched for. - list_of_weights: List[NDArrays] - List of weights that will be searched through. - - Returns - ------- - index: int - The index of `reference_weights` in the `list_of_weights`. - - Raises - ------ - ValueError - If `reference_weights` is not found in `list_of_weights`. - """ - for idx, weights in enumerate(list_of_weights): - if _check_weights_equality(reference_weights, weights): - return idx - raise ValueError("The reference weights not found in list_of_weights.") - - -def _aggregate_n_closest_weights( - reference_weights: NDArrays, results: List[Tuple[NDArrays, int]], beta_closest: int -) -> NDArrays: - """Calculate element-wise mean of the `N` closest values. - - Note, each i-th coordinate of the result weight is the average of the beta_closest - -ith coordinates to the reference weights - - - Parameters - ---------- - reference_weights: NDArrays - The weights from which the distances will be computed - results: List[Tuple[NDArrays, int]] - The weights from models - beta_closest: int - The number of the closest distance weights that will be averaged - - Returns - ------- - aggregated_weights: NDArrays - Averaged (element-wise) beta weights that have the closest distance to - reference weights - """ - list_of_weights = [weights for weights, num_examples in results] - aggregated_weights = [] - - for layer_id, layer_weights in enumerate(reference_weights): - other_weights_layer_list = [] - for other_w in list_of_weights: - other_weights_layer = other_w[layer_id] - other_weights_layer_list.append(other_weights_layer) - other_weights_layer_np = np.array(other_weights_layer_list) - diff_np = np.abs(layer_weights - other_weights_layer_np) - # Create indices of the smallest differences - # We do not need the exact order but just the beta closest weights - # therefore np.argpartition is used instead of np.argsort - indices = np.argpartition(diff_np, kth=beta_closest - 1, axis=0) - # Take the weights (coordinate-wise) corresponding to the beta of the - # closest distances - beta_closest_weights = np.take_along_axis( - other_weights_layer_np, indices=indices, axis=0 - )[:beta_closest] - aggregated_weights.append(np.mean(beta_closest_weights, axis=0)) - return aggregated_weights - - # calls hardthreshold function for each list element in weights_all def hardthreshold_list(weights_all, num_keep: int) -> NDArrays: """Call hardthreshold.""" @@ -374,7 +36,6 @@ def hardthreshold_list(weights_all, num_keep: int) -> NDArrays: ] return [params] - # hardthreshold function applied to array def hardthreshold(weights_prime, num_keep: int) -> NDArrays: """Perform hardthresholding on single array.""" @@ -403,7 +64,7 @@ def hardthreshold(weights_prime, num_keep: int) -> NDArrays: return np.array(params) - +# hardthreshold aggregation def aggregate_hardthreshold( results: List[Tuple[NDArrays, int]], num_keep: int, iterht: bool ) -> NDArrays: From 23f712535849aab7b8a2da8dcbdd0ceb77c9db59 Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Fri, 1 Nov 2024 19:32:17 -0400 Subject: [PATCH 38/99] Update README.md --- baselines/fedht/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/baselines/fedht/README.md b/baselines/fedht/README.md index 5d47947b073d..c9b9a41dd47d 100644 --- a/baselines/fedht/README.md +++ b/baselines/fedht/README.md @@ -20,6 +20,13 @@ dataset: [MNIST] Once inside the fedht folder (where the pyproject.toml is), you can install the project using [poetry][https://github.com/pyenv/pyenv](https://github.com/python-poetry/poetry), which automatically creates a virtual enviornment. ```bash +# Set python version +pyenv install 3.11.3 +pyenv local 3.11.3 + +# Tell poetry to use python 3.11 +poetry env use 3.11.3 + # Install the project poetry install From c858d86e9e9c8cf8d8cd50e3bc95b8d294068be8 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Fri, 1 Nov 2024 22:07:09 -0400 Subject: [PATCH 39/99] adding gpu support --- baselines/fedht/fedht/client.py | 18 +++++++++++------- baselines/fedht/fedht/conf/base_mnist.yaml | 3 +++ baselines/fedht/fedht/conf/base_simII.yaml | 3 +++ baselines/fedht/fedht/main.py | 16 +++++++++++++--- baselines/fedht/fedht/model.py | 8 ++++---- baselines/fedht/fedht/server.py | 5 +++-- 6 files changed, 37 insertions(+), 16 deletions(-) diff --git a/baselines/fedht/fedht/client.py b/baselines/fedht/fedht/client.py index 8f634f2ac1f2..bf9afbcf1f3e 100644 --- a/baselines/fedht/fedht/client.py +++ b/baselines/fedht/fedht/client.py @@ -26,6 +26,7 @@ def __init__( num_features, num_classes, cfg: DictConfig, + device ) -> None: """SimII client for simulation II experimentation.""" self.trainloader = trainloader @@ -35,6 +36,7 @@ def __init__( self.num_features = num_features self.num_classes = num_classes self.cfg = cfg + self.device = device # get parameters from existing model def get_parameters(self, config): @@ -52,7 +54,7 @@ def fit(self, parameters, config): self.model.train() # training for local epochs defined by config - train(self.model, self.trainloader, self.cfg) + train(self.model, self.trainloader, self.cfg, self.device) return self.get_parameters(self.model), self.num_obs, {} @@ -64,14 +66,14 @@ def evaluate(self, parameters, config): self.model.load_state_dict(state_dict, strict=True) # need to change from log_loss to torch.loss and change other metrics - loss, accuracy = test(self.model, self.trainloader) + loss, accuracy = test(self.model, self.trainloader, self.device) return loss, self.num_obs, {"accuracy": accuracy} # client fn for input into simulation def generate_client_fn_simII( - dataset, num_features, num_classes, model, cfg: DictConfig + dataset, num_features, num_classes, model, cfg: DictConfig, device: torch.device ): """Generate client function for simulated FL.""" @@ -91,7 +93,7 @@ def client_fn(context: Context) -> Client: testloader = DataLoader(test_dataset, batch_size=cfg.batch_size, shuffle=True) return SimIIClient( - trainloader, testloader, model, num_obs, num_features, num_classes, cfg + trainloader, testloader, model, num_obs, num_features, num_classes, cfg, device ).to_client() return client_fn @@ -110,6 +112,7 @@ def __init__( num_features, num_classes, cfg: DictConfig, + device ) -> None: """MNIST client for MNIST experimentation.""" self.trainloader = trainloader @@ -119,6 +122,7 @@ def __init__( self.num_features = num_features self.num_classes = num_classes self.cfg = cfg + self.device = device # get parameters from existing model def get_parameters(self, config): @@ -136,7 +140,7 @@ def fit(self, parameters, config): self.model.train() # training for local epochs defined by config - train(self.model, self.trainloader, self.cfg) + train(self.model, self.trainloader, self.cfg, self.device) return self.get_parameters(self.model), self.num_obs, {} @@ -155,7 +159,7 @@ def evaluate(self, parameters, config): # client fn for input into simulation def generate_client_fn_mnist( - dataset, num_features, num_classes, model, cfg: DictConfig + dataset, num_features, num_classes, model, cfg: DictConfig, device: torch.device ): """Generate client function for simulated FL.""" @@ -177,7 +181,7 @@ def client_fn(context: Context) -> Client: testloader = DataLoader(test_dataset, batch_size=cfg.batch_size, shuffle=True) return MnistClient( - trainloader, testloader, model, num_obs, num_features, num_classes, cfg + trainloader, testloader, model, num_obs, num_features, num_classes, cfg, device ).to_client() return client_fn diff --git a/baselines/fedht/fedht/conf/base_mnist.yaml b/baselines/fedht/fedht/conf/base_mnist.yaml index fc81461dc9ce..84bf5d159ae6 100644 --- a/baselines/fedht/fedht/conf/base_mnist.yaml +++ b/baselines/fedht/fedht/conf/base_mnist.yaml @@ -13,6 +13,9 @@ data: mnist client_resources: num_cpus: 7 + num_gpus: .5 + +server_device: cpu use_cuda: false specified_device: null # the ID of cuda device, if null, then use defaults torch.device("cuda") diff --git a/baselines/fedht/fedht/conf/base_simII.yaml b/baselines/fedht/fedht/conf/base_simII.yaml index 6530c58e30db..dd52cabfa7a4 100644 --- a/baselines/fedht/fedht/conf/base_simII.yaml +++ b/baselines/fedht/fedht/conf/base_simII.yaml @@ -13,6 +13,9 @@ data: simII client_resources: num_cpus: 4 + num_gpus: .5 + +server_device: cpu use_cuda: false specified_device: null # the ID of cuda device, if null, then use defaults torch.device("cuda") diff --git a/baselines/fedht/fedht/main.py b/baselines/fedht/fedht/main.py index f2a1081d11a6..d77455a2caaf 100644 --- a/baselines/fedht/fedht/main.py +++ b/baselines/fedht/fedht/main.py @@ -2,6 +2,7 @@ import pickle import random +import torch import flwr as fl import hydra @@ -32,6 +33,10 @@ def main(cfg: DictConfig): # set seed random.seed(2024) + # this vs. setting in cfg; what is preferred? + device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + print(device) + if cfg.data == "mnist": # set up mnist data # set partitioner @@ -59,6 +64,7 @@ def main(cfg: DictConfig): num_classes=num_classes, model=model, cfg=cfg, + device=device ) elif cfg.data == "simII": @@ -84,6 +90,7 @@ def main(cfg: DictConfig): num_classes=num_classes, model=model, cfg=cfg, + device=device ) # initialize global model to all zeros @@ -96,7 +103,7 @@ def main(cfg: DictConfig): strategy_fedht = FedHT( min_available_clients=cfg.strategy.min_available_clients, num_keep=cfg.num_keep, - evaluate_fn=get_evaluate_fn(testloader, model), + evaluate_fn=get_evaluate_fn(testloader, model, device), on_fit_config_fn=fit_round, iterht=cfg.iterht, initial_parameters=init_params, @@ -105,7 +112,7 @@ def main(cfg: DictConfig): # define strategy: fedavg strategy_fedavg = fl.server.strategy.FedAvg( min_available_clients=cfg.strategy.min_available_clients, - evaluate_fn=get_evaluate_fn(testloader, model), + evaluate_fn=get_evaluate_fn(testloader, model, device), on_fit_config_fn=fit_round, initial_parameters=init_params, ) @@ -125,7 +132,10 @@ def main(cfg: DictConfig): num_clients=cfg.num_clients, config=fl.server.ServerConfig(num_rounds=cfg.num_rounds), strategy=strategy, - client_resources={"num_cpus": cfg.client_resources.num_cpus}, + client_resources={ + "num_cpus": cfg.client_resources.num_cpus, + "num_gpus": cfg.client_resources.num_gpus, + }, ) if cfg.iterht: diff --git a/baselines/fedht/fedht/model.py b/baselines/fedht/fedht/model.py index fd53ceb1c479..7f2ba79a3f75 100644 --- a/baselines/fedht/fedht/model.py +++ b/baselines/fedht/fedht/model.py @@ -27,7 +27,7 @@ def forward(self, input_tensor: torch.Tensor) -> torch.Tensor: # define train function that will be called by each client to train the model -def train(model, trainloader: DataLoader, cfg: DictConfig) -> None: +def train(model, trainloader: DataLoader, cfg: DictConfig, device: torch.device) -> None: """Train model.""" criterion = nn.CrossEntropyLoss() optimizer = optim.SGD( @@ -38,7 +38,7 @@ def train(model, trainloader: DataLoader, cfg: DictConfig) -> None: for _epoch in range(cfg.num_local_epochs): for _i, data in enumerate(trainloader): - inputs, labels = data["image"], data["label"] + inputs, labels = data["image"].to(device), data["label"].to(device) # Zero the gradients optimizer.zero_grad() @@ -53,7 +53,7 @@ def train(model, trainloader: DataLoader, cfg: DictConfig) -> None: optimizer.step() -def test(model, testloader: DataLoader): +def test(model, testloader: DataLoader, device: torch.device): """Test model.""" criterion = nn.CrossEntropyLoss() @@ -65,7 +65,7 @@ def test(model, testloader: DataLoader): with torch.no_grad(): for _i, data in enumerate(testloader): - images, labels = data["image"], data["label"] + images, labels = data["image"].to(device), data["label"].to(device) outputs = model(images.float()) total += labels.size(0) diff --git a/baselines/fedht/fedht/server.py b/baselines/fedht/fedht/server.py index 0e215cfc1c60..e79c2edf876a 100644 --- a/baselines/fedht/fedht/server.py +++ b/baselines/fedht/fedht/server.py @@ -14,7 +14,7 @@ def fit_round(server_round: int) -> Dict: return {"server_round": server_round} -def get_evaluate_fn(testloader, model): +def get_evaluate_fn(testloader, model, device: torch.device): """Get evaluate function for centralized metrics.""" # global evaluation @@ -24,8 +24,9 @@ def evaluate(server_round, parameters, config): # type: ignore params_dict = zip(model.state_dict().keys(), parameters) state_dict = OrderedDict({k: torch.Tensor(v) for k, v in params_dict}) model.load_state_dict(state_dict, strict=True) + model.to(device) - loss, accuracy = test(model, testloader) + loss, accuracy = test(model, testloader, device) return loss, {"accuracy": accuracy} return evaluate From 42d695195fb8f8e48e778725d3a6e08148cd6f4b Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Fri, 1 Nov 2024 22:44:42 -0400 Subject: [PATCH 40/99] gpu support --- baselines/fedht/fedht/client.py | 2 +- baselines/fedht/fedht/main.py | 1 - baselines/fedht/pyproject.toml | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/baselines/fedht/fedht/client.py b/baselines/fedht/fedht/client.py index bf9afbcf1f3e..63818f440cf2 100644 --- a/baselines/fedht/fedht/client.py +++ b/baselines/fedht/fedht/client.py @@ -152,7 +152,7 @@ def evaluate(self, parameters, config): self.model.load_state_dict(state_dict, strict=True) # need to change from log_loss to torch.loss and change other metrics - loss, accuracy = test(self.model, self.trainloader) + loss, accuracy = test(self.model, self.trainloader, self.device) return loss, self.num_obs, {"accuracy": accuracy} diff --git a/baselines/fedht/fedht/main.py b/baselines/fedht/fedht/main.py index d77455a2caaf..10f2c99f576f 100644 --- a/baselines/fedht/fedht/main.py +++ b/baselines/fedht/fedht/main.py @@ -36,7 +36,6 @@ def main(cfg: DictConfig): # this vs. setting in cfg; what is preferred? device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") print(device) - if cfg.data == "mnist": # set up mnist data # set partitioner diff --git a/baselines/fedht/pyproject.toml b/baselines/fedht/pyproject.toml index c36345e2d41e..59a1969b336a 100644 --- a/baselines/fedht/pyproject.toml +++ b/baselines/fedht/pyproject.toml @@ -20,8 +20,8 @@ flwr-datasets = { extras = ["vision"], version = ">=0.3.0" } scikit-learn =">=1.1.1" matplotlib = ">=3.9.2" hydra-core = "1.3.2" -torch = ">=2.4.1" -torchvision = ">=0.19.1" +torch = "2.0.1+cu117" +torchvision = "0.15.2+cu117" numpy = ">=1.21.0, <2.0.0" [tool.poetry.dev-dependencies] From d632f12b0268ba8ea0aca9c51c9271953f8d734e Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Fri, 1 Nov 2024 22:46:21 -0400 Subject: [PATCH 41/99] Update README.md --- baselines/fedht/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/baselines/fedht/README.md b/baselines/fedht/README.md index c9b9a41dd47d..92c7c9dd89fc 100644 --- a/baselines/fedht/README.md +++ b/baselines/fedht/README.md @@ -60,7 +60,7 @@ The data generation procedure for the simulated dataset matches that of Simulati | `num_keep` | `500` | | `learning_rate` | `0.0005` | | `weight_decay` | `0.000` | -| `client resources` | `{'num_cpus': 2}` | +| `client resources` | `{'num_cpus': 2, 'num_gpus':.5}` | | `iterht` | `False` | | Description | Default Value (Simulation II) | @@ -72,7 +72,7 @@ The data generation procedure for the simulated dataset matches that of Simulati | `num_keep` | `200` | | `learning_rate` | `0.0001` | | `weight_decay` | `0.000` | -| `client resources` | `{'num_cpus': 2}` | +| `client resources` | `{'num_cpus': 2, 'num_gpus':.5}` | | `iterht` | `False` | We note that in the current implementation, only weights (and not biases) of the model(s) are subject to hardthresholding; this practice aligns with sparse model literature. Additionally, the `num_keep` hardthresholding parameter is enforced at the output layer level, as opposed to constraining the number of parameters across the entire model. Specifically, for a fully connected layer with $i$ inputs and $j$ outputs, the $j$-th output's parameters are constrained by `num_keep`. From 7397eb891d521ed15eff519738e13543d10211db Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sat, 2 Nov 2024 09:40:49 -0400 Subject: [PATCH 42/99] fedht as fedavg class --- baselines/fedht/fedht/fedht.py | 116 +-------------------------------- 1 file changed, 3 insertions(+), 113 deletions(-) diff --git a/baselines/fedht/fedht/fedht.py b/baselines/fedht/fedht/fedht.py index 550a1b869253..bb4f319a50ee 100644 --- a/baselines/fedht/fedht/fedht.py +++ b/baselines/fedht/fedht/fedht.py @@ -33,7 +33,7 @@ from flwr.common.logger import log from flwr.server.client_manager import ClientManager from flwr.server.client_proxy import ClientProxy -from flwr.server.strategy.strategy import Strategy +from flwr.server.strategy.fedavg import FedAvg from fedht.aggregate import aggregate_hardthreshold, weighted_loss_avg @@ -46,7 +46,7 @@ # pylint: disable=line-too-long -class FedHT(Strategy): +class FedHT(FedAvg): """Federated Hardthreshold strategy. Implementation based on https://arxiv.org/abs/1602.05629 @@ -139,88 +139,9 @@ def __init__( def __repr__(self) -> str: """Compute a string representation of the strategy.""" - rep = f"FedAvg(accept_failures={self.accept_failures})" + rep = f"FedHT(accept_failures={self.accept_failures})" return rep - def num_fit_clients(self, num_available_clients: int) -> Tuple[int, int]: - """Return the sample size and the required number of available clients.""" - num_clients = int(num_available_clients * self.fraction_fit) - return max(num_clients, self.min_fit_clients), self.min_available_clients - - def num_evaluation_clients(self, num_available_clients: int) -> Tuple[int, int]: - """Use a fraction of available clients for evaluation.""" - num_clients = int(num_available_clients * self.fraction_evaluate) - return max(num_clients, self.min_evaluate_clients), self.min_available_clients - - def initialize_parameters( - self, client_manager: ClientManager - ) -> Optional[Parameters]: - """Initialize global model parameters.""" - initial_parameters = self.initial_parameters - self.initial_parameters = None # Don't keep initial parameters in memory - return initial_parameters - - def evaluate( - self, server_round: int, parameters: Parameters - ) -> Optional[Tuple[float, Dict[str, Scalar]]]: - """Evaluate model parameters using an evaluation function.""" - if self.evaluate_fn is None: - # No evaluation function provided - return None - parameters_ndarrays = parameters_to_ndarrays(parameters) - eval_res = self.evaluate_fn(server_round, parameters_ndarrays, {}) - if eval_res is None: - return None - loss, metrics = eval_res - return loss, metrics - - def configure_fit( - self, server_round: int, parameters: Parameters, client_manager: ClientManager - ) -> List[Tuple[ClientProxy, FitIns]]: - """Configure the next round of training.""" - config = {} - if self.on_fit_config_fn is not None: - # Custom fit config function provided - config = self.on_fit_config_fn(server_round) - fit_ins = FitIns(parameters, config) - - # Sample clients - sample_size, min_num_clients = self.num_fit_clients( - client_manager.num_available() - ) - clients = client_manager.sample( - num_clients=sample_size, min_num_clients=min_num_clients - ) - - # Return client/config pairs - return [(client, fit_ins) for client in clients] - - def configure_evaluate( - self, server_round: int, parameters: Parameters, client_manager: ClientManager - ) -> List[Tuple[ClientProxy, EvaluateIns]]: - """Configure the next round of evaluation.""" - # Do not configure federated evaluation if fraction eval is 0. - if self.fraction_evaluate == 0.0: - return [] - - # Parameters and config - config = {} - if self.on_evaluate_config_fn is not None: - # Custom evaluation config function provided - config = self.on_evaluate_config_fn(server_round) - evaluate_ins = EvaluateIns(parameters, config) - - # Sample clients - sample_size, min_num_clients = self.num_evaluation_clients( - client_manager.num_available() - ) - clients = client_manager.sample( - num_clients=sample_size, min_num_clients=min_num_clients - ) - - # Return client/config pairs - return [(client, evaluate_ins) for client in clients] - def aggregate_fit( self, server_round: int, @@ -255,34 +176,3 @@ def aggregate_fit( log(WARNING, "No fit_metrics_aggregation_fn provided") return parameters_aggregated, metrics_aggregated - - def aggregate_evaluate( - self, - server_round: int, - results: List[Tuple[ClientProxy, EvaluateRes]], - failures: List[Union[Tuple[ClientProxy, EvaluateRes], BaseException]], - ) -> Tuple[Optional[float], Dict[str, Scalar]]: - """Aggregate evaluation losses using weighted average.""" - if not results: - return None, {} - # Do not aggregate if there are failures and failures are not accepted - if not self.accept_failures and failures: - return None, {} - - # Aggregate loss - loss_aggregated = weighted_loss_avg( - [ - (evaluate_res.num_examples, evaluate_res.loss) - for _, evaluate_res in results - ] - ) - - # Aggregate custom metrics if aggregation fn was provided - metrics_aggregated = {} - if self.evaluate_metrics_aggregation_fn: - eval_metrics = [(res.num_examples, res.metrics) for _, res in results] - metrics_aggregated = self.evaluate_metrics_aggregation_fn(eval_metrics) - elif server_round == 1: # Only log this warning once - log(WARNING, "No evaluate_metrics_aggregation_fn provided") - - return loss_aggregated, metrics_aggregated From 085b5d4bc8828bdcbc93cb1d0fa7ef6f7c2189bf Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sat, 2 Nov 2024 09:41:12 -0400 Subject: [PATCH 43/99] code cleanup --- baselines/fedht/fedht/fedht.py | 1 - 1 file changed, 1 deletion(-) diff --git a/baselines/fedht/fedht/fedht.py b/baselines/fedht/fedht/fedht.py index bb4f319a50ee..fb6402954cb4 100644 --- a/baselines/fedht/fedht/fedht.py +++ b/baselines/fedht/fedht/fedht.py @@ -31,7 +31,6 @@ parameters_to_ndarrays, ) from flwr.common.logger import log -from flwr.server.client_manager import ClientManager from flwr.server.client_proxy import ClientProxy from flwr.server.strategy.fedavg import FedAvg From 737c71d287a7a407420340d5c355dc7f595c946b Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sat, 2 Nov 2024 09:54:26 -0400 Subject: [PATCH 44/99] removing debug lines --- baselines/fedht/fedht/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/baselines/fedht/fedht/main.py b/baselines/fedht/fedht/main.py index 10f2c99f576f..7acd8bd7a672 100644 --- a/baselines/fedht/fedht/main.py +++ b/baselines/fedht/fedht/main.py @@ -35,7 +35,7 @@ def main(cfg: DictConfig): # this vs. setting in cfg; what is preferred? device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") - print(device) + if cfg.data == "mnist": # set up mnist data # set partitioner From 76f01d5ff3c32ac1fb3608dc27e847ad29e77f33 Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Sat, 2 Nov 2024 11:50:34 -0400 Subject: [PATCH 45/99] Update README.md --- baselines/fedht/README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/baselines/fedht/README.md b/baselines/fedht/README.md index 92c7c9dd89fc..8cf3cb06e806 100644 --- a/baselines/fedht/README.md +++ b/baselines/fedht/README.md @@ -85,7 +85,10 @@ python -m fedht.main --config-name base_mnist agg=fedht num_keep=500 num_local_e python -m fedht.main --config-name base_mnist agg=fedht iterht=True num_keep=500 num_local_epochs=10 learning_rate=0.00005 python -m fedht.main --config-name base_mnist agg=fedht num_keep=500 num_local_epochs=1 learning_rate=0.00005 ``` - + +| *Experiments: Comparison of Aggregation Approaches to Fed-HT for MNIST* | +|:--:| +| ![loss_results_mnist.png](_static/loss_results_mnist.png) | ### Simulation II (`num_keep` = 200) ``` @@ -94,4 +97,7 @@ python -m fedht.main --config-name base_simII agg=fedht num_local_epochs=5 learn python -m fedht.main --config-name base_simII agg=fedht iterht=True num_local_epochs=5 learning_rate=0.01 python -m fedht.main --config-name base_simII agg=fedht num_local_epochs=1 learning_rate=0.01 ``` - + +| *Experiments: Comparison of Aggregation Approaches to Fed-HT for Simulation II* | +|:--:| +| ![loss_results_simII.png](_static/loss_results_simII.png) | From 97b7bf7d05fea1c4ce507e4f110e125390d9ca5a Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sat, 2 Nov 2024 12:02:57 -0400 Subject: [PATCH 46/99] feat(baselines) adjusting fedht --- baselines/fedht/fedht/fedht.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/baselines/fedht/fedht/fedht.py b/baselines/fedht/fedht/fedht.py index fb6402954cb4..85bb207fb5ef 100644 --- a/baselines/fedht/fedht/fedht.py +++ b/baselines/fedht/fedht/fedht.py @@ -19,9 +19,6 @@ from typing import Callable, Dict, List, Optional, Tuple, Union from flwr.common import ( - EvaluateIns, - EvaluateRes, - FitIns, FitRes, MetricsAggregationFn, NDArrays, From 682a3a1be40975fce4b158187e14d57741205f15 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Mon, 4 Nov 2024 22:27:13 -0500 Subject: [PATCH 47/99] gpu support by default in config for both experiments --- baselines/fedht/fedht/conf/base_mnist.yaml | 7 +------ baselines/fedht/fedht/conf/base_simII.yaml | 7 +------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/baselines/fedht/fedht/conf/base_mnist.yaml b/baselines/fedht/fedht/conf/base_mnist.yaml index 84bf5d159ae6..47a8ee7b4041 100644 --- a/baselines/fedht/fedht/conf/base_mnist.yaml +++ b/baselines/fedht/fedht/conf/base_mnist.yaml @@ -13,12 +13,7 @@ data: mnist client_resources: num_cpus: 7 - num_gpus: .5 - -server_device: cpu - -use_cuda: false -specified_device: null # the ID of cuda device, if null, then use defaults torch.device("cuda") + num_gpus: 1 dataset: name: mnist diff --git a/baselines/fedht/fedht/conf/base_simII.yaml b/baselines/fedht/fedht/conf/base_simII.yaml index dd52cabfa7a4..d85a26166613 100644 --- a/baselines/fedht/fedht/conf/base_simII.yaml +++ b/baselines/fedht/fedht/conf/base_simII.yaml @@ -13,12 +13,7 @@ data: simII client_resources: num_cpus: 4 - num_gpus: .5 - -server_device: cpu - -use_cuda: false -specified_device: null # the ID of cuda device, if null, then use defaults torch.device("cuda") + num_gpus: 1 dataset: name: mnist From 4d3cf24783b69b4a9758b2ecb1464626796686c6 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Thu, 7 Nov 2024 12:33:17 -0500 Subject: [PATCH 48/99] feat(baselines) changing filenames for runs --- baselines/fedht/fedht/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/baselines/fedht/fedht/main.py b/baselines/fedht/fedht/main.py index 7acd8bd7a672..5b4d941407ee 100644 --- a/baselines/fedht/fedht/main.py +++ b/baselines/fedht/fedht/main.py @@ -143,7 +143,8 @@ def main(cfg: DictConfig): iterstr = "" filename = ( - "simII_" + cfg.data + + "_" + cfg.agg + iterstr + "_local" From 4346deb93bb49f9852c331a37f2bef44659d030d Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Thu, 7 Nov 2024 20:38:11 -0500 Subject: [PATCH 49/99] feat(baselines) adding fedmss baseline --- baselines/fedmss/.gitignore | 160 ++++++++++++++++++++++++ baselines/fedmss/README.md | 43 +++++++ baselines/fedmss/fedmss/__init__.py | 1 + baselines/fedmss/fedmss/aggregate.py | 137 +++++++++++++++++++++ baselines/fedmss/fedmss/client.py | 78 ++++++++++++ baselines/fedmss/fedmss/fedht.py | 174 +++++++++++++++++++++++++++ baselines/fedmss/fedmss/main.py | 159 ++++++++++++++++++++++++ baselines/fedmss/fedmss/model.py | 82 +++++++++++++ baselines/fedmss/fedmss/server.py | 30 +++++ baselines/fedmss/fedmss/utils.py | 54 +++++++++ baselines/fedmss/pyproject.toml | 128 ++++++++++++++++++++ 11 files changed, 1046 insertions(+) create mode 100644 baselines/fedmss/.gitignore create mode 100644 baselines/fedmss/README.md create mode 100644 baselines/fedmss/fedmss/__init__.py create mode 100644 baselines/fedmss/fedmss/aggregate.py create mode 100644 baselines/fedmss/fedmss/client.py create mode 100644 baselines/fedmss/fedmss/fedht.py create mode 100644 baselines/fedmss/fedmss/main.py create mode 100644 baselines/fedmss/fedmss/model.py create mode 100644 baselines/fedmss/fedmss/server.py create mode 100644 baselines/fedmss/fedmss/utils.py create mode 100644 baselines/fedmss/pyproject.toml diff --git a/baselines/fedmss/.gitignore b/baselines/fedmss/.gitignore new file mode 100644 index 000000000000..68bc17f9ff21 --- /dev/null +++ b/baselines/fedmss/.gitignore @@ -0,0 +1,160 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/baselines/fedmss/README.md b/baselines/fedmss/README.md new file mode 100644 index 000000000000..522939ac82d2 --- /dev/null +++ b/baselines/fedmss/README.md @@ -0,0 +1,43 @@ +# fedmss + +## Install dependencies + +```bash +pip install . +``` + +## Run (Simulation Engine) + +In the `fedmss` directory, use `flwr run` to run a local simulation: + +```bash +flwr run +``` + +## Run (Deployment Engine) + +### Start the SuperLink + +```bash +flower-superlink --insecure +``` + +### Start the long-running Flower client + +In a new terminal window, start the first long-running Flower client: + +```bash +flower-client-app client:app --insecure +``` + +In yet another new terminal window, start the second long-running Flower client: + +```bash +flower-client-app client:app --insecure +``` + +### Start the ServerApp + +```bash +flower-server-app server:app --insecure +``` diff --git a/baselines/fedmss/fedmss/__init__.py b/baselines/fedmss/fedmss/__init__.py new file mode 100644 index 000000000000..940414f17601 --- /dev/null +++ b/baselines/fedmss/fedmss/__init__.py @@ -0,0 +1 @@ +"""FedMSS package.""" diff --git a/baselines/fedmss/fedmss/aggregate.py b/baselines/fedmss/fedmss/aggregate.py new file mode 100644 index 000000000000..cbb303e20861 --- /dev/null +++ b/baselines/fedmss/fedmss/aggregate.py @@ -0,0 +1,137 @@ +# Copyright 2020 Flower Labs GmbH. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""Aggregation functions for strategy implementations.""" +# mypy: disallow_untyped_calls=False + +from functools import reduce +from typing import List, Tuple + +import numpy as np +from flwr.common import NDArrays + +def weighted_loss_avg(results: List[Tuple[int, float]]) -> float: + """Aggregate evaluation results obtained from multiple clients.""" + num_total_evaluation_examples = sum(num_examples for (num_examples, _) in results) + weighted_losses = [num_examples * loss for num_examples, loss in results] + return sum(weighted_losses) / num_total_evaluation_examples + +# calls hardthreshold function for each list element in weights_all +def hardthreshold_list(weights_all, num_keep: int) -> NDArrays: + """Call hardthreshold.""" + + params = [ + hardthreshold(each, num_keep) for each in weights_all + ] + return [params] + +# hardthreshold function applied to array +def hardthreshold(weights_prime, num_keep: int) -> NDArrays: + """Perform hardthresholding on single array.""" + # check for len of array + val_len = weights_prime.size + + # intercepts not hardthresholded + if val_len > 1: + if num_keep > val_len: + params = weights_prime + print( + "num_keep parameter greater than length of vector. All parameters kept." + ) + else: + # Compute the magnitudes + magnitudes = np.abs(weights_prime) + + # Get the k-th largest value in the vector + threshold = np.partition(magnitudes, -num_keep)[-num_keep] + + # Create a new vector where values below the threshold are set to zero + params = np.where(magnitudes >= threshold, weights_prime, 0) + + else: + params = weights_prime + + return np.array(params) + +# hardthreshold aggregation +def aggregate_hardthreshold( + results: List[Tuple[NDArrays, int]], num_keep: int, iterht: bool +) -> NDArrays: + """Apply hard thresholding to keep only the k largest weights. + + Fed-HT (Fed-IterHT) can be found at + https://arxiv.org/abs/2101.00052 + """ + if num_keep <= 0: + raise ValueError("k must be a positive integer.") + + """Compute weighted average.""" + # Calculate the total number of examples used during training + num_examples_total = sum(num_examples for (_, num_examples) in results) + + green = "\033[92m" + reset = "\033[0m" + + # check for iterht=True; set in cfg + if iterht: + print( + f"{green}INFO {reset}:\t\tUsing Fed-IterHT with num_keep = ", + num_keep, + ) + + # apply across all models within each client + + # 'results' is a collection of tuples of the form (params, num_obs) + # we want to adjust the 'params' portion of that tuple + # i: iterates through clients + # ignoring second element in results[i] skips over number of observations + # j: iterates through all layers of a model + # k: iterates through the slices of each layer + for i in range(len(results)): + for j in range(len(results[i][0])): + for k in range(len(results[i][0][j])): + results[i][0][j][k] = hardthreshold(results[i][0][j][k], num_keep) + + weighted_weights1 = [ + [layer * num_examples for layer in weights] + for weights, num_examples in results + ] + weighted_weights2 = weighted_weights1 + + else: + print( + f"{green}INFO {reset}:\t\tUsing Fed-HT with num_keep = ", + num_keep, + ) + + weighted_weights1 = [ + [layer * num_examples for layer in weights] + for weights, num_examples in results + ] + weighted_weights2 = weighted_weights1 + + hold = [ + reduce(np.add, layer_updates) / num_examples_total + for layer_updates in zip(*weighted_weights2) + ] + + new_result: NDArrays = [ + val + for sublist in ( + hardthreshold_list(layer_updates, num_keep) for layer_updates in hold + ) + for val in sublist + ] + + return new_result diff --git a/baselines/fedmss/fedmss/client.py b/baselines/fedmss/fedmss/client.py new file mode 100644 index 000000000000..4c2fa998b922 --- /dev/null +++ b/baselines/fedmss/fedmss/client.py @@ -0,0 +1,78 @@ +"""Generate client for fedht baseline.""" + +from typing import cast + +import torch +import warnings + +from flwr.client import Client, NumPyClient +from flwr.common import Context +from omegaconf import DictConfig + +from fedmss.utils import set_model_params, get_model_parameters +from sklearn.metrics import log_loss + + +# UCI-HD client +class UCIHDClient(NumPyClient): + """Define UCIHDClient class.""" + + def __init__( + self, + trainloader, + testloader, + model, + num_obs, + num_features, + num_classes, + cfg: DictConfig, + device + ) -> None: + """UCIHD client for UCI-HD experimentation.""" + self.X_train, self.y_train = trainloader + self.X_test, self.y_test = testloader + self.model = model + self.num_obs = num_obs + self.num_features = num_features + self.num_classes = num_classes + self.cfg = cfg + self.device = device + + def fit(self, parameters, config): + set_model_params(self.model, parameters, self.cfg) + # Ignore convergence failure due to low local epochs + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + self.model.fit(self.X_train, self.y_train) + return get_model_parameters(self.model, self.cfg), len(self.X_train), {} + + def evaluate(self, parameters, config): + set_model_params(self.model, parameters, self.cfg) + loss = log_loss(self.y_test, self.model.predict_proba(self.X_test)) + accuracy = self.model.score(self.X_test, self.y_test) + return loss, len(self.X_test), {"accuracy": accuracy} + + +# client fn for input into simulation +def generate_client_fn_ucihd( + dataset, num_features, num_classes, model, cfg: DictConfig, device: torch.device +): + """Generate client function for simulated FL.""" + + # def client_fn(cid: int): + def client_fn(context: Context) -> Client: + """Define client function for centralized metrics.""" + # Get node_config value to fetch partition_id + partition_id = cast(int, context.node_config["partition-id"]) + + # Load the partition data + X_train, y_train, X_val, y_val = dataset + train_dataset = X_train[int(partition_id)], y_train[int(partition_id)] + test_dataset = X_val[int(partition_id)], y_val[int(partition_id)] + num_obs = train_dataset[1].shape[0] + + return UCIHDClient( + train_dataset, test_dataset, model, num_obs, num_features, num_classes, cfg, device + ).to_client() + + return client_fn diff --git a/baselines/fedmss/fedmss/fedht.py b/baselines/fedmss/fedmss/fedht.py new file mode 100644 index 000000000000..85bb207fb5ef --- /dev/null +++ b/baselines/fedmss/fedmss/fedht.py @@ -0,0 +1,174 @@ +# Copyright 2020 Flower Labs GmbH. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""Federated Hardthresholding (FedHT).""" + + +from logging import WARNING +from typing import Callable, Dict, List, Optional, Tuple, Union + +from flwr.common import ( + FitRes, + MetricsAggregationFn, + NDArrays, + Parameters, + Scalar, + ndarrays_to_parameters, + parameters_to_ndarrays, +) +from flwr.common.logger import log +from flwr.server.client_proxy import ClientProxy +from flwr.server.strategy.fedavg import FedAvg + +from fedht.aggregate import aggregate_hardthreshold, weighted_loss_avg + +WARNING_MIN_AVAILABLE_CLIENTS_TOO_LOW = """ +Setting `min_available_clients` lower than `min_fit_clients` or +`min_evaluate_clients` can cause the server to fail when there are too few clients +connected to the server. `min_available_clients` must be set to a value larger +than or equal to the values of `min_fit_clients` and `min_evaluate_clients`. +""" + + +# pylint: disable=line-too-long +class FedHT(FedAvg): + """Federated Hardthreshold strategy. + + Implementation based on https://arxiv.org/abs/1602.05629 + + Parameters + ---------- + fraction_fit : float, optional + Fraction of clients used during training. In case `min_fit_clients` + is larger than `fraction_fit * available_clients`, `min_fit_clients` + will still be sampled. Defaults to 1.0. + fraction_evaluate : float, optional + Fraction of clients used during validation. In case `min_evaluate_clients` + is larger than `fraction_evaluate * available_clients`, + `min_evaluate_clients` will still be sampled. Defaults to 1.0. + num_keep : int, number of parameters to keep different from 0. Defaults to 5. + iterht : boolean, if true, utilizes the Fed-IterHT strategy. Defaults to False. + min_fit_clients : int, optional + Minimum number of clients used during training. Defaults to 2. + min_evaluate_clients : int, optional + Minimum number of clients used during validation. Defaults to 2. + min_available_clients : int, optional + Minimum number of total clients in the system. Defaults to 2. + evaluate_fn : Optional[Callable[[int, NDArrays, Dict[str, Scalar]], + Optional[Tuple[float, Dict[str, Scalar]]]]] + Optional function used for validation. Defaults to None. + on_fit_config_fn : Callable[[int], Dict[str, Scalar]], optional + Function used to configure training. Defaults to None. + on_evaluate_config_fn : Callable[[int], Dict[str, Scalar]], optional + Function used to configure validation. Defaults to None. + accept_failures : bool, optional + Whether or not accept rounds containing failures. Defaults to True. + initial_parameters : Parameters, optional + Initial global model parameters. + fit_metrics_aggregation_fn : Optional[MetricsAggregationFn] + Metrics aggregation function, optional. + evaluate_metrics_aggregation_fn : Optional[MetricsAggregationFn] + Metrics aggregation function, optional. + inplace : bool (default: True) + Enable (True) or disable (False) in-place aggregation of model updates. + """ + + # pylint: disable=too-many-arguments,too-many-instance-attributes, line-too-long + def __init__( + self, + *, + fraction_fit: float = 1.0, + fraction_evaluate: float = 1.0, + num_keep: int = 5, + iterht: bool = False, + min_fit_clients: int = 2, + min_evaluate_clients: int = 2, + min_available_clients: int = 2, + evaluate_fn: Optional[ + Callable[ + [int, NDArrays, Dict[str, Scalar]], + Optional[Tuple[float, Dict[str, Scalar]]], + ] + ] = None, + on_fit_config_fn: Optional[Callable[[int], Dict[str, Scalar]]] = None, + on_evaluate_config_fn: Optional[Callable[[int], Dict[str, Scalar]]] = None, + accept_failures: bool = True, + initial_parameters: Optional[Parameters] = None, + fit_metrics_aggregation_fn: Optional[MetricsAggregationFn] = None, + evaluate_metrics_aggregation_fn: Optional[MetricsAggregationFn] = None, + inplace: bool = True, + ) -> None: + super().__init__() + + if ( + min_fit_clients > min_available_clients + or min_evaluate_clients > min_available_clients + ): + log(WARNING, WARNING_MIN_AVAILABLE_CLIENTS_TOO_LOW) + + self.fraction_fit = fraction_fit + self.fraction_evaluate = fraction_evaluate + self.num_keep = num_keep + self.iterht = iterht + self.min_fit_clients = min_fit_clients + self.min_evaluate_clients = min_evaluate_clients + self.min_available_clients = min_available_clients + self.evaluate_fn = evaluate_fn + self.on_fit_config_fn = on_fit_config_fn + self.on_evaluate_config_fn = on_evaluate_config_fn + self.accept_failures = accept_failures + self.initial_parameters = initial_parameters + self.fit_metrics_aggregation_fn = fit_metrics_aggregation_fn + self.evaluate_metrics_aggregation_fn = evaluate_metrics_aggregation_fn + self.inplace = inplace + + def __repr__(self) -> str: + """Compute a string representation of the strategy.""" + rep = f"FedHT(accept_failures={self.accept_failures})" + return rep + + def aggregate_fit( + self, + server_round: int, + results: List[Tuple[ClientProxy, FitRes]], + failures: List[Union[Tuple[ClientProxy, FitRes], BaseException]], + ) -> Tuple[Optional[Parameters], Dict[str, Scalar]]: + """Aggregate fit results using weighted average.""" + if not results: + return None, {} + # Do not aggregate if there are failures and failures are not accepted + if not self.accept_failures and failures: + return None, {} + + # no in-place aggregation for FedHT + weights_results = [ + (parameters_to_ndarrays(fit_res.parameters), fit_res.num_examples) + for _, fit_res in results + ] + + # use hardthresholding + aggregated_ndarrays = aggregate_hardthreshold( + weights_results, self.num_keep, self.iterht + ) + parameters_aggregated = ndarrays_to_parameters(aggregated_ndarrays) + + # Aggregate custom metrics if aggregation fn was provided + metrics_aggregated = {} + if self.fit_metrics_aggregation_fn: + fit_metrics = [(res.num_examples, res.metrics) for _, res in results] + metrics_aggregated = self.fit_metrics_aggregation_fn(fit_metrics) + elif server_round == 1: # Only log this warning once + log(WARNING, "No fit_metrics_aggregation_fn provided") + + return parameters_aggregated, metrics_aggregated diff --git a/baselines/fedmss/fedmss/main.py b/baselines/fedmss/fedmss/main.py new file mode 100644 index 000000000000..cfe9e1be9877 --- /dev/null +++ b/baselines/fedmss/fedmss/main.py @@ -0,0 +1,159 @@ +"""Run main for fedmss baseline.""" + +import pickle +import random +import torch +import flwr as fl +import hydra + +import numpy as np +from flwr.common import NDArrays, ndarrays_to_parameters +from flwr.server.strategy.strategy import Strategy +from omegaconf import DictConfig +from sklearn.model_selection import train_test_split, KFold + +from fedmss.client import generate_client_fn_ucihd +from fedmss.fedht import FedHT +from fedmss.server import fit_round, get_evaluate_fn +from fedmss.utils import create_log_reg_and_instantiate_parameters, get_model_parameters +from ucimlrepo import fetch_ucirepo + +@hydra.main(config_path="conf", config_name="base_ucihd", version_base=None) +def main(cfg: DictConfig): + """Run main file for fedmss baseline. + + Parameters + ---------- + cfg : DictConfig + Config file for federated baseline; read from fedht/conf. + """ + # set seed + random.seed(2024) + + # this vs. setting in cfg; what is preferred? + device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + + if cfg.data == "ucihd": + + # load UCI-HD data + num_features = 13 + num_classes = 2 # binary classification + + # fetch dataset + heart_disease = fetch_ucirepo(id=45) + + # data (as pandas dataframes) + Xall = heart_disease.data.features + yall = heart_disease.data.targets + + mask = np.isnan(Xall).any(axis=1) + + # Filter out rows with NaN values from both X and y + X = np.array(Xall[~mask]) + y = np.array(yall[~mask]) + y = y > 0 + + # Split the data into training and testing sets (80% train, 20% test) + X_train_all, X_test, y_train_all, y_test = train_test_split(X, y, test_size=0.2, random_state=42) + test_dataset = X_test, y_test + + # partition train data into clients + kf = KFold(n_splits=cfg.num_clients, shuffle=True, random_state=43) + X_train = [None] * cfg.num_clients + y_train = [None] * cfg.num_clients + X_val = [None] * cfg.num_clients + y_val = [None] * cfg.num_clients + for fold, (train_idx, val_idx) in enumerate(kf.split(X_train_all), 1): + X_train[fold-1], X_val[fold-1] = np.array(X_train_all)[train_idx,:], np.array(X_train_all)[val_idx,:] + y_train[fold-1], y_val[fold-1] = np.array(y_train_all)[train_idx,:], np.array(y_train_all)[val_idx,:] + + train_dataset = X_train, y_train, X_val, y_val + + # define model + model = create_log_reg_and_instantiate_parameters(cfg) + + #initial fit from first client + model.fit(X_train[0], y_train[0]) + ndarrays = get_model_parameters(model, cfg) + global_model_init = ndarrays_to_parameters(ndarrays) + + # set client function + client_fn = generate_client_fn_ucihd( + train_dataset, + num_features=cfg.num_features, + num_classes=cfg.num_classes, + model=model, + cfg=cfg, + device=device + ) + + # initialize global model to all zeros + # weights = np.zeros((num_classes, num_features)) + # bias = np.zeros(num_classes) + # init_params_arr: NDArrays = [weights, bias] + # init_params = ndarrays_to_parameters(init_params_arr) + + # define strategy: fedht + strategy_fedht = FedHT( + min_available_clients=cfg.strategy.min_available_clients, + num_keep=cfg.num_keep, + evaluate_fn=get_evaluate_fn(test_dataset, model, cfg), + on_fit_config_fn=fit_round, + iterht=cfg.iterht, + initial_parameters=global_model_init + ) + + # define strategy: fedavg + strategy_fedavg = fl.server.strategy.FedAvg( + min_available_clients=cfg.strategy.min_available_clients, + evaluate_fn=get_evaluate_fn(test_dataset, model, device), + on_fit_config_fn=fit_round, + initial_parameters=global_model_init + ) + + strategy: Strategy + if cfg.agg == "fedht": + strategy = strategy_fedht + elif cfg.agg == "fedavg": + strategy = strategy_fedavg + else: + print("Must select either fedht or fedavg for the aggregation strategy.") + + # # start simulation + random.seed(2025) + hist_mnist = fl.simulation.start_simulation( + client_fn=client_fn, + num_clients=cfg.num_clients, + config=fl.server.ServerConfig(num_rounds=cfg.num_rounds), + strategy=strategy, + client_resources={ + "num_cpus": cfg.client_resources.num_cpus, + "num_gpus": cfg.client_resources.num_gpus, + }, + ) + + if cfg.iterht: + iterstr = "iter" + else: + iterstr = "" + + filename = ( + cfg.data + + "_" + + cfg.agg + + iterstr + + "_local" + + str(cfg.num_local_epochs) + + "_lr" + + str(cfg.learning_rate) + + "_numkeep" + + str(cfg.num_keep) + + ".pkl" + ) + + with open(filename, "wb") as file: + pickle.dump(hist_mnist, file) + + +if __name__ == "__main__": + main() diff --git a/baselines/fedmss/fedmss/model.py b/baselines/fedmss/fedmss/model.py new file mode 100644 index 000000000000..7f2ba79a3f75 --- /dev/null +++ b/baselines/fedmss/fedmss/model.py @@ -0,0 +1,82 @@ +"""Define model for fedht baseline.""" + +import torch +import torch.nn as nn +import torch.optim as optim +from omegaconf import DictConfig +from torch.utils.data import DataLoader + + +# model code initially pulled from fedprox baseline +# generates multinomial logistic regression model via torch +class LogisticRegression(nn.Module): + """Define LogisticRegression class.""" + + def __init__(self, num_features, num_classes: int) -> None: + """Define model.""" + super().__init__() + + # one single linear layer + self.linear = nn.Linear(num_features, num_classes) + + def forward(self, input_tensor: torch.Tensor) -> torch.Tensor: + """Define forward pass.""" + # forward pass; sigmoid transform included in CBELoss criterion + output_tensor = self.linear(torch.flatten(input_tensor, 1)) + return output_tensor + + +# define train function that will be called by each client to train the model +def train(model, trainloader: DataLoader, cfg: DictConfig, device: torch.device) -> None: + """Train model.""" + criterion = nn.CrossEntropyLoss() + optimizer = optim.SGD( + model.parameters(), lr=cfg.learning_rate, weight_decay=cfg.weight_decay + ) + + # train + for _epoch in range(cfg.num_local_epochs): + for _i, data in enumerate(trainloader): + + inputs, labels = data["image"].to(device), data["label"].to(device) + + # Zero the gradients + optimizer.zero_grad() + + # Forward pass + model.train() + + loss = criterion(model(inputs.float()), labels.long()) + + # Backward pass and optimization + loss.backward() + optimizer.step() + + +def test(model, testloader: DataLoader, device: torch.device): + """Test model.""" + criterion = nn.CrossEntropyLoss() + + # initialize + correct, total, loss = 0, 0, 0.0 + + # put into evlauate mode + model.eval() + with torch.no_grad(): + for _i, data in enumerate(testloader): + + images, labels = data["image"].to(device), data["label"].to(device) + + outputs = model(images.float()) + total += labels.size(0) + loss += criterion(outputs, labels.long()).item() + _, predicted = torch.max(outputs.data, 1) + correct += (predicted == labels).sum().item() + + if len(testloader.dataset) == 0: + raise ValueError("Testloader can't be 0, exiting...") + + loss /= len(testloader) + accuracy = correct / total + + return loss, accuracy diff --git a/baselines/fedmss/fedmss/server.py b/baselines/fedmss/fedmss/server.py new file mode 100644 index 000000000000..7c426c56e8f5 --- /dev/null +++ b/baselines/fedmss/fedmss/server.py @@ -0,0 +1,30 @@ +"""Generate server for fedht baseline.""" + +from typing import Dict + +import torch + +from fedmss.utils import set_model_params, get_model_parameters +from sklearn.metrics import log_loss + + +# send fit round for history +def fit_round(server_round: int) -> Dict: + """Send round number to client.""" + return {"server_round": server_round} + + +def get_evaluate_fn(testloader, model, cfg): + """Get evaluate function for centralized metrics.""" + + # global evaluation + def evaluate(server_round, parameters, cfg): # type: ignore + """Define evaluate function for centralized metrics.""" + + set_model_params(model, parameters, cfg) + X_test, y_test = testloader + loss = log_loss(y_test, model.predict_proba(X_test)) + accuracy = model.score(X_test, y_test) + return loss, {"accuracy": accuracy} + + return evaluate diff --git a/baselines/fedmss/fedmss/utils.py b/baselines/fedmss/fedmss/utils.py new file mode 100644 index 000000000000..92ebfb59e5b5 --- /dev/null +++ b/baselines/fedmss/fedmss/utils.py @@ -0,0 +1,54 @@ +"""Execute utility functions for fedht baseline.""" + +import numpy as np +from sklearn.linear_model import SGDClassifier +from flwr.common.typing import NDArrays + +def set_model_params(model: SGDClassifier, params: NDArrays, cfg) -> SGDClassifier: + """Sets the parameters of a sklean LogisticRegression model.""" + model.coef_ = params[0] + if model.fit_intercept: + model.intercept_ = params[1] + return model + +def get_model_parameters(model: SGDClassifier, cfg) -> NDArrays: + """Returns the parameters of a sklearn LogisticRegression model.""" + if model.fit_intercept: + params = [ + model.coef_, + model.intercept_, + ] + else: + params = [ + model.coef_, + ] + return params + +def set_initial_params(model: SGDClassifier, cfg) -> None: + """Sets initial parameters as zeros Required since model params are uninitialized + until model.fit is called. + + But server asks for initial parameters from clients at launch. Refer to + sklearn.linear_model.LogisticRegression documentation for more information. + """ + model.classes_ = np.arange(cfg.num_classes) + + model.coef_ = np.zeros((1, cfg.num_features)) + if model.fit_intercept: + model.intercept_ = np.zeros((1,)) + + +def create_log_reg_and_instantiate_parameters(cfg): + """Helper function to create a LogisticRegression model.""" + model = SGDClassifier( + loss='log_loss', + learning_rate='constant', + tol=.001, + eta0=cfg.learning_rate, + max_iter=cfg.num_local_epochs, # local epoch + warm_start=True, # prevent refreshing weights when fitting, + ) + + # # Setting initial parameters, akin to model.compile for keras models + # set_initial_params(model, cfg) + return model diff --git a/baselines/fedmss/pyproject.toml b/baselines/fedmss/pyproject.toml new file mode 100644 index 000000000000..ef74355615d9 --- /dev/null +++ b/baselines/fedmss/pyproject.toml @@ -0,0 +1,128 @@ +[build-system] +requires = ["poetry-core>=1.4.0"] +build-backend = "poetry.masonry.api" + +[tool.poetry] +name = "fedmss" +version = "1.0.0" +description = "Federated nonconvex sparse learning" +license = "Apache-2.0" +authors = ["Chancellor Johnstone "] +readme = "README.md" +homepage = "https://flower.ai" +repository = "https://github.com/adap/flower" +documentation = "https://flower.ai" + +[tool.poetry.dependencies] +python = ">=3.10.0,<4.0" +flwr = { extras = ["simulation"], version = ">=1.11.0" } +flwr-datasets = { extras = ["vision"], version = ">=0.3.0" } +scikit-learn =">=1.1.1" +matplotlib = ">=3.9.2" +hydra-core = "1.3.2" +torch = "2.0.1+cu117" +torchvision = "0.15.2+cu117" +numpy = ">=1.21.0, <2.0.0" +ucimlrepo = ">=0.0.7" + +[tool.poetry.dev-dependencies] +isort = "==5.13.2" +black = "==24.2.0" +docformatter = "==1.7.5" +mypy = "==1.4.1" +pylint = "==2.8.2" +flake8 = "==3.9.2" +pytest = "==6.2.4" +pytest-watch = "==4.2.0" +ruff = "==0.0.272" +types-requests = "==2.27.7" +virtualenv = "==20.21.0" + +[tool.isort] +line_length = 88 +indent = " " +multi_line_output = 3 +include_trailing_comma = true +force_grid_wrap = 0 +use_parentheses = true + +[tool.black] +line-length = 88 +target-version = ["py38", "py39", "py310", "py311"] + +[tool.pytest.ini_options] +minversion = "6.2" +addopts = "-qq" +testpaths = ["flwr_baselines"] + +[tool.mypy] +ignore_missing_imports = true +strict = false +plugins = "numpy.typing.mypy_plugin" + +[tool.pylint."MESSAGES CONTROL"] +good-names = "i,j,k,_,x,y,X,Y" +signature-mutators = "hydra.main.main" + +[tool.pylint."TYPECHECK"] +generated-members = "numpy.*, torch.*, tensorflow.*" + +[[tool.mypy.overrides]] +module = ["importlib.metadata.*", "importlib_metadata.*"] +follow_imports = "skip" +follow_imports_for_stubs = true +disallow_untyped_calls = false + +[[tool.mypy.overrides]] +module = "torch.*" +follow_imports = "skip" +follow_imports_for_stubs = true + +[tool.docformatter] +wrap-summaries = 88 +wrap-descriptions = 88 + +[tool.ruff] +target-version = "py38" +line-length = 88 +select = ["D", "E", "F", "W", "B", "ISC", "C4"] +fixable = ["D", "E", "F", "W", "B", "ISC", "C4"] +ignore = ["B024", "B027"] +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".hg", + ".mypy_cache", + ".nox", + ".pants.d", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "venv", + "proto", +] + +[tool.ruff.pydocstyle] +convention = "numpy" + +[tool.flwr.app] +publisher = [ + "The Flower Authors ", + "Chancellor Johnstone ", +] + +[tool.flwr.federations] +default = "local-simulation" + +[tool.flwr.federations.local-simulation] +options.num-supernodes = 10 From 323fa26fc940a36800b49de1f81d1a9f148a67e2 Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Mon, 11 Nov 2024 13:42:45 -0500 Subject: [PATCH 50/99] Delete baselines/fedmss/.gitignore --- baselines/fedmss/.gitignore | 160 ------------------------------------ 1 file changed, 160 deletions(-) delete mode 100644 baselines/fedmss/.gitignore diff --git a/baselines/fedmss/.gitignore b/baselines/fedmss/.gitignore deleted file mode 100644 index 68bc17f9ff21..000000000000 --- a/baselines/fedmss/.gitignore +++ /dev/null @@ -1,160 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/#use-with-ide -.pdm.toml - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ From ddd7ad9db93c9ec7c533982853d9af4b087a2d7c Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Mon, 11 Nov 2024 13:43:12 -0500 Subject: [PATCH 51/99] Delete baselines/fedmss/pyproject.toml --- baselines/fedmss/pyproject.toml | 128 -------------------------------- 1 file changed, 128 deletions(-) delete mode 100644 baselines/fedmss/pyproject.toml diff --git a/baselines/fedmss/pyproject.toml b/baselines/fedmss/pyproject.toml deleted file mode 100644 index ef74355615d9..000000000000 --- a/baselines/fedmss/pyproject.toml +++ /dev/null @@ -1,128 +0,0 @@ -[build-system] -requires = ["poetry-core>=1.4.0"] -build-backend = "poetry.masonry.api" - -[tool.poetry] -name = "fedmss" -version = "1.0.0" -description = "Federated nonconvex sparse learning" -license = "Apache-2.0" -authors = ["Chancellor Johnstone "] -readme = "README.md" -homepage = "https://flower.ai" -repository = "https://github.com/adap/flower" -documentation = "https://flower.ai" - -[tool.poetry.dependencies] -python = ">=3.10.0,<4.0" -flwr = { extras = ["simulation"], version = ">=1.11.0" } -flwr-datasets = { extras = ["vision"], version = ">=0.3.0" } -scikit-learn =">=1.1.1" -matplotlib = ">=3.9.2" -hydra-core = "1.3.2" -torch = "2.0.1+cu117" -torchvision = "0.15.2+cu117" -numpy = ">=1.21.0, <2.0.0" -ucimlrepo = ">=0.0.7" - -[tool.poetry.dev-dependencies] -isort = "==5.13.2" -black = "==24.2.0" -docformatter = "==1.7.5" -mypy = "==1.4.1" -pylint = "==2.8.2" -flake8 = "==3.9.2" -pytest = "==6.2.4" -pytest-watch = "==4.2.0" -ruff = "==0.0.272" -types-requests = "==2.27.7" -virtualenv = "==20.21.0" - -[tool.isort] -line_length = 88 -indent = " " -multi_line_output = 3 -include_trailing_comma = true -force_grid_wrap = 0 -use_parentheses = true - -[tool.black] -line-length = 88 -target-version = ["py38", "py39", "py310", "py311"] - -[tool.pytest.ini_options] -minversion = "6.2" -addopts = "-qq" -testpaths = ["flwr_baselines"] - -[tool.mypy] -ignore_missing_imports = true -strict = false -plugins = "numpy.typing.mypy_plugin" - -[tool.pylint."MESSAGES CONTROL"] -good-names = "i,j,k,_,x,y,X,Y" -signature-mutators = "hydra.main.main" - -[tool.pylint."TYPECHECK"] -generated-members = "numpy.*, torch.*, tensorflow.*" - -[[tool.mypy.overrides]] -module = ["importlib.metadata.*", "importlib_metadata.*"] -follow_imports = "skip" -follow_imports_for_stubs = true -disallow_untyped_calls = false - -[[tool.mypy.overrides]] -module = "torch.*" -follow_imports = "skip" -follow_imports_for_stubs = true - -[tool.docformatter] -wrap-summaries = 88 -wrap-descriptions = 88 - -[tool.ruff] -target-version = "py38" -line-length = 88 -select = ["D", "E", "F", "W", "B", "ISC", "C4"] -fixable = ["D", "E", "F", "W", "B", "ISC", "C4"] -ignore = ["B024", "B027"] -exclude = [ - ".bzr", - ".direnv", - ".eggs", - ".git", - ".hg", - ".mypy_cache", - ".nox", - ".pants.d", - ".pytype", - ".ruff_cache", - ".svn", - ".tox", - ".venv", - "__pypackages__", - "_build", - "buck-out", - "build", - "dist", - "node_modules", - "venv", - "proto", -] - -[tool.ruff.pydocstyle] -convention = "numpy" - -[tool.flwr.app] -publisher = [ - "The Flower Authors ", - "Chancellor Johnstone ", -] - -[tool.flwr.federations] -default = "local-simulation" - -[tool.flwr.federations.local-simulation] -options.num-supernodes = 10 From 9eb934e881085ccc4014930486dfb5c92346def8 Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Mon, 11 Nov 2024 13:43:54 -0500 Subject: [PATCH 52/99] Delete baselines/fedmss/README.md --- baselines/fedmss/README.md | 43 -------------------------------------- 1 file changed, 43 deletions(-) delete mode 100644 baselines/fedmss/README.md diff --git a/baselines/fedmss/README.md b/baselines/fedmss/README.md deleted file mode 100644 index 522939ac82d2..000000000000 --- a/baselines/fedmss/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# fedmss - -## Install dependencies - -```bash -pip install . -``` - -## Run (Simulation Engine) - -In the `fedmss` directory, use `flwr run` to run a local simulation: - -```bash -flwr run -``` - -## Run (Deployment Engine) - -### Start the SuperLink - -```bash -flower-superlink --insecure -``` - -### Start the long-running Flower client - -In a new terminal window, start the first long-running Flower client: - -```bash -flower-client-app client:app --insecure -``` - -In yet another new terminal window, start the second long-running Flower client: - -```bash -flower-client-app client:app --insecure -``` - -### Start the ServerApp - -```bash -flower-server-app server:app --insecure -``` From 53182e959a0c58aed6a5f7f018791249060096ab Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Mon, 11 Nov 2024 13:44:14 -0500 Subject: [PATCH 53/99] Delete baselines/fedmss/fedmss/__init__.py --- baselines/fedmss/fedmss/__init__.py | 1 - 1 file changed, 1 deletion(-) delete mode 100644 baselines/fedmss/fedmss/__init__.py diff --git a/baselines/fedmss/fedmss/__init__.py b/baselines/fedmss/fedmss/__init__.py deleted file mode 100644 index 940414f17601..000000000000 --- a/baselines/fedmss/fedmss/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""FedMSS package.""" From d091acb37064d116584e70dc848008b2c3d475ad Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Mon, 11 Nov 2024 13:44:29 -0500 Subject: [PATCH 54/99] Delete baselines/fedmss/fedmss/aggregate.py --- baselines/fedmss/fedmss/aggregate.py | 137 --------------------------- 1 file changed, 137 deletions(-) delete mode 100644 baselines/fedmss/fedmss/aggregate.py diff --git a/baselines/fedmss/fedmss/aggregate.py b/baselines/fedmss/fedmss/aggregate.py deleted file mode 100644 index cbb303e20861..000000000000 --- a/baselines/fedmss/fedmss/aggregate.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright 2020 Flower Labs GmbH. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== -"""Aggregation functions for strategy implementations.""" -# mypy: disallow_untyped_calls=False - -from functools import reduce -from typing import List, Tuple - -import numpy as np -from flwr.common import NDArrays - -def weighted_loss_avg(results: List[Tuple[int, float]]) -> float: - """Aggregate evaluation results obtained from multiple clients.""" - num_total_evaluation_examples = sum(num_examples for (num_examples, _) in results) - weighted_losses = [num_examples * loss for num_examples, loss in results] - return sum(weighted_losses) / num_total_evaluation_examples - -# calls hardthreshold function for each list element in weights_all -def hardthreshold_list(weights_all, num_keep: int) -> NDArrays: - """Call hardthreshold.""" - - params = [ - hardthreshold(each, num_keep) for each in weights_all - ] - return [params] - -# hardthreshold function applied to array -def hardthreshold(weights_prime, num_keep: int) -> NDArrays: - """Perform hardthresholding on single array.""" - # check for len of array - val_len = weights_prime.size - - # intercepts not hardthresholded - if val_len > 1: - if num_keep > val_len: - params = weights_prime - print( - "num_keep parameter greater than length of vector. All parameters kept." - ) - else: - # Compute the magnitudes - magnitudes = np.abs(weights_prime) - - # Get the k-th largest value in the vector - threshold = np.partition(magnitudes, -num_keep)[-num_keep] - - # Create a new vector where values below the threshold are set to zero - params = np.where(magnitudes >= threshold, weights_prime, 0) - - else: - params = weights_prime - - return np.array(params) - -# hardthreshold aggregation -def aggregate_hardthreshold( - results: List[Tuple[NDArrays, int]], num_keep: int, iterht: bool -) -> NDArrays: - """Apply hard thresholding to keep only the k largest weights. - - Fed-HT (Fed-IterHT) can be found at - https://arxiv.org/abs/2101.00052 - """ - if num_keep <= 0: - raise ValueError("k must be a positive integer.") - - """Compute weighted average.""" - # Calculate the total number of examples used during training - num_examples_total = sum(num_examples for (_, num_examples) in results) - - green = "\033[92m" - reset = "\033[0m" - - # check for iterht=True; set in cfg - if iterht: - print( - f"{green}INFO {reset}:\t\tUsing Fed-IterHT with num_keep = ", - num_keep, - ) - - # apply across all models within each client - - # 'results' is a collection of tuples of the form (params, num_obs) - # we want to adjust the 'params' portion of that tuple - # i: iterates through clients - # ignoring second element in results[i] skips over number of observations - # j: iterates through all layers of a model - # k: iterates through the slices of each layer - for i in range(len(results)): - for j in range(len(results[i][0])): - for k in range(len(results[i][0][j])): - results[i][0][j][k] = hardthreshold(results[i][0][j][k], num_keep) - - weighted_weights1 = [ - [layer * num_examples for layer in weights] - for weights, num_examples in results - ] - weighted_weights2 = weighted_weights1 - - else: - print( - f"{green}INFO {reset}:\t\tUsing Fed-HT with num_keep = ", - num_keep, - ) - - weighted_weights1 = [ - [layer * num_examples for layer in weights] - for weights, num_examples in results - ] - weighted_weights2 = weighted_weights1 - - hold = [ - reduce(np.add, layer_updates) / num_examples_total - for layer_updates in zip(*weighted_weights2) - ] - - new_result: NDArrays = [ - val - for sublist in ( - hardthreshold_list(layer_updates, num_keep) for layer_updates in hold - ) - for val in sublist - ] - - return new_result From 3e12b4884a88b9e3d10574554a7b92ca3cf5e89f Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Mon, 11 Nov 2024 13:44:47 -0500 Subject: [PATCH 55/99] Delete baselines/fedmss/fedmss/client.py --- baselines/fedmss/fedmss/client.py | 78 ------------------------------- 1 file changed, 78 deletions(-) delete mode 100644 baselines/fedmss/fedmss/client.py diff --git a/baselines/fedmss/fedmss/client.py b/baselines/fedmss/fedmss/client.py deleted file mode 100644 index 4c2fa998b922..000000000000 --- a/baselines/fedmss/fedmss/client.py +++ /dev/null @@ -1,78 +0,0 @@ -"""Generate client for fedht baseline.""" - -from typing import cast - -import torch -import warnings - -from flwr.client import Client, NumPyClient -from flwr.common import Context -from omegaconf import DictConfig - -from fedmss.utils import set_model_params, get_model_parameters -from sklearn.metrics import log_loss - - -# UCI-HD client -class UCIHDClient(NumPyClient): - """Define UCIHDClient class.""" - - def __init__( - self, - trainloader, - testloader, - model, - num_obs, - num_features, - num_classes, - cfg: DictConfig, - device - ) -> None: - """UCIHD client for UCI-HD experimentation.""" - self.X_train, self.y_train = trainloader - self.X_test, self.y_test = testloader - self.model = model - self.num_obs = num_obs - self.num_features = num_features - self.num_classes = num_classes - self.cfg = cfg - self.device = device - - def fit(self, parameters, config): - set_model_params(self.model, parameters, self.cfg) - # Ignore convergence failure due to low local epochs - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - self.model.fit(self.X_train, self.y_train) - return get_model_parameters(self.model, self.cfg), len(self.X_train), {} - - def evaluate(self, parameters, config): - set_model_params(self.model, parameters, self.cfg) - loss = log_loss(self.y_test, self.model.predict_proba(self.X_test)) - accuracy = self.model.score(self.X_test, self.y_test) - return loss, len(self.X_test), {"accuracy": accuracy} - - -# client fn for input into simulation -def generate_client_fn_ucihd( - dataset, num_features, num_classes, model, cfg: DictConfig, device: torch.device -): - """Generate client function for simulated FL.""" - - # def client_fn(cid: int): - def client_fn(context: Context) -> Client: - """Define client function for centralized metrics.""" - # Get node_config value to fetch partition_id - partition_id = cast(int, context.node_config["partition-id"]) - - # Load the partition data - X_train, y_train, X_val, y_val = dataset - train_dataset = X_train[int(partition_id)], y_train[int(partition_id)] - test_dataset = X_val[int(partition_id)], y_val[int(partition_id)] - num_obs = train_dataset[1].shape[0] - - return UCIHDClient( - train_dataset, test_dataset, model, num_obs, num_features, num_classes, cfg, device - ).to_client() - - return client_fn From 4bb5e020b5e544cee767c1952aa65e1675578a4c Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Mon, 11 Nov 2024 13:45:04 -0500 Subject: [PATCH 56/99] Delete baselines/fedmss/fedmss/fedht.py --- baselines/fedmss/fedmss/fedht.py | 174 ------------------------------- 1 file changed, 174 deletions(-) delete mode 100644 baselines/fedmss/fedmss/fedht.py diff --git a/baselines/fedmss/fedmss/fedht.py b/baselines/fedmss/fedmss/fedht.py deleted file mode 100644 index 85bb207fb5ef..000000000000 --- a/baselines/fedmss/fedmss/fedht.py +++ /dev/null @@ -1,174 +0,0 @@ -# Copyright 2020 Flower Labs GmbH. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== -"""Federated Hardthresholding (FedHT).""" - - -from logging import WARNING -from typing import Callable, Dict, List, Optional, Tuple, Union - -from flwr.common import ( - FitRes, - MetricsAggregationFn, - NDArrays, - Parameters, - Scalar, - ndarrays_to_parameters, - parameters_to_ndarrays, -) -from flwr.common.logger import log -from flwr.server.client_proxy import ClientProxy -from flwr.server.strategy.fedavg import FedAvg - -from fedht.aggregate import aggregate_hardthreshold, weighted_loss_avg - -WARNING_MIN_AVAILABLE_CLIENTS_TOO_LOW = """ -Setting `min_available_clients` lower than `min_fit_clients` or -`min_evaluate_clients` can cause the server to fail when there are too few clients -connected to the server. `min_available_clients` must be set to a value larger -than or equal to the values of `min_fit_clients` and `min_evaluate_clients`. -""" - - -# pylint: disable=line-too-long -class FedHT(FedAvg): - """Federated Hardthreshold strategy. - - Implementation based on https://arxiv.org/abs/1602.05629 - - Parameters - ---------- - fraction_fit : float, optional - Fraction of clients used during training. In case `min_fit_clients` - is larger than `fraction_fit * available_clients`, `min_fit_clients` - will still be sampled. Defaults to 1.0. - fraction_evaluate : float, optional - Fraction of clients used during validation. In case `min_evaluate_clients` - is larger than `fraction_evaluate * available_clients`, - `min_evaluate_clients` will still be sampled. Defaults to 1.0. - num_keep : int, number of parameters to keep different from 0. Defaults to 5. - iterht : boolean, if true, utilizes the Fed-IterHT strategy. Defaults to False. - min_fit_clients : int, optional - Minimum number of clients used during training. Defaults to 2. - min_evaluate_clients : int, optional - Minimum number of clients used during validation. Defaults to 2. - min_available_clients : int, optional - Minimum number of total clients in the system. Defaults to 2. - evaluate_fn : Optional[Callable[[int, NDArrays, Dict[str, Scalar]], - Optional[Tuple[float, Dict[str, Scalar]]]]] - Optional function used for validation. Defaults to None. - on_fit_config_fn : Callable[[int], Dict[str, Scalar]], optional - Function used to configure training. Defaults to None. - on_evaluate_config_fn : Callable[[int], Dict[str, Scalar]], optional - Function used to configure validation. Defaults to None. - accept_failures : bool, optional - Whether or not accept rounds containing failures. Defaults to True. - initial_parameters : Parameters, optional - Initial global model parameters. - fit_metrics_aggregation_fn : Optional[MetricsAggregationFn] - Metrics aggregation function, optional. - evaluate_metrics_aggregation_fn : Optional[MetricsAggregationFn] - Metrics aggregation function, optional. - inplace : bool (default: True) - Enable (True) or disable (False) in-place aggregation of model updates. - """ - - # pylint: disable=too-many-arguments,too-many-instance-attributes, line-too-long - def __init__( - self, - *, - fraction_fit: float = 1.0, - fraction_evaluate: float = 1.0, - num_keep: int = 5, - iterht: bool = False, - min_fit_clients: int = 2, - min_evaluate_clients: int = 2, - min_available_clients: int = 2, - evaluate_fn: Optional[ - Callable[ - [int, NDArrays, Dict[str, Scalar]], - Optional[Tuple[float, Dict[str, Scalar]]], - ] - ] = None, - on_fit_config_fn: Optional[Callable[[int], Dict[str, Scalar]]] = None, - on_evaluate_config_fn: Optional[Callable[[int], Dict[str, Scalar]]] = None, - accept_failures: bool = True, - initial_parameters: Optional[Parameters] = None, - fit_metrics_aggregation_fn: Optional[MetricsAggregationFn] = None, - evaluate_metrics_aggregation_fn: Optional[MetricsAggregationFn] = None, - inplace: bool = True, - ) -> None: - super().__init__() - - if ( - min_fit_clients > min_available_clients - or min_evaluate_clients > min_available_clients - ): - log(WARNING, WARNING_MIN_AVAILABLE_CLIENTS_TOO_LOW) - - self.fraction_fit = fraction_fit - self.fraction_evaluate = fraction_evaluate - self.num_keep = num_keep - self.iterht = iterht - self.min_fit_clients = min_fit_clients - self.min_evaluate_clients = min_evaluate_clients - self.min_available_clients = min_available_clients - self.evaluate_fn = evaluate_fn - self.on_fit_config_fn = on_fit_config_fn - self.on_evaluate_config_fn = on_evaluate_config_fn - self.accept_failures = accept_failures - self.initial_parameters = initial_parameters - self.fit_metrics_aggregation_fn = fit_metrics_aggregation_fn - self.evaluate_metrics_aggregation_fn = evaluate_metrics_aggregation_fn - self.inplace = inplace - - def __repr__(self) -> str: - """Compute a string representation of the strategy.""" - rep = f"FedHT(accept_failures={self.accept_failures})" - return rep - - def aggregate_fit( - self, - server_round: int, - results: List[Tuple[ClientProxy, FitRes]], - failures: List[Union[Tuple[ClientProxy, FitRes], BaseException]], - ) -> Tuple[Optional[Parameters], Dict[str, Scalar]]: - """Aggregate fit results using weighted average.""" - if not results: - return None, {} - # Do not aggregate if there are failures and failures are not accepted - if not self.accept_failures and failures: - return None, {} - - # no in-place aggregation for FedHT - weights_results = [ - (parameters_to_ndarrays(fit_res.parameters), fit_res.num_examples) - for _, fit_res in results - ] - - # use hardthresholding - aggregated_ndarrays = aggregate_hardthreshold( - weights_results, self.num_keep, self.iterht - ) - parameters_aggregated = ndarrays_to_parameters(aggregated_ndarrays) - - # Aggregate custom metrics if aggregation fn was provided - metrics_aggregated = {} - if self.fit_metrics_aggregation_fn: - fit_metrics = [(res.num_examples, res.metrics) for _, res in results] - metrics_aggregated = self.fit_metrics_aggregation_fn(fit_metrics) - elif server_round == 1: # Only log this warning once - log(WARNING, "No fit_metrics_aggregation_fn provided") - - return parameters_aggregated, metrics_aggregated From 26950a87c7a7a592d74485720bb88b267db9c2a4 Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Mon, 11 Nov 2024 13:45:20 -0500 Subject: [PATCH 57/99] Delete baselines/fedmss/fedmss/main.py --- baselines/fedmss/fedmss/main.py | 159 -------------------------------- 1 file changed, 159 deletions(-) delete mode 100644 baselines/fedmss/fedmss/main.py diff --git a/baselines/fedmss/fedmss/main.py b/baselines/fedmss/fedmss/main.py deleted file mode 100644 index cfe9e1be9877..000000000000 --- a/baselines/fedmss/fedmss/main.py +++ /dev/null @@ -1,159 +0,0 @@ -"""Run main for fedmss baseline.""" - -import pickle -import random -import torch -import flwr as fl -import hydra - -import numpy as np -from flwr.common import NDArrays, ndarrays_to_parameters -from flwr.server.strategy.strategy import Strategy -from omegaconf import DictConfig -from sklearn.model_selection import train_test_split, KFold - -from fedmss.client import generate_client_fn_ucihd -from fedmss.fedht import FedHT -from fedmss.server import fit_round, get_evaluate_fn -from fedmss.utils import create_log_reg_and_instantiate_parameters, get_model_parameters -from ucimlrepo import fetch_ucirepo - -@hydra.main(config_path="conf", config_name="base_ucihd", version_base=None) -def main(cfg: DictConfig): - """Run main file for fedmss baseline. - - Parameters - ---------- - cfg : DictConfig - Config file for federated baseline; read from fedht/conf. - """ - # set seed - random.seed(2024) - - # this vs. setting in cfg; what is preferred? - device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") - - if cfg.data == "ucihd": - - # load UCI-HD data - num_features = 13 - num_classes = 2 # binary classification - - # fetch dataset - heart_disease = fetch_ucirepo(id=45) - - # data (as pandas dataframes) - Xall = heart_disease.data.features - yall = heart_disease.data.targets - - mask = np.isnan(Xall).any(axis=1) - - # Filter out rows with NaN values from both X and y - X = np.array(Xall[~mask]) - y = np.array(yall[~mask]) - y = y > 0 - - # Split the data into training and testing sets (80% train, 20% test) - X_train_all, X_test, y_train_all, y_test = train_test_split(X, y, test_size=0.2, random_state=42) - test_dataset = X_test, y_test - - # partition train data into clients - kf = KFold(n_splits=cfg.num_clients, shuffle=True, random_state=43) - X_train = [None] * cfg.num_clients - y_train = [None] * cfg.num_clients - X_val = [None] * cfg.num_clients - y_val = [None] * cfg.num_clients - for fold, (train_idx, val_idx) in enumerate(kf.split(X_train_all), 1): - X_train[fold-1], X_val[fold-1] = np.array(X_train_all)[train_idx,:], np.array(X_train_all)[val_idx,:] - y_train[fold-1], y_val[fold-1] = np.array(y_train_all)[train_idx,:], np.array(y_train_all)[val_idx,:] - - train_dataset = X_train, y_train, X_val, y_val - - # define model - model = create_log_reg_and_instantiate_parameters(cfg) - - #initial fit from first client - model.fit(X_train[0], y_train[0]) - ndarrays = get_model_parameters(model, cfg) - global_model_init = ndarrays_to_parameters(ndarrays) - - # set client function - client_fn = generate_client_fn_ucihd( - train_dataset, - num_features=cfg.num_features, - num_classes=cfg.num_classes, - model=model, - cfg=cfg, - device=device - ) - - # initialize global model to all zeros - # weights = np.zeros((num_classes, num_features)) - # bias = np.zeros(num_classes) - # init_params_arr: NDArrays = [weights, bias] - # init_params = ndarrays_to_parameters(init_params_arr) - - # define strategy: fedht - strategy_fedht = FedHT( - min_available_clients=cfg.strategy.min_available_clients, - num_keep=cfg.num_keep, - evaluate_fn=get_evaluate_fn(test_dataset, model, cfg), - on_fit_config_fn=fit_round, - iterht=cfg.iterht, - initial_parameters=global_model_init - ) - - # define strategy: fedavg - strategy_fedavg = fl.server.strategy.FedAvg( - min_available_clients=cfg.strategy.min_available_clients, - evaluate_fn=get_evaluate_fn(test_dataset, model, device), - on_fit_config_fn=fit_round, - initial_parameters=global_model_init - ) - - strategy: Strategy - if cfg.agg == "fedht": - strategy = strategy_fedht - elif cfg.agg == "fedavg": - strategy = strategy_fedavg - else: - print("Must select either fedht or fedavg for the aggregation strategy.") - - # # start simulation - random.seed(2025) - hist_mnist = fl.simulation.start_simulation( - client_fn=client_fn, - num_clients=cfg.num_clients, - config=fl.server.ServerConfig(num_rounds=cfg.num_rounds), - strategy=strategy, - client_resources={ - "num_cpus": cfg.client_resources.num_cpus, - "num_gpus": cfg.client_resources.num_gpus, - }, - ) - - if cfg.iterht: - iterstr = "iter" - else: - iterstr = "" - - filename = ( - cfg.data - + "_" - + cfg.agg - + iterstr - + "_local" - + str(cfg.num_local_epochs) - + "_lr" - + str(cfg.learning_rate) - + "_numkeep" - + str(cfg.num_keep) - + ".pkl" - ) - - with open(filename, "wb") as file: - pickle.dump(hist_mnist, file) - - -if __name__ == "__main__": - main() From c5178ee3476a2505011d6c818efec2a087907ab1 Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Mon, 11 Nov 2024 13:45:41 -0500 Subject: [PATCH 58/99] Delete baselines/fedmss/fedmss/model.py --- baselines/fedmss/fedmss/model.py | 82 -------------------------------- 1 file changed, 82 deletions(-) delete mode 100644 baselines/fedmss/fedmss/model.py diff --git a/baselines/fedmss/fedmss/model.py b/baselines/fedmss/fedmss/model.py deleted file mode 100644 index 7f2ba79a3f75..000000000000 --- a/baselines/fedmss/fedmss/model.py +++ /dev/null @@ -1,82 +0,0 @@ -"""Define model for fedht baseline.""" - -import torch -import torch.nn as nn -import torch.optim as optim -from omegaconf import DictConfig -from torch.utils.data import DataLoader - - -# model code initially pulled from fedprox baseline -# generates multinomial logistic regression model via torch -class LogisticRegression(nn.Module): - """Define LogisticRegression class.""" - - def __init__(self, num_features, num_classes: int) -> None: - """Define model.""" - super().__init__() - - # one single linear layer - self.linear = nn.Linear(num_features, num_classes) - - def forward(self, input_tensor: torch.Tensor) -> torch.Tensor: - """Define forward pass.""" - # forward pass; sigmoid transform included in CBELoss criterion - output_tensor = self.linear(torch.flatten(input_tensor, 1)) - return output_tensor - - -# define train function that will be called by each client to train the model -def train(model, trainloader: DataLoader, cfg: DictConfig, device: torch.device) -> None: - """Train model.""" - criterion = nn.CrossEntropyLoss() - optimizer = optim.SGD( - model.parameters(), lr=cfg.learning_rate, weight_decay=cfg.weight_decay - ) - - # train - for _epoch in range(cfg.num_local_epochs): - for _i, data in enumerate(trainloader): - - inputs, labels = data["image"].to(device), data["label"].to(device) - - # Zero the gradients - optimizer.zero_grad() - - # Forward pass - model.train() - - loss = criterion(model(inputs.float()), labels.long()) - - # Backward pass and optimization - loss.backward() - optimizer.step() - - -def test(model, testloader: DataLoader, device: torch.device): - """Test model.""" - criterion = nn.CrossEntropyLoss() - - # initialize - correct, total, loss = 0, 0, 0.0 - - # put into evlauate mode - model.eval() - with torch.no_grad(): - for _i, data in enumerate(testloader): - - images, labels = data["image"].to(device), data["label"].to(device) - - outputs = model(images.float()) - total += labels.size(0) - loss += criterion(outputs, labels.long()).item() - _, predicted = torch.max(outputs.data, 1) - correct += (predicted == labels).sum().item() - - if len(testloader.dataset) == 0: - raise ValueError("Testloader can't be 0, exiting...") - - loss /= len(testloader) - accuracy = correct / total - - return loss, accuracy From 018609d00e563d03b6dcf5f700f67e37156a2dce Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Mon, 11 Nov 2024 13:45:55 -0500 Subject: [PATCH 59/99] Delete baselines/fedmss/fedmss/server.py --- baselines/fedmss/fedmss/server.py | 30 ------------------------------ 1 file changed, 30 deletions(-) delete mode 100644 baselines/fedmss/fedmss/server.py diff --git a/baselines/fedmss/fedmss/server.py b/baselines/fedmss/fedmss/server.py deleted file mode 100644 index 7c426c56e8f5..000000000000 --- a/baselines/fedmss/fedmss/server.py +++ /dev/null @@ -1,30 +0,0 @@ -"""Generate server for fedht baseline.""" - -from typing import Dict - -import torch - -from fedmss.utils import set_model_params, get_model_parameters -from sklearn.metrics import log_loss - - -# send fit round for history -def fit_round(server_round: int) -> Dict: - """Send round number to client.""" - return {"server_round": server_round} - - -def get_evaluate_fn(testloader, model, cfg): - """Get evaluate function for centralized metrics.""" - - # global evaluation - def evaluate(server_round, parameters, cfg): # type: ignore - """Define evaluate function for centralized metrics.""" - - set_model_params(model, parameters, cfg) - X_test, y_test = testloader - loss = log_loss(y_test, model.predict_proba(X_test)) - accuracy = model.score(X_test, y_test) - return loss, {"accuracy": accuracy} - - return evaluate From 18b2c418e896cf310197cfed641febda62e492f7 Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Mon, 11 Nov 2024 13:46:16 -0500 Subject: [PATCH 60/99] Delete baselines/fedmss/fedmss/utils.py --- baselines/fedmss/fedmss/utils.py | 54 -------------------------------- 1 file changed, 54 deletions(-) delete mode 100644 baselines/fedmss/fedmss/utils.py diff --git a/baselines/fedmss/fedmss/utils.py b/baselines/fedmss/fedmss/utils.py deleted file mode 100644 index 92ebfb59e5b5..000000000000 --- a/baselines/fedmss/fedmss/utils.py +++ /dev/null @@ -1,54 +0,0 @@ -"""Execute utility functions for fedht baseline.""" - -import numpy as np -from sklearn.linear_model import SGDClassifier -from flwr.common.typing import NDArrays - -def set_model_params(model: SGDClassifier, params: NDArrays, cfg) -> SGDClassifier: - """Sets the parameters of a sklean LogisticRegression model.""" - model.coef_ = params[0] - if model.fit_intercept: - model.intercept_ = params[1] - return model - -def get_model_parameters(model: SGDClassifier, cfg) -> NDArrays: - """Returns the parameters of a sklearn LogisticRegression model.""" - if model.fit_intercept: - params = [ - model.coef_, - model.intercept_, - ] - else: - params = [ - model.coef_, - ] - return params - -def set_initial_params(model: SGDClassifier, cfg) -> None: - """Sets initial parameters as zeros Required since model params are uninitialized - until model.fit is called. - - But server asks for initial parameters from clients at launch. Refer to - sklearn.linear_model.LogisticRegression documentation for more information. - """ - model.classes_ = np.arange(cfg.num_classes) - - model.coef_ = np.zeros((1, cfg.num_features)) - if model.fit_intercept: - model.intercept_ = np.zeros((1,)) - - -def create_log_reg_and_instantiate_parameters(cfg): - """Helper function to create a LogisticRegression model.""" - model = SGDClassifier( - loss='log_loss', - learning_rate='constant', - tol=.001, - eta0=cfg.learning_rate, - max_iter=cfg.num_local_epochs, # local epoch - warm_start=True, # prevent refreshing weights when fitting, - ) - - # # Setting initial parameters, akin to model.compile for keras models - # set_initial_params(model, cfg) - return model From 542c6f5f55ab8ee6710b369c993233298427598e Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Sat, 16 Nov 2024 09:17:35 -0500 Subject: [PATCH 61/99] feat(baselines) corrected arXiv link to fedht paper Co-authored-by: Javier --- baselines/fedht/fedht/fedht.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/baselines/fedht/fedht/fedht.py b/baselines/fedht/fedht/fedht.py index 85bb207fb5ef..117554d0f0f9 100644 --- a/baselines/fedht/fedht/fedht.py +++ b/baselines/fedht/fedht/fedht.py @@ -45,7 +45,7 @@ class FedHT(FedAvg): """Federated Hardthreshold strategy. - Implementation based on https://arxiv.org/abs/1602.05629 + Implementation based on https://arxiv.org/abs/2101.00052 Parameters ---------- From 886ed62cb0b576f582162ff9292203ae9871a8ac Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sat, 16 Nov 2024 10:05:28 -0500 Subject: [PATCH 62/99] feat(baselines) clean up aggregate --- baselines/fedht/fedht/aggregate.py | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/baselines/fedht/fedht/aggregate.py b/baselines/fedht/fedht/aggregate.py index cbb303e20861..f8b194dd66ae 100644 --- a/baselines/fedht/fedht/aggregate.py +++ b/baselines/fedht/fedht/aggregate.py @@ -1,17 +1,3 @@ -# Copyright 2020 Flower Labs GmbH. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== """Aggregation functions for strategy implementations.""" # mypy: disallow_untyped_calls=False @@ -21,12 +7,6 @@ import numpy as np from flwr.common import NDArrays -def weighted_loss_avg(results: List[Tuple[int, float]]) -> float: - """Aggregate evaluation results obtained from multiple clients.""" - num_total_evaluation_examples = sum(num_examples for (num_examples, _) in results) - weighted_losses = [num_examples * loss for num_examples, loss in results] - return sum(weighted_losses) / num_total_evaluation_examples - # calls hardthreshold function for each list element in weights_all def hardthreshold_list(weights_all, num_keep: int) -> NDArrays: """Call hardthreshold.""" From 3a65b6dfc4aad49bba6bdd59f2446c640e944d49 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sat, 16 Nov 2024 10:08:07 -0500 Subject: [PATCH 63/99] feat(baselines) clean up fedht and aggregate --- baselines/fedht/fedht/aggregate.py | 1 + baselines/fedht/fedht/fedht.py | 88 +----------------------------- 2 files changed, 3 insertions(+), 86 deletions(-) diff --git a/baselines/fedht/fedht/aggregate.py b/baselines/fedht/fedht/aggregate.py index f8b194dd66ae..30e76f6f2941 100644 --- a/baselines/fedht/fedht/aggregate.py +++ b/baselines/fedht/fedht/aggregate.py @@ -1,4 +1,5 @@ """Aggregation functions for strategy implementations.""" + # mypy: disallow_untyped_calls=False from functools import reduce diff --git a/baselines/fedht/fedht/fedht.py b/baselines/fedht/fedht/fedht.py index 117554d0f0f9..adf7292c70d7 100644 --- a/baselines/fedht/fedht/fedht.py +++ b/baselines/fedht/fedht/fedht.py @@ -1,20 +1,5 @@ -# Copyright 2020 Flower Labs GmbH. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== """Federated Hardthresholding (FedHT).""" - from logging import WARNING from typing import Callable, Dict, List, Optional, Tuple, Union @@ -49,89 +34,20 @@ class FedHT(FedAvg): Parameters ---------- - fraction_fit : float, optional - Fraction of clients used during training. In case `min_fit_clients` - is larger than `fraction_fit * available_clients`, `min_fit_clients` - will still be sampled. Defaults to 1.0. - fraction_evaluate : float, optional - Fraction of clients used during validation. In case `min_evaluate_clients` - is larger than `fraction_evaluate * available_clients`, - `min_evaluate_clients` will still be sampled. Defaults to 1.0. num_keep : int, number of parameters to keep different from 0. Defaults to 5. iterht : boolean, if true, utilizes the Fed-IterHT strategy. Defaults to False. - min_fit_clients : int, optional - Minimum number of clients used during training. Defaults to 2. - min_evaluate_clients : int, optional - Minimum number of clients used during validation. Defaults to 2. - min_available_clients : int, optional - Minimum number of total clients in the system. Defaults to 2. - evaluate_fn : Optional[Callable[[int, NDArrays, Dict[str, Scalar]], - Optional[Tuple[float, Dict[str, Scalar]]]]] - Optional function used for validation. Defaults to None. - on_fit_config_fn : Callable[[int], Dict[str, Scalar]], optional - Function used to configure training. Defaults to None. - on_evaluate_config_fn : Callable[[int], Dict[str, Scalar]], optional - Function used to configure validation. Defaults to None. - accept_failures : bool, optional - Whether or not accept rounds containing failures. Defaults to True. - initial_parameters : Parameters, optional - Initial global model parameters. - fit_metrics_aggregation_fn : Optional[MetricsAggregationFn] - Metrics aggregation function, optional. - evaluate_metrics_aggregation_fn : Optional[MetricsAggregationFn] - Metrics aggregation function, optional. - inplace : bool (default: True) - Enable (True) or disable (False) in-place aggregation of model updates. """ # pylint: disable=too-many-arguments,too-many-instance-attributes, line-too-long def __init__( self, - *, - fraction_fit: float = 1.0, - fraction_evaluate: float = 1.0, num_keep: int = 5, iterht: bool = False, - min_fit_clients: int = 2, - min_evaluate_clients: int = 2, - min_available_clients: int = 2, - evaluate_fn: Optional[ - Callable[ - [int, NDArrays, Dict[str, Scalar]], - Optional[Tuple[float, Dict[str, Scalar]]], - ] - ] = None, - on_fit_config_fn: Optional[Callable[[int], Dict[str, Scalar]]] = None, - on_evaluate_config_fn: Optional[Callable[[int], Dict[str, Scalar]]] = None, - accept_failures: bool = True, - initial_parameters: Optional[Parameters] = None, - fit_metrics_aggregation_fn: Optional[MetricsAggregationFn] = None, - evaluate_metrics_aggregation_fn: Optional[MetricsAggregationFn] = None, - inplace: bool = True, + **kwargs, ) -> None: - super().__init__() - - if ( - min_fit_clients > min_available_clients - or min_evaluate_clients > min_available_clients - ): - log(WARNING, WARNING_MIN_AVAILABLE_CLIENTS_TOO_LOW) - - self.fraction_fit = fraction_fit - self.fraction_evaluate = fraction_evaluate + super().__init__(**kwargs) self.num_keep = num_keep self.iterht = iterht - self.min_fit_clients = min_fit_clients - self.min_evaluate_clients = min_evaluate_clients - self.min_available_clients = min_available_clients - self.evaluate_fn = evaluate_fn - self.on_fit_config_fn = on_fit_config_fn - self.on_evaluate_config_fn = on_evaluate_config_fn - self.accept_failures = accept_failures - self.initial_parameters = initial_parameters - self.fit_metrics_aggregation_fn = fit_metrics_aggregation_fn - self.evaluate_metrics_aggregation_fn = evaluate_metrics_aggregation_fn - self.inplace = inplace def __repr__(self) -> str: """Compute a string representation of the strategy.""" From fcf65bb3acb3c09b65db421a6b84f7e19397a6a2 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sat, 16 Nov 2024 10:23:54 -0500 Subject: [PATCH 64/99] feat(baselines) adding device discovery to client constructor for mnist and simII --- baselines/fedht/fedht/client.py | 14 +++++---- baselines/fedht/fedht/main.py | 56 +++++++++++++++------------------ 2 files changed, 33 insertions(+), 37 deletions(-) diff --git a/baselines/fedht/fedht/client.py b/baselines/fedht/fedht/client.py index 63818f440cf2..d1ee376c39f9 100644 --- a/baselines/fedht/fedht/client.py +++ b/baselines/fedht/fedht/client.py @@ -36,7 +36,8 @@ def __init__( self.num_features = num_features self.num_classes = num_classes self.cfg = cfg - self.device = device + + self.device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") # get parameters from existing model def get_parameters(self, config): @@ -73,7 +74,7 @@ def evaluate(self, parameters, config): # client fn for input into simulation def generate_client_fn_simII( - dataset, num_features, num_classes, model, cfg: DictConfig, device: torch.device + dataset, num_features, num_classes, model, cfg: DictConfig ): """Generate client function for simulated FL.""" @@ -93,7 +94,7 @@ def client_fn(context: Context) -> Client: testloader = DataLoader(test_dataset, batch_size=cfg.batch_size, shuffle=True) return SimIIClient( - trainloader, testloader, model, num_obs, num_features, num_classes, cfg, device + trainloader, testloader, model, num_obs, num_features, num_classes, cfg ).to_client() return client_fn @@ -122,7 +123,8 @@ def __init__( self.num_features = num_features self.num_classes = num_classes self.cfg = cfg - self.device = device + + self.device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") # get parameters from existing model def get_parameters(self, config): @@ -159,7 +161,7 @@ def evaluate(self, parameters, config): # client fn for input into simulation def generate_client_fn_mnist( - dataset, num_features, num_classes, model, cfg: DictConfig, device: torch.device + dataset, num_features, num_classes, model, cfg: DictConfig ): """Generate client function for simulated FL.""" @@ -181,7 +183,7 @@ def client_fn(context: Context) -> Client: testloader = DataLoader(test_dataset, batch_size=cfg.batch_size, shuffle=True) return MnistClient( - trainloader, testloader, model, num_obs, num_features, num_classes, cfg, device + trainloader, testloader, model, num_obs, num_features, num_classes, cfg ).to_client() return client_fn diff --git a/baselines/fedht/fedht/main.py b/baselines/fedht/fedht/main.py index 5b4d941407ee..f33f8044c497 100644 --- a/baselines/fedht/fedht/main.py +++ b/baselines/fedht/fedht/main.py @@ -33,7 +33,7 @@ def main(cfg: DictConfig): # set seed random.seed(2024) - # this vs. setting in cfg; what is preferred? + # set device to cuda:0, if available device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") if cfg.data == "mnist": @@ -47,8 +47,8 @@ def main(cfg: DictConfig): ) # load MNIST data - num_features = 28 * 28 - num_classes = 10 + num_features = cfg.num_features + num_classes = cfg.num_classes dataset = FederatedDataset(dataset="mnist", partitioners={"train": partitioner}) test_dataset = dataset.load_split("test").with_format("numpy") testloader = DataLoader(test_dataset, batch_size=cfg.batch_size, shuffle=False) @@ -62,17 +62,16 @@ def main(cfg: DictConfig): num_features=num_features, num_classes=num_classes, model=model, - cfg=cfg, - device=device + cfg=cfg ) elif cfg.data == "simII": # simulate data from Simulation II in Tong et al - num_obs = 1000 + num_obs = cfg.num_obs num_clients = cfg.num_clients - num_features = 1000 - num_classes = 2 + num_features = cfg.num_features + num_classes = cfg.num_classes dataset = sim_data(num_obs, num_clients, num_features, 1, 1) X_test, y_test = sim_data(num_obs, 1, num_features, 1, 1) @@ -88,8 +87,7 @@ def main(cfg: DictConfig): num_features=num_features, num_classes=num_classes, model=model, - cfg=cfg, - device=device + cfg=cfg ) # initialize global model to all zeros @@ -98,33 +96,29 @@ def main(cfg: DictConfig): init_params_arr: NDArrays = [weights, bias] init_params = ndarrays_to_parameters(init_params_arr) - # define strategy: fedht - strategy_fedht = FedHT( - min_available_clients=cfg.strategy.min_available_clients, - num_keep=cfg.num_keep, - evaluate_fn=get_evaluate_fn(testloader, model, device), - on_fit_config_fn=fit_round, - iterht=cfg.iterht, - initial_parameters=init_params, - ) - - # define strategy: fedavg - strategy_fedavg = fl.server.strategy.FedAvg( - min_available_clients=cfg.strategy.min_available_clients, - evaluate_fn=get_evaluate_fn(testloader, model, device), - on_fit_config_fn=fit_round, - initial_parameters=init_params, - ) - strategy: Strategy if cfg.agg == "fedht": - strategy = strategy_fedht + # define strategy: fedht + strategy = FedHT( + min_available_clients=cfg.strategy.min_available_clients, + num_keep=cfg.num_keep, + evaluate_fn=get_evaluate_fn(testloader, model, device), + on_fit_config_fn=fit_round, + iterht=cfg.iterht, + initial_parameters=init_params, + ) elif cfg.agg == "fedavg": - strategy = strategy_fedavg + # define strategy: fedavg + strategy = fl.server.strategy.FedAvg( + min_available_clients=cfg.strategy.min_available_clients, + evaluate_fn=get_evaluate_fn(testloader, model, device), + on_fit_config_fn=fit_round, + initial_parameters=init_params, + ) else: print("Must select either fedht or fedavg for the aggregation strategy.") - # # start simulation + # start simulation random.seed(2025) hist_mnist = fl.simulation.start_simulation( client_fn=client_fn, From de4eb576f2934e547804f83119bb0f0507b30d0a Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sat, 16 Nov 2024 10:24:34 -0500 Subject: [PATCH 65/99] deat(baslines) moving data/model param selection to config --- baselines/fedht/fedht/conf/base_mnist.yaml | 2 ++ baselines/fedht/fedht/conf/base_simII.yaml | 3 +++ 2 files changed, 5 insertions(+) diff --git a/baselines/fedht/fedht/conf/base_mnist.yaml b/baselines/fedht/fedht/conf/base_mnist.yaml index 47a8ee7b4041..bf202730eca6 100644 --- a/baselines/fedht/fedht/conf/base_mnist.yaml +++ b/baselines/fedht/fedht/conf/base_mnist.yaml @@ -1,6 +1,8 @@ --- num_clients: 10 # total number of clients num_local_epochs: 10 # number of local epochs +num_features: 28 * 28 +num_classes: 10 batch_size: 50 num_rounds: 100 learning_rate: 0.003 diff --git a/baselines/fedht/fedht/conf/base_simII.yaml b/baselines/fedht/fedht/conf/base_simII.yaml index d85a26166613..5a52784213b0 100644 --- a/baselines/fedht/fedht/conf/base_simII.yaml +++ b/baselines/fedht/fedht/conf/base_simII.yaml @@ -1,6 +1,9 @@ --- num_clients: 25 # total number of clients num_local_epochs: 10 # number of local epochs +num_obs: 1000 +num_features: 1000 +num_classes: 2 batch_size: 50 num_rounds: 100 learning_rate: 0.0001 From 26845813b23e8c5ed571c0e0ee447b507fa4f19d Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sat, 16 Nov 2024 10:31:04 -0500 Subject: [PATCH 66/99] feat(baselines) config argument clean up --- baselines/fedht/fedht/client.py | 26 ++++++++++---------------- baselines/fedht/fedht/main.py | 4 ---- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/baselines/fedht/fedht/client.py b/baselines/fedht/fedht/client.py index d1ee376c39f9..2810c5b18d2f 100644 --- a/baselines/fedht/fedht/client.py +++ b/baselines/fedht/fedht/client.py @@ -23,18 +23,15 @@ def __init__( testloader, model, num_obs, - num_features, - num_classes, - cfg: DictConfig, - device + cfg: DictConfig ) -> None: """SimII client for simulation II experimentation.""" self.trainloader = trainloader self.testloader = testloader self.model = model self.num_obs = num_obs - self.num_features = num_features - self.num_classes = num_classes + self.num_features = cfg.num_features + self.num_classes = cfg.num_classes self.cfg = cfg self.device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") @@ -74,7 +71,7 @@ def evaluate(self, parameters, config): # client fn for input into simulation def generate_client_fn_simII( - dataset, num_features, num_classes, model, cfg: DictConfig + dataset, model, cfg: DictConfig ): """Generate client function for simulated FL.""" @@ -94,7 +91,7 @@ def client_fn(context: Context) -> Client: testloader = DataLoader(test_dataset, batch_size=cfg.batch_size, shuffle=True) return SimIIClient( - trainloader, testloader, model, num_obs, num_features, num_classes, cfg + trainloader, testloader, model, num_obs, cfg ).to_client() return client_fn @@ -110,18 +107,15 @@ def __init__( testloader, model, num_obs, - num_features, - num_classes, - cfg: DictConfig, - device + cfg: DictConfig ) -> None: """MNIST client for MNIST experimentation.""" self.trainloader = trainloader self.testloader = testloader self.model = model self.num_obs = num_obs - self.num_features = num_features - self.num_classes = num_classes + self.num_features = cfg.num_features + self.num_classes = cfg.num_classes self.cfg = cfg self.device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") @@ -161,7 +155,7 @@ def evaluate(self, parameters, config): # client fn for input into simulation def generate_client_fn_mnist( - dataset, num_features, num_classes, model, cfg: DictConfig + dataset, model, cfg: DictConfig ): """Generate client function for simulated FL.""" @@ -183,7 +177,7 @@ def client_fn(context: Context) -> Client: testloader = DataLoader(test_dataset, batch_size=cfg.batch_size, shuffle=True) return MnistClient( - trainloader, testloader, model, num_obs, num_features, num_classes, cfg + trainloader, testloader, model, num_obs, cfg ).to_client() return client_fn diff --git a/baselines/fedht/fedht/main.py b/baselines/fedht/fedht/main.py index f33f8044c497..6307516eca4c 100644 --- a/baselines/fedht/fedht/main.py +++ b/baselines/fedht/fedht/main.py @@ -59,8 +59,6 @@ def main(cfg: DictConfig): # set client function client_fn = generate_client_fn_mnist( dataset, - num_features=num_features, - num_classes=num_classes, model=model, cfg=cfg ) @@ -84,8 +82,6 @@ def main(cfg: DictConfig): # set client function client_fn = generate_client_fn_simII( dataset, - num_features=num_features, - num_classes=num_classes, model=model, cfg=cfg ) From 246fea2db3c6e14f272fdb4db390c1122ccfcc36 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sat, 16 Nov 2024 10:42:16 -0500 Subject: [PATCH 67/99] feat(baselines) moving model instantiation into generate_client_fn and get_evaluate_fn for both mnist and simII --- baselines/fedht/fedht/client.py | 12 +++++++++--- baselines/fedht/fedht/main.py | 9 ++------- baselines/fedht/fedht/server.py | 10 +++++++--- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/baselines/fedht/fedht/client.py b/baselines/fedht/fedht/client.py index 2810c5b18d2f..942af60e014a 100644 --- a/baselines/fedht/fedht/client.py +++ b/baselines/fedht/fedht/client.py @@ -11,7 +11,7 @@ from fedht.model import test, train from fedht.utils import MyDataset - +from fedht.model import LogisticRegression # SimII client class SimIIClient(NumPyClient): @@ -71,7 +71,7 @@ def evaluate(self, parameters, config): # client fn for input into simulation def generate_client_fn_simII( - dataset, model, cfg: DictConfig + dataset, cfg: DictConfig ): """Generate client function for simulated FL.""" @@ -90,6 +90,9 @@ def client_fn(context: Context) -> Client: trainloader = DataLoader(train_dataset, batch_size=cfg.batch_size, shuffle=True) testloader = DataLoader(test_dataset, batch_size=cfg.batch_size, shuffle=True) + # define model + model = LogisticRegression(cfg.num_features, cfg.num_classes) + return SimIIClient( trainloader, testloader, model, num_obs, cfg ).to_client() @@ -155,7 +158,7 @@ def evaluate(self, parameters, config): # client fn for input into simulation def generate_client_fn_mnist( - dataset, model, cfg: DictConfig + dataset, cfg: DictConfig ): """Generate client function for simulated FL.""" @@ -176,6 +179,9 @@ def client_fn(context: Context) -> Client: trainloader = DataLoader(train_dataset, batch_size=cfg.batch_size, shuffle=True) testloader = DataLoader(test_dataset, batch_size=cfg.batch_size, shuffle=True) + # define model + model = LogisticRegression(cfg.num_features, cfg.num_classes) + return MnistClient( trainloader, testloader, model, num_obs, cfg ).to_client() diff --git a/baselines/fedht/fedht/main.py b/baselines/fedht/fedht/main.py index 6307516eca4c..78cb5a1e8d3f 100644 --- a/baselines/fedht/fedht/main.py +++ b/baselines/fedht/fedht/main.py @@ -59,7 +59,6 @@ def main(cfg: DictConfig): # set client function client_fn = generate_client_fn_mnist( dataset, - model=model, cfg=cfg ) @@ -76,13 +75,9 @@ def main(cfg: DictConfig): test_dataset = MyDataset(X_test[0, :, :], y_test[:, 0]) testloader = DataLoader(test_dataset, batch_size=cfg.batch_size, shuffle=False) - # define model - model = LogisticRegression(num_features, num_classes) - # set client function client_fn = generate_client_fn_simII( dataset, - model=model, cfg=cfg ) @@ -98,7 +93,7 @@ def main(cfg: DictConfig): strategy = FedHT( min_available_clients=cfg.strategy.min_available_clients, num_keep=cfg.num_keep, - evaluate_fn=get_evaluate_fn(testloader, model, device), + evaluate_fn=get_evaluate_fn(testloader, device), on_fit_config_fn=fit_round, iterht=cfg.iterht, initial_parameters=init_params, @@ -107,7 +102,7 @@ def main(cfg: DictConfig): # define strategy: fedavg strategy = fl.server.strategy.FedAvg( min_available_clients=cfg.strategy.min_available_clients, - evaluate_fn=get_evaluate_fn(testloader, model, device), + evaluate_fn=get_evaluate_fn(testloader, device), on_fit_config_fn=fit_round, initial_parameters=init_params, ) diff --git a/baselines/fedht/fedht/server.py b/baselines/fedht/fedht/server.py index e79c2edf876a..aff3f06a639e 100644 --- a/baselines/fedht/fedht/server.py +++ b/baselines/fedht/fedht/server.py @@ -6,7 +6,7 @@ import torch from fedht.model import test - +from fedht.model import LogisticRegression # send fit round for history def fit_round(server_round: int) -> Dict: @@ -14,12 +14,16 @@ def fit_round(server_round: int) -> Dict: return {"server_round": server_round} -def get_evaluate_fn(testloader, model, device: torch.device): +def get_evaluate_fn(testloader, device: torch.device): """Get evaluate function for centralized metrics.""" # global evaluation - def evaluate(server_round, parameters, config): # type: ignore + def evaluate(server_round, parameters, cfg): # type: ignore """Define evaluate function for centralized metrics.""" + + # define model + model = LogisticRegression(cfg.num_features, cfg.num_classes) + # set model parameters params_dict = zip(model.state_dict().keys(), parameters) state_dict = OrderedDict({k: torch.Tensor(v) for k, v in params_dict}) From 11ea7338952392a748d7273065bb55a783dad9c2 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sat, 16 Nov 2024 10:45:05 -0500 Subject: [PATCH 68/99] feat(baselines) main and client clean up --- baselines/fedht/fedht/client.py | 8 ++++---- baselines/fedht/fedht/main.py | 3 --- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/baselines/fedht/fedht/client.py b/baselines/fedht/fedht/client.py index 942af60e014a..dd1b74dbf0f4 100644 --- a/baselines/fedht/fedht/client.py +++ b/baselines/fedht/fedht/client.py @@ -37,11 +37,11 @@ def __init__( self.device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") # get parameters from existing model - def get_parameters(self, config): + def get_parameters(self): """Get parameters.""" return [val.cpu().numpy() for _, val in self.model.state_dict().items()] - def fit(self, parameters, config): + def fit(self, parameters): """Fit model.""" # set model parameters params_dict = zip(self.model.state_dict().keys(), parameters) @@ -124,11 +124,11 @@ def __init__( self.device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") # get parameters from existing model - def get_parameters(self, config): + def get_parameters(self): """Get parameters.""" return [val.cpu().numpy() for _, val in self.model.state_dict().items()] - def fit(self, parameters, config): + def fit(self, parameters): """Fit model.""" # set model parameters params_dict = zip(self.model.state_dict().keys(), parameters) diff --git a/baselines/fedht/fedht/main.py b/baselines/fedht/fedht/main.py index 78cb5a1e8d3f..86b93a8fcc49 100644 --- a/baselines/fedht/fedht/main.py +++ b/baselines/fedht/fedht/main.py @@ -53,9 +53,6 @@ def main(cfg: DictConfig): test_dataset = dataset.load_split("test").with_format("numpy") testloader = DataLoader(test_dataset, batch_size=cfg.batch_size, shuffle=False) - # define model - model = LogisticRegression(num_features, num_classes) - # set client function client_fn = generate_client_fn_mnist( dataset, From 50cdcf9584655911cdec272be5f34a65e1953ff8 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sat, 16 Nov 2024 10:58:55 -0500 Subject: [PATCH 69/99] feat(baselines) updating error messages in main --- baselines/fedht/fedht/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/baselines/fedht/fedht/main.py b/baselines/fedht/fedht/main.py index 86b93a8fcc49..899360eb73a4 100644 --- a/baselines/fedht/fedht/main.py +++ b/baselines/fedht/fedht/main.py @@ -104,7 +104,7 @@ def main(cfg: DictConfig): initial_parameters=init_params, ) else: - print("Must select either fedht or fedavg for the aggregation strategy.") + raise ValueError("Must select either fedht or fedavg for the aggregation strategy in this baseline.") # start simulation random.seed(2025) From e7e65504d7c833666cd23ef666bb6525522a2f9f Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Sat, 16 Nov 2024 12:07:44 -0500 Subject: [PATCH 70/99] feat(baselines) fixing device issues --- baselines/fedht/fedht/client.py | 34 ++++++++++++---------- baselines/fedht/fedht/conf/base_mnist.yaml | 2 +- baselines/fedht/fedht/fedht.py | 6 ++-- baselines/fedht/fedht/main.py | 7 ++--- baselines/fedht/fedht/model.py | 5 ++++ baselines/fedht/fedht/server.py | 5 ++-- 6 files changed, 31 insertions(+), 28 deletions(-) diff --git a/baselines/fedht/fedht/client.py b/baselines/fedht/fedht/client.py index dd1b74dbf0f4..fc877a058d05 100644 --- a/baselines/fedht/fedht/client.py +++ b/baselines/fedht/fedht/client.py @@ -23,7 +23,8 @@ def __init__( testloader, model, num_obs, - cfg: DictConfig + cfg: DictConfig, + device ) -> None: """SimII client for simulation II experimentation.""" self.trainloader = trainloader @@ -33,15 +34,14 @@ def __init__( self.num_features = cfg.num_features self.num_classes = cfg.num_classes self.cfg = cfg - - self.device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + self.device = device # get parameters from existing model - def get_parameters(self): + def get_parameters(self, config): """Get parameters.""" return [val.cpu().numpy() for _, val in self.model.state_dict().items()] - def fit(self, parameters): + def fit(self, parameters, config): """Fit model.""" # set model parameters params_dict = zip(self.model.state_dict().keys(), parameters) @@ -90,11 +90,12 @@ def client_fn(context: Context) -> Client: trainloader = DataLoader(train_dataset, batch_size=cfg.batch_size, shuffle=True) testloader = DataLoader(test_dataset, batch_size=cfg.batch_size, shuffle=True) - # define model - model = LogisticRegression(cfg.num_features, cfg.num_classes) + # define model and set device + device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + model = LogisticRegression(cfg.num_features, cfg.num_classes).to(device) return SimIIClient( - trainloader, testloader, model, num_obs, cfg + trainloader, testloader, model, num_obs, cfg, device ).to_client() return client_fn @@ -110,7 +111,8 @@ def __init__( testloader, model, num_obs, - cfg: DictConfig + cfg: DictConfig, + device ) -> None: """MNIST client for MNIST experimentation.""" self.trainloader = trainloader @@ -120,15 +122,14 @@ def __init__( self.num_features = cfg.num_features self.num_classes = cfg.num_classes self.cfg = cfg - - self.device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + self.device = device # get parameters from existing model - def get_parameters(self): + def get_parameters(self, config): """Get parameters.""" return [val.cpu().numpy() for _, val in self.model.state_dict().items()] - def fit(self, parameters): + def fit(self, parameters, config): """Fit model.""" # set model parameters params_dict = zip(self.model.state_dict().keys(), parameters) @@ -179,11 +180,12 @@ def client_fn(context: Context) -> Client: trainloader = DataLoader(train_dataset, batch_size=cfg.batch_size, shuffle=True) testloader = DataLoader(test_dataset, batch_size=cfg.batch_size, shuffle=True) - # define model - model = LogisticRegression(cfg.num_features, cfg.num_classes) + # define model and set device + device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + model = LogisticRegression(cfg.num_features, cfg.num_classes).to(device) return MnistClient( - trainloader, testloader, model, num_obs, cfg + trainloader, testloader, model, num_obs, cfg, device ).to_client() return client_fn diff --git a/baselines/fedht/fedht/conf/base_mnist.yaml b/baselines/fedht/fedht/conf/base_mnist.yaml index bf202730eca6..36e12fd124dd 100644 --- a/baselines/fedht/fedht/conf/base_mnist.yaml +++ b/baselines/fedht/fedht/conf/base_mnist.yaml @@ -1,7 +1,7 @@ --- num_clients: 10 # total number of clients num_local_epochs: 10 # number of local epochs -num_features: 28 * 28 +num_features: 784 num_classes: 10 batch_size: 50 num_rounds: 100 diff --git a/baselines/fedht/fedht/fedht.py b/baselines/fedht/fedht/fedht.py index adf7292c70d7..3e98202d5f56 100644 --- a/baselines/fedht/fedht/fedht.py +++ b/baselines/fedht/fedht/fedht.py @@ -1,12 +1,10 @@ """Federated Hardthresholding (FedHT).""" from logging import WARNING -from typing import Callable, Dict, List, Optional, Tuple, Union +from typing import Dict, List, Optional, Tuple, Union from flwr.common import ( FitRes, - MetricsAggregationFn, - NDArrays, Parameters, Scalar, ndarrays_to_parameters, @@ -16,7 +14,7 @@ from flwr.server.client_proxy import ClientProxy from flwr.server.strategy.fedavg import FedAvg -from fedht.aggregate import aggregate_hardthreshold, weighted_loss_avg +from fedht.aggregate import aggregate_hardthreshold WARNING_MIN_AVAILABLE_CLIENTS_TOO_LOW = """ Setting `min_available_clients` lower than `min_fit_clients` or diff --git a/baselines/fedht/fedht/main.py b/baselines/fedht/fedht/main.py index 899360eb73a4..733144b0a760 100644 --- a/baselines/fedht/fedht/main.py +++ b/baselines/fedht/fedht/main.py @@ -16,8 +16,7 @@ from fedht.client import generate_client_fn_mnist, generate_client_fn_simII from fedht.fedht import FedHT -from fedht.model import LogisticRegression -from fedht.server import fit_round, get_evaluate_fn +from fedht.server import fit_round, gen_evaluate_fn from fedht.utils import MyDataset, sim_data @@ -90,7 +89,7 @@ def main(cfg: DictConfig): strategy = FedHT( min_available_clients=cfg.strategy.min_available_clients, num_keep=cfg.num_keep, - evaluate_fn=get_evaluate_fn(testloader, device), + evaluate_fn=gen_evaluate_fn(testloader, cfg, device), on_fit_config_fn=fit_round, iterht=cfg.iterht, initial_parameters=init_params, @@ -99,7 +98,7 @@ def main(cfg: DictConfig): # define strategy: fedavg strategy = fl.server.strategy.FedAvg( min_available_clients=cfg.strategy.min_available_clients, - evaluate_fn=get_evaluate_fn(testloader, device), + evaluate_fn=gen_evaluate_fn(testloader, cfg, device), on_fit_config_fn=fit_round, initial_parameters=init_params, ) diff --git a/baselines/fedht/fedht/model.py b/baselines/fedht/fedht/model.py index 7f2ba79a3f75..83f9f477f73d 100644 --- a/baselines/fedht/fedht/model.py +++ b/baselines/fedht/fedht/model.py @@ -34,6 +34,8 @@ def train(model, trainloader: DataLoader, cfg: DictConfig, device: torch.device) model.parameters(), lr=cfg.learning_rate, weight_decay=cfg.weight_decay ) + device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + # train for _epoch in range(cfg.num_local_epochs): for _i, data in enumerate(trainloader): @@ -55,6 +57,9 @@ def train(model, trainloader: DataLoader, cfg: DictConfig, device: torch.device) def test(model, testloader: DataLoader, device: torch.device): """Test model.""" + + device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + criterion = nn.CrossEntropyLoss() # initialize diff --git a/baselines/fedht/fedht/server.py b/baselines/fedht/fedht/server.py index aff3f06a639e..d6a7c44d48b1 100644 --- a/baselines/fedht/fedht/server.py +++ b/baselines/fedht/fedht/server.py @@ -13,12 +13,11 @@ def fit_round(server_round: int) -> Dict: """Send round number to client.""" return {"server_round": server_round} - -def get_evaluate_fn(testloader, device: torch.device): +def gen_evaluate_fn(testloader, cfg, device: torch.device): """Get evaluate function for centralized metrics.""" # global evaluation - def evaluate(server_round, parameters, cfg): # type: ignore + def evaluate(server_round, parameters, config): # type: ignore """Define evaluate function for centralized metrics.""" # define model From a086952d3705468cd295aef90e8fbe2905309dc8 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Mon, 18 Nov 2024 17:02:05 -0500 Subject: [PATCH 71/99] feat(baselines) fixing warning on imutable array --- baselines/fedht/fedht/client.py | 3 ++- baselines/fedht/fedht/conf/base_simII.yaml | 4 ++-- baselines/fedht/fedht/main.py | 7 ++++--- baselines/fedht/fedht/model.py | 3 ++- baselines/fedht/fedht/utils.py | 7 +++++-- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/baselines/fedht/fedht/client.py b/baselines/fedht/fedht/client.py index fc877a058d05..0b9e416b4d07 100644 --- a/baselines/fedht/fedht/client.py +++ b/baselines/fedht/fedht/client.py @@ -4,6 +4,7 @@ from typing import cast import torch +import copy from flwr.client import Client, NumPyClient from flwr.common import Context from omegaconf import DictConfig @@ -82,7 +83,7 @@ def client_fn(context: Context) -> Client: partition_id = cast(int, context.node_config["partition-id"]) # Load the partition data - X_train, y_train = dataset + X_train, y_train = copy.deepcopy(dataset) num_obs = X_train.shape[1] test_dataset = train_dataset = MyDataset( X_train[int(partition_id), :, :], y_train[:, int(partition_id)] diff --git a/baselines/fedht/fedht/conf/base_simII.yaml b/baselines/fedht/fedht/conf/base_simII.yaml index 5a52784213b0..7927543ccdf2 100644 --- a/baselines/fedht/fedht/conf/base_simII.yaml +++ b/baselines/fedht/fedht/conf/base_simII.yaml @@ -15,8 +15,8 @@ iterht: False data: simII client_resources: - num_cpus: 4 - num_gpus: 1 + num_cpus: 25 + num_gpus: 0 dataset: name: mnist diff --git a/baselines/fedht/fedht/main.py b/baselines/fedht/fedht/main.py index 733144b0a760..de0a1c079023 100644 --- a/baselines/fedht/fedht/main.py +++ b/baselines/fedht/fedht/main.py @@ -29,8 +29,6 @@ def main(cfg: DictConfig): cfg : DictConfig Config file for federated baseline; read from fedht/conf. """ - # set seed - random.seed(2024) # set device to cuda:0, if available device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") @@ -60,6 +58,9 @@ def main(cfg: DictConfig): elif cfg.data == "simII": + # set seed + random.seed(2024) + # simulate data from Simulation II in Tong et al num_obs = cfg.num_obs num_clients = cfg.num_clients @@ -81,7 +82,7 @@ def main(cfg: DictConfig): weights = np.zeros((num_classes, num_features)) bias = np.zeros(num_classes) init_params_arr: NDArrays = [weights, bias] - init_params = ndarrays_to_parameters(init_params_arr) + init_params = ndarrays_to_parameters(init_params_arr.copy()) strategy: Strategy if cfg.agg == "fedht": diff --git a/baselines/fedht/fedht/model.py b/baselines/fedht/fedht/model.py index 83f9f477f73d..23d9b92c4949 100644 --- a/baselines/fedht/fedht/model.py +++ b/baselines/fedht/fedht/model.py @@ -35,7 +35,7 @@ def train(model, trainloader: DataLoader, cfg: DictConfig, device: torch.device) ) device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") - + # train for _epoch in range(cfg.num_local_epochs): for _i, data in enumerate(trainloader): @@ -53,6 +53,7 @@ def train(model, trainloader: DataLoader, cfg: DictConfig, device: torch.device) # Backward pass and optimization loss.backward() optimizer.step() + def test(model, testloader: DataLoader, device: torch.device): diff --git a/baselines/fedht/fedht/utils.py b/baselines/fedht/fedht/utils.py index 54232ccdd0ed..c97620f9e499 100644 --- a/baselines/fedht/fedht/utils.py +++ b/baselines/fedht/fedht/utils.py @@ -1,6 +1,7 @@ """Execute utility functions for fedht baseline.""" import numpy as np +import random from torch.utils.data import Dataset @@ -48,6 +49,9 @@ def partition_data(data, num_partitions): def sim_data(ni: int, num_clients: int, num_features: int, alpha=1, beta=1): """Simulate data for simII.""" + + random.seed(2024) + # generate client-based model coefs u = np.random.normal(0, alpha, num_clients) x = np.zeros((num_features, num_clients)) @@ -77,5 +81,4 @@ def sim_data(ni: int, num_clients: int, num_features: int, alpha=1, beta=1): y[mask, j] = 1 y[~mask, j] = 0 - # might need to adjust; vague data generating process - return z, y + return z.copy(), y.copy() From 22254575197409da8893fb91c1e07f4ece857022 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Mon, 18 Nov 2024 17:31:41 -0500 Subject: [PATCH 72/99] feat(baselines) adjusting random seed --- baselines/fedht/fedht/main.py | 4 ++-- baselines/fedht/fedht/utils.py | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/baselines/fedht/fedht/main.py b/baselines/fedht/fedht/main.py index de0a1c079023..a2f4c156606c 100644 --- a/baselines/fedht/fedht/main.py +++ b/baselines/fedht/fedht/main.py @@ -59,7 +59,7 @@ def main(cfg: DictConfig): elif cfg.data == "simII": # set seed - random.seed(2024) + np.random.seed(2024) # simulate data from Simulation II in Tong et al num_obs = cfg.num_obs @@ -107,7 +107,7 @@ def main(cfg: DictConfig): raise ValueError("Must select either fedht or fedavg for the aggregation strategy in this baseline.") # start simulation - random.seed(2025) + np.random.seed(2025) hist_mnist = fl.simulation.start_simulation( client_fn=client_fn, num_clients=cfg.num_clients, diff --git a/baselines/fedht/fedht/utils.py b/baselines/fedht/fedht/utils.py index c97620f9e499..0005d0c56590 100644 --- a/baselines/fedht/fedht/utils.py +++ b/baselines/fedht/fedht/utils.py @@ -1,7 +1,6 @@ """Execute utility functions for fedht baseline.""" import numpy as np -import random from torch.utils.data import Dataset @@ -50,8 +49,6 @@ def partition_data(data, num_partitions): def sim_data(ni: int, num_clients: int, num_features: int, alpha=1, beta=1): """Simulate data for simII.""" - random.seed(2024) - # generate client-based model coefs u = np.random.normal(0, alpha, num_clients) x = np.zeros((num_features, num_clients)) From d0f3c0c00065cdbb572e96ac9280fc78d730a713 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Mon, 18 Nov 2024 18:41:26 -0500 Subject: [PATCH 73/99] feat(baselines) reproducibility fix --- baselines/fedht/fedht/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/baselines/fedht/fedht/utils.py b/baselines/fedht/fedht/utils.py index 0005d0c56590..96483462ac60 100644 --- a/baselines/fedht/fedht/utils.py +++ b/baselines/fedht/fedht/utils.py @@ -49,6 +49,8 @@ def partition_data(data, num_partitions): def sim_data(ni: int, num_clients: int, num_features: int, alpha=1, beta=1): """Simulate data for simII.""" + np.random.seed(2025) + # generate client-based model coefs u = np.random.normal(0, alpha, num_clients) x = np.zeros((num_features, num_clients)) From e1ee81f6e459cb0c8971c0d0da21e478e0abf78a Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Tue, 19 Nov 2024 16:54:11 -0500 Subject: [PATCH 74/99] feat(baselines) change randomization for simII --- baselines/fedht/fedht/main.py | 13 +++++++++++-- baselines/fedht/fedht/utils.py | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/baselines/fedht/fedht/main.py b/baselines/fedht/fedht/main.py index a2f4c156606c..8629a17b0044 100644 --- a/baselines/fedht/fedht/main.py +++ b/baselines/fedht/fedht/main.py @@ -67,8 +67,17 @@ def main(cfg: DictConfig): num_features = cfg.num_features num_classes = cfg.num_classes - dataset = sim_data(num_obs, num_clients, num_features, 1, 1) - X_test, y_test = sim_data(num_obs, 1, num_features, 1, 1) + # import data from fedht/data folder + with open('fedht/data/simII_train.pkl', 'rb') as file: + dataset = pickle.load(file) + + with open('fedht/data/simII_test.pkl', 'rb') as file: + test_dataset = pickle.load(file) + + X_test, y_test = test_dataset + + # dataset = sim_data(num_obs, num_clients, num_features, 1, 1) + # X_test, y_test = sim_data(num_obs, 1, num_features, 1, 1) test_dataset = MyDataset(X_test[0, :, :], y_test[:, 0]) testloader = DataLoader(test_dataset, batch_size=cfg.batch_size, shuffle=False) diff --git a/baselines/fedht/fedht/utils.py b/baselines/fedht/fedht/utils.py index 96483462ac60..b766507d0271 100644 --- a/baselines/fedht/fedht/utils.py +++ b/baselines/fedht/fedht/utils.py @@ -49,7 +49,7 @@ def partition_data(data, num_partitions): def sim_data(ni: int, num_clients: int, num_features: int, alpha=1, beta=1): """Simulate data for simII.""" - np.random.seed(2025) + # np.random.seed(2025) # generate client-based model coefs u = np.random.normal(0, alpha, num_clients) From bc9c9bb8b3ed347489e7668b7641d5237e195db8 Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Thu, 21 Nov 2024 12:37:59 -0500 Subject: [PATCH 75/99] Update README.md --- baselines/fedht/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/baselines/fedht/README.md b/baselines/fedht/README.md index 8cf3cb06e806..66b401e0ab78 100644 --- a/baselines/fedht/README.md +++ b/baselines/fedht/README.md @@ -92,10 +92,10 @@ python -m fedht.main --config-name base_mnist agg=fedht num_keep=500 num_local_e ### Simulation II (`num_keep` = 200) ``` -python -m fedht.main --config-name base_simII agg=fedavg num_local_epochs=5 learning_rate=0.01 -python -m fedht.main --config-name base_simII agg=fedht num_local_epochs=5 learning_rate=0.01 -python -m fedht.main --config-name base_simII agg=fedht iterht=True num_local_epochs=5 learning_rate=0.01 -python -m fedht.main --config-name base_simII agg=fedht num_local_epochs=1 learning_rate=0.01 +python -m fedht.main --config-name base_simII agg=fedavg num_local_epochs=2 learning_rate=0.00001 +python -m fedht.main --config-name base_simII agg=fedht num_local_epochs=2 learning_rate=0.00001 +python -m fedht.main --config-name base_simII agg=fedht iterht=True num_local_epochs=2 learning_rate=0.00001 +python -m fedht.main --config-name base_simII agg=fedht num_local_epochs=1 learning_rate=0.00001 ``` | *Experiments: Comparison of Aggregation Approaches to Fed-HT for Simulation II* | From 0c1a9ed6073934993b64499c7e857092246f0037 Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:41:58 -0500 Subject: [PATCH 76/99] Update README.md --- baselines/fedht/README.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/baselines/fedht/README.md b/baselines/fedht/README.md index 66b401e0ab78..95d64b41f7dd 100644 --- a/baselines/fedht/README.md +++ b/baselines/fedht/README.md @@ -36,7 +36,7 @@ poetry shell ## About this baseline -The purpose of this baseline is 1) implement the federated aggregation strategies introduced in Tong et. al. 2020, and 2) showcase the aggregation strategies with the datasets included in the paper. The two strategies introduced include Fed-HT and FedIter-HT. Fed-HT and FedIter-HT both apply hardthresholding (restricted by the hardthresholding parameter $\tau$) following the aggregation step. FedIter-HT, additionally, applies hardthresholding to each client model prior to aggregation. We also include FedAvg and Distributed-IHT , i.e., Fed-HT with `num_local_epochs` set to 1. +The purpose of this baseline is 1) implement the federated aggregation strategies introduced in Tong et. al. 2020, and 2) showcase the aggregation strategies with the datasets included in the paper. The two strategies introduced include Fed-HT and FedIter-HT. Fed-HT and FedIter-HT both apply hardthresholding (restricted by the hardthresholding parameter $\tau$) following the aggregation step. FedIter-HT, additionally, applies hardthresholding to each client model prior to aggregation. We also include results using FedAvg. Two federated classification models are implemented, the first using the well-known MNIST dataset (with 10 clients) and the second using a simulated dataset (with 25 clients). @@ -80,22 +80,21 @@ We note that in the current implementation, only weights (and not biases) of the ## Expected Results ### MNIST (`num_keep` = 500) ``` -python -m fedht.main --config-name base_mnist agg=fedavg num_keep=500 num_local_epochs=10 learning_rate=0.00005 -python -m fedht.main --config-name base_mnist agg=fedht num_keep=500 num_local_epochs=10 learning_rate=0.00005 -python -m fedht.main --config-name base_mnist agg=fedht iterht=True num_keep=500 num_local_epochs=10 learning_rate=0.00005 -python -m fedht.main --config-name base_mnist agg=fedht num_keep=500 num_local_epochs=1 learning_rate=0.00005 +python -m fedht.main --config-name base_mnist agg=fedavg num_keep=500 num_local_epochs=5 learning_rate=0.00001 +python -m fedht.main --config-name base_mnist agg=fedht num_keep=500 num_local_epochs=5 learning_rate=0.00001 +python -m fedht.main --config-name base_mnist agg=fedht iterht=True num_keep=500 num_local_epochs=5 learning_rate=0.00001 ``` | *Experiments: Comparison of Aggregation Approaches to Fed-HT for MNIST* | |:--:| -| ![loss_results_mnist.png](_static/loss_results_mnist.png) | +| ![loss_results_mnist.png](_static/loss_results_mnist_centralized.png) | +| ![loss_results_mnist.png](_static/loss_results_mnist_distributed.png) | ### Simulation II (`num_keep` = 200) ``` python -m fedht.main --config-name base_simII agg=fedavg num_local_epochs=2 learning_rate=0.00001 python -m fedht.main --config-name base_simII agg=fedht num_local_epochs=2 learning_rate=0.00001 python -m fedht.main --config-name base_simII agg=fedht iterht=True num_local_epochs=2 learning_rate=0.00001 -python -m fedht.main --config-name base_simII agg=fedht num_local_epochs=1 learning_rate=0.00001 ``` | *Experiments: Comparison of Aggregation Approaches to Fed-HT for Simulation II* | From 098dae5194b62ea36e219fa735907473221af2e5 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Wed, 27 Nov 2024 16:47:16 -0500 Subject: [PATCH 77/99] feat(baselines) adding updated images --- .../_static/loss_results_mnist_centralized.png | Bin 0 -> 32800 bytes .../_static/loss_results_mnist_distributed.png | Bin 0 -> 33058 bytes baselines/fedht/fedht/conf/base_mnist.yaml | 4 ++-- 3 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 baselines/fedht/_static/loss_results_mnist_centralized.png create mode 100644 baselines/fedht/_static/loss_results_mnist_distributed.png diff --git a/baselines/fedht/_static/loss_results_mnist_centralized.png b/baselines/fedht/_static/loss_results_mnist_centralized.png new file mode 100644 index 0000000000000000000000000000000000000000..811b39b24ffc103085bb6b296877dbb6e0f674dd GIT binary patch literal 32800 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 Q6#OX5tIHMMxgYd@05|odUH||9 literal 0 HcmV?d00001 diff --git a/baselines/fedht/fedht/conf/base_mnist.yaml b/baselines/fedht/fedht/conf/base_mnist.yaml index 36e12fd124dd..879d09acfb63 100644 --- a/baselines/fedht/fedht/conf/base_mnist.yaml +++ b/baselines/fedht/fedht/conf/base_mnist.yaml @@ -14,8 +14,8 @@ iterht: False data: mnist client_resources: - num_cpus: 7 - num_gpus: 1 + num_cpus: 10 + num_gpus: 0 dataset: name: mnist From b940c9eae4f11362df2002c0415b7f3c9fa922f6 Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:51:41 -0500 Subject: [PATCH 78/99] adding new images for mnist --- .../_static/loss_results_mnist_centralized.png | Bin 0 -> 32800 bytes .../_static/loss_results_mnist_distributed.png | Bin 0 -> 33058 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 baselines/fedht/_static/loss_results_mnist_centralized.png create mode 100644 baselines/fedht/_static/loss_results_mnist_distributed.png diff --git a/baselines/fedht/_static/loss_results_mnist_centralized.png b/baselines/fedht/_static/loss_results_mnist_centralized.png new file mode 100644 index 0000000000000000000000000000000000000000..811b39b24ffc103085bb6b296877dbb6e0f674dd GIT binary patch literal 32800 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 Q6#OX5tIHMMxgYd@05|odUH||9 literal 0 HcmV?d00001 From 9c31e06def4024cb2f15717842d6a296b301b0ff Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Mon, 2 Dec 2024 10:51:03 -0500 Subject: [PATCH 79/99] feat(baselines) adjusting simdata function for simulation --- baselines/fedht/fedht/utils.py | 39 +++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/baselines/fedht/fedht/utils.py b/baselines/fedht/fedht/utils.py index b766507d0271..9b6c4430d267 100644 --- a/baselines/fedht/fedht/utils.py +++ b/baselines/fedht/fedht/utils.py @@ -49,14 +49,14 @@ def partition_data(data, num_partitions): def sim_data(ni: int, num_clients: int, num_features: int, alpha=1, beta=1): """Simulate data for simII.""" - # np.random.seed(2025) + np.random.seed(2025) # generate client-based model coefs u = np.random.normal(0, alpha, num_clients) x = np.zeros((num_features, num_clients)) x[0:99, :] = np.random.multivariate_normal(u, np.diag(np.ones(num_clients)), 99) - # generate observations + # generate train observations ivec = np.arange(1, num_features + 1) vari = np.diag(1 / (ivec**1.2)) @@ -69,6 +69,7 @@ def sim_data(ni: int, num_clients: int, num_features: int, alpha=1, beta=1): # (num_clients, ni, num_feaures) for i in range(z.shape[0]): + # train z[i, :, :] = np.random.multivariate_normal(v[i], vari, ni) hold = np.matmul(z[i, :, :], x[:, i]) + error[:, i] y[:, i] = np.exp(hold) / (1 + np.exp(hold)) @@ -80,4 +81,36 @@ def sim_data(ni: int, num_clients: int, num_features: int, alpha=1, beta=1): y[mask, j] = 1 y[~mask, j] = 0 - return z.copy(), y.copy() + # generate test observations + ntest = ni + ivec = np.arange(1, num_features + 1) + vari = np.diag(1 / (ivec**1.2)) + + # B = np.random.normal(0, beta, num_features) + # v = np.random.multivariate_normal(B, np.diag(np.ones(num_features)), num_clients) + + error = np.random.multivariate_normal(u, np.diag(np.ones(num_clients)), ntest) + ztest = np.zeros((num_clients, ntest, num_features)) + ytest = np.zeros((ni, num_clients)) + + # (num_clients, ni, num_feaures) + for i in range(ztest.shape[0]): + # train + ztest[i, :, :] = np.random.multivariate_normal(v[i], vari, ntest) + hold = np.matmul(ztest[i, :, :], x[:, i]) + error[:, i] + ytest[:, i] = np.exp(hold) / (1 + np.exp(hold)) + + for j in range(num_clients): + top_indices = np.argpartition(ytest[:, j], -100)[-100:] + mask = np.zeros(ytest[:, j].shape, dtype=bool) + mask[top_indices] = True + ytest[mask, j] = 1 + ytest[~mask, j] = 0 + + zstack = [] + ystack = [] + for i in range(z.shape[0]): + zstack = np.vstack(ztest[i, :, :]) + ystack = np.vstack(ytest[:, i]) + + return z.copy(), y.copy(), zstack.copy(), ystack.copy() From 3714b2240156b6a872a73ad90b34a9b4536b1759 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Mon, 2 Dec 2024 10:53:17 -0500 Subject: [PATCH 80/99] feat(baselines) adjusting main based on simII changes --- baselines/fedht/fedht/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/baselines/fedht/fedht/main.py b/baselines/fedht/fedht/main.py index 8629a17b0044..14ce1a8caa57 100644 --- a/baselines/fedht/fedht/main.py +++ b/baselines/fedht/fedht/main.py @@ -78,7 +78,8 @@ def main(cfg: DictConfig): # dataset = sim_data(num_obs, num_clients, num_features, 1, 1) # X_test, y_test = sim_data(num_obs, 1, num_features, 1, 1) - test_dataset = MyDataset(X_test[0, :, :], y_test[:, 0]) + # test_dataset = MyDataset(X_test[0, :, :], y_test[:, 0]) + test_dataset = MyDataset(X_test, y_test[:,0]) testloader = DataLoader(test_dataset, batch_size=cfg.batch_size, shuffle=False) # set client function From 55f8366b4d32c84aeb5433cf63d5d84bb3a49361 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Mon, 2 Dec 2024 17:19:37 -0500 Subject: [PATCH 81/99] feat(baselines) editing simII data generation --- baselines/fedht/fedht/client.py | 4 ++-- baselines/fedht/fedht/main.py | 19 +++++++++---------- baselines/fedht/fedht/utils.py | 10 ++++++---- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/baselines/fedht/fedht/client.py b/baselines/fedht/fedht/client.py index 0b9e416b4d07..28fe2e9b7708 100644 --- a/baselines/fedht/fedht/client.py +++ b/baselines/fedht/fedht/client.py @@ -88,8 +88,8 @@ def client_fn(context: Context) -> Client: test_dataset = train_dataset = MyDataset( X_train[int(partition_id), :, :], y_train[:, int(partition_id)] ) - trainloader = DataLoader(train_dataset, batch_size=cfg.batch_size, shuffle=True) - testloader = DataLoader(test_dataset, batch_size=cfg.batch_size, shuffle=True) + trainloader = DataLoader(train_dataset, batch_size=cfg.batch_size, shuffle=False) + testloader = DataLoader(test_dataset, batch_size=cfg.batch_size, shuffle=False) # define model and set device device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") diff --git a/baselines/fedht/fedht/main.py b/baselines/fedht/fedht/main.py index 14ce1a8caa57..11e4e97702d3 100644 --- a/baselines/fedht/fedht/main.py +++ b/baselines/fedht/fedht/main.py @@ -3,6 +3,7 @@ import pickle import random import torch +import gzip import flwr as fl import hydra @@ -68,23 +69,21 @@ def main(cfg: DictConfig): num_classes = cfg.num_classes # import data from fedht/data folder - with open('fedht/data/simII_train.pkl', 'rb') as file: - dataset = pickle.load(file) + # with open('fedht/data/simII_train.pkl', 'rb') as file: + # dataset = pickle.load(file) - with open('fedht/data/simII_test.pkl', 'rb') as file: - test_dataset = pickle.load(file) + # with open('fedht/data/simII_test.pkl', 'rb') as file: + # test_dataset = pickle.load(file) - X_test, y_test = test_dataset - - # dataset = sim_data(num_obs, num_clients, num_features, 1, 1) - # X_test, y_test = sim_data(num_obs, 1, num_features, 1, 1) - # test_dataset = MyDataset(X_test[0, :, :], y_test[:, 0]) + # simulate data + X_train, y_train, X_test, y_test = sim_data(200, num_clients, 1000, 1, 1) + train_dataset = X_train, y_train test_dataset = MyDataset(X_test, y_test[:,0]) testloader = DataLoader(test_dataset, batch_size=cfg.batch_size, shuffle=False) # set client function client_fn = generate_client_fn_simII( - dataset, + train_dataset, cfg=cfg ) diff --git a/baselines/fedht/fedht/utils.py b/baselines/fedht/fedht/utils.py index 9b6c4430d267..14a2d821c7f1 100644 --- a/baselines/fedht/fedht/utils.py +++ b/baselines/fedht/fedht/utils.py @@ -49,12 +49,12 @@ def partition_data(data, num_partitions): def sim_data(ni: int, num_clients: int, num_features: int, alpha=1, beta=1): """Simulate data for simII.""" - np.random.seed(2025) + np.random.seed(2024) # generate client-based model coefs u = np.random.normal(0, alpha, num_clients) x = np.zeros((num_features, num_clients)) - x[0:99, :] = np.random.multivariate_normal(u, np.diag(np.ones(num_clients)), 99) + x[0:100, :] = np.random.multivariate_normal(u, np.diag(np.ones(num_clients)), 100) # generate train observations ivec = np.arange(1, num_features + 1) @@ -89,7 +89,9 @@ def sim_data(ni: int, num_clients: int, num_features: int, alpha=1, beta=1): # B = np.random.normal(0, beta, num_features) # v = np.random.multivariate_normal(B, np.diag(np.ones(num_features)), num_clients) - error = np.random.multivariate_normal(u, np.diag(np.ones(num_clients)), ntest) + error2 = np.random.multivariate_normal(u, np.diag(np.ones(num_clients)), ntest) + xtest = np.zeros((num_features, num_clients)) + xtest[0:100, :] = np.random.multivariate_normal(u, np.diag(np.ones(num_clients)), 100) ztest = np.zeros((num_clients, ntest, num_features)) ytest = np.zeros((ni, num_clients)) @@ -97,7 +99,7 @@ def sim_data(ni: int, num_clients: int, num_features: int, alpha=1, beta=1): for i in range(ztest.shape[0]): # train ztest[i, :, :] = np.random.multivariate_normal(v[i], vari, ntest) - hold = np.matmul(ztest[i, :, :], x[:, i]) + error[:, i] + hold = np.matmul(ztest[i, :, :], xtest[:, i]) + error2[:, i] ytest[:, i] = np.exp(hold) / (1 + np.exp(hold)) for j in range(num_clients): From 3e27ddf1b53c55a7cee37d727efae8157856546b Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Wed, 4 Dec 2024 09:20:40 -0500 Subject: [PATCH 82/99] feat(baselines) adjusting simII experiment --- .../_static/loss_results_simII_centralized.png | Bin 0 -> 43748 bytes .../_static/loss_results_simII_distributed.png | Bin 0 -> 55129 bytes baselines/fedht/fedht/main.py | 7 ------- baselines/fedht/fedht/utils.py | 5 +---- 4 files changed, 1 insertion(+), 11 deletions(-) create mode 100644 baselines/fedht/_static/loss_results_simII_centralized.png create mode 100644 baselines/fedht/_static/loss_results_simII_distributed.png diff --git a/baselines/fedht/_static/loss_results_simII_centralized.png b/baselines/fedht/_static/loss_results_simII_centralized.png new file mode 100644 index 0000000000000000000000000000000000000000..dcdb92be72a9dd3118cd45e6ddbc76bf017b7ac9 GIT binary patch literal 43748 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
KgnG>CE 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 literal 0 HcmV?d00001 diff --git a/baselines/fedht/_static/loss_results_simII_distributed.png b/baselines/fedht/_static/loss_results_simII_distributed.png new file mode 100644 index 0000000000000000000000000000000000000000..35ec81a2a13c749726917b3bc17fdf255a9ee0dc GIT binary patch literal 55129 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 Date: Wed, 4 Dec 2024 09:23:43 -0500 Subject: [PATCH 83/99] feat(baselines) adjust simII config --- baselines/fedht/fedht/conf/base_simII.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/baselines/fedht/fedht/conf/base_simII.yaml b/baselines/fedht/fedht/conf/base_simII.yaml index 7927543ccdf2..f008d8ff734a 100644 --- a/baselines/fedht/fedht/conf/base_simII.yaml +++ b/baselines/fedht/fedht/conf/base_simII.yaml @@ -1,8 +1,6 @@ --- num_clients: 25 # total number of clients num_local_epochs: 10 # number of local epochs -num_obs: 1000 -num_features: 1000 num_classes: 2 batch_size: 50 num_rounds: 100 From 21e08b919eb06e7c11da8f2661ecbb38f412aa1f Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Wed, 4 Dec 2024 09:24:10 -0500 Subject: [PATCH 84/99] Update README.md --- baselines/fedht/README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/baselines/fedht/README.md b/baselines/fedht/README.md index 95d64b41f7dd..7eaa6eb8d289 100644 --- a/baselines/fedht/README.md +++ b/baselines/fedht/README.md @@ -92,11 +92,12 @@ python -m fedht.main --config-name base_mnist agg=fedht iterht=True num_keep=500 ### Simulation II (`num_keep` = 200) ``` -python -m fedht.main --config-name base_simII agg=fedavg num_local_epochs=2 learning_rate=0.00001 -python -m fedht.main --config-name base_simII agg=fedht num_local_epochs=2 learning_rate=0.00001 -python -m fedht.main --config-name base_simII agg=fedht iterht=True num_local_epochs=2 learning_rate=0.00001 +python -m fedht.main --config-name base_simII agg=fedavg num_local_epochs=2 learning_rate=0.0001 +python -m fedht.main --config-name base_simII agg=fedht num_local_epochs=2 learning_rate=0.0001 +python -m fedht.main --config-name base_simII agg=fedht iterht=True num_local_epochs=2 learning_rate=0.0001 ``` | *Experiments: Comparison of Aggregation Approaches to Fed-HT for Simulation II* | |:--:| -| ![loss_results_simII.png](_static/loss_results_simII.png) | +| ![loss_results_simII.png](_static/loss_results_simII_centralized.png) | +| ![loss_results_simII.png](_static/loss_results_simII_distributed.png) | From 78efb382214b40143337ce08a467621e7539ee24 Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Thu, 6 Feb 2025 11:00:26 -0500 Subject: [PATCH 85/99] Update README.md --- baselines/fedht/README.md | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/baselines/fedht/README.md b/baselines/fedht/README.md index 7eaa6eb8d289..7f23ea9928c5 100644 --- a/baselines/fedht/README.md +++ b/baselines/fedht/README.md @@ -38,14 +38,11 @@ poetry shell The purpose of this baseline is 1) implement the federated aggregation strategies introduced in Tong et. al. 2020, and 2) showcase the aggregation strategies with the datasets included in the paper. The two strategies introduced include Fed-HT and FedIter-HT. Fed-HT and FedIter-HT both apply hardthresholding (restricted by the hardthresholding parameter $\tau$) following the aggregation step. FedIter-HT, additionally, applies hardthresholding to each client model prior to aggregation. We also include results using FedAvg. -Two federated classification models are implemented, the first using the well-known MNIST dataset (with 10 clients) and the second using a simulated dataset (with 25 clients). +A federated logistic regression classification model is implemented using the well-known MNIST dataset (with 10 clients). | Dataset | Model | Features | Classes | | ------------------| ---------------------------------|----------|---------| | `MNIST` | `Multinomial Regression` |724 | 10 | -| `Simulation II` | `Logistic Regression` |1000 | 2 | - -The data generation procedure for the simulated dataset matches that of Simulation II in Tong et. al. For both datasets, the initial global model parameters are all set to 0. Additionally, the MNIST data is heterogeneous, with only two out of the ten classes included in each client dataset. **Contributors:** Chancellor Johnstone @@ -63,18 +60,6 @@ The data generation procedure for the simulated dataset matches that of Simulati | `client resources` | `{'num_cpus': 2, 'num_gpus':.5}` | | `iterht` | `False` | -| Description | Default Value (Simulation II) | -| --------------------- | ----------------------------------- | -| `num_clients` | `25` | -| `num_rounds` | `100` | -| `batch_size` | `50` | -| `num_local_epochs` | `5` | -| `num_keep` | `200` | -| `learning_rate` | `0.0001` | -| `weight_decay` | `0.000` | -| `client resources` | `{'num_cpus': 2, 'num_gpus':.5}` | -| `iterht` | `False` | - We note that in the current implementation, only weights (and not biases) of the model(s) are subject to hardthresholding; this practice aligns with sparse model literature. Additionally, the `num_keep` hardthresholding parameter is enforced at the output layer level, as opposed to constraining the number of parameters across the entire model. Specifically, for a fully connected layer with $i$ inputs and $j$ outputs, the $j$-th output's parameters are constrained by `num_keep`. ## Expected Results @@ -90,14 +75,3 @@ python -m fedht.main --config-name base_mnist agg=fedht iterht=True num_keep=500 | ![loss_results_mnist.png](_static/loss_results_mnist_centralized.png) | | ![loss_results_mnist.png](_static/loss_results_mnist_distributed.png) | -### Simulation II (`num_keep` = 200) -``` -python -m fedht.main --config-name base_simII agg=fedavg num_local_epochs=2 learning_rate=0.0001 -python -m fedht.main --config-name base_simII agg=fedht num_local_epochs=2 learning_rate=0.0001 -python -m fedht.main --config-name base_simII agg=fedht iterht=True num_local_epochs=2 learning_rate=0.0001 -``` - -| *Experiments: Comparison of Aggregation Approaches to Fed-HT for Simulation II* | -|:--:| -| ![loss_results_simII.png](_static/loss_results_simII_centralized.png) | -| ![loss_results_simII.png](_static/loss_results_simII_distributed.png) | From 6324d32e60f1357afa2902b86be32324c45cba87 Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Thu, 6 Feb 2025 11:03:59 -0500 Subject: [PATCH 86/99] Update README.md --- baselines/fedht/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/baselines/fedht/README.md b/baselines/fedht/README.md index 7f23ea9928c5..9369f10ce17a 100644 --- a/baselines/fedht/README.md +++ b/baselines/fedht/README.md @@ -75,3 +75,5 @@ python -m fedht.main --config-name base_mnist agg=fedht iterht=True num_keep=500 | ![loss_results_mnist.png](_static/loss_results_mnist_centralized.png) | | ![loss_results_mnist.png](_static/loss_results_mnist_distributed.png) | +Based on the centralized and distributed loss shown in the figures above, we see that FedHT and FedIter-HT are comparable to FedAvg from a performance perspective. However, these plots do not show potential gains made with respect to 1) communicaton efficiency due to the sparse nature of Fed-HT and FedIter-HT or 2) interpretability due to a more parsimonious classification model. + From a6efe785fb5c14926fae734cffe7a3e34136f9b2 Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Thu, 6 Feb 2025 12:00:32 -0500 Subject: [PATCH 87/99] Update README.md --- baselines/fedht/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/baselines/fedht/README.md b/baselines/fedht/README.md index 9369f10ce17a..e2dfafa0122b 100644 --- a/baselines/fedht/README.md +++ b/baselines/fedht/README.md @@ -57,7 +57,7 @@ A federated logistic regression classification model is implemented using the we | `num_keep` | `500` | | `learning_rate` | `0.0005` | | `weight_decay` | `0.000` | -| `client resources` | `{'num_cpus': 2, 'num_gpus':.5}` | +| `client resources` | `{'num_cpus': 10, 'num_gpus':0}` | | `iterht` | `False` | We note that in the current implementation, only weights (and not biases) of the model(s) are subject to hardthresholding; this practice aligns with sparse model literature. Additionally, the `num_keep` hardthresholding parameter is enforced at the output layer level, as opposed to constraining the number of parameters across the entire model. Specifically, for a fully connected layer with $i$ inputs and $j$ outputs, the $j$-th output's parameters are constrained by `num_keep`. From 4c37db9ed112080630db254a9b5fea99d3de4d4c Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Thu, 6 Feb 2025 12:12:58 -0500 Subject: [PATCH 88/99] Update README.md --- baselines/fedht/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/baselines/fedht/README.md b/baselines/fedht/README.md index e2dfafa0122b..64119aa384cc 100644 --- a/baselines/fedht/README.md +++ b/baselines/fedht/README.md @@ -75,5 +75,5 @@ python -m fedht.main --config-name base_mnist agg=fedht iterht=True num_keep=500 | ![loss_results_mnist.png](_static/loss_results_mnist_centralized.png) | | ![loss_results_mnist.png](_static/loss_results_mnist_distributed.png) | -Based on the centralized and distributed loss shown in the figures above, we see that FedHT and FedIter-HT are comparable to FedAvg from a performance perspective. However, these plots do not show potential gains made with respect to 1) communicaton efficiency due to the sparse nature of Fed-HT and FedIter-HT or 2) interpretability due to a more parsimonious classification model. +Based on the centralized and distributed loss shown in the figures above, we see that FedIter-HT is comparable to FedAvg from a performance perspective and, with certain local epoch selection. i.e., one local epoch, Fed-HT outperforms FedAvg. Additionally, these plots do not show potential gains made with respect to 1) communicaton efficiency due to the sparse nature of Fed-HT and FedIter-HT or 2) interpretability due to a more parsimonious classification model. From 6fa18d07449e75ea100353ef33fb96bbc8d304fa Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Thu, 6 Feb 2025 12:45:38 -0500 Subject: [PATCH 89/99] added new plots with distIHT --- .../loss_results_mnist_centralized.png | Bin 32800 -> 46585 bytes .../loss_results_mnist_distributed.png | Bin 33058 -> 46105 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/baselines/fedht/_static/loss_results_mnist_centralized.png b/baselines/fedht/_static/loss_results_mnist_centralized.png index 811b39b24ffc103085bb6b296877dbb6e0f674dd..4d329612436ccf2ca1321cc58d382b8a2c91bd07 100644 GIT binary patch literal 46585 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�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 YaLbj}`gI2mNcb~tinD#vq(wXa0mZ+4+W-In literal 33058 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 Q6#OX5tIHMMxgYd@05|odUH||9 From c8caac0470732f325f362ade3339793d645bb92a Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Thu, 6 Feb 2025 12:45:48 -0500 Subject: [PATCH 90/99] Update README.md --- baselines/fedht/README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/baselines/fedht/README.md b/baselines/fedht/README.md index 64119aa384cc..5eca0f18823d 100644 --- a/baselines/fedht/README.md +++ b/baselines/fedht/README.md @@ -68,12 +68,13 @@ We note that in the current implementation, only weights (and not biases) of the python -m fedht.main --config-name base_mnist agg=fedavg num_keep=500 num_local_epochs=5 learning_rate=0.00001 python -m fedht.main --config-name base_mnist agg=fedht num_keep=500 num_local_epochs=5 learning_rate=0.00001 python -m fedht.main --config-name base_mnist agg=fedht iterht=True num_keep=500 num_local_epochs=5 learning_rate=0.00001 +python -m fedht.main --config-name base_mnist agg=fedht num_keep=500 num_local_epochs=1 learning_rate=0.00001 ``` -| *Experiments: Comparison of Aggregation Approaches to Fed-HT for MNIST* | -|:--:| -| ![loss_results_mnist.png](_static/loss_results_mnist_centralized.png) | -| ![loss_results_mnist.png](_static/loss_results_mnist_distributed.png) | +

+ + +

Based on the centralized and distributed loss shown in the figures above, we see that FedIter-HT is comparable to FedAvg from a performance perspective and, with certain local epoch selection. i.e., one local epoch, Fed-HT outperforms FedAvg. Additionally, these plots do not show potential gains made with respect to 1) communicaton efficiency due to the sparse nature of Fed-HT and FedIter-HT or 2) interpretability due to a more parsimonious classification model. From 96f2cd3eddf804064540dde0dfdfb1390c284e05 Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Thu, 6 Feb 2025 15:34:05 -0500 Subject: [PATCH 91/99] Update README.md --- baselines/fedht/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/baselines/fedht/README.md b/baselines/fedht/README.md index 5eca0f18823d..f79dd4178f11 100644 --- a/baselines/fedht/README.md +++ b/baselines/fedht/README.md @@ -9,7 +9,7 @@ dataset: [MNIST] > Note: If you use this Flower baseline in your work, please remember to cite the original authors of the paper, as well as Flower. -**Paper:** [https://arxiv.org/abs/2101.00052](https://arxiv.org/abs/2101.00052) +**Paper:** [https://par.nsf.gov/servlets/purl/10378100](https://par.nsf.gov/servlets/purl/10378100) **Authors:** Qianqian Tong, Guannan Liang, Tan Zhu, Jinbo Bi @@ -76,5 +76,5 @@ python -m fedht.main --config-name base_mnist agg=fedht num_keep=500 num_local_e

-Based on the centralized and distributed loss shown in the figures above, we see that FedIter-HT is comparable to FedAvg from a performance perspective and, with certain local epoch selection. i.e., one local epoch, Fed-HT outperforms FedAvg. Additionally, these plots do not show potential gains made with respect to 1) communicaton efficiency due to the sparse nature of Fed-HT and FedIter-HT or 2) interpretability due to a more parsimonious classification model. +Based on the centralized and distributed loss shown in the figures above, we see that FedIter-HT is comparable to FedAvg from a performance perspective and, with certain local epoch selection. i.e., one local epoch, Fed-HT outperforms FedAvg. Additionally, these plots do not show potential gains made with respect to 1) communicaton efficiency due to the sparse nature of Fed-HT and FedIter-HT or 2) interpretability due to a more parsimonious classification model. Results differ slightly from published work due to differing hyperparameters. From b0ffacb4d694a696fa8a44eeb53c0a8e6c280129 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Thu, 6 Feb 2025 15:39:17 -0500 Subject: [PATCH 92/99] accuracy results --- .../_static/acc_results_mnist_centralized.png | Bin 0 -> 37567 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 baselines/fedht/_static/acc_results_mnist_centralized.png diff --git a/baselines/fedht/_static/acc_results_mnist_centralized.png b/baselines/fedht/_static/acc_results_mnist_centralized.png new file mode 100644 index 0000000000000000000000000000000000000000..6f425329514c0f00ddf3c707d09d8fae8b4c9aa0 GIT binary patch literal 37567 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 literal 0 HcmV?d00001 From 85836afe1025fd1c4fc3d40bc8c8b3fd3f66f718 Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Thu, 6 Feb 2025 15:41:03 -0500 Subject: [PATCH 93/99] Update README.md --- baselines/fedht/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/baselines/fedht/README.md b/baselines/fedht/README.md index f79dd4178f11..f6880c0c63b9 100644 --- a/baselines/fedht/README.md +++ b/baselines/fedht/README.md @@ -73,7 +73,7 @@ python -m fedht.main --config-name base_mnist agg=fedht num_keep=500 num_local_e

- +

Based on the centralized and distributed loss shown in the figures above, we see that FedIter-HT is comparable to FedAvg from a performance perspective and, with certain local epoch selection. i.e., one local epoch, Fed-HT outperforms FedAvg. Additionally, these plots do not show potential gains made with respect to 1) communicaton efficiency due to the sparse nature of Fed-HT and FedIter-HT or 2) interpretability due to a more parsimonious classification model. Results differ slightly from published work due to differing hyperparameters. From 47dfa856c854f0a74482c38b173f79d432ba979a Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Thu, 6 Feb 2025 15:54:59 -0500 Subject: [PATCH 94/99] Update README.md --- baselines/fedht/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/baselines/fedht/README.md b/baselines/fedht/README.md index f6880c0c63b9..f7f8039b3f8f 100644 --- a/baselines/fedht/README.md +++ b/baselines/fedht/README.md @@ -17,7 +17,7 @@ dataset: [MNIST] # Environment Setup -Once inside the fedht folder (where the pyproject.toml is), you can install the project using [poetry][https://github.com/pyenv/pyenv](https://github.com/python-poetry/poetry), which automatically creates a virtual enviornment. +Once inside the fedht folder (where the pyproject.toml is), you can install the project using [poetry](https://github.com/python-poetry/poetry), which automatically creates a virtual enviornment. ```bash # Set python version From dc8cb67b3fb9300ace4ccfb753cec305818b4f3a Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Sat, 8 Feb 2025 10:46:31 -0500 Subject: [PATCH 95/99] Update README.md --- baselines/fedht/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/baselines/fedht/README.md b/baselines/fedht/README.md index f7f8039b3f8f..a7937e54c1b7 100644 --- a/baselines/fedht/README.md +++ b/baselines/fedht/README.md @@ -76,5 +76,5 @@ python -m fedht.main --config-name base_mnist agg=fedht num_keep=500 num_local_e

-Based on the centralized and distributed loss shown in the figures above, we see that FedIter-HT is comparable to FedAvg from a performance perspective and, with certain local epoch selection. i.e., one local epoch, Fed-HT outperforms FedAvg. Additionally, these plots do not show potential gains made with respect to 1) communicaton efficiency due to the sparse nature of Fed-HT and FedIter-HT or 2) interpretability due to a more parsimonious classification model. Results differ slightly from published work due to differing hyperparameters. +Based on the loss and accuracy results shown in the figures above, we see that FedIter-HT is comparable to FedAvg from a performance perspective and, with certain local epoch selection. i.e., one local epoch, Fed-HT outperforms FedAvg. Additionally, these plots do not show potential gains made with respect to 1) communicaton efficiency due to the sparse nature of Fed-HT and FedIter-HT or 2) interpretability due to a more parsimonious classification model. Results differ slightly from published work due to differing hyperparameters. From efd5f6ab5fe46a0182efc295e666e7202d07941b Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Sat, 8 Feb 2025 10:47:12 -0500 Subject: [PATCH 96/99] Update README.md --- baselines/fedht/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/baselines/fedht/README.md b/baselines/fedht/README.md index a7937e54c1b7..94175d2d81c4 100644 --- a/baselines/fedht/README.md +++ b/baselines/fedht/README.md @@ -66,9 +66,9 @@ We note that in the current implementation, only weights (and not biases) of the ### MNIST (`num_keep` = 500) ``` python -m fedht.main --config-name base_mnist agg=fedavg num_keep=500 num_local_epochs=5 learning_rate=0.00001 +python -m fedht.main --config-name base_mnist agg=fedht num_keep=500 num_local_epochs=1 learning_rate=0.00001 python -m fedht.main --config-name base_mnist agg=fedht num_keep=500 num_local_epochs=5 learning_rate=0.00001 python -m fedht.main --config-name base_mnist agg=fedht iterht=True num_keep=500 num_local_epochs=5 learning_rate=0.00001 -python -m fedht.main --config-name base_mnist agg=fedht num_keep=500 num_local_epochs=1 learning_rate=0.00001 ```
Z+8ZWBH#TgF>zB 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 Bx5xkh literal 0 HcmV?d00001 diff --git a/baselines/fedht/_static/loss_results_mnist_distributed_5.png b/baselines/fedht/_static/loss_results_mnist_distributed_5.png new file mode 100644 index 0000000000000000000000000000000000000000..878142b8075333aeac43a74a755c335b64d5c252 GIT binary patch literal 38035 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 literal 0 HcmV?d00001 From 3b20fc8cb75ed4a6e8f7fdb9ad83aa1bfdb7a5b1 Mon Sep 17 00:00:00 2001 From: chancejohnstone <37714277+chancejohnstone@users.noreply.github.com> Date: Tue, 18 Feb 2025 09:35:36 -0500 Subject: [PATCH 98/99] feat(baselines) update README --- baselines/fedht/README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/baselines/fedht/README.md b/baselines/fedht/README.md index 94175d2d81c4..d3e83df755a9 100644 --- a/baselines/fedht/README.md +++ b/baselines/fedht/README.md @@ -72,8 +72,13 @@ python -m fedht.main --config-name base_mnist agg=fedht iterht=True num_keep=500 ```

- - + + +

+ +

+ +

Based on the loss and accuracy results shown in the figures above, we see that FedIter-HT is comparable to FedAvg from a performance perspective and, with certain local epoch selection. i.e., one local epoch, Fed-HT outperforms FedAvg. Additionally, these plots do not show potential gains made with respect to 1) communicaton efficiency due to the sparse nature of Fed-HT and FedIter-HT or 2) interpretability due to a more parsimonious classification model. Results differ slightly from published work due to differing hyperparameters. From fe3163c8361ced251dcea8a6bbc324fa3e12ed90 Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Tue, 18 Feb 2025 09:55:03 -0500 Subject: [PATCH 99/99] feat(baselines) change images --- .../acc_results_mnist_centralized_1.png | Bin 32556 -> 32447 bytes .../loss_results_mnist_centralized_1.png | Bin 38439 -> 38367 bytes .../loss_results_mnist_distributed_1.png | Bin 38418 -> 38357 bytes baselines/fedht/fedht/test.py | 47 ++++++++++++++++++ 4 files changed, 47 insertions(+) create mode 100644 baselines/fedht/fedht/test.py diff --git a/baselines/fedht/_static/acc_results_mnist_centralized_1.png b/baselines/fedht/_static/acc_results_mnist_centralized_1.png index 5c2dd6cf073441b2a34160cdc75626b48a80e96b..680f18557448ea887b7f07b01cc79a599ab847a5 100644 GIT binary patch literal 32447 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 NZRdV1w&wA3{|nr&NjLxi diff --git a/baselines/fedht/_static/loss_results_mnist_centralized_1.png b/baselines/fedht/_static/loss_results_mnist_centralized_1.png index be0e660af52b91fc28005776f6dbe796a0bdeb41..05a54bf685757ef8ccc9a7d53b14a8a1d068f842 100644 GIT binary patch literal 38367 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%0A2Bi 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 JE@}4Se*hr9DvAIA diff --git a/baselines/fedht/_static/loss_results_mnist_distributed_1.png b/baselines/fedht/_static/loss_results_mnist_distributed_1.png index 734a7c2937cea299b1805217341d82af05b711ab..75540cad916a6ae8ff63b355bb4e94a1c97a762e 100644 GIT binary patch literal 38357 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 BpThtE literal 38418 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 Bx5xkh diff --git a/baselines/fedht/fedht/test.py b/baselines/fedht/fedht/test.py new file mode 100644 index 000000000000..b17a9ccc1fbb --- /dev/null +++ b/baselines/fedht/fedht/test.py @@ -0,0 +1,47 @@ +from utils import sim_data +import pickle +import numpy as np +import bz2 + +dataset = sim_data(200, 25, 1000, .1, .1) +# datase2 = sim_data(500, 25, 1000, .1, .1) +# X1, y1 = sim_data(100, 1, 1000, .1, .1) +# X2, y2 = sim_data(100, 1, 1000, .1, .1) +# X3, y3 = sim_data(100, 1, 1000, .1, .1) +# X4, y4 = sim_data(100, 1, 1000, .1, .1) +# X5, y5 = sim_data(100, 1, 1000, .1, .1) +# X6, y6 = sim_data(100, 1, 1000, .1, .1) +# X7, y7 = sim_data(100, 1, 1000, .1, .1) +# X8, y8 = sim_data(100, 1, 1000, .1, .1) +# X9, y9 = sim_data(100, 1, 1000, .1, .1) +# X10, y10 = sim_data(100, 1, 1000, .1, .1) + +# X = np.concatenate((X1,X2,X3,X4,X5,X6,X7,X8,X9,X10), axis = 0) +# y = np.concatenate((y1,y2,y3,y4,y5,y6,y7,y8,y9,y10), axis = 0) +# test_dataset = X, y + +X, y, Xtest, ytest = dataset +train_dataset = X, y +test_dataset = Xtest, ytest + +# print(Xtest) +# print(ytest) +filename_train = 'simII_train.bz2' +filename_test = 'simII_test.bz2' + +with bz2.open(filename_train, "wb") as file: + pickle.dump(train_dataset, file) + +with bz2.open(filename_test, "wb") as file: + pickle.dump(test_dataset, file) + +# with gzip.open('fedht/data/simII_train.pkl', 'rb') as file: +# dataset = pickle.load(file) + +# with gzip.open('fedht/data/simII_test.pkl', 'rb') as file: +# test_dataset = pickle.load(file) + +# X_test, y_test = test_dataset +# X_test, y_test = test_dataset + +# print(X_test) \ No newline at end of file

From 46ec2ccfbed58b48a92dc30c644edb985602f89e Mon Sep 17 00:00:00 2001 From: chancejohnstone Date: Tue, 18 Feb 2025 09:34:25 -0500 Subject: [PATCH 97/99] feat(baselines) 1 local epoch --- .../_static/acc_results_mnist_centralized_1.png | Bin 0 -> 32556 bytes .../_static/acc_results_mnist_centralized_5.png | Bin 0 -> 31050 bytes .../loss_results_mnist_centralized_1.png | Bin 0 -> 38439 bytes .../loss_results_mnist_centralized_5.png | Bin 0 -> 38470 bytes .../loss_results_mnist_distributed_1.png | Bin 0 -> 38418 bytes .../loss_results_mnist_distributed_5.png | Bin 0 -> 38035 bytes 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 baselines/fedht/_static/acc_results_mnist_centralized_1.png create mode 100644 baselines/fedht/_static/acc_results_mnist_centralized_5.png create mode 100644 baselines/fedht/_static/loss_results_mnist_centralized_1.png create mode 100644 baselines/fedht/_static/loss_results_mnist_centralized_5.png create mode 100644 baselines/fedht/_static/loss_results_mnist_distributed_1.png create mode 100644 baselines/fedht/_static/loss_results_mnist_distributed_5.png diff --git a/baselines/fedht/_static/acc_results_mnist_centralized_1.png b/baselines/fedht/_static/acc_results_mnist_centralized_1.png new file mode 100644 index 0000000000000000000000000000000000000000..5c2dd6cf073441b2a34160cdc75626b48a80e96b GIT binary patch literal 32556 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 NZRdV1w&wA3{|nr&NjLxi literal 0 HcmV?d00001 diff --git a/baselines/fedht/_static/acc_results_mnist_centralized_5.png b/baselines/fedht/_static/acc_results_mnist_centralized_5.png new file mode 100644 index 0000000000000000000000000000000000000000..2b21ca8b08c0ebe62eddf077299a1253ee02a3ee GIT binary patch literal 31050 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%0A2Bi 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 JE@}4Se*hr9DvAIA literal 0 HcmV?d00001 diff --git a/baselines/fedht/_static/loss_results_mnist_centralized_5.png b/baselines/fedht/_static/loss_results_mnist_centralized_5.png new file mode 100644 index 0000000000000000000000000000000000000000..dc21131d345a47c2687419d904161f96f6f4c473 GIT binary patch literal 38470 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%0AYq{Pgy~#9&($h>%m#NOl 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