From a3111ed48d7119921a5b7296922f17f1e9d1fcef Mon Sep 17 00:00:00 2001 From: Javier Date: Fri, 10 Nov 2023 10:39:00 +0000 Subject: [PATCH] Add Whisper example for federated downstreaming (#2569) --- .../whisper-federated-finetuning/README.md | 250 ++++++++++++++++++ .../federated_finetuning_flower_pipeline.png | Bin 0 -> 52869 bytes .../_static/keyword_spotting_overview.png | Bin 0 -> 32776 bytes .../_static/whisper_flower_acc.png | Bin 0 -> 61897 bytes .../_static/whisper_flower_data.png | Bin 0 -> 33138 bytes .../centralised.py | 138 ++++++++++ .../whisper-federated-finetuning/client.py | 183 +++++++++++++ .../pyproject.toml | 19 ++ .../requirements.txt | 7 + .../whisper-federated-finetuning/rpi_setup.md | 49 ++++ .../whisper-federated-finetuning/server.py | 104 ++++++++ examples/whisper-federated-finetuning/sim.py | 95 +++++++ .../whisper-federated-finetuning/utils.py | 210 +++++++++++++++ 13 files changed, 1055 insertions(+) create mode 100644 examples/whisper-federated-finetuning/README.md create mode 100644 examples/whisper-federated-finetuning/_static/federated_finetuning_flower_pipeline.png create mode 100644 examples/whisper-federated-finetuning/_static/keyword_spotting_overview.png create mode 100644 examples/whisper-federated-finetuning/_static/whisper_flower_acc.png create mode 100644 examples/whisper-federated-finetuning/_static/whisper_flower_data.png create mode 100644 examples/whisper-federated-finetuning/centralised.py create mode 100644 examples/whisper-federated-finetuning/client.py create mode 100644 examples/whisper-federated-finetuning/pyproject.toml create mode 100644 examples/whisper-federated-finetuning/requirements.txt create mode 100644 examples/whisper-federated-finetuning/rpi_setup.md create mode 100644 examples/whisper-federated-finetuning/server.py create mode 100644 examples/whisper-federated-finetuning/sim.py create mode 100644 examples/whisper-federated-finetuning/utils.py diff --git a/examples/whisper-federated-finetuning/README.md b/examples/whisper-federated-finetuning/README.md new file mode 100644 index 000000000000..712cf0e88369 --- /dev/null +++ b/examples/whisper-federated-finetuning/README.md @@ -0,0 +1,250 @@ +# On-device Federated Finetuning for Speech Classification + +This example demonstrates how to, from a pre-trained [Whisper](https://openai.com/research/whisper) model, finetune it for the downstream task of keyword spotting. We'll be implementing a federated downstream finetuning pipeline using Flower involving a total of 100 clients. As for the downstream dataset, we'll be using the [Google Speech Commands](https://huggingface.co/datasets/speech_commands) dataset for keyword spotting. We'll take the encoder part of the [Whisper-tiny](https://huggingface.co/openai/whisper-tiny) model, freeze its parameters, and learn a lightweight classification (\<800K parameters !!) head to correctly classify a spoken word. + +![Keyword Spotting with Whisper overview](_static/keyword_spotting_overview.png) + +This example can be run in three modes: + +- **Centralized training**: the standard way of training ML models, where all the data is available to the node doing the finetuning. +- **Federated Learning**: the better way of doing ML, where a model is finetuned collaboratively by nodes (i.e. clients), each using their own data. These clients can run: + - in _simulation_ mode: a client is an ephemeral Python process with a portion of the system resources assigned to it. + - in _on-device_ mode: clients are detached entities and each can run on a different device. + +## Running the example + +Start by cloning the code example. We prepared a single-line command that you can copy into your shell which will checkout the example for you: + +```shell +git clone --depth=1 https://github.com/adap/flower.git && mv flower/examples/whisper-federated-finetuning . && rm -rf flower && cd whisper-federated-finetuning +``` + +This will create a new directory called `whisper-federated-finetuning` containing the following files: + +``` +-- README.md <- Your're reading this right now +-- rpi_setup.md <- A guide that illustrates how to setup your RPi from scratch +-- sim.py <- Runs the example with Flower simulation +-- server.py <- Defines the server-side logic for the on-device setting +-- client.py <- Defines the client-side logic for the on-device setting +-- utils.py <- auxiliary functions for this example +-- centralised.py <- Runs the example in centralized mode +-- pyproject.toml <- Example dependencies (if you use Poetry) +-- requirements.txt <- Example dependencies +``` + +This example can be run in different ways, please refer to the corresponding section for further instructions. This example was tested with `PyTorch 2.1.0` for all the different ways of running this example except when running on the Raspberry Pi, which seemed to only work with `PyTorch 1.13.1`. Please note the requirement files do not specify a version of PyTorch, therefore you need to choose one that works for you and your system. + +## Centralized Training + +This section describes how to finetune `Whisper-tiny` for keyword spotting without making use of Federated Learning. This means that the whole training set is available at any point and therefore it is in its entirety to finetune the model each epoch. + +On your favorite Python environment manager, install a recent version of [PyTorch](https://pytorch.org/get-started/locally/) (PyTorch 2.0+ is recommended for faster training times). Then install the rest of the requirements. For instance: + +```bash +pip install torch==2.1.0 --index-url https://download.pytorch.org/whl/cu118 +pip install -r requirements.txt +``` + +Then run centralized training as follows. Please note that the first time you run the code, the `SpeechCommnads` dataset will be downloaded and pre-processed using πŸ€— API (which takes a little while -- approx 40min -- and is cached in `~/.cache/huggingface/datasets/speechcommands` wiht a footprint of ~83GB). Subsequent runs shouldn't require this preprocessing. + +```bash +python centralised.py --compile # don't use `--compile` flag if you are using pytorch < 2.0 + +# The script will save a checkpoint of the classifier head after each epoch +# These checkpoints followo the naming style: `classifier_.pt` + +# You can load a checkpoint by passing it like this: +python centralised.py --checkpoint .pt +``` + +Within 2 epochs you should see a validation accuracy of over 95%. On an RTX 3090Ti each epoch takes ~3min30sec. The final test set consistently reaches 97%+. Below is the log you should expect to see: + +```bash +... +classifier_head_params = 781964 +Initial (loss, acc): loss = 0.04124763025785586, accuracy = 0.03215788419154478 +Epoch: 0 +100%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 84928/84928 [03:05<00:00, 456.93it/s, avg_loss=0.7269, avg_acc=0.8282] +VALIDATION ---> loss = 0.0051703976778501234, accuracy = 0.9319775596072931 +Epoch: 1 +100%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 84928/84928 [03:07<00:00, 454.06it/s, avg_loss=0.1588, avg_acc=0.9629] +VALIDATION ---> loss = 0.003613288299632327, accuracy = 0.943097575636145 +Epoch: 2 +100%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 84928/84928 [03:06<00:00, 454.16it/s, avg_loss=0.1208, avg_acc=0.9675] +VALIDATION ---> loss = 0.0022978041400064466, accuracy = 0.9610298537367261 +Training done... +Evaluating test set. Loading best model +TEST ---> loss = 0.001703281509680464, accuracy = 0.9740286298568507 +``` + +> You made it work better ? Let us know how you did it by opening an GitHub issue or a PR and we'll gladly incorporate your suggestions! + +## Federated Learning + +Centralized training is ok but in many settings it cannot be realised. Primarily because the training data must remain distributed (i.e. on the client side) and cannot be aggregated into a single node (e.g. your server). With Flower we can easily design a federated finetuning pipeline by which clients locally train the classification head on their data, before communicating it to a central server. There, the updates sent by the clients get aggregated and re-distributed among clients for another round of FL. This process is repeated until convergence. Note that, unlike the encoder part of the Whisper model, the classification head is incredibly lightweight (just 780K parameters), adding little communication costs as a result. + +In this example, we partition the training set along the `speaker_id` column into 100 buckets to simulate that many groups of people. You can think of each group as an individual FL _client_ that contains several users/speakers. One way to think about this is to view each client as an office with several people working there, each interacting with the Keyword spotting system. This example exclusively federates the training of the classification head. + +```python +from datasets import load_dataset +sc_train = load_dataset("speech_commands", "v0.02", split="train", token=False) +print(sc_train) +# Dataset({ +# features: ['file', 'audio', 'label', 'is_unknown', 'speaker_id', 'utterance_id'], +# num_rows: 84848 +# }) + +# The training set is comprised of ~85K 1-second audio clips from 2112 individual speakers +ids = set(sc_train['speaker_id']) +print(len(ids)) +# 2113 # <--- +1 since a "None" speaker is included (for clips to construct the _silence_ training examples) +``` + +![Federated Whisper Finetuning pipeline](_static/federated_finetuning_flower_pipeline.png) + +An overview of the FL pipeline built with Flower for this example is illustrated above. + +1. At the start of a round, the server communicates the classification head to a fraction of the clients. At round #0, the classification head is randomly intialised. +2. Each client, using a frozen pre-trained Whisper encoder, trains the classification head using its own data samples. +3. Once on-site training is completed, each client sends back the (now updated) classification head to the Flower server. +4. The Flower server aggregates (via FedAvg) the classification heads in order to obtain a new _global_ classification head. This head will be shared with clients in the next round. + +Flower supports two ways of doing Federated Learning: simulated and non-simulated FL. The former, managed by the [`VirtualClientEngine`](https://flower.dev/docs/framework/how-to-run-simulations.html), allows you to run large-scale workloads in a system-aware manner, that scales with the resources available on your system (whether it is a laptop, a desktop with a single GPU, or a cluster of GPU servers). The latter is better suited for settings where clients are unique devices (e.g. a server, a smart device, etc). This example shows you how to use both. + +### Preparing the dataset + +If you have run the centralized version of this example first, you probably realized that it takes some time to get a fully pre-processed SpeechCommands dataset using the πŸ€— HuggingFace API. This pre-processing is ideal so nothing slowdowns our training once we launch the experiment. For the federated part of this example, we also need to pre-process the data however in a different way since first the training set needs to be split into N different buckets, one for each FL client. + +To launch a Flower client we need a `client_fn` callable that will: (1) Load the dataset of the client; then, (2) return the Client object itself. In `client.py` we have included a few lines of code that preprocess the training partition of a given client and save it to disk (so this doesn't have to be repeated each time you run the experiment). The average pre-processed partition is ~0.5GB. You can run the experiment right away and the data will be pre-processed on-demand (i.e. when the `i`-th client is spawned for the first time), or you can pre-process all client partitions first. In order to do so, please run: + +```bash +# will write to disk all pre-processed data partitions +# by default these will go to a new directory named `client_datasets` +# Similarly to the centralised setting, this preprocessing will take a while (30mins approx) +python sim.py --preprocess +``` + +The resulting data partitions are not equal-sized (which is what you'd often find in practice in the real world) because not all `speaker_id` contributed the same amount of audio clips when the [Speech Commands Dataset](https://arxiv.org/abs/1804.03209) was created. If we make a bar plot showing the amount of data each client has this is the result. + +![Amount of data per client](_static/whisper_flower_data.png) + +### Federated Finetuning (Simulation) + +The setup instructions for simulations are the same as those described for the centralized setting above: install PyTorch and then `pip install -r requirements.txt`. Then, you can launch your simulation as shown below. Without changes to the code or input arguments, the simulation will sample `10` clients per round, these would do 1 local epoch of finetuning the classification head while the encoder remains frozen. Once this is completed, the classification head is sent to the server for aggregation via `FedAvg`. By default, this example assumes you have a GPU available. + +```bash +# By default it will run 2 clients in parallel on a single GPU (which should be fine if your GPU has at least 16GB ) +# If that's too much, consider reduing either the batch size or raise `num_gpus` passed to `start_simulation` +python sim.py # append --num_gpus=0 if you don't have GPUs on your system + +# Once finished centralised evaluation loss/acc metrics will be shown + +INFO flwr 2023-11-08 14:03:57,557 | app.py:229 | app_fit: metrics_centralized {'val_accuracy': [(0, 0.03977158885994791), + (1, 0.6940492887196954), (2, 0.5969745541975556), (3, 0.8794830695251452), (4, 0.9021238228811861), (5, 0.8943097575636145), + (6, 0.9047285113203767), (7, 0.9330795431777199), (8, 0.9446002805049089), (9, 0.9556201162091765)], + 'test_accuracy': [(10, 0.9719836400817996)]} +``` + +![Global validation accuracy FL with Whisper model](_static/whisper_flower_acc.png) + +With just 5 FL rounds, the global model should be reaching ~95% validation accuracy. A test accuracy of 97% can be reached with 10 rounds of FL training using the default hyperparameters. On an RTX 3090Ti, each round takes ~20-30s depending on the amount of data the clients selected in a round have. + +Take a look at the [Documentation](https://flower.dev/docs/framework/how-to-run-simulations.html) for more details on how you can customize your simulation. + +### Federated Finetuning (non-simulated) + +Running the exact same FL pipeline as in the simulation setting can be done without using Flower's simulation engine. To achieve this, you need to launch first a server and then two or more clients. You can do this on your development machine assuming you have set up your environment already. + +First, launch the server, which will orchestrate the FL process: + +```bash +# The server will wait until at least two clients are connected +# you can use `--server_address='localhost'` if you are running everything on the same machine. +python server.py --server_addres= +``` + +Then on different (new) terminals run: + +```bash +# use a difference `--cid` (client id) to make the client load a particular dataset partition (any integer between 0-99) +# you can use `--server_address='localhost'` if you are running everything on the same machine. +python client.py --server_address= --cid=0 + +# and on a new terminal/machine (and optionally a different `cid`) +python client.py --server_address= --cid=1 +``` + +Once the second client connects to the server, the FL process will begin. Each client will report its training progress. The server process will do the same + +```bash +# python client.py --server_address='localhost' --cid=50 +# This client runs on a NVIDIA RTX 3090Ti +INFO flwr 2023-11-08 14:12:50,135 | grpc.py:49 | Opened insecure gRPC connection (no certificates were passed) +DEBUG flwr 2023-11-08 14:12:50,136 | connection.py:42 | ChannelConnectivity.IDLE +DEBUG flwr 2023-11-08 14:12:50,136 | connection.py:42 | ChannelConnectivity.CONNECTING +DEBUG flwr 2023-11-08 14:12:50,140 | connection.py:42 | ChannelConnectivity.READY +99%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 920/925 [00:09<00:00, 93.39it/s, avg_loss=2.4414, avg_acc=0.1837] +99%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 920/925 [00:04<00:00, 216.93it/s, avg_loss=2.0191, avg_acc=0.3315] +99%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 920/925 [00:04<00:00, 214.29it/s, avg_loss=1.5950, avg_acc=0.5500] +99%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 920/925 [00:04<00:00, 212.70it/s, avg_loss=1.1883, avg_acc=0.7348] +99%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 920/925 [00:04<00:00, 208.69it/s, avg_loss=0.8466, avg_acc=0.8228] +99%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 920/925 [00:04<00:00, 206.31it/s, avg_loss=0.6353, avg_acc=0.8837] +99%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 920/925 [00:03<00:00, 266.73it/s, avg_loss=0.4842, avg_acc=0.9207] +99%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 920/925 [00:04<00:00, 212.13it/s, avg_loss=0.3519, avg_acc=0.9391] +99%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 920/925 [00:04<00:00, 213.17it/s, avg_loss=0.3233, avg_acc=0.9359] +99%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 920/925 [00:04<00:00, 205.12it/s, avg_loss=0.2646, avg_acc=0.9543] +DEBUG flwr 2023-11-08 14:20:01,065 | connection.py:139 | gRPC channel closed +INFO flwr 2023-11-08 14:20:01,065 | app.py:215 | Disconnect and shut down +``` + +### Federated Finetuning on Raspberry Pi + +Setting up the environment for the Raspberry Pi is not that different from the steps you'd follow on any other Ubuntu machine (this example assumes your Raspberry Pi -- either 5 or 4 -- runs Ubuntu server 22.04/23.10 64bits). Using the code as-is, RAM usage on the Raspberry Pi does not exceed 1.5GB. Note that unlike in the previous sections of this example, clients for Raspberry Pi work better when using PyTorch 1.13.1 (or earlier versions to PyTorch 2.0 in general). + +> Please follow the steps [here](rpi_setup.md) if you are looking for a step-by-step guide on how to setup your Raspberry Pi to run this example. + +In order to run this example on a Raspberry Pi, you'll need to follow the same steps as outlined above in the `non-simulated` section. First, launch the server on your development machine. + +```bash +# The server will wait until at least two clients are connected +python server.py --server_addres= +``` + +Then, on each of your Raspberry Pi do the following. If you only have one RPi, you can still run the example! But you will need two clients. In addition to the one on the Raspberry Pi, you could launch a client in a separate terminal on your development machine (as shown above in the `non-simulated` section). + +```bash +# use a difference `--cid` (client id) to make this device load a particular dataset partition +# we pass the `--no-compile` option since for RPi we are not using PyTorch 2.0+ +python client.py --server_address= --cid=0 --no-compile +``` + +The first time you run a client on the RPi, the dataset of a client needs to be extracted from the full train set and then pre-processed. The Raspberry Pi 5 is also faster in this pre-processing stage using `.filter()` and `.map()` of πŸ€— HuggingFace Dataset. `map()` used `num_proc=4`: + +| **Stage** | Notes | **RPi 4** | **RPi 5** | +| :-------------------------------------: | :----------------------------------------------: | --------- | --------- | +| Filter through training set (~85k rows) | doing `.filter()` in `client.client_fn` | 1:58 | 0.37 | +| Encode 845 rows with `WhisperProcessor` | doing `.map()` passing `utils.prepare_dataset()` | 1:55 | 1:06 | + +Some clients have more data than others, but on average, the RPi5 is 1.9x faster than an RPi4 when training the classification head given a frozen encoder. A client with 925 training examples needs ~20min on an RPi to complete an epoch of on-device finetuning. + +```bash +# Running the 50-th client on a RPi 5 showed the following log (a RPi4 ran client 83) +python client.py --cid=50 --server_address= --no-compile +INFO flwr 2023-11-08 16:20:33,331 | grpc.py:49 | Opened insecure gRPC connection (no certificates were passed) +DEBUG flwr 2023-11-08 16:20:33,333 | connection.py:42 | ChannelConnectivity.IDLE +DEBUG flwr 2023-11-08 16:20:33,334 | connection.py:42 | ChannelConnectivity.CONNECTING +DEBUG flwr 2023-11-08 16:20:33,349 | connection.py:42 | ChannelConnectivity.READY +99%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 920/925 [20:09<00:06, 1.31s/it, avg_loss=2.4392, avg_acc=0.1902] +99%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 920/925 [20:06<00:06, 1.31s/it, avg_loss=1.9830, avg_acc=0.3533] +99%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 920/925 [20:06<00:06, 1.31s/it, avg_loss=1.6069, avg_acc=0.5641] +99%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 920/925 [20:07<00:06, 1.31s/it, avg_loss=1.1933, avg_acc=0.7402] +99%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 920/925 [20:07<00:06, 1.31s/it, avg_loss=0.8749, avg_acc=0.8478] +99%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 920/925 [20:06<00:06, 1.31s/it, avg_loss=0.5933, avg_acc=0.9109] +99%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 920/925 [20:08<00:06, 1.31s/it, avg_loss=0.4882, avg_acc=0.9359] +99%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 920/925 [20:01<00:06, 1.31s/it, avg_loss=0.4022, avg_acc=0.9304] +99%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 920/925 [20:10<00:06, 1.32s/it, avg_loss=0.3219, avg_acc=0.9533] +99%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 920/925 [20:13<00:06, 1.32s/it, avg_loss=0.2729, avg_acc=0.9641] +DEBUG flwr 2023-11-08 19:47:56,544 | connection.py:139 | gRPC channel closed +INFO flwr 2023-11-08 19:47:56,544 | app.py:215 | Disconnect and shut down +``` diff --git a/examples/whisper-federated-finetuning/_static/federated_finetuning_flower_pipeline.png b/examples/whisper-federated-finetuning/_static/federated_finetuning_flower_pipeline.png new file mode 100644 index 0000000000000000000000000000000000000000..8b931e43c80e8ee1ecd01ed2e2e9b0c1892b6b49 GIT binary patch literal 52869 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 literal 0 HcmV?d00001 diff --git a/examples/whisper-federated-finetuning/_static/whisper_flower_acc.png b/examples/whisper-federated-finetuning/_static/whisper_flower_acc.png new file mode 100644 index 0000000000000000000000000000000000000000..9988cdaefa35765742898f817026c1fa2b4db14f GIT binary patch literal 61897 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Ν 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 literal 0 HcmV?d00001 diff --git a/examples/whisper-federated-finetuning/_static/whisper_flower_data.png b/examples/whisper-federated-finetuning/_static/whisper_flower_data.png new file mode 100644 index 0000000000000000000000000000000000000000..92a29ceff9799fd97433bef508ed87059a1762da GIT binary patch literal 33138 zcmd43c{tW<+cx~8K}CilsU(UFNl7UYnJQ9dO2!hBsF2JG3C*sQLWZjhNreoVA`KL| zkRo%6%tK~+_tRSU^Q`-M@Auud@B9AwY^${_xqibr9LIj_`+l64wGZrBylBNDilP?p z+pDTeQFEjyis|`+`S?kX#-u;~v%^`<*jdl_+JgYiymJozBWhNjd!2FKl!?V%j))_mrin_-}$u|aLC=wTC{%Ne#hD@+P9 z(&G&@GxTr7eqM7y{F$un4OO)zT+cs_Pq)ZszS)0g)2+Mv8dHA`yt{w4SK(*lj?wbz zZ|z6!aw_`(IhFM4YW?BPDi$7b zec$~e#W#gDha1*iG&-DV^I+KFQb|jDdjKw!$4H+rb9AUn-+gbJgUm*g%u}Y3FKlqV z>+g?coo)*}&?TnHT_6_iHS$%nOkGWFx5zD;QnE(iv#F>Rtn;PlzEetPZ@g#k+}rT-634DFD#N~3J^M_1b9Z;Bh*pFJBYxkz z^77Az@^;1M{U!S`?N+J38d5`nwd9oFK2ESka z+_h!yne0_~1HKd034UkVOJfdaoLIi&O#GYr{tNS1*W7yV^y32`-C1yMX}A;z2J!p% zWAxnh2crrZj3v{PqqTKsmdnkkhqG<*lJ!lu`SIay|Hz1;M%C}Hi8r5nj-SzgY&<}# z5I<3NXY)}`PR<8m54T^O9ve7%=WeI#t>7zHO2&tNZg%Z9j{H1_ii(PIn;P5v;p0aa zv)ZmN1wM+6oKSJ%)zM0@?U3 z)MePW%}h^jKi9j;@UgMqjkSA=f`4O{mO2w zKWJfTIZ#(N^t;x)?Q@FBcK2b?2wBG`Vc|5I^>r5aH=k_w8SERS;|^_+=NiQ-GdFI}wc8B5ckm(}@xrDifjw7O z=^TE9e>3?SEi(J?;RD;qy4Uz6i=#*N9@~Ysk5(IpT{S z$Hc^BIkjBS$nL4nj62nk^Yr<1W$dGYwrh$>kF$t{l5*~;yDyNe(8Z7YuC=XIDhelix=mt;^#-? z@(T?OH8Q^SOTniit2L?tJI3y$nDg;G>h43jWoKXBl{ z0#?>y>}1LIOuMT21yh5pry6s`Qdmk1qGNk248Qe`bf?G$PnO)edGpf?2NPoxlQ?WG z!f_~zXT@xK^xtA>y*Yf!@{YG(T_>zS^1=BWnk@&2I- zMNPZ)2kw(#h&v?>$Wb$sR}22kcz6!JU!QO!Tl#TI$_*NAp3+dyp~$Y<)Z*%D?vD{$ z_1>ptW*SXwHXKfV{aRJVv2j&OdTDa|RcOH&!` zZ6ITHC5lDI6C1OCPHbG9PWBn=FJHgL9E{%e?!yPwQ>QkP$+oev`BGPB)!+Q;-N%mx z5qoy;W;*^cBH%<#(ksL(Gc&WXv9@alr3(c&d5oOQ9eA_y>5CWY^)DQ1ddrwTj$W(q z80!}#*TK5OqA9}q;+JzS?5uhea{tG-?6cz2a+^1EN#EUXy|Uw5_Lhik4&q+kBX`5G z6ijo96x{kPI(K?|7Ysc2y0dYEdHyPFtf%Se{x4td6k5BM=Dp|kM$<)ldV2Kq3dKLG zr0=q|y}cIBSj5U|X=8I~-z|}Sc6DP~lEIfRFGQ$kyugUWp7zDA_Ve|nRw=mai%oY_ z#w}oDQw>=rS^Z;&oLo}goMXR#)#xg6ELfnVtb9Q?KgY(bwYBvVV$yawIfTDu87FFr z-U|3^XqR~LO6%UX7A&X0Pv`socZrj{RK=InB4In_r!lIoFAOa&d$Dkvz33{NZ9c}zRwJa z_a8pI#+peEGsKFc3NxF$LxO@9`1|`?%uJ2Hd;gy7Qu1i`cbrwPD@5=OY0UE|e*2bX z=*P!mB$Le;Kr$)fzppIc%-r=krMUUk#m&b*EMK>NeOsAS!RxzQY@CKWbm(Pq;Zo-6 z1>Vz}tX?l*Te)q&+*6ZhR`Wc)yejchckbS`e0!DO?a%b6V0al9AKyaC`1!Gg>ep5; zHZd{TA1TK}HMh1hH+QB8rR;7~7X^H#2-Yb%{FXytm;Rx*hy+sd#SNK`=58JyM;mj`Z}*yznHeqkLn71A&x-KU z4|lh)R6W#EY0CE+=zZDOiWfG1agq;7HeicQd0}NG7lvN#_;Hb$@t*=4%yO4gX5+Ub zSvXdcDe0(5=A9Vn36V5;LDGAIQR-?WjHx0!R#sN7B}*cTR~!?wBXQCsalZ!k7CZjQUQOBs83!-fsP84Ag5ot>AFd+q)VNOP=~R|?-~ z`Uzt^+*Lcf`_-?mT8I%~OtWdwQVxFU;mN({mhQ5{5u0NtjUoFBjsE+o&B+YfAlW?;!7NRfFdYS(`k=LujLfU0NrINDJhl? z4yNB_5Oo21)Sa9*VK^_Bg-abR3Fg_mcklHP*4?{zUoUIle&%~ny1Dk_i@1>HklfiH zY|qouDw7N(#I#lxB&zYryKPbrUG9f`oHzLD6c*9xv3|>MS*KOxjuYt7=rw98W5<}g z<@g6B%mq@dpnu`Bqi;EvFHd;xa|Or`*CqSz-7)D6#!sm{_uob4KGO!@9SnFk9#N`& zVyaW%?e*dQj-|-(nBNGEr%dwG3bgmf?#S3JVK5j^kG)?qmxZOR`)eAbCK1si?(EO< z{-L2_tU?PbE1}5!3Z7$|B4q4hWhyco-C4Jus4heZ7xdSze`7R$#rtbtB9a}+mGfAa ztx-_m$7W`v?{w`}GBh+y%)Vf};X-I5AmuzN1pir9D7Qw*>>t%f=orj%X7)`9`5doC@Z8fafgcc z?{)UEE?mfkfiut;`u&>+G00GCC-#Bm(W7gh9LbIi+jV~MnB|ER;s8p*{+w2mBR$N% z1U%?JeDFYN?!0*mDJyJj@geWeesfrYkGy^V{=M=7K`MxE%kjNc*xf1=r%E83PJ~fK zl!98oBA)mP*;C&*A3l6&RepbmM*Gav&_T=N#~+5RUa=x+SV2^D!Z%j3B6G6PZ;tL^ ztmJvrz(?7py>{8z*+n%q2E)A#CK{Rt4j3MiMeJkty|d%&LtTX{E4D82_V#A{#N*@L z-rp3~J#1+iV_W+qQoYNg`^z(C-|ZNNu#x^2m6o0!$=uvrRs4MzyOOH9dcKo)#chM( zx7dxBFI-@zNk@$IV80{uI|d@Pv0O(jC1jbLZJon4^vk>uopewatk)S_vPCB*5 zd!loF;t|XqZDb(0N4+6mxys8}hkMfZ=O2ZaQx$!BJY9KsUX96Use&n8ij)mp%JL0T zU+U{aE-zh=>tN5vDhDiTP04Irg0(_cymHW^$%RXoE*)roHAm2Y#9J>!nk-FAOTqnG zS|u+W>SdyO4*~v$P;2t1M@$6$InVVq@=&i|U1TBKJ0LMJk)TwJ;C)3OGfI16T6gN9 zeuDPy_(zX4I~AvgZ&7PpyT1t1#a@^MAU?8EBtfdZIQVQa0OX;%YnX3@{54b%N-*UG z1H(HLb>adSEL^Bl7kK8(8UM`8%#Ken7d3WH{O)>GDJT9*cEZK`pp)x` zry5pZ-SSFr*Ioj=hW(}&CM1-_^qE}RH&$3&Gtz9rhF7d5TVDpt((0j zoM|rIzkW>=*^vcHuRtKq^7)hRY*K-tUK3tci6lt3c5-o1!|v1nOy35qD;}Bh?CDdb zTOwNhwdOu{GI=@H?yhx+I>q0P42_C^!gLki(6wlXLR`Y?Ab#yGzb#0Hw;D7)JF%S z-=LtS+}_gPQVbR*b$Ek+QCDve>f2>L5|ddmAg>QXvQU-YTbF-Zrir7)ur1>-9Lzh29PAAP!^lDf}4|*!a}P~H8;;$5<6zzaBd1LQJm-aAj77_ zyHBkt(n?`^xay!(Ui)dx1`4KIyGFrnojP@jvCVa)`_Pd*cOHbBxA;SMK?$)o-LYvk z*0643+xYaQZF$|J=_rzH>(a%LvP9|hM~~XDl+@hZ+!9yl9@fisc+73bh`(INGLmYR zw}>JkzOJ*or-zA>b!?nVRVV1E(D$bt&Is#u$#?%Pj&+Zy`q(wf&dyFFI$B_!Rtqp7 z_@DW$ZEXg2GYw@7A3h!e3p&*0a*m zqigWcD}p0OfX6Hx9j_%Dr7ACoam}0a>3zpK$J~LyQub5~1g`T<^HIk0=emcQ+RMU? za->Y2A7lNV9qHK@F>!H}v< z7WP#^prhIlgkK{Uo*o~xUMC_VuJ9ANI2ieWJ|`25M*7LGO9BD{wtG))KXT;A^{A*P z+_~K6_~i?(@Ys0dpX-ZaY|%tT&v?=L<3}JaZ!tdDo`qon=(~uk9|f)BboOlV%f5UG zt)IQUPX^c6qmX8j&-?ijMLih^WV4tv67SQ>=&Vxp?2e7OBm~obC)h} zMc8y>_}VR7mV&>${>vwSmywYX>OGAvdO}}@E?q-|#ozY_z~r(G#$a<7U?vMIDmYAY zoL}L+bWjosJz|Pl?Z(5+y=K?0)%YU&4pD03k*o*li^s;sL>rj(j-;k>%!z8$HNEeZ zZI!3^=N=ZRUy?zx`0siQgQ}WZ%Rt*(9%;Sm6Ou@A!v1en@}3Gcz;BFI^>Prbo?(yT2|4v9p^%$KSsUzPGn0HZ;kXp(H}t ztihxK1RQ@$=He!@2M)6MY*f0g&nxn6!)Vs-2k z__>y%xPPgCco!O%b`?%FwU9~?BRo8x1 z9Op`u&QSgVKmFRa|z=2qx3xcHd%wn5MC*8BPuJL zi@_zDyg0f0rE3r8iWR|gSlFAfM{dnO_UYJHfPu@Qp=>mr1(>O;s2SHNDiSKvFFHE9 zj_ZY4`%4K_XzcuQ{^Vof=H9Pxhi7(GBI)+E>>i`NlArr-wtlf{DGE?yFP@(2>rLzO zKvk|$z$axt8Dd-4`}FCi(3P_6$cV48`$HG6+y=6<;LCaoP4M{EO4uIM;mY7TdN@_JIc*f z`SeMfwxt*tRjR&8&t?A6jTaMw2FS9!^!{;Uz; z?s0nK(BR;8+%Wy>JsCS@?1fdkJeOnTz42eTl#*{~ESUHu(OsW;aN_ZUD^2CqBxl`St_XgnWoO|Kj!H8 zN26YS@7^G1lbIps8pp`{_sPn>vQkzE=xWck)sj_G#oxDJeeK=50Qq$Dx!xlg#W{^H z&v6u12_ywBUP)*Kjd0br)>i%A)V}&mDR70GQ7O{K5xQ+<=00l;El#u9fQwz&dxPp)jwr@#EgvX47XWwPC|+WMY@xV_e0wHim1ee9PYtR>o=Y z`@YT@r{>Nt6UfX^P{Q9-oXic%%TsXeYrKNb#-S8qFsYrMbOl*Qh~`%;0?i1Y2M%;|ctz5XnyPR>=UHcacSx*xgyw6eQv z%OR*wuYqL@$|o=iqKm&qgohJZAO?vK_5ZEg5}>0X*y%=`0#^gHs)nkFNokRLjl*bg z^WJ+ZkrGh?M2*vR-`BGiBn&ep&aw{oVW#Y=(*V)$+`D%apVXLSbzzZ?=oN@IGB!?l z9(~%?HSp!jm!(fX{z47j^7`^pR13b^uZ(3h4{$nNmCuio#uNPjP>(khs2^@yI1Xk>tWc&K{tN4(?s--+7A3ti@`HY{W z^s61yw-Lo*-@bi!Hd!*GxZ_&3?BOluKy1yUK z_yA8chF!uW&;MhKj0`tI{_E?4D!@fov8i6w)*9KZb!^DlO1Pr)LqA$T*K~Y&wiY5E zN&1AXGxr=&yCHQ}pZpm7-zU(Qp`d^9m}9?^Qz36YKBHa>_=0gR#HV%g++9#T#F@Ty zCC} zGiRh@LaY#q+@~*ZYx3mC4l;DuwF!sP zMDE|ePw0;76ceLG{%=D0E?&RRnKE^Ka(p}n6ab;y!CXo$rajI&6W=Alw0MP#lB()l z!&I{?$m8j#kjW-S)m|C5ha=sgei;=%s)SmZ1GB0|0t-avsfn&s#;37GWCxCdMIy=o z;Z#@1*phmP6hYuC3x6URGOQOaT2zF{-9OsoBk{Y-Fy*jiO_BjBfA>zO27GrhE^aMA z#KN6BcS4bTfKqXVVpog@3*XIkA z$>g?z`kZ9I3hm*|gWVUyN(q4B-?C+k=XiU#8>nO*Y+bT+k?I+)NAN#b0E%uyA7xLY z%SJ;nYjeMskpY57p0OqR5+rTt=jHHRKzdf zT_rNo5}Irs7A#&&7{RCKhmJdg1b_E4uGD|Y>V{BfrOok2m6eNF%lNlDDM7MXz{0`; zs3DA~Au@mQigg$ty8bCd(pY>~#9-)RF$4jFTtg1a)7KkQAcs365xeV;V$Dk9c48ybP7(qTM-B{gf1ma{zGFGVN^@m@1#Yo zfVN`C&Y>PvjjU4 zhZGChVQn&+sZoLssn(R@M8RQnSN)bt|MUDq_j|_A<4Q|mN#Mjb zUIya2=);HoS=rf_L3C^0-{o~U@o}s? zSb~+zQ$~F=)`vf!G)7vxE`QOYMXyn?tI;)RpkVcWd8DTC?{XjB3*L4S)MvtqB1Q== z)|=>j6rJy6%JW-+JHyAY_Q}+gCl?gm+`PO1P{;lK{hWFa_j4VLlGllNfh()-*~5w~ zr-MLJ`A}=+>6xkXc&s*aEKN*_fXkOJBTdDD-$d)G&KQLD!K z_||%OObRu9a(GMtqCQCYunfR8T4am^Ul)PZ2J#IA9Y8Dx z$eZHrAY?D*xOBBbJG2Fkg%5@xP_nWpr~c;MG7fdz$hImdtownz5RRJ(piENW+oJ<3;_)xa>FC;-^ z;U8~Q$h=Q7QSGc16#W!OGGNjL+KhH!Fh8#)+0gta< zG2xaA0Ii@intjAETp~!ps?AdM`L(GFtZr8w!*48qr~J^Uw^dq^<~%s-U2Rt zLUI%9KhHy`0Q~MIL=sqKEkKhIo72?P6pU^m1ffEyCDq@qnX#jYCoEL_pPKQW%%)G- zXKfHed7#h}J0!qMAj}fYu&V;k76oyw)rR$naMwf^QBtA+R)X)_XlN{|d*KiQHFZCV z4NRMHp8Hm$l}Ea+NMfjhORx&bg2Y-P9Q3==(jtr<6ZJp+Cl>bhtSAx=5SsNv4Y2Av%?1SRK=PfXPHTemL4gxH4VPquWD ze&PkFkCw2w5@xAX(g9YA5_lFjd5$>|r`7xSiSH)j;^Me?cml8%kHSbw7*!~fPksJ) zJ9mE>p!XQTLdbkL-PRAUrP(zNTMDVzp1O1yf=bp)oC0Xk$@e^uVV#3&t`L^OO(&{F zNrgiZvq0!-g%yODFH?yA(>Ogf_0((9^-BF-)p%7-E}CVu=ip^(4dkwbZ3WO*!O$F; zm;(JtkWw&^4gCy|`+-=K%ziu#qEdqeTHNIkf+_-lyIcx;Wy`{BP2)@}t#tZS!$T%kSbf8FL zXIQcI#5y|Gt0sY9U{*!76#vbUAhcu0A&P)+AxX&|`fu|*cLOpQQ3@Wx2r6{>>?0ee zvFPk8@uxE_Hp*tw6lA#?2&b^m_l|7jB}g82jOz^UX^HLIjT5h;E+^RT2G~OS_{4;G z&15bBz#>X$>(<}&G7Z&X`NnMkHg#CLFfNEh(tJMS@B2~uf$wg@BAr4R{w2-kn#Orl zhS<0JqU~#v<{={x&8M^Ua%*3~%mgc?ZEPHbSQ87No^UuLNx%H#$HlnjTj-Y8X|&%O zWk1{6E@DG>o(1^TPBxTG>}|~fSb=;W0#qty%9mhXuuIOl!xteWkKuW)r!D}YGzQpS z!(lN82f|2q%^l>fR*1@K5L7S|PMr*T_-E@ACkWoAe2;A0<;XZ{pbsD@L>bYj*@GP&%7YrE)^F~P|oq!93-1P9x zZ}IsKWBARjQGr2w{W7m`aq$qTfrMF<&Zm3owo<+{8V?*CPay;&k%-fL+OEjzv#;h~ zwW_(LCFbUQ`Mi&86gc-wpscHt3^eJBfrb6x>%}-MHaBmwl2Spft&ZCB=}-= zY+VlQWAg69B&(G~rRJElRo9$tDVj$Nr|^CkXlRGuy_=}xg|h4|Ci*HAQ^I;_jE;z0X4b=duwo9!kl~?0mE8@hQKwj79&aog8w{Te*ZFYog%ptK{fDDG+vifau;xBfN6P6+dqUVF=L z(5V(<{ViXxR2t9!jI2GSg2ip&;BW<|uVg|EF9dDJF;{Z%VkPK9!uu@_} zY8w%Ma#()nG)=;YQd&{-7{86t+jwT(OCtm1UN~OhGPRS*Qqtkh6pgrd&pzXdRM$c9 z9HcN-&wR(lrEsl+pv`5=moK2UyZj2%&1t>;Y~fOo2eAEW{K0SrB39Y<1kg~|68?}N z9eT`F#1f)`AaxcsHm*jNHlj2tG|6JaYVv8H;CZF4uKq@|EGIiV21}i|l66sRR~+qY z%!4zpOd=x&%Z*692cqP~=m0$jt@$O&rxZONDoROlVBn1IJ|a|k{k!UqZD4}I=+tKA zL4|&GO>sul3ruej@Z3c_ARJUY6mc(tl27*9C5|OBZ{EBWwWE)bD+*t~o(mhOel|n@ zzQh^`On~`|3H$|VoAh0A=iD==5rT7{N(qaIXw>s_>6{)YB}){eOiI%M08Yzy*99qG z=uRgaa(G}?79A|1;d$TkntS@Cr^|pTEpXilq2mhdjzs!EFxds${?ijR;@_dKyt*(? zHyZ_4EDD?D__WCWJH(HIK7jdule}h$z2r6E_r?J2LG(pkTnVL9DBK=;8bP|ubLNn8 z$u8j-I3wu7dAchhde63kb>;6kT+SiVrgsX$?W$>jl1Hky9%pp4K$b$w@ zyg;KxfGJ#mIUWdci#SWx(#}9=D7Mi~MJAc$$w4p;hUhMcU`W&uI$e0lk|oW( zy;o87+R3NhxwD)M864KdP};OJP8h^Ld?*CP{Lj=72h#C1tIUf3NK8{vji=6|Y6WGo z%p+e_mD;W^%~eS}ZSY^p%1*tS!wU{0E})m9y1K*QOAS};blq=V2hV+Fx}6>|WC39b0Ve8y*M9%&m$&xXTUHhvxb(UEHV655r6S=4a1gt8C7}+R*?0Z` zf}yBs5-Qzwjnj@_v8_(HK^PHKkh}zrfil<47f}&r6@2}>tF{C>TtISiGH^IALe4U* z9UkZaH%dh(j9`vuqzCtnef=GE^QI7iL*VVG2i3%(u>iC>dK(NLTMG;6USDV#f6!qG zm)$-r^(4cTU~mlED9f>7qvf3?vy z(?Aj6OPWM>^7T%%Uzm1SB)p7}sc3mXLErgu$87gqs@4KOpbb*kiHJJ*^A`Ed%I?4CSjC$v(h~|`c=qnOc@ge{MqvTd;HJO*9FTeD&E3!SNZJd9>`u@ zFLxYk{$jpYuUkTYRku`p;_$FD1ZvH=nxh=wb2(o80~b6Cw#V%@11{4u#vLfcmsfzG_yKGPaDjv)OyiS1wTf5`0HWI<1!SH5 z$qpGnxSe;_M#_yEe!FY;a$@dx>H2JmN>9#w*X&)~cf>6h2W&1!%szW#oYZ|#TAo_G zVip5=ae~nVugn-|G?F<%PyN+p1@@;I5gFT`kMwKA$FC#0i__`TyMYji6umz}W+5t7 z6laG>mL+CTOnzTBAI}cjXTcHhfflm{vwdaOcdlL;tT<*rFW9IgyK)E zNQmbuaLNH`u%KxuaX*A{Voyie{Q%Cx6I~S_KdPNN^<;Pl+6$=b(b3mXnelJfYPxKoe3q-5Q*^6-I%u$L7z|? zghq*vk1vE+hW0v*s+_E>HPDc$>o;%4a%YJS@|;(@E9;EGT>XuG%AV;tN?L z5)L+H*c)B07U1PI68f#(S%}qdWggRN;S&=M98-A?YQ}|TwTlo`%NQ}qK61I`T79K1Zrik{e$augj};YrjvPV7 z*g>5=dp7d3+M8?t*mF5Biss*=9&pcB*9!UvDC&r*=?&=Q1mPu1 zVM)S8N-HJByxr>H%6Ht>)z8qVM5og=fQfdTV^$Z+)PA@vFFs#!gh!lxgqOiRxrm2l zk~J?hW|$*xxkI-aq*}7*frTXK=GkZFA!kvfLyoBDV5)Uz5Ts8-=7t2pOaUSc0K}tS z)DmCH%gf^7;Xx}T8xCY`l+Ka!@5{^IKsAkrJN`Yf-V%8c)<3dO2+Ke?BA@``pa&<% z0#@uC<%^An<}yMG5&r-wpA_6>a3dnlQc5U_TTsk_S+oGg*}HGwtM$>I7v8%|;=%;gfe+{beKR)*VvOhcIHuBDFn#n0?R?{52sWJO-4Oai(o55`t zn(OW(myS2Vb3m~0|L6htQ8bwf*q3bg-?Q%8wB(a>1jI+ z4=-GXd&S=Ul|-jE1vj@_qwFV+3=~6!Fj)vIB6=ct={7S{;B86A6}U43n^AYfpl?^HB>TlJ2#J2uLf1jb-n7wRuu- zGpjf|r)x|g)}rteq==%)Yvuxx_k#vOYnV9vo%}h6ViwoKQfBd!+f-uV5q&dMD@_7+TmtGf=*0p48RT zqm_#8k|<3$YG=0)^c~F^5xIA6sOqVhi3gb^Xwn$P_JdO5cCxz`8|V$zJewWCDBVm` z=DMbBSNQ)KNXaZBD-&l7LlImM33?tQ@(OXQmoEqWXM+b>qpGK=KmZvPN)WA-kcOS! z`B;~rT9V(Hy!mU{eP3Qe3-9hedE}+5)&v4*Xqrt0c-4glN%}h33S}_T!&LuQ`!6K? zj_Smvq<#ia7Q#>g$|@Th-;sHI2AVch+$%9Em#olpxZP()K7Xv06|IR`-r^bb!=2!&cSg@v;*tM{;kC}H=$>7AF#pX3s=kI|DOOcXJ7 zgZRIQ25J!ap$NUdk`7Z!C?O%@AsI1I0|Nul?!uUPsRiupf#@DRaDTfX&8{YirmM|^ zUS8m+HW)MH9HeG1pLZXZN1;9sR^+)9vB?34D4#sJf#_h6CrP*3`rl|2oC~p68B!>^ zBRD9)9KW=*G~FHWL2Sn!5*AhyZ|mtv(SK7?qE;$lo}XJcapvq6C(Q8D)qAG!|& z-LrwaGwdG-`W8dQ)SU5ovO+v5bUo#ojvUwH4 z$&9E(5#=)miVhB};ou-#yjv`9i z*+;c~bN$6quZ(u&nZKVFGd36Q3PQsY+6g7j918Bb3wTufjE!o|XJChfUrR~Yb^$v4 z)z#-C@wK4Gj3Ni2AlZ|{255KXT*L>5hUiyhH*REydr^Gw_GSu<$bUxewApzaIY;J^ zk)GP&A&_7%bvWP1bmZoj&zdVnC8dt*u^>Qf5;t?EPlpsV-auRmmn<~ zq~D1+24M4`HMX?E%?A;PaEWMO9YfPM>AYjVwV#BrV{4pU+uMoOkIoWoT;dod3MdAE z@ZIRh4RFjKS8qE{JT%ByhnNakhq#e#VMUlMk_HR)79N7Fr+8;~F>O`kJSk$zN1^@< zg*MomeVybdn{x6(wDDRix zCFk*@1lBuu%eFK$3SR(c+nIDY??2Ee`)rKNa?bT##eoT}&`Bu@UDn@nTv$o4K=I;Y zkkUw47Ss)g4jqDh?bErln1GJ0{h>Q-ld{W0mkY6ADzEb*gu;!hte z)RsNLv9Y2v>8BxA-hIQ1;c)9ZlwS>c*WAC2O~~Egn-6E+uE;CfMm7k}_2(4U;rVMK zbmhD({=mrp238niJf@V;LP=Z;xoFqVd2A?!boJlq+s2hF#{_-Gp?+zs4btL+mOyn2 zi!~HsDe!{DpdU%6y{SP8?|%_Vog3T^!6;2kP-yBqsDbX&o!~pKAtQ$8{LNrFYhtG% zMWQ>z(jQ zm52Jzzf3V42{JXMgj?vqCXM}QCcggGd*_A`(8dB4osVSG@SI3X`7JYz>y{ONKo!6#kfGaWVmW%f?ReNFT$N03po=bBF22 zsJ(q`on{6qC&)QuH=f+-IPnIi(j5fAAi?}fTJ+bmtNfK7+RzvUp;x@SySo{L=WyzF z(j$aT{0fXI+*YW}j7oWxE&l4sQ-LQ0y#}FZWe}(BJ9uyp5DHv8O%gwV zKjY-)o(ua6>@I{^NKNmag+B3KaxWbo!wv z5J#`^(w7BQ0?7d);Lv^AUo7RSOm6WS1-U7Ym;?5M@My3~_ty>mf<^=9`QvQ|7x=-9 z(|qgTu2=97Eu_dH9ykf*0!~07u6|+^$IWkCzfR#yDys0gb=fDnCa>qnR~rzYB$yHg z37RV*%FwSth#E)^e{-|%sE!S8iO?Zo=z5&|RTCuhjhipy#7$H)?CBN-0(;sW0@Dfgl(E+ zYMJ&(fwZ9i#Mb@@QoeL_*plO1fH^P1si>Qump3i((FpcBScO$V-{S0(qN-RmR)j&) ze5;$C?==Zuwnfz@a5jMm0(gYG_!NJO;YIXC(vm`N;mj6mwnXyN>$5a2E=1^#4j`f_VjZGng zW-d`VCrMl)rw7HTu+Wxm|ecRvsYTu+n%sT};bJTiX|@!aQo|$k{D>ZYP7QZJO8R) z2qwN5P~isXaFFDJ*vTm3C|^L&wQ_PRAYGKee&B?l?+GHsIM)s5M*J1x-5n5GiE9J! znY4Tx+%vy#V+L}7kgXcgJQa5k+AJV6-`Rdzh;-JFU<3i1bRpnl^mDmXA)>-&xU2}o zf{mb~;iW7Nxe{;RxqZ6@fCl&r7|R7(EC*${>!0U(es2HHdoe! zM$4a%9sXKZw*st{IubfvLYh3gs%iuQbC@IK5JUH_ydo&i1E5S>fEjX<>ek3aW^&Z- z>PqfA4iB7M?geF4eOGKyWTnOA6eL7EqVEuC3YwPpSPKj0VUJPG60Uu2_n0fYEFaj8 zWktt5SI6^0)8|5WBWd!6KKdF2Cvh9X;unj2o0D`qp}KYVT%E6l4v|3#wwNgD0g<9` zC=aK$9nF3V)8uVqp1;1%KAhEqS>+SJ9fAvmLC+GG3XHS} z!lY%4bPyQT+`O?V{-QCNLTPrqA#pZiLt0{~QodLx4`2u)8U<+K6M5!S`Axr*Ia+HZ0S17>6n@TDV0 zPm_=WjCqU0a6SP90_Tf0pa}>=S8I?ly9D$#Fr(S$`c~hMj5J`NV=SKggLdVZ(&^6l z-fa)QDf1Q78@;Yyid9w0j@QG+A!oLbQ!=RQ%(wqc_xYe8tv1M*e|h%;+%j*$HJ%_D z1TSlFM8y6<5*7ls6b{J*j3MG9IjJN0M5l1p*$u0sYSv>#-+a?{Ew;1UZyJ`IRuG+{ zqM}OJW>0|z;ZRu%OpQ{E4EcZH@vwqaRhVRqXQ1woNhzQjZTCKN^^1u)~A9qMF2#pCc5;&P^Z`}>`~ z<$3Uet_J^3YQ@cP#iFa@*V%!fAhy*9tvGKyYo001qJdq*(~yobt`!sMZ6K&x(7{p+ zl#BBtem#fDmAL1~-!oB7o^g%meC*h<2S^lO((RU#K?cZ)fr%y6pcY#Lu8jcrZpmSkm|~Qs>_x@J zOcZ)lC<^*9y%Pfvp9?!B7MVo*y}Nf`y??(?Jnq0h!6c=*)DOw~kx-Z-T)gfs#} zv?O33Lr;!0K@Zrzs^2)g0mTh5Qvr%T^PYA;lJ9xQt`Yt+!oL8x)O_ZLW`*cZ8~*ae z80U!=d_F_M2^Zf52rp!w;RSe_nOSSHlh7fgZbltLFPukVBap~IPWnXnE=S(D0IjG}2%Mu$fwQ|oTG z?vK0kB>kt@w*nt?hr2f)rZv;S55f4(rx#u>UjvqR7Lum7tC{__JnEHN5#HWuzw`W!)(?6sCzm;7sG0w!4Zr9Y z%1JnAQl;?H@qEpM^D{Q7(yEd*EOf7?*pytx3&-=c|JB@?K=qinef*9GAw~=)X0#wm zQ-onqNHvtuA5kbu_APse7$PxLk}{T>qL3}sM6xeKma_ApX*45iD&wip`?>BKnmJ~k zXP)=G=e+-OW{xHG|KIoTzLxLx{eG|Hw74?83*>;2eq^ph-7G5$C*HWmG(fT`Q=-LmA3{IoSgEYS&2{SwLXxrBs;RKG0~}`0nM1u~ zr6`bldv5QysqLhjZFdb%tyr9uP2T9;i_1V_w#W&G~sR4u9rKZQ49u=3Sgq zKKD1Rn^4_?UR(@DkSyv@T_ruAt!snD`U}6-w$=T|wmENn@3=jmz+vaH^BjLcixu%m z_+sqj{CD($tO@y9z==tV!mGUp&YmpxQ@F=R$z&LUs1Q;o3`j?~IN5eNdFO&)LHfL1 z>awa}=zxrHtPf`uT3AM`g0t`Zn>s{&JdlGb*sI_^whMoi(=bN`yfbr%+a-ho?ajT zH!qpIncYVQb{GfqY7%Tn&`PI$M&1E7(==wJU!Gz=p^=7Y>-VDN4 ze&$#DhP2mQjGG5%wV}2Kp8baFI6i4NmqSl1X3&v|G;PrC;d<2U=|#~QfYs0=3=q9U zp8k(OvF*1HLM?bVuv3OcYI8-pzqvCguJ4dW1y^I+KR?pVu|aNGouo~%pI^G1+`y@D z^HYnGDM9Il4hQuQuP`ji`=#*vshRyZM@?=O`SbW0_X~^yzJGG*7d@NqmM6Q-TJ$)y zuXa>=(jF*Sby1(%QQ=85EcG0v1|;Y1`M`uZTvNB5jy`v8zy=ieO+KHN{(^ihv1TI4 z3!zO+HekGDqabp+0pp!sM8sE;b3i-APA%3K5LZA6rUiqB&0!&d%Bl16@`4zdkmOxX z0}B5@4RY%AY5RE>aV)7rXS19{rDQ{lyf$G3<3~Cc!PgX_2*OuL`#9}k)*mQ!ZA(|x zym%3(mpH&L?}IQzVy*j+=u-Ihd}0UP4Y?L)4h%Ft8+KkNx$;E}ywX)~QddW#Ju#)3 zL7&)cS!lhcw9%UfdbejskKg-+#<8Ol@m|Q zo%VbKHyz^r;H(+BmN4wPzd1BuTZo5x0Qz^d7-HUQN>qlyZB3&BoylEIrZju?dCU82 z+)vgknd$8N&y;ERHCd(W8)v>M7#}iL`_0*UY7NR01bW+|PJ6~&tKp5vy&}(Fe$?Ab zxeSaMcIPldrYh*vl>Ykn=KKNwT05mi?LV7S?zr}Aecj*}*`6>4Rv03l z^534aHeqn|ntMO`YD7VW_}QbT~dS?bOgvb!%w&ph)aIX&PjSfccm`Mg|57X35RdQQQlE zo4arvbs)lR^RNh@Es>xLw8fV(^P){kDcajz5efjDM0>iCOki-CUezk!jnezb%-MH_ zf{VQXG&4Co)h_(t?58Ukls1a62>n6ELL`F3vz81E7Z{e4j2CD9Ko=_rp4j{gp9~F` zor}x*tnTW&j?TGU5*9AwI#htR;pY99*CYUT9lgKIzHL3pu~9fE+!@?J@|Cm6wwKv0 zd5XlA9G|eQPhFcHfDUr<bHM1b&d}2zVngeymf8Ec%=}ygaDDctJ z5F^dR!3`bD)2Fi=bj3@D)RHOKe4N!>=T*q{v`J*a1zYKFu8 z|1s~oGE=59xBD;oG4E9RHt{;{)^Cai62Mk{*44#fK3-v%hB!yrR6}te&7BiZvdCwE zY51BfgY-f8m;MYz{JLq6)k3oOCQaisvieBs3C?}Gri8L>uh*Hn!sVd)K1rm!ymN?! z$r0>LaQ79PHV*t=x;3~|b>X*I51K!TBMZHmxQLFL-%v2%_nzGj zzRw21cQ!a7XEg_OKvYYLV6PCL1o05Mv2YPN^fu5(k#`p*^;U02QJD}GC$9v51Tmuw zZHE_Fu%NyzSw^0!qI05QV&~|XRiaPQ{!bq@f_0g#qgfPd;>18(P`KJ?){P$4Q%43 zCIlBtGd%Xy#gJA3Qh; z%ZW>7!?^L<^`^h?gD1{0RPSDq0l+*13(-3<0(^P>8;wtHE=l|}Y9?WXVyqB0?k@f& z$H<53R0_Y7qfgo!cka5qwtwR-fvqq2KFW%C<*_+7@Tl3MFSl*7?74gRn7vQUCi?~? zZyFc*O6^vE73JCwg`J-d%z|2Ras^GIU!i7D_)7b^3TS zS`wC$zW$c_9%^r5q;WCbod)6&#n(HGM+hnts&&>dcUS3mZ$|h8gE)h%TNfrIdL%7H z=4F^+idR|fMrJ(_y-PU{pA$BmOH`wBB_VlRbY1TTa75~nY@J;I5|1(oAFD( zRiA!YpI%)Au9#9G)r>#mqDIzWoA^5J!Y+@!L{VJ<=?CM!IL{LWX_qN_YP>tVQu89C zd=FOG!pf26PdIsfz|H|l*REY#nYFD}la<(c@1oBK`F?7|7S19>>~fBx5kzc6ESJfj1?X(+TD-vQ-HxVIT8~cEXwOC#x4|j-g?T+<^oy;v}@2y-nLs`DbTo0hx#h z<4g2VMUSpIp2Mh6A{e1<+LN2@q@^J5oq%TWa$iNHll4?RhG9yWP~=PAmN&}GPGkj0 z-WTA|$0{y6f`X#ZF4)0gn)do%J{=}r*P+d`(`f38nG9Hl(2F*7oY+X{25f#{QvJ{? zQ@xSev90I&x)UuF@vczT@~|?b2IqA2{*QyG=}S4QojMIE`Ib#P%H9sT;xkl|5m5m@o5G1sX0)LIxY?}cY?_!gcR$s6d<%@iI+BHKl#>N z@jvK1f6%tB`VBRY3V-Wl7Q4Qk-nXmwlHsWt#KzlF&t`Ube(P;W=fk1Lk`r4L=tSx3 zO(#T_{LucuQ`;+i=(p!3hph0eg-ybcVcTND4rY79JG+@Wqm|G-I^CPw%YpLM&fUEk zh=3TT#%|xXO;#J2&cMJjw%EkJ`M`C)oBwW!{ih)^GV}D(dV+{8eEs69Cos3^a2<-7 zaEL>Q43Q5A+f%9s9i4wDNJesKo^2`k2N^@_;|k(7ZxT_h{25DfxDXaj@y6}S!N_UJ zrcIw|G#W(#TbH#Yyu@MT@5jr774dTW)qhwp@=z3zN_!JObeUpPqKy2Y)WvinR@hW7 zo)69j^X8YXUR{R+K*gI8;-({K!N}AmzNUKzy$%=3wJpR@ll|gni=r&{!ZtO21~*z4 ziC9uzaWln-M_#NJ3|dbGdz5uRm}FK`{@FQFdj*5Numz$Lx%=He`)@ZA{;n4MUF=ie z(Enny;eV%`iPLS+psFmMVpSFc*v`HMG3guCw=(GGc5{6%GT{@InAn>@zO)yuAiQBp z0>4hH)oa<;E5G1d0dMJzK4CozJ*Lnw3AJ8;+9gw($klg_7_;qp^-n_5(z+om@Pz)V ziG+e;kWG z&bo3%OR>>T@^)O=_u-*x)gQnhjbphOaBu+Sf{7n4I-PMZfDsU7=~NC6IPQ;7FTy-A zRS|EosV@u9ou3$qlJN8zeG9*fY}?~}kyonKs8PczSw~x2yX}VXG5bH?!SW95DHb0o zd)t(Us$|MH$96ULpX_{QZAnnnt_J15sx`9n<7y*^U2PZh*0c5FfAA}|SlI$q31RHU z?6z}#Wu7h$@C+|vi})Xv)s9W4e)enTC5Xo6>z})=K5$o zn?>znW*txd_(HvsS|f)(UN+ez(y*v;FSk~FVc)(*rDwb8-&_=c%7+2eUKu9ftt;X= zhm>GFQA_xB>Uli*6YE}X7L$TTwU=-8``?&b7Z!XAvx-PjWv5Gm4fIZNx_J$!RS+?z zU+!J}%DDw=BO=BRaJJNI_Io|w_;UkFYE5lA{~`?T)g(I+jKU&UD{8>@)m0j2=S?2X zgml3CHhP(fzo~#K^uZ>*^4h%DMqt$-C+y#C0v+c}8-ke#f~;k-!HO!iWJ>+KTlnlD zY*RppnASnJY=#%-eoX{Mp$3!#vZ<5374u#_ODKpQWIDfcf9B;8HTG`0U(@Glg>@ls z`^y?jW>8`nvCd-YQ3?TEZ36gHL)dfpDPHpm>f?bOi_Bq>H-9G}XoV@(*fWzn7nyAQ z08Q{lj0Xc@!CN^?j3-Y*lM3FnDIxKT_NXzq38P?J$(DnDTdY?z4ltnTL|4RPmzPPg z!Wyzba$s`;Jl&OeRY0g|@h58D*fTDn=`AZYk6lV@nFtpSh|M2;ra0aq-4`3XuW*Qo zwWJt63P(hAi?Yl@T)S%4JjOt-=;rjShPxsFc99SD_@IJIls?Ck?Y?2mEoh&~;Hb$a6u${N%a8aMpKZRgJYzTT2I%Qjar zkR+RzX#57vcps%6dJav5ERv#m7Yo!)2SSIqAHWgrBM^ACsiiLvmM7C_tK?NWPJgV~ zV$%mg8ON!0ncwzR<-Kv7W{YxjNbg%@Y1!79_VW5LsUT50 zW@bwP>WGa?0%4J-b7}vHhs3Fv$qi! z&?a09Q{N|Tigt&Sh}f)!bPHIs=^6tT{6n>*P)s zu?9Gw;*TxFk96e%-tohW{@KN{gRFF9^~efl(f=wb5q^k#O6+lOd7OQipWN>AxS{)u zaLO5M|E7n2lrMy zBMFV%QC4(Rr=5&FAToDOq+;;0=38@JMI?9?(`u{`lz8 z4$IDiu?Zrj&AL7Z<&|8`KZJ4N&3K*feg993!6A>jikUDN@Xa~rHZafli6eFrp+)Q> zP6k@m@V8?;43`1Z#_1edn0u}vooE$>VTSzts~+5}X*nlr{u*xC8o0vP_}uaZj6U&| zR8VSKIhEMaeN*w$jI<8B^-e{yV`D(x=uiADA9BbVL#XtmA(CfD9&-2@E4j;56h-4G z@x)yBHE>G~eM8m}VEOor`Ogox60Z?>b`~-fVtaQDxsED@Pp~ z$ZCoekg3J-z7|dqV*D4MK@DpPQgNz(_ctvFFReruFAKtiHhgA%s5Qxc1-z0Vs?3#) zOZH8^^M%mU#Z5R~r_}LQ5mwMX*h9;IT0KM;i?@J10M9_}DAyy%8bie;q_a=XQXaV# z0v^jEp^LXOvhBnW$XMPy1Uy@E;TFYmUccX~HytB&P8S}lU0Ta(B5Vs`w21}s)!ie8?ZWuj#KSy1h?1=)oD;DRzMV4F!kj$? z+pmXMdwY8aN}MC0*iqR9qS%x@#w)3_)Q>YW`>0mZ858oRjrN6-Q(#VZsJX-^2JAz; zoEu@jchz;Hw@tUgd4r|GHZrOzUL*4Q`|v?!Y{1l~oC04%;c!A%G?qngi(AQ(0GOt9`^$S5T?;bDF@rAdwU6i$Dp3em>Qxn#zlRKvF23#@v##-?kyd2A3oIPZ|3^?MhOFH zzjtk}fs7kS#ao9KawndSlJ><-W~98^=F};qUejXKl1(nOqS2P4EEhRuM84FTsE zbnMZjq7xFsSK$#e2$hKeIZ;c2ou#~00L*`I%$7bicLS`qBUU6u8)!A%IU*jvT zpCL2wRBu5W2+3N^Y6z=`uP=}dk-{h0(%qvaRvk_P3Jy2vZyd_bkRvX4Ev8d_2ze&u zqI|5zKZ%w%rXe^V)e7uc9ZI5@kS)Q%QJD*xbeggX+mEAAY?%oSDcSzaH8PsX*LTc8 zek!m?q|9X`-JD74#KIQ%?3CXK9N$ENE9ft?=u`YlDR`Kme+=H&{B zy4Z9|FUDx^1*>}`E{Nb<2GfuM$94D!sv(Peg`6f8pX3rSe*DIDjg2GACi&sgMwRt7 zDq6H`gLdUg*TzBz$-sh9;`c-s*aEV@$F5?2-s~TiCU4%W4oQu&4BbEMF3hQ{WF1sV jBwhK@|9|#H$IG-%D7(*k%{*YH@akjN&vv)X$R+;+&%4aY literal 0 HcmV?d00001 diff --git a/examples/whisper-federated-finetuning/centralised.py b/examples/whisper-federated-finetuning/centralised.py new file mode 100644 index 000000000000..6af591a7502b --- /dev/null +++ b/examples/whisper-federated-finetuning/centralised.py @@ -0,0 +1,138 @@ +import argparse +from datasets import load_dataset +from transformers import WhisperForConditionalGeneration, WhisperProcessor +import torch +from torch.utils.data import DataLoader, WeightedRandomSampler +import numpy as np +from datasets import concatenate_datasets +import random + +from utils import ( + get_model, + train_one_epoch, + eval_model, + prepare_silences_dataset, + get_encoding_fn, + remove_cols, +) + +random.seed(1989) +torch.set_float32_matmul_precision( + "high" +) # If β€œhigh” or β€œmedium” are set then the TensorFloat32 is used +NUM_CLASSES = 12 +parser = argparse.ArgumentParser(description="Whisper centralised") + +parser.add_argument("--checkpoint", type=str, help="path to classifier`s checkpoint") +parser.add_argument( + "--epochs", type=int, default=3, help="Number of epochs of training." +) +parser.add_argument( + "--compile", action="store_true", help="compiles model (pytorch 2.0+ only)" +) + + +def save_classifier(classifier, acc: float): + filename = f"classifier_{acc:.4f}.pt" + torch.save(classifier.cpu().state_dict(), filename) + return filename + + +def main(): + args = parser.parse_args() + + # load train and test partitions + sc = load_dataset("speech_commands", "v0.02", split="train", token=False) + sc_val = load_dataset("speech_commands", "v0.02", split="validation", token=False) + sc_test = load_dataset("speech_commands", "v0.02", split="test", token=False) + + # pre-process dataset + # ! If you know how to speedup this pre-processing stage, please do let us know! + # ! Become a contributor by proposing as a new PR ! + processor = WhisperProcessor.from_pretrained("openai/whisper-tiny") + prepare_dataset_fn = get_encoding_fn(processor) + og_threads = torch.get_num_threads() + print(f"{og_threads = }") + torch.set_num_threads( + 1 + ) # not clear to me why we need this in order to be able to use `num_proc > 1 for .map` + train_encoded = sc.map(prepare_dataset_fn, num_proc=4, remove_columns=remove_cols) + val_encoded = sc_val.map(prepare_dataset_fn, num_proc=4, remove_columns=remove_cols) + test_encoded = sc_test.map( + prepare_dataset_fn, num_proc=4, remove_columns=remove_cols + ) + + # create and pre-process the dataset of silences + silences_dataset = prepare_silences_dataset(sc, ratio_silence=0.1) + # ! You might want to save this encoded_silences dataset to disk, so this stage is not + # ! needed each time you run the code. Alternatively, this silence generation could be + # ! implemented as part of a `collate_fn` in the standard PyTorch dataloader... + encoded_silences = silences_dataset.map( + prepare_dataset_fn, num_proc=4, remove_columns=remove_cols + ) + full_train_dataset = concatenate_datasets([train_encoded, encoded_silences]) + + torch.set_num_threads(og_threads) + + lbls = set(full_train_dataset["targets"]) + print(f"{lbls = }") + hist = np.histogram(full_train_dataset["targets"], bins=12) + print(f"{[int(count) for count in hist[0]]}") + + # make balanced batches with a WeightedRandomSampler + w_per_class = ( + len(full_train_dataset) / hist[0] + ) # doesn't have to add up to 1 (relative is what matters) + print(f"{w_per_class = }") + w_ss = [w_per_class[t] for t in full_train_dataset["targets"]] + sampler = WeightedRandomSampler(w_ss, len(w_ss)) + + # prepare dataloaders + train_dataset = full_train_dataset.with_format("torch", columns=["data", "targets"]) + train_loader = DataLoader( + train_dataset, batch_size=64, shuffle=False, num_workers=4, sampler=sampler + ) + val_encoded = val_encoded.with_format("torch", columns=["data", "targets"]) + val_loader = DataLoader(val_encoded, batch_size=64, num_workers=4) + test_dataset = test_encoded.with_format("torch", columns=["data", "targets"]) + test_loader = DataLoader(test_dataset, batch_size=64, num_workers=4) + + # model to cuda, set criterion, classification layer to train and optimiser + device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + encoder, classifier = get_model(device, num_classes=12) + criterion = torch.nn.CrossEntropyLoss() + + if args.checkpoint: + print(f"Loading checkpoint: {args.checkpoint = }") + classifier.load_state_dict(torch.load(args.checkpoint)) + classifier = classifier.to(device) + optimizer = torch.optim.SGD(classifier.parameters(), lr=0.001) + encoder.eval() + + # Let's count the size of the classification head + classifier_head_params = sum(p.numel() for p in classifier.parameters()) + print(f"{classifier_head_params = }") + + # eval initial model + loss, accuracy = eval_model(encoder, classifier, criterion, val_loader, device) + print(f"Initial (loss, acc): {loss = }, {accuracy = }") + best = [-float("inf"), None] + for e in range(args.epochs): + print(f"Epoch: {e}") + train_one_epoch(encoder, classifier, optimizer, criterion, train_loader, device) + loss, accuracy = eval_model(encoder, classifier, criterion, val_loader, device) + last_saved = save_classifier(classifier, accuracy) + if accuracy > best[0]: + best[0] = accuracy + best[1] = last_saved + print(f"VALIDATION ---> {loss = }, {accuracy = }") + + print("Training done...") + print("Evaluating test set. Loading best model") + classifier.load_state_dict(torch.load(best[1])) + loss, accuracy = eval_model(encoder, classifier, criterion, test_loader, device) + print(f"TEST ---> {loss = }, {accuracy = }") + + +if __name__ == "__main__": + main() diff --git a/examples/whisper-federated-finetuning/client.py b/examples/whisper-federated-finetuning/client.py new file mode 100644 index 000000000000..2bfeadfbdae6 --- /dev/null +++ b/examples/whisper-federated-finetuning/client.py @@ -0,0 +1,183 @@ +import argparse +import torch +import flwr as fl +import numpy as np +from torch.utils.data import DataLoader, WeightedRandomSampler +from datasets import load_dataset, load_from_disk, concatenate_datasets +from transformers import WhisperProcessor + +from utils import ( + get_model, + set_params, + train_one_epoch, + remove_cols, + prepare_silences_dataset, + construct_client_mapping, + get_encoding_fn, +) + +parser = argparse.ArgumentParser(description="Flower+Whisper") +parser.add_argument("--cid", type=int, required=True, help="Client id.") +parser.add_argument( + "--server_address", type=str, required=True, help="IP of the server." +) +parser.add_argument( + "--no-compile", action="store_true", help="To not compile client models." +) + +CLIENT_DATA = "client_datasets" + + +class WhisperFlowerClient(fl.client.NumPyClient): + """A Flower client that does trains a classification head attached to the encoder of + a Whisper-tiny encoder for Keyword spotting.""" + + def __init__(self, trainset, num_classes: int, disable_tqdm: bool, compile: bool): + self.disable_tqdm = disable_tqdm + self.trainset = trainset.with_format("torch", columns=["data", "targets"]) + + # Determine device + self.device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + + # processor = WhisperProcessor.from_pretrained("openai/whisper-tiny") + self.encoder, self.classifier = get_model(self.device, num_classes, compile) + + def get_parameters(self, config): + """Return parameters in a format that is understood by the server.""" + return [val.cpu().numpy() for _, val in self.classifier.state_dict().items()] + + def fit(self, parameters, config): + """Do on-device training. + + Here the client receives the parameters of the classification head from the + server. Then trains that classifier using the data that belongs to this client. + Finally, The updated classifier is sent back to the server for aggregation. + """ + + # Apply the classifier parameters to the model in this client + set_params(self.classifier, parameters) + + # Read from config + batch, epochs = config["batch_size"], config["epochs"] + + # construct sampler in order to have balanced batches + hist = np.histogram(self.trainset["targets"], bins=12) + w_per_class = ( + len(self.trainset) / hist[0] + ) # doesn't have to add up to 1 (relative is what matters) + # print(f"{w_per_class = }") + w_ss = [w_per_class[t] for t in self.trainset["targets"]] + ss = WeightedRandomSampler(w_ss, len(w_ss)) + + # Construct dataloader + train_loader = DataLoader( + self.trainset, + batch_size=batch, + shuffle=False, + num_workers=0, + sampler=ss, + drop_last=True, + ) + + # Define optimizer and criterion + criterion = torch.nn.CrossEntropyLoss() + optimizer = torch.optim.SGD(self.classifier.parameters(), lr=0.001) + # Train + train_one_epoch( + self.encoder, + self.classifier, + optimizer, + criterion, + train_loader, + self.device, + disable_tqdm=self.disable_tqdm, + ) + + # Return local classification head and statistics + return self.get_parameters({}), len(train_loader.dataset), {} + + +def get_client_fn( + full_data, + encoding_fn, + client_mapping, + client_data_path: str = "./", + num_classes: int = 12, + disable_tqdm: bool = False, + compile: bool = True, +): + """Return a function that can be used to instantiate a particular client.""" + + def client_fn(cid: str): + torch.set_float32_matmul_precision( + "high" + ) # If β€œhigh” or β€œmedium” are set then the TensorFloat32 is used + + # if dataset hasn't been processed for this client, do so. + # else, just load it + try: + full_train_dataset = load_from_disk(f"{client_data_path}/client{cid}.hf") + except: + # get this client's data and preprocess it + print(f"Dataset for client {cid} not found. Pre-processing...") + og_threads = torch.get_num_threads() + torch.set_num_threads(1) + sc_client = full_data.filter( + lambda example: example["speaker_id"] in client_mapping[int(cid)] + ) + client_train_data = sc_client.map( + encoding_fn, num_proc=4, remove_columns=remove_cols + ) + + # now let's add some _silence_ training examples (add 10% of total examples in this client's data) + ratio_silences_for_client = 0.1 * (len(client_train_data) / len(full_data)) + silence_dataset = prepare_silences_dataset( + full_data, ratio_silences_for_client + ) + print( + f"adding {len(silence_dataset)} to client data ({len(client_train_data)})" + ) + silence_enc = silence_dataset.map(encoding_fn, remove_columns=remove_cols) + + full_train_dataset = concatenate_datasets([client_train_data, silence_enc]) + # save dataset. It will be loaded next time this client is spawned + full_train_dataset.save_to_disk(f"{client_data_path}/client{cid}.hf") + torch.set_num_threads(og_threads) + + return WhisperFlowerClient( + full_train_dataset, num_classes, disable_tqdm, compile + ) + + return client_fn + + +def run_client(): + """Run clinet.""" + + # Parse input arguments + args = parser.parse_args() + + sc_train = load_dataset("speech_commands", "v0.02", split="train", token=False) + + # generate splits + client_mapping = construct_client_mapping(sc_train, num_clients=100) + + # pre-process all partitions (+store to disk) + processor = WhisperProcessor.from_pretrained("openai/whisper-tiny") + prepare_dataset_fn = get_encoding_fn(processor) + + client_fn = get_client_fn( + sc_train, + prepare_dataset_fn, + client_mapping, + compile=not (args.no_compile), + client_data_path=CLIENT_DATA, + ) + + fl.client.start_numpy_client( + server_address=f"{args.server_address}:8080", client=client_fn(args.cid) + ) + + +if __name__ == "__main__": + run_client() diff --git a/examples/whisper-federated-finetuning/pyproject.toml b/examples/whisper-federated-finetuning/pyproject.toml new file mode 100644 index 000000000000..dd5578b8b3d0 --- /dev/null +++ b/examples/whisper-federated-finetuning/pyproject.toml @@ -0,0 +1,19 @@ +[build-system] +requires = ["poetry-core>=1.4.0"] +build-backend = "poetry.core.masonry.api" + +[tool.poetry] +name = "whisper-flower" +version = "0.1.0" +description = "On-device Federated Downstreaming for Speech Classification" +authors = ["The Flower Authors "] + +[tool.poetry.dependencies] +python = ">=3.8,<3.11" +flwr = { extras = ["simulation"], version = ">=1.0,<2.0" } +transformers = "4.32.1" +tokenizers = "0.13.3" +datasets = "2.14.6" +soundfile = "0.12.1" +librosa = "0.10.1" +# this example was tested with pytorch 2.1.0 \ No newline at end of file diff --git a/examples/whisper-federated-finetuning/requirements.txt b/examples/whisper-federated-finetuning/requirements.txt new file mode 100644 index 000000000000..eb4a5d7eb47b --- /dev/null +++ b/examples/whisper-federated-finetuning/requirements.txt @@ -0,0 +1,7 @@ +transformers==4.32.1 +tokenizers==0.13.3 +datasets==2.14.6 +soundfile==0.12.1 +librosa==0.10.1 +flwr==1.5.0 +ray==2.6.3 \ No newline at end of file diff --git a/examples/whisper-federated-finetuning/rpi_setup.md b/examples/whisper-federated-finetuning/rpi_setup.md new file mode 100644 index 000000000000..d49bbd6a472b --- /dev/null +++ b/examples/whisper-federated-finetuning/rpi_setup.md @@ -0,0 +1,49 @@ +# Setting up your RaspberryPi + +> This guide assumes you have a fresh install of Ubuntu Server (either 22.04 or 23.10) and that you have successfully `ssh`-ed into your device. + +## Setting up your device for Python developemnet + +We are going to use [`pyenv`](https://github.com/pyenv/pyenv) to manage different Python versions and to create an environment. First, we need to install some system dependencies + +```bash +sudo apt update +# the last package is needed for whisper +sudo apt install build-essential zlib1g-dev libssl-dev libsqlite3-dev libreadline-dev libbz2-dev libffi-dev liblzma-dev libsndfile1 +``` + +Create Python environment with `pyenv`: + +```bash + +# Ensure you have installed pyenv, else do the below: +git clone https://github.com/pyenv/pyenv.git ~/.pyenv +echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc +echo 'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc +echo 'eval "$(pyenv init -)"' >> ~/.bashrc + +# Install python 3.9+ +pyenv install 3.9.17 + +# Install pyenv virtual env plugin +git clone https://github.com/pyenv/pyenv-virtualenv.git $(pyenv root)/plugins/pyenv-virtualenv +# Restart your shell +exec "$SHELL" + +# Create the environment +pyenv virtualenv 3.9.17 flower-whisperer +``` + +## Installing the dependencies for Whisper+Flower + +With our environmnet ready, let's install the dependencies. Please note that at the time of writing, PyTorch 2.0+ won't work properly on `aarm64`. Because of this, we'll be using an earlier version of this package. + +```bash +# activate your environment +pyenv activate flower-whisperer + +# install pytorch (RPi aren't ready for PyTorch 2.0+ apparently...) +pip install torch==1.13.1 +# install rest of requirerments +pip install -r requirements.txt +``` diff --git a/examples/whisper-federated-finetuning/server.py b/examples/whisper-federated-finetuning/server.py new file mode 100644 index 000000000000..101d43f04ec2 --- /dev/null +++ b/examples/whisper-federated-finetuning/server.py @@ -0,0 +1,104 @@ +import argparse + +import torch +from datasets import load_dataset +from transformers import WhisperProcessor +from torch.utils.data import DataLoader +import flwr as fl + +from utils import eval_model, get_model, set_params, remove_cols, get_encoding_fn + + +parser = argparse.ArgumentParser(description="Flower+Whisper") +parser.add_argument("--num_rounds", type=int, default=5, help="Number of FL rounds.") +parser.add_argument( + "--server_address", type=str, required=True, help="IP of the server." +) + + +NUM_CLASSES = 12 +NUM_CLIENTS = 100 + + +def fit_config(server_round: int): + """Return a configuration with static batch size and (local) epochs.""" + config = { + "epochs": 1, # Number of local epochs done by clients + "batch_size": 8, # Batch size to use by clients during fit() + } + return config + + +def get_evaluate_fn(val_set, test_set, encoding_fn, num_rounds): + def evaluate(server_round: int, parameters: fl.common.NDArrays, config): + """Use the entire CIFAR-10 test set for evaluation.""" + + # Determine device + device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + + # prepare model + encoder, classifier = get_model(device, NUM_CLASSES) + set_params(classifier, parameters) + classifier.to(device) + + # prepare dataset + og_threads = torch.get_num_threads() + torch.set_num_threads( + 1 + ) # ! still, not clear to me why this is needed if we want `num_proc>1` + if server_round == num_rounds: + prefix = "test" + encoded = test_set.map(encoding_fn, num_proc=4, remove_columns=remove_cols) + else: + prefix = "val" + encoded = val_set.map(encoding_fn, num_proc=4, remove_columns=remove_cols) + torch.set_num_threads(og_threads) + + val_encoded = encoded.with_format("torch", columns=["data", "targets"]) + val_loader = DataLoader(val_encoded, batch_size=64, num_workers=4) + + # Run global evaluation + criterion = torch.nn.CrossEntropyLoss() + loss, accuracy = eval_model(encoder, classifier, criterion, val_loader, device) + + print(f"{prefix}: --> {loss = }, {accuracy = }") + + return loss, {f"{prefix}_accuracy": accuracy} + + return evaluate + + +def main(): + # Parse input arguments + args = parser.parse_args() + + # The sever will use the validation set to assess the performance of the global + # model after each round. Then, the test set will be used for evaluating the global + # model after the last round + sc_val = load_dataset("speech_commands", "v0.02", split="validation", token=False) + sc_test = load_dataset("speech_commands", "v0.02", split="test", token=False) + + processor = WhisperProcessor.from_pretrained("openai/whisper-tiny") + prepare_dataset_fn = get_encoding_fn(processor) + + # We use a standard FedAvg strategy + strategy = fl.server.strategy.FedAvg( + fraction_fit=0.00001, + min_fit_clients=2, # the strategy will wait until at least 2 clients are sampled for fit + fraction_evaluate=0.0, # we don't do federated evaluation in this example + min_available_clients=2, # the strategy will do nothing until 2 clients are connected to the server + on_fit_config_fn=fit_config, + evaluate_fn=get_evaluate_fn( + sc_val, sc_test, prepare_dataset_fn, args.num_rounds + ), + ) + + fl.server.start_server( + server_address=f"{args.server_address}:8080", + config=fl.server.ServerConfig(num_rounds=args.num_rounds), + strategy=strategy, + ) + + +if __name__ == "__main__": + main() diff --git a/examples/whisper-federated-finetuning/sim.py b/examples/whisper-federated-finetuning/sim.py new file mode 100644 index 000000000000..c04f768bb24a --- /dev/null +++ b/examples/whisper-federated-finetuning/sim.py @@ -0,0 +1,95 @@ +import argparse + +import torch +from datasets import load_dataset +from transformers import WhisperProcessor + +import flwr as fl + +from client import get_client_fn +from server import fit_config, get_evaluate_fn +from utils import construct_client_mapping, get_encoding_fn + +parser = argparse.ArgumentParser(description="Flower+Whisper") + +parser.add_argument("--num_rounds", type=int, default=10, help="Number of FL rounds.") +parser.add_argument( + "--num_cpus", type=int, default=4, help="Number of CPUs reserved for each client." +) +parser.add_argument( + "--num_gpus", + type=float, + default=0.5, + help="GPU ratio reserved for each client (`num_gpus`=1.0 means one client gets the whole GPU)", +) +parser.add_argument( + "--preprocess", + action="store_true", + help="Preprocesses all client's datasets and exits (creates ~83GB data)", +) + +NUM_CLASSES = 12 +NUM_CLIENTS = 100 +CLIENT_DATA = "client_datasets" +torch.set_float32_matmul_precision( + "high" +) # If β€œhigh” or β€œmedium” are set then the TensorFloat32 is used + + +def main(): + # Parse input arguments + args = parser.parse_args() + + # dataset download and preparation + sc_train = load_dataset("speech_commands", "v0.02", split="train", token=False) + sc_val = load_dataset("speech_commands", "v0.02", split="validation", token=False) + sc_test = load_dataset("speech_commands", "v0.02", split="test", token=False) + + # generate splits + client_mapping = construct_client_mapping(sc_train, num_clients=NUM_CLIENTS) + + # pre-process all partitions (+store to disk) + processor = WhisperProcessor.from_pretrained("openai/whisper-tiny") + prepare_dataset_fn = get_encoding_fn(processor) + if args.preprocess: + import sys + + client_fn = get_client_fn( + sc_train, prepare_dataset_fn, client_mapping, CLIENT_DATA, NUM_CLASSES + ) + + for i in range(NUM_CLIENTS): + _ = client_fn(str(i)) + print("Preprocessing completed. Run the code again without `--preprocess`") + sys.exit(0) + + strategy = fl.server.strategy.FedAvg( + fraction_fit=0.00001, + min_fit_clients=10, + fraction_evaluate=0.0, + min_available_clients=NUM_CLIENTS, + on_fit_config_fn=fit_config, + evaluate_fn=get_evaluate_fn( + sc_val, sc_test, prepare_dataset_fn, args.num_rounds + ), + ) + + # Start simulation + fl.simulation.start_simulation( + client_fn=get_client_fn( + sc_train, + prepare_dataset_fn, + client_mapping, + CLIENT_DATA, + NUM_CLASSES, + disable_tqdm=True, + ), + num_clients=NUM_CLIENTS, + client_resources={"num_cpus": args.num_cpus, "num_gpus": args.num_gpus}, + config=fl.server.ServerConfig(num_rounds=args.num_rounds), + strategy=strategy, + ) + + +if __name__ == "__main__": + main() diff --git a/examples/whisper-federated-finetuning/utils.py b/examples/whisper-federated-finetuning/utils.py new file mode 100644 index 000000000000..21fe0309151c --- /dev/null +++ b/examples/whisper-federated-finetuning/utils.py @@ -0,0 +1,210 @@ +from tqdm import tqdm +import torch +import random +from datasets import Dataset +import numpy as np +from collections import OrderedDict +from transformers import WhisperForConditionalGeneration + +from typing import List + +import flwr as fl + + +remove_cols = ["file", "audio", "label", "is_unknown", "speaker_id", "utterance_id"] + + +class RunningAvg: + def __init__(self): + self.n = 0 + self.total = 0 + + def update(self, val): + self.total += val + self.n += 1 + + def __call__(self): + return self.total / self.n + + +def train_one_epoch( + model, + classifier, + optimizer, + criterion, + dataloader, + device, + disable_tqdm: bool = False, +): + """Train the classification head. + + This is a very standard looking way of training PyTorch models. + """ + model.eval() + classifier.train() + classifier.to(device) + loss_avg, acc_avg = RunningAvg(), RunningAvg() + with tqdm(total=len(dataloader.dataset), disable=disable_tqdm) as t: + for b in dataloader: + optimizer.zero_grad() + data = b["data"].squeeze().to(device) + # print(data.shape) + labels = b["targets"].to(device) + with torch.no_grad(): + res = model(data)[0] + + resres = classifier(res) + + loss = criterion(resres.float(), labels) + loss.backward() + optimizer.step() + _, predicted = torch.max(resres.data, 1) + correct = (predicted == labels).sum().item() + acc = correct / data.shape[0] + loss_ = loss.cpu().item() + + loss_avg.update(loss_) + acc_avg.update(acc) + + t.update(data.shape[0]) + t.set_postfix( + {"avg_loss": f"{loss_avg():.4f}", "avg_acc": f"{acc_avg():.4f}"} + ) + + +def eval_model(model, classifier, criterion, dataloader, device): + """Evaluate a model on a validation/test set. + + This is a very normal looking way of doing this with PyTorch. + """ + model.eval() + classifier.eval() + classifier.to(device) + correct = 0 + loss_ = 0 + total = 0 + with torch.no_grad(): + for b in dataloader: + data = b["data"].squeeze().to(device) + # print(data.shape) + labels = b["targets"].to(device) + res = model(data)[0] + resres = classifier(res) + + loss = criterion(resres.float(), labels) + _, predicted = torch.max(resres.data, 1) + correct += (predicted == labels).sum().item() + total += data.shape[0] + loss_ += loss.cpu().item() + + accuracy = correct / total + loss = loss_ / total + + return loss, accuracy + + +def prepare_silences_dataset(train_dataset, ratio_silence: float = 0.1) -> Dataset: + """Generate silences for the train set. + + One of the classes in the SpeechCommands datatset is `silence`. However, the dataset + does not include clips of silence. It does however include 5 long files with different + background sounds. The taks of this function is to extract several (defined by `ratio_silence`) + one-second long clips from those background audio files. Later, those audio clips will be + included into the training set. + """ + # retrieve original silence audio clips + silences = [d for d in train_dataset if d["label"] == 35] + # figure out how many to add + num_silence_total = int(len(train_dataset) * ratio_silence) + # num new entries per background noise clip + num_silence_per_bkg = num_silence_total // len(silences) + + silence_to_add = [] + for sil in silences: + sil_array = sil["audio"]["array"] + sr = sil["audio"]["sampling_rate"] + print(f"Extracting audio from: {sil['file']} ...") + for _ in range(num_silence_per_bkg): + random_offset = random.randint(0, len(sil_array) - sr - 1) + sil_array_crop = sil_array[random_offset : random_offset + sr] + + entry = sil + silence_to_add.append(entry) + silence_to_add[-1]["audio"]["array"] = sil_array_crop + + return Dataset.from_list(silence_to_add) + + +def construct_client_mapping(full_trainset, num_clients: int = 100): + """Create a mapping to partition the dataset into `num_client` buckets. + + These buckets contain the same number of `spekaer_id` but likely different + number of training exampes since each `speaker_id` in SpeechCommands does + provide different amounts of data to the dataset. + """ + client_ids = list(set(full_trainset["speaker_id"])) + client_ids.remove( + None + ) # remove "none" which corresponds to the _silence_ audio clips + client_ids.sort() # we sort this as a quick way of ensuring our client mapping is consistent between runs + len( + client_ids + ) # should be 2112 (i.e. the number of participats in SpeechCommands dataset v0.02) + + # split into groups (each group represents a client) + client_mapping = np.array_split(client_ids, num_clients) + + return client_mapping + + +def get_encoding_fn(processor): + """Return a function to use to pre-process/encode the SpeechCommands dataset. + + We are working with the 12classes version of this dataset, therefore we need to do + some reassignment of labels. + """ + + def prepare_dataset(batch): + audio = batch["audio"] + data = {} + data["data"] = processor( + audio["array"], sampling_rate=audio["sampling_rate"], return_tensors="pt" + ).input_features + + # All unknown keywords are assigned label 11. The silence clips get assigned label 10 + # In this way we have 12 classes with labels 0-11 + data["targets"] = ( + 11 + if batch["is_unknown"] + else (10 if batch["label"] == 35 else batch["label"]) + ) + return data + + return prepare_dataset + + +def set_params(model: torch.nn.ModuleList, params: List[fl.common.NDArrays]): + """Set model weights from a list of NumPy ndarrays.""" + params_dict = zip(model.state_dict().keys(), params) + state_dict = OrderedDict({k: torch.Tensor(v) for k, v in params_dict}) + model.load_state_dict(state_dict, strict=True) + + +def get_model(device, num_classes, compile: bool = True): + """Create model: Whisper-tiny Encoder + classification head""" + encoder = WhisperForConditionalGeneration.from_pretrained( + "openai/whisper-tiny" + ).get_encoder() + encoder = encoder.to(device) + if compile: + encoder = torch.compile(encoder) + + # This classification head is 782K parameters + # This is the only part of the model that is trained in federation + classifier = torch.nn.Sequential( + torch.nn.Conv1d(1500, 128, kernel_size=1), + torch.nn.ReLU(), + torch.nn.Flatten(1), + torch.nn.Linear(128 * 384, num_classes), + ).to(device) + return encoder, classifier