From 8a9d67f2f693f3c44d2abf983e001423cc321313 Mon Sep 17 00:00:00 2001 From: Ziyue Xu Date: Sat, 22 Feb 2025 11:56:04 -0500 Subject: [PATCH] Add chapter-8 LLM notebooks (#3226) Fixes # . ### Description Add tutorial notebooks for chapter-8 LLM ### Types of changes - [x] Non-breaking change (fix or new feature that would not break existing functionality). - [ ] Breaking change (fix or new feature that would cause existing functionality to change). - [ ] New tests added to cover the changes. - [ ] Quick tests passed locally by running `./runtest.sh`. - [ ] In-line docstrings updated. - [ ] Documentation updated. --- .../04.2.0_introduction.ipynb | 33 --- .../08.0_introduction/introduction.ipynb | 106 ++++++++ .../08.1_fed_bert/code/ner_model_test.py | 96 +++++++ .../08.1_fed_bert/code/nlp_fl_job.py | 84 ++++++ .../08.1_fed_bert/code/prepare_data.sh | 4 + .../08.1_fed_bert/code/requirements.txt | 6 + .../08.1_fed_bert/code/src/data_sequence.py | 83 ++++++ .../08.1_fed_bert/code/src/nlp_fl.py | 193 ++++++++++++++ .../08.1_fed_bert/code/src/nlp_models.py | 52 ++++ .../08.1_fed_bert/code/test_global_model.sh | 4 + .../08.1_fed_bert/code/utils/data_split.py | 66 +++++ .../federated_nlp_with_bert.ipynb | 234 ++++++++++++++++ .../08.1_fed_bert/figs/sample.png | Bin 0 -> 24371 bytes .../08.1_llm_p_tuning/LLM_prompt_tuning.ipynb | 33 --- .../08.2_llm_peft/LLM_PEFT.ipynb | 33 --- .../08.2_llm_sft/LLM_SFT.ipynb | 231 ++++++++++++++++ .../08.2_llm_sft/figs/diff.png | Bin 0 -> 233527 bytes .../08.2_llm_sft/figs/diff_fl_1.png | Bin 0 -> 48498 bytes .../08.2_llm_sft/figs/diff_fl_2.png | Bin 0 -> 141173 bytes .../08.2_llm_sft/requirements.txt | 7 + .../08.2_llm_sft/sft_job.py | 194 ++++++++++++++ .../08.2_llm_sft/src/hf_sft_model.py | 28 ++ .../08.2_llm_sft/src/hf_sft_peft_fl.py | 250 ++++++++++++++++++ .../08.2_llm_sft/utils/hf_sft_peft.py | 154 +++++++++++ .../08.2_llm_sft/utils/hf_sft_peft_iter.py | 193 ++++++++++++++ .../08.2_llm_sft/utils/preprocess_dolly.py | 93 +++++++ .../08.3_llm_peft/LLM_PEFT.ipynb | 162 ++++++++++++ .../08.3_llm_peft/peft_job.py | 194 ++++++++++++++ .../08.3_llm_peft/requirements.txt | 7 + .../08.3_llm_peft/src/hf_peft_model.py | 38 +++ .../08.3_llm_peft/src/hf_sft_peft_fl.py | 250 ++++++++++++++++++ .../08.3_llm_peft/utils/hf_sft_peft.py | 154 +++++++++++ .../08.3_llm_peft/utils/preprocess_dolly.py | 93 +++++++ .../08.3_llm_peft/utils/preprocess_oasst1.py | 101 +++++++ .../08.3_llm_sft/LLM_SFT.ipynb | 33 --- .../federated_nlp_with_bert.ipynb | 33 --- .../LLM_quantization.ipynb | 164 ++++++++++++ .../08.4_llm_quantization/sft_job.py | 194 ++++++++++++++ .../08.4_llm_quantization/src/hf_sft_model.py | 28 ++ .../src/hf_sft_peft_fl.py | 250 ++++++++++++++++++ .../utils/hf_sft_peft.py | 154 +++++++++++ .../utils/preprocess_dolly.py | 93 +++++++ .../08.5_llm_streaming/LLM_streaming.ipynb | 138 ++++++++++ .../08.5_llm_streaming/container_stream.sh | 2 + .../08.5_llm_streaming/file_stream.sh | 2 + .../regular_transmission.sh | 3 + .../src/streaming_controller.py | 126 +++++++++ .../src/streaming_executor.py | 110 ++++++++ .../08.5_llm_streaming/streaming_job.py | 80 ++++++ .../08.5_llm_streaming/utils/log_memory.sh | 9 + .../federated_retriever_model_training.ipynb | 33 --- .../LLM_quantization.ipynb | 33 --- .../08.6_recap/recap.ipynb | 77 ++++++ .../08.7_llm_streaming/LLM_streaming.ipynb | 33 --- .../08.8_recap/recap.ipynb | 33 --- 55 files changed, 4507 insertions(+), 297 deletions(-) delete mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.0_introduction/04.2.0_introduction.ipynb create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.0_introduction/introduction.ipynb create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/ner_model_test.py create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/nlp_fl_job.py create mode 100755 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/prepare_data.sh create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/requirements.txt create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/src/data_sequence.py create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/src/nlp_fl.py create mode 100755 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/src/nlp_models.py create mode 100755 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/test_global_model.sh create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/utils/data_split.py create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/federated_nlp_with_bert.ipynb create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/figs/sample.png delete mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_llm_p_tuning/LLM_prompt_tuning.ipynb delete mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_peft/LLM_PEFT.ipynb create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/LLM_SFT.ipynb create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/figs/diff.png create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/figs/diff_fl_1.png create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/figs/diff_fl_2.png create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/requirements.txt create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/sft_job.py create mode 100755 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/src/hf_sft_model.py create mode 100755 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/src/hf_sft_peft_fl.py create mode 100755 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/utils/hf_sft_peft.py create mode 100755 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/utils/hf_sft_peft_iter.py create mode 100755 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/utils/preprocess_dolly.py create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/LLM_PEFT.ipynb create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/peft_job.py create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/requirements.txt create mode 100755 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/src/hf_peft_model.py create mode 100755 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/src/hf_sft_peft_fl.py create mode 100755 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/utils/hf_sft_peft.py create mode 100755 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/utils/preprocess_dolly.py create mode 100755 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/utils/preprocess_oasst1.py delete mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_sft/LLM_SFT.ipynb delete mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.4_fed_nlp/federated_nlp_with_bert.ipynb create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.4_llm_quantization/LLM_quantization.ipynb create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.4_llm_quantization/sft_job.py create mode 100755 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.4_llm_quantization/src/hf_sft_model.py create mode 100755 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.4_llm_quantization/src/hf_sft_peft_fl.py create mode 100755 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.4_llm_quantization/utils/hf_sft_peft.py create mode 100755 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.4_llm_quantization/utils/preprocess_dolly.py create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/LLM_streaming.ipynb create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/container_stream.sh create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/file_stream.sh create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/regular_transmission.sh create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/src/streaming_controller.py create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/src/streaming_executor.py create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/streaming_job.py create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/utils/log_memory.sh delete mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_retiever_model_training/federated_retriever_model_training.ipynb delete mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.6_llm_quantization/LLM_quantization.ipynb create mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.6_recap/recap.ipynb delete mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.7_llm_streaming/LLM_streaming.ipynb delete mode 100644 examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.8_recap/recap.ipynb diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.0_introduction/04.2.0_introduction.ipynb b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.0_introduction/04.2.0_introduction.ipynb deleted file mode 100644 index d973d21d6a..0000000000 --- a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.0_introduction/04.2.0_introduction.ipynb +++ /dev/null @@ -1,33 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "d3ef5590-c631-4aed-b892-999dd4a3eb5f", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "nvflare_example", - "language": "python", - "name": "nvflare_example" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.2" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.0_introduction/introduction.ipynb b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.0_introduction/introduction.ipynb new file mode 100644 index 0000000000..09602efb77 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.0_introduction/introduction.ipynb @@ -0,0 +1,106 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "1e333915-6759-4d82-9f83-2bccc42ca047", + "metadata": {}, + "source": [ + "# Introduction: Federated Language Models - from NLP to LLM" + ] + }, + { + "cell_type": "markdown", + "id": "c96f5007-8c62-4d61-bd97-6b31a3f5b0db", + "metadata": {}, + "source": [ + "In this chapter, we will explore the federated learning applications on language models.\n", + "\n", + "Natural Language Processing (NLP) is a subfield of artificial intelligence, focuses on enabling computers to process and analyze natural language data. Recently, Large Language Models (LLMs) have emerged as a transformative force in the field of NLP, enabling AI to understand, generate, and interact with human language at an unprecedented scale. Models such as BERT and GPT is able to leverage vast amounts of text data and deep learning techniques to perform various linguistic tasks, including text generation, translation, summarization, and question-answering.\n", + "\n", + "The development of LLMs relies on robust training schemes that enable these models to capture linguistic structures, contextual dependencies, and semantic meanings. Common training methodologies include unsupervised pretraining on large text corpora, followed by further fine-tuning using supervised (supervised finetuning - SFT) or reinforcement learning (reinforcement learning from human feedback - RLHF) approaches, refining their capabilities for practical applications with human interactions.\n", + "\n", + "Further, when adapting to a particular downstream task, instead of making updates to all model parameters as SFT/RLHF which can be computationally expensive and memory-intensive, Parameter-Efficient Fine-Tuning (PEFT) methods have emerged as a more efficient approach. Techniques such as Low-Rank Adaptation (LoRA), P-Tuning, and Adapter Layers enable fine-tuning by updating only a small subset of parameters, significantly reducing computational costs while maintaining performance. " + ] + }, + { + "cell_type": "markdown", + "id": "4fb8b73a-5058-44fe-8ecc-e355ef7178fb", + "metadata": {}, + "source": [ + "In the following sections, we will start with federated learning using a smaller-scale BERT model, then we extend our study to more recent open-source LLMs and their SFT and PEFT in a federated finetuning scheme. And finally to address a major challenge in federated LLM training - communication efficiency, we further visit potential solutions including quantization and streaming, and we will conclude with a recap of the covered topics." + ] + }, + { + "cell_type": "markdown", + "id": "9bffcb04-6839-4463-b4a5-e1792024adce", + "metadata": {}, + "source": [ + "8.1. **Federated BERT**\n", + "\n", + "Task-specific model training with BERT in a federated setting\n", + "* [Federated NLP with BERT Model](../08.1_fed_bert/federated_nlp_with_bert.ipynb)\n", + "\n", + "8.2. **Federated LLM Training with SFT**\n", + "\n", + "Supervised Fine-Tuning and its role in adapting LLMs in federated learning\n", + "* [Federated LLM Tuning with SFT](../08.2_llm_sft/LLM_SFT.ipynb)\n", + "\n", + "8.3. **Federated LLM Training with PEFT**\n", + "\n", + "Importance of PEFT in adapting LLMs for specific tasks, which can be achieve in a federated setting\n", + "* [Federated LLM Tuning with PEFT](../08.3_llm_peft/LLM_PEFT.ipynb)\n", + "\n", + "8.4. **Model Transmission with Quantization**\n", + "\n", + "One major hurdle of adapting LLMs in federated learning is the significant communication burden when performing federated SFT. To reduce the message size, quantization method can be applied as filters.\n", + "* [Model Quantization for Transmission](../08.4_llm_quantization/LLM_quantization.ipynb)\n", + "\n", + "8.5 **Model Transmission with Streaming**\n", + "\n", + "While quantization reduced communication cost, system memory requirement is still high for prepareing the message on either side. Therefore, we enabled streaming capabilities for more efficient and robust model communication.\n", + "* [Message Streaming for Model Transmission](../08.5_llm_streaming/LLM_streaming.ipynb)\n", + "\n", + "8.6. **Recap**\n", + "\n", + "[Recap](../08.6_recap/recap.ipynb) for federated LLM applications and features" + ] + }, + { + "cell_type": "markdown", + "id": "f8864f21-ce74-4adf-8b5b-240879424424", + "metadata": {}, + "source": [ + "Let's get started with [Federated NLP with BERT Model](../08.1_fed_bert/federated_nlp_with_bert.ipynb)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0ceaab09-f41e-41e4-8ecd-a784328b468a", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/ner_model_test.py b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/ner_model_test.py new file mode 100644 index 0000000000..3bed4fdf20 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/ner_model_test.py @@ -0,0 +1,96 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. 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. + +import argparse +import os + +import pandas as pd +import torch +from seqeval.metrics import classification_report +from src.data_sequence import DataSequence +from src.nlp_models import BertModel, GPTModel +from torch.utils.data import DataLoader + +os.environ["TOKENIZERS_PARALLELISM"] = "False" + + +def data_split_args_parser(): + parser = argparse.ArgumentParser(description="Perform model testing by loading the best global model") + parser.add_argument("--data_path", type=str, help="Path to data file") + parser.add_argument("--model_path", type=str, help="Path to workspace server folder") + parser.add_argument("--num_labels", type=int, help="Number of labels for the candidate dataset") + parser.add_argument("--model_name", type=str, default="bert-base-uncased", help="Model name") + return parser + + +if __name__ == "__main__": + parser = data_split_args_parser() + args = parser.parse_args() + device = torch.device("cuda") + + model_path = args.model_path + data_path = args.data_path + num_labels = args.num_labels + model_name = args.model_name + ignore_token = -100 + + df_test = pd.read_csv(os.path.join(data_path, "test.csv")) + # label and id conversion + labels = [] + for x in df_test["labels"].values: + labels.extend(x.split(" ")) + unique_labels = set(labels) + labels_to_ids = {k: v for v, k in enumerate(sorted(unique_labels))} + ids_to_labels = {v: k for v, k in enumerate(sorted(unique_labels))} + + # model + if model_name == "bert-base-uncased": + model = BertModel(model_name=model_name, num_labels=num_labels).to(device) + elif model_name == "gpt2": + model = GPTModel(model_name=model_name, num_labels=num_labels).to(device) + else: + raise ValueError("model not supported") + model_weights = torch.load(os.path.join(model_path, "best_FL_global_model.pt")) + model.load_state_dict(state_dict=model_weights["model"]) + tokenizer = model.tokenizer + + # data + test_dataset = DataSequence(df_test, labels_to_ids, tokenizer=tokenizer, ignore_token=ignore_token) + test_loader = DataLoader(test_dataset, num_workers=4, batch_size=64, shuffle=False) + + # validate + model.eval() + with torch.no_grad(): + total_acc_test, total_loss_test, test_total = 0, 0, 0 + test_y_pred, test_y_true = [], [] + for test_data, test_label in test_loader: + test_label = test_label.to(device) + test_total += test_label.shape[0] + mask = test_data["attention_mask"].squeeze(1).to(device) + input_id = test_data["input_ids"].squeeze(1).to(device) + loss, logits = model(input_id, mask, test_label) + + for i in range(logits.shape[0]): + # remove pad tokens + logits_clean = logits[i][test_label[i] != ignore_token] + label_clean = test_label[i][test_label[i] != ignore_token] + # calcluate acc and store prediciton and true labels + predictions = logits_clean.argmax(dim=1) + acc = (predictions == label_clean).float().mean() + total_acc_test += acc.item() + test_y_pred.append([ids_to_labels[x.item()] for x in predictions]) + test_y_true.append([ids_to_labels[x.item()] for x in label_clean]) + # metric summary + summary = classification_report(y_true=test_y_true, y_pred=test_y_pred, zero_division=0) + print(summary) diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/nlp_fl_job.py b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/nlp_fl_job.py new file mode 100644 index 0000000000..79bfee4606 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/nlp_fl_job.py @@ -0,0 +1,84 @@ +# Copyright (c) 2025, NVIDIA CORPORATION. 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. + +import argparse + +from src.nlp_models import BertModel, GPTModel + +from nvflare.app_common.widgets.intime_model_selector import IntimeModelSelector +from nvflare.app_common.workflows.fedavg import FedAvg +from nvflare.app_opt.pt.job_config.model import PTModel +from nvflare.job_config.api import FedJob +from nvflare.job_config.script_runner import ScriptRunner + + +def define_parser(): + parser = argparse.ArgumentParser() + parser.add_argument( + "--model_name", + type=str, + default="Bert", + help="Which model to choose, either Bert or GPT", + ) + return parser.parse_args() + + +def main(): + args = define_parser() + model_name = args.model_name + + # Create the FedJob + if model_name.lower() == "bert": + num_clients = 4 + job = FedJob(name="Bert", min_clients=num_clients) + train_model_name = "bert-base-uncased" + model = PTModel(BertModel(num_labels=3, model_name=train_model_name)) + output_path = "Bert" + elif model_name.lower() == "gpt": + num_clients = 2 + job = FedJob(name="GPT", min_clients=num_clients) + train_model_name = "gpt2" + model = PTModel(GPTModel(num_labels=3, model_name=train_model_name)) + output_path = "GPT" + else: + raise ValueError(f"Invalid model_name: {model_name}, only Bert and GPT are supported.") + + # Local training parameters + num_rounds = 5 + dataset_path = f"/tmp/nvflare/dataset/nlp_ner/{num_clients}_split" + train_script = "src/nlp_fl.py" + train_args = f"--dataset_path {dataset_path} --model_name {train_model_name}" + + # Define the controller workflow and send to server + controller = FedAvg( + num_clients=num_clients, + num_rounds=num_rounds, + ) + job.to_server(controller) + + # Define the initial global model and send to server + job.to_server(model) + job.to(IntimeModelSelector(key_metric="eval_acc"), "server") + + # Add executor to clients + executor = ScriptRunner(script=train_script, script_args=train_args) + job.to_clients(executor) + + # Export job config and run the job + job.export_job("/tmp/nvflare/workspace/jobs/") + job.simulator_run(f"/tmp/nvflare/workspace/works/{output_path}", n_clients=num_clients, gpu="0") + + +if __name__ == "__main__": + main() diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/prepare_data.sh b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/prepare_data.sh new file mode 100755 index 0000000000..53eba5f43f --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/prepare_data.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +DATASET_ROOT=${1} +echo "4-client" +python3 code/utils/data_split.py --data_path ${DATASET_ROOT} --num_clients 4 --random_seed 0 --site_name_prefix 'site-' \ No newline at end of file diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/requirements.txt b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/requirements.txt new file mode 100644 index 0000000000..b7340693d5 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/requirements.txt @@ -0,0 +1,6 @@ +torch +torchvision +tensorboard +transformers +pandas +seqeval diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/src/data_sequence.py b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/src/data_sequence.py new file mode 100644 index 0000000000..f488affc73 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/src/data_sequence.py @@ -0,0 +1,83 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. 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. + +import torch + + +def align_label( + texts_encoded, + labels_raw, + labels_to_ids, + ignore_token, +): + # generate label id vector for the network + # mark the tokens to be ignored + labels_aligned = [] + # single sentence each time, so always use 0 index + # get the index mapping from token to word + # this can be dependent on the specific tokenizer + word_ids = texts_encoded.word_ids(batch_index=0) + previous_word_idx = None + for word_idx in word_ids: + if word_idx is None: + # set None the ignore tokens + labels_aligned.append(ignore_token) + elif word_idx != previous_word_idx: + # only label the first token of a word + labels_aligned.append(labels_to_ids[labels_raw[word_idx]]) + else: + labels_aligned.append(ignore_token) + previous_word_idx = word_idx + return labels_aligned + + +class DataSequence(torch.utils.data.Dataset): + def __init__(self, df, labels_to_ids, tokenizer, ignore_token=-100, max_length=150): + # Raw texts and corresponding labels + texts_batch_raw = [i.split(" ") for i in df["text"].values.tolist()] + labels_batch_raw = [i.split(" ") for i in df["labels"].values.tolist()] + # Iterate through all cases + self.texts = [] + self.labels = [] + for batch_idx in range(len(texts_batch_raw)): + texts_raw = texts_batch_raw[batch_idx] + labels_raw = labels_batch_raw[batch_idx] + # Encode texts with tokenizer + texts_encoded = tokenizer.encode_plus( + texts_raw, + padding="max_length", + max_length=max_length, + add_special_tokens=True, + truncation=True, + is_split_into_words=True, + return_attention_mask=True, + return_tensors="pt", + ) + labels_aligned = align_label(texts_encoded, labels_raw, labels_to_ids, ignore_token) + self.texts.append(texts_encoded) + self.labels.append(labels_aligned) + + def __len__(self): + return len(self.labels) + + def get_batch_data(self, idx): + return self.texts[idx] + + def get_batch_labels(self, idx): + return torch.LongTensor(self.labels[idx]) + + def __getitem__(self, idx): + batch_data = self.get_batch_data(idx) + batch_labels = self.get_batch_labels(idx) + return batch_data, batch_labels diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/src/nlp_fl.py b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/src/nlp_fl.py new file mode 100644 index 0000000000..81eb856a3d --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/src/nlp_fl.py @@ -0,0 +1,193 @@ +# Copyright (c) 2025, NVIDIA CORPORATION. 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. + +import argparse +import os + +import pandas as pd +import torch +from data_sequence import DataSequence +from nlp_models import BertModel, GPTModel +from seqeval.metrics import classification_report +from torch.optim import AdamW +from torch.utils.data import DataLoader +from torch.utils.tensorboard import SummaryWriter + +# import nvflare client API +import nvflare.client as flare + +# (optional) We change to use GPU to speed things up. +# if you want to use CPU, change DEVICE="cpu" +DEVICE = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + +os.environ["TOKENIZERS_PARALLELISM"] = "false" + + +def define_parser(): + parser = argparse.ArgumentParser() + parser.add_argument("--dataset_path", type=str, nargs="?") + parser.add_argument("--batch_size", type=int, default=16, nargs="?") + parser.add_argument("--learning_rate", type=float, default=1e-5, nargs="?") + parser.add_argument("--num_workers", type=int, default=1, nargs="?") + parser.add_argument("--local_epochs", type=int, default=1, nargs="?") + parser.add_argument("--model_name", type=str, default="bert-base-uncased", nargs="?") + parser.add_argument("--num_labels", type=int, default=3, nargs="?") + parser.add_argument("--ignore_token", type=int, default=-100, nargs="?") + return parser.parse_args() + + +def get_labels(df_train, num_labels): + labels = [] + for x in df_train["labels"].values: + labels.extend(x.split(" ")) + unique_labels = set(labels) + # check label length + if len(unique_labels) != num_labels: + raise ValueError(f"num_labels {num_labels} need to align with dataset, actual data {len(unique_labels)}!") + labels_to_ids = {k: v for v, k in enumerate(sorted(unique_labels))} + ids_to_labels = {v: k for v, k in enumerate(sorted(unique_labels))} + return labels_to_ids, ids_to_labels + + +def main(): + # define local parameters + args = define_parser() + dataset_path = args.dataset_path + batch_size = args.batch_size + lr = args.learning_rate + num_workers = args.num_workers + local_epochs = args.local_epochs + model_name = args.model_name + num_labels = args.num_labels + ignore_token = args.ignore_token + + # Initializes NVFlare client API and get site_name from flare + flare.init() + site_name = flare.get_site_name() + + # load data + df_train = pd.read_csv(os.path.join(dataset_path, site_name + "_train.csv")) + df_valid = pd.read_csv(os.path.join(dataset_path, site_name + "_val.csv")) + labels_to_ids, ids_to_labels = get_labels(df_train, num_labels) + + # training components + writer = SummaryWriter("./") + if model_name == "bert-base-uncased": + model = BertModel(model_name=model_name, num_labels=num_labels) + elif model_name == "gpt2": + model = GPTModel(model_name=model_name, num_labels=num_labels) + else: + raise ValueError(f"Model {model_name} not supported!") + tokenizer = model.tokenizer + train_dataset = DataSequence(df_train, labels_to_ids, tokenizer=tokenizer, ignore_token=ignore_token) + valid_dataset = DataSequence(df_valid, labels_to_ids, tokenizer=tokenizer, ignore_token=ignore_token) + train_loader = DataLoader(train_dataset, num_workers=num_workers, batch_size=batch_size, shuffle=True) + valid_loader = DataLoader(valid_dataset, num_workers=num_workers, batch_size=batch_size, shuffle=False) + print(f"Training Size: {len(train_loader.dataset)}, Validation Size: {len(valid_loader.dataset)}") + optimizer = AdamW(model.parameters(), lr=lr) + local_model_file = "local_model.pt" + best_global_model_file = "best_global_model_file.pt" + best_acc = 0.0 + + # Train federated rounds + # start with global model at the beginning of each round + while flare.is_running(): + # receive FLModel from NVFlare + global_model = flare.receive() + curr_round = global_model.current_round + epoch_global = local_epochs * curr_round + print(f"({site_name}) current_round={curr_round + 1}/{global_model.total_rounds}") + + # load global model from NVFlare + model.load_state_dict(global_model.params) + model.to(DEVICE) + + # wraps evaluation logic into a method to re-use for + # evaluation on both trained and received model + def evaluate(tb_id): + model.eval() + with torch.no_grad(): + total_acc_val, total_loss_val, val_total = 0, 0, 0 + val_y_pred, val_y_true = [], [] + for val_data, val_label in valid_loader: + val_label = val_label.to(DEVICE) + val_total += val_label.shape[0] + mask = val_data["attention_mask"].squeeze(1).to(DEVICE) + input_id = val_data["input_ids"].squeeze(1).to(DEVICE) + # Inference + loss, logits = model(input_id, mask, val_label) + # Add items for metric computation + for i in range(logits.shape[0]): + # remove pad tokens + logits_clean = logits[i][val_label[i] != ignore_token] + label_clean = val_label[i][val_label[i] != ignore_token] + # calcluate acc and store prediciton and true labels + predictions = logits_clean.argmax(dim=1) + acc = (predictions == label_clean).float().mean() + total_acc_val += acc.item() + val_y_pred.append([ids_to_labels[x.item()] for x in predictions]) + val_y_true.append([ids_to_labels[x.item()] for x in label_clean]) + # compute metric + metric_dict = classification_report( + y_true=val_y_true, y_pred=val_y_pred, output_dict=True, zero_division=0 + ) + # tensorboard record id prefix, add to record if provided + writer.add_scalar(tb_id + "_precision", metric_dict["macro avg"]["precision"], epoch_global) + writer.add_scalar(tb_id + "_recall", metric_dict["macro avg"]["recall"], epoch_global) + writer.add_scalar(tb_id + "_f1-score", metric_dict["macro avg"]["f1-score"], epoch_global) + return metric_dict["macro avg"]["f1-score"] + + # evaluate on received global model + val_acc = evaluate("global_val_acc") + if val_acc > best_acc: + best_acc = val_acc + torch.save(model.state_dict(), best_global_model_file) + + # train local model + epoch_len = len(train_loader) + for epoch in range(local_epochs): + model.train() + print(f"Local epoch {site_name}: {epoch + 1}/{local_epochs} (lr={lr})") + + for i, batch_data in enumerate(train_loader): + mask = batch_data[0]["attention_mask"].squeeze(1).to(DEVICE) + input_id = batch_data[0]["input_ids"].squeeze(1).to(DEVICE) + train_label = batch_data[1].to(DEVICE) + # model output + loss, logits = model(input_id, mask, train_label) + # optimize + optimizer.zero_grad() + loss.backward() + optimizer.step() + # record loss + current_step = epoch_len * epoch_global + i + writer.add_scalar("train_loss", loss.item(), current_step) + + # evaluation on local trained model + val_acc_local = evaluate("local_val_acc") + torch.save(model.state_dict(), local_model_file) + + # construct trained FL model + output_model = flare.FLModel( + params=model.cpu().state_dict(), + metrics={"eval_acc": val_acc_local}, + meta={"NUM_STEPS_CURRENT_ROUND": epoch_len * local_epochs}, + ) + + # send model back to NVFlare + flare.send(output_model) + + +if __name__ == "__main__": + main() diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/src/nlp_models.py b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/src/nlp_models.py new file mode 100755 index 0000000000..82e97e662d --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/src/nlp_models.py @@ -0,0 +1,52 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. 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. + +import torch +from transformers import AutoModelForTokenClassification, AutoTokenizer + + +class BertModel(torch.nn.Module): + def __init__(self, model_name, num_labels): + super(BertModel, self).__init__() + self.num_labels = num_labels + self.model_name = model_name + self.model = AutoModelForTokenClassification.from_pretrained( + self.model_name, num_labels=self.num_labels, output_attentions=False, output_hidden_states=False + ) + self.tokenizer = AutoTokenizer.from_pretrained(self.model_name) + + def forward(self, input_id, mask, label): + output = self.model(input_ids=input_id, attention_mask=mask, labels=label, return_dict=False) + return output + + +class GPTModel(torch.nn.Module): + def __init__(self, model_name, num_labels): + super(GPTModel, self).__init__() + self.num_labels = num_labels + self.model_name = model_name + self.model = AutoModelForTokenClassification.from_pretrained( + self.model_name, + num_labels=self.num_labels, + output_attentions=False, + output_hidden_states=False, + ) + self.tokenizer = AutoTokenizer.from_pretrained(self.model_name, add_prefix_space=True) + self.tokenizer.pad_token = self.tokenizer.eos_token + self.model.config.pad_token_id = self.model.config.eos_token_id + self.model.resize_token_embeddings(len(self.tokenizer)) + + def forward(self, input_id, mask, label): + output = self.model(input_ids=input_id, attention_mask=mask, labels=label, return_dict=False) + return output diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/test_global_model.sh b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/test_global_model.sh new file mode 100755 index 0000000000..49815811f2 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/test_global_model.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +DATASET_ROOT=${1} +echo "BERT" +python3 ner_model_test.py --model_path "/tmp/nvflare/workspace/works/Bert/server/simulate_job/app_server/" --model_name "bert-base-uncased" --data_path ${DATASET_ROOT} --num_labels 3 \ No newline at end of file diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/utils/data_split.py b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/utils/data_split.py new file mode 100644 index 0000000000..7cdd0c33b7 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/code/utils/data_split.py @@ -0,0 +1,66 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. 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. + +import argparse +import os + +import numpy as np +import pandas as pd + + +def data_split_args_parser(): + parser = argparse.ArgumentParser(description="Generate data split for dataset") + parser.add_argument("--data_path", type=str, help="Path to data file") + parser.add_argument("--num_clients", type=int, help="Total number of clients") + parser.add_argument("--random_seed", type=int, help="Random seed") + parser.add_argument("--site_name_prefix", type=str, default="site-", help="Site name prefix") + return parser + + +def split_df_by_num(df, num=1): + df_len = df.shape[0] + df_1_len = num + idx = list(range(df_len)) + np.random.shuffle(idx) + df_1 = df.iloc[idx[:df_1_len]] + df_2 = df.iloc[idx[df_1_len:]] + df_1.reset_index(drop=True, inplace=True) + df_2.reset_index(drop=True, inplace=True) + return df_1, df_2 + + +def main(): + parser = data_split_args_parser() + args = parser.parse_args() + num_clients = args.num_clients + data_path = args.data_path + site_name_prefix = args.site_name_prefix + np.random.seed(args.random_seed) + for mode in ["train", "dev"]: + saved_name = "val" if mode == "dev" else mode + df = pd.read_csv(os.path.join(data_path, mode + ".csv")) + client_size = int(df.shape[0] / num_clients) + os.makedirs(f"{data_path}/{num_clients}_split", exist_ok=True) + for i in range(num_clients): + if i != num_clients - 1: + client_df, df = split_df_by_num(df, client_size) + else: + client_df = df + print(df.shape, client_df.shape) + # split into train, test, val + client_df.to_csv(f"{data_path}/{num_clients}_split/{site_name_prefix}{i + 1}_{saved_name}.csv") + + +if __name__ == "__main__": + main() diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/federated_nlp_with_bert.ipynb b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/federated_nlp_with_bert.ipynb new file mode 100644 index 0000000000..236c1a8243 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/federated_nlp_with_bert.ipynb @@ -0,0 +1,234 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "59e85865-d59b-4809-9ae0-ca5260df37bc", + "metadata": {}, + "source": [ + "# Federated NLP with BERT Model" + ] + }, + { + "cell_type": "markdown", + "id": "466606e1-7f8f-45e4-bed1-c136d89258c1", + "metadata": {}, + "source": [ + "## Introduction \n", + "In this example, we show how to use [NVIDIA FLARE](https://nvidia.github.io/NVFlare) for a Natural Language Processing (NLP) task using [BERT](https://github.com/google-research/bert) model from [Hugging Face](https://huggingface.co/). We select [BERT-base-uncased](https://huggingface.co/bert-base-uncased) as our base model. " + ] + }, + { + "cell_type": "markdown", + "id": "022ca051-add4-474b-9637-24c16040d7b6", + "metadata": {}, + "source": [ + "## Setup\n", + "Install required packages for training" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ee1d1f87-502a-4c30-aa40-f55ae65a1da7", + "metadata": {}, + "outputs": [], + "source": [ + "%pip install -r code/requirements.txt" + ] + }, + { + "cell_type": "markdown", + "id": "734aa5da-3e4e-4c22-a0d2-b8ee6b6be142", + "metadata": {}, + "source": [ + "## Download Data \n", + "The raw data can be accessed from [official page](https://www.ncbi.nlm.nih.gov/CBBresearch/Dogan/DISEASE/). \n", + "In this example, we use the preprocessed csv-files from the reference repo above, which can be downloaded [here](https://drive.google.com/drive/folders/13wROtEAnMgWpLMIGHB5CY1BQ1Xe2XqhG). \n", + "\n", + "In the following, we download three files `train.csv`, `dev.csv`, and `test.csv` and save them to `/tmp/nvflare/dataset/nlp_ner`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "efd478cb-1565-4283-a4bb-87f15585932a", + "metadata": {}, + "outputs": [], + "source": [ + "%%sh\n", + "mkdir -p /tmp/nvflare/dataset/nlp_ner\n", + "wget --no-check-certificate 'https://docs.google.com/uc?export=download&id=1YWGBElsqj5ENsW0PtYwMlk_ShBt8MsLD' -O /tmp/nvflare/dataset/nlp_ner/dev.csv\n", + "wget --no-check-certificate 'https://docs.google.com/uc?export=download&id=12kXGQPW-do-F7T-TLGycl0DCw6eQIaZc' -O /tmp/nvflare/dataset/nlp_ner/test.csv\n", + "wget --no-check-certificate 'https://docs.google.com/uc?export=download&id=1fjsf0jFKWu_-bbx236oB6e7DqOqGmw3y' -O /tmp/nvflare/dataset/nlp_ner/train.csv" + ] + }, + { + "cell_type": "markdown", + "id": "6fd3f038-9b4c-416e-abeb-132625e7fefa", + "metadata": {}, + "source": [ + "## Data Preprocessing \n", + "We then use the preprocessed data to generate random splits for both 4-client and 2-client experiments. \n", + "Please modify the `DATASET_ROOT` below to point to folder containing the four downloaded csv-files." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "459c5dc6-c423-4d80-9577-add2d062c326", + "metadata": {}, + "outputs": [], + "source": [ + "! code/prepare_data.sh /tmp/nvflare/dataset/nlp_ner" + ] + }, + { + "cell_type": "markdown", + "id": "5f9186c7-1eb7-4eec-bfcd-ec64d12ceaf7", + "metadata": {}, + "source": [ + "The expected output is\n", + "```\n", + "4-client\n", + "(7594, 5) (2531, 5)\n", + "(5063, 5) (2531, 5)\n", + "(2532, 5) (2531, 5)\n", + "(2532, 5) (2532, 5)\n", + "(950, 5) (316, 5)\n", + "(634, 5) (316, 5)\n", + "(318, 5) (316, 5)\n", + "(318, 5) (318, 5)\n", + "```\n", + "The task here is to categorize each word in the text into three classes specified by the label. For example, the sentence \n", + "`Recent progress has resulted in part of the gene mutated in Duchenne and the milder Becker muscular dystrophies being cloned and has suggested that the gene itself extends over 1 , 000 to 2 , 000 kilobases ( kb ) .` into label vector `O O O O O O O O O O O B I I I I I I O O O O O O O O O O O O O O O O O O O O O O O`. `B` marks the beginning of an entity, `I` marks each entity word, and `O` represents other words.\n", + "Let's take a closer look at the word-label correspondence:\n", + "![data sample](./figs/sample.png)\n", + "As shown above, the task is to capture the keywords related to medical findings." + ] + }, + { + "cell_type": "markdown", + "id": "62476dd2-97e7-48ad-908f-6df02f48f86e", + "metadata": {}, + "source": [ + "## Run automated experiments\n", + "We run the federated training on 4 clients for BERT model using NVFlare Simulator via [JobAPI](https://nvflare.readthedocs.io/en/main/programming_guide/fed_job_api.html). To save time, we only run 5 rounds of fedrated training." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f48f1d5f-e656-4f71-b925-94035c60ace0", + "metadata": {}, + "outputs": [], + "source": [ + "%cd code\n", + "! python nlp_fl_job.py --model_name Bert\n", + "%cd .." + ] + }, + { + "cell_type": "markdown", + "id": "0683f68f-63bc-463a-bae2-f17d51fe735c", + "metadata": {}, + "source": [ + "## Results\n", + "### Validation curve on each site\n", + "In this example, each client computes their validation scores using their own\n", + "validation set. We recorded the loss, F1 score, precision, and recall. \n", + "The curves can be viewed with TensorBoard, each training for 50 epochs (50 FL rounds, 1 local epoch per round).\n", + "\n", + "For BERT model, the TensorBoard curves can be visualized:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "74a719a9-5dfb-4a8e-a540-08fb05492495", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext tensorboard\n", + "%tensorboard --logdir /tmp/nvflare/workspace/works/Bert/" + ] + }, + { + "cell_type": "markdown", + "id": "f1a42d7c-641f-45f3-92fc-367264cae669", + "metadata": {}, + "source": [ + "### Testing score\n", + "The testing score is computed for the global model over the testing set.\n", + "We provide a script for performing validation on testing data. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4c142aa0-4502-4108-9b4d-462050d37a64", + "metadata": {}, + "outputs": [], + "source": [ + "%cd code\n", + "! sh test_global_model.sh /tmp/nvflare/dataset/nlp_ner\n", + "%cd .." + ] + }, + { + "cell_type": "markdown", + "id": "1e32473e-2338-4176-b25d-ec7976f8440d", + "metadata": {}, + "source": [ + "The test results are:\n", + "```\n", + "BERT\n", + " precision recall f1-score support\n", + "\n", + " _ 0.83 0.92 0.87 1255\n", + "\n", + " micro avg 0.83 0.92 0.87 1255\n", + " macro avg 0.83 0.92 0.87 1255\n", + "weighted avg 0.83 0.92 0.87 1255\n", + "```\n", + "Note that training is not deterministic so the numbers can have some variations." + ] + }, + { + "cell_type": "markdown", + "id": "8076c89e-39b2-44f3-903c-fc9ff8446d67", + "metadata": {}, + "source": [ + "In this section, we showed how to train a BERT model with standard Pytorch training loop. Now let's move on to the next section [LLM Supervised Fine-Tuning (SFT)](../08.2_llm_sft/LLM_SFT.ipynb) where we will see how to utilize existing Trainer scripts via HuggingFace APIs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "26111bcd-ed0a-4298-9956-b1e821409197", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/figs/sample.png b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_fed_bert/figs/sample.png new file mode 100644 index 0000000000000000000000000000000000000000..eeef9338cb43064c3993c51b7f531532a336aaab GIT binary patch literal 24371 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 literal 0 HcmV?d00001 diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_llm_p_tuning/LLM_prompt_tuning.ipynb b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_llm_p_tuning/LLM_prompt_tuning.ipynb deleted file mode 100644 index 1b04a051ae..0000000000 --- a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.1_llm_p_tuning/LLM_prompt_tuning.ipynb +++ /dev/null @@ -1,33 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "f94cdcee-04ee-4a9e-8182-fe9eeb15671a", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "nvflare_example", - "language": "python", - "name": "nvflare_example" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.2" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_peft/LLM_PEFT.ipynb b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_peft/LLM_PEFT.ipynb deleted file mode 100644 index dcf490f775..0000000000 --- a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_peft/LLM_PEFT.ipynb +++ /dev/null @@ -1,33 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "78e7b1bd-f0c9-4329-9338-848df841a899", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "nvflare_example", - "language": "python", - "name": "nvflare_example" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.2" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/LLM_SFT.ipynb b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/LLM_SFT.ipynb new file mode 100644 index 0000000000..6158429c0e --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/LLM_SFT.ipynb @@ -0,0 +1,231 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "2da4b68c-e68b-4245-b5a6-fba66d3af819", + "metadata": {}, + "source": [ + "# LLM Supervised Fine-Tuning (SFT) via HuggingFace Trainer APIs\n", + "In this section, we illustrate how to use [NVIDIA FLARE](https://nvidia.github.io/NVFlare) for Large Language Models (LLMs) SFT task. Unlike the last section [Federated NLP with BERT Model](../08.1_fed_bert/federated_nlp_with_bert.ipynb) where we showed standard Pytorch training logic, it illustrates how to adapt a local training script with [HuggingFace](https://huggingface.co/) Trainer to NVFlare, which is widely used in LLM training.\n", + "\n", + "We show supervised fine-tuning (SFT) using the [SFT Trainer](https://huggingface.co/docs/trl/sft_trainer) from [HuggingFace](https://huggingface.co/), together with the [Llama-3.2-1B model](https://huggingface.co/meta-llama/Llama-3.2-1B) to showcase the functionality of federated SFT, allowing HuggingFace models to be trained and adapted to federated application with NVFlare. All other models from HuggingFace can be easily adapted following the same steps.\n", + "\n", + "We conducted these experiments on a single 48GB RTX 6000 Ada GPU. " + ] + }, + { + "cell_type": "markdown", + "id": "4b50353e-1ad9-419c-8712-187a49879978", + "metadata": {}, + "source": [ + "## Setup\n", + "To use Llama-3.2-1B model, please request access to the model here https://huggingface.co/meta-llama/Llama-3.2-1B and login with an access token using huggingface-cli. Git LFS is also necessary for downloads, please follow the steps in this [link](https://github.com/git-lfs/git-lfs/blob/main/INSTALLING.md).\n", + "\n", + "Install required packages for training" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aad7de64-ce02-45c6-8718-1bd6c08c91d4", + "metadata": {}, + "outputs": [], + "source": [ + "%pip install -r requirements.txt" + ] + }, + { + "cell_type": "markdown", + "id": "a814dedb-6d93-4782-a9b5-68644b901184", + "metadata": {}, + "source": [ + "## Data Preparation\n", + "We use one dataset to illustrate the SFT. We download and preprocess [databricks-dolly-15k](https://huggingface.co/datasets/databricks/databricks-dolly-15k)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c2924146-d635-4e4d-bdf6-50b87a035de6", + "metadata": {}, + "outputs": [], + "source": [ + "! git clone https://huggingface.co/datasets/databricks/databricks-dolly-15k /tmp/nvflare/dataset/llm/dolly" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "056d9797-3c18-4310-9f4e-26d345623ec9", + "metadata": {}, + "outputs": [], + "source": [ + "! python utils/preprocess_dolly.py --training_file /tmp/nvflare/dataset/llm/dolly/databricks-dolly-15k.jsonl --output_dir /tmp/nvflare/dataset/llm/dolly" + ] + }, + { + "cell_type": "markdown", + "id": "c77f5eff-f88c-42b9-aa61-604f68b8b5ec", + "metadata": {}, + "source": [ + "## Adaptation of Centralized Training Script to Federated\n", + "To illustrate the adaptation process, we use a single dataset with three training epochs. \n", + "### One-call training\n", + "Centralized trainings, as the baseline for comparison with other results, are done with the following command:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "32ac443b-25cf-4b45-8876-41ff91bd1ee0", + "metadata": {}, + "outputs": [], + "source": [ + "! python utils/hf_sft_peft.py --output_path /tmp/nvflare/workspace/llm/dolly_cen_sft --train_mode SFT" + ] + }, + { + "cell_type": "markdown", + "id": "35043ab0-fdbc-4cf2-b79d-d532724d9364", + "metadata": {}, + "source": [ + "### Adaptation Step 1: iterative training\n", + "To adapt the centralized training script to federated application, we first need to \"break\" the single call to `trainer.train()` into iterative calls, one for each round of training.\n", + "For this purpose, we provided `utils/hf_sft_peft_iter.py` as an example, which is a modified version of `utils/hf_sft_peft.py`.\n", + "Their differences are highlighted below:\n", + "\n", + "![diff](./figs/diff.png)\n", + "\n", + "Note that the `trainer.train()` call is replaced by a `for` loop, and the three training epochs becomes three rounds, one epoch per round. \n", + "\n", + "This setting (1 epoch per round) is for simplicity of this example. In practice, we can set the number of rounds and local epoch per round according to the needs: e.g. 2 rounds with 2 epochs per round will result in 4 training epochs in total.\n", + "\n", + "At the beginning of each round, we intentionally load a fixed model weights saved at the beginning, over-writing the previous round's saved model weights, then call `trainer.train(resume_from_checkpoint=True)` with `trainer.args.num_train_epochs` incremented by 1 so that previous logging results are not overwritten. \n", + "\n", + "The purpose of doing so is to tell if the intended weights are succesfully loaded at each round. Without using a fixed starting model, even if the model weights are not properly loaded, the training loss curve will still follow the one-call result, which is not what we want to see. \n", + "\n", + "If the intended model weights (serving as the starting point for each round, the \"global model\" for FL use case) is properly loaded, then we shall observe a \"zig-zag\" pattern in the training loss curve. This is because the model weights are reset to the same starting point at the beginning of each round, in contrast to the one-shot centralized training, where the model weights are updated continuously, and the training loss curve should follow an overall decreasing trend.\n", + "\n", + "To run iterative training, we use the following command:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "99d9a8ba-4540-43d5-9cec-e867acc4d300", + "metadata": {}, + "outputs": [], + "source": [ + "! python utils/hf_sft_peft_iter.py --output_path /tmp/nvflare/workspace/llm/dolly_cen_sft_iter --train_mode SFT" + ] + }, + { + "cell_type": "markdown", + "id": "0671750e-ea31-4310-974a-ecadb380ff49", + "metadata": {}, + "source": [ + "We can observe the SFT curves with tensorboard shown below. As expected, we can see the \"zig-zag\" pattern in the iterative training loss curve." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aa413644-630c-4c4b-be72-828202b2a29b", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext tensorboard\n", + "%tensorboard --logdir /tmp/nvflare/workspace/llm" + ] + }, + { + "cell_type": "markdown", + "id": "d7f97580-cc0e-4389-b28a-0b3b07de38a1", + "metadata": {}, + "source": [ + "### Adaptation Step 2: federated with NVFlare\n", + "Once we have the iterative training script ready with \"starting model\" loading capability, it can be easily adapted to a NVFlare trainer by using [Client API](../../hello-world/ml-to-fl/pt/README.md).\n", + "\n", + "The major code modifications are for receiving and returning the global model (replacing the constant one used by iterative training), as shown below:\n", + "\n", + "![diff](./figs/diff_fl_1.png)\n", + "![diff](./figs/diff_fl_2.png)" + ] + }, + { + "cell_type": "markdown", + "id": "7d3c82c6-d6d7-4773-8b00-132510c68adc", + "metadata": {}, + "source": [ + "### Federated Training Results\n", + "We run the federated training on a single client using NVFlare Simulator via [JobAPI](https://nvflare.readthedocs.io/en/main/programming_guide/fed_job_api.html)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5f5f3351-b1ca-4da3-8416-3e8ad1a75dc6", + "metadata": {}, + "outputs": [], + "source": [ + "! python sft_job.py --client_ids dolly --data_path /tmp/nvflare/dataset/llm/ --workspace_dir /tmp/nvflare/workspace/llm/dolly_fl_sft --job_dir /tmp/nvflare/workspace/jobs/llm_hf_sft --train_mode SFT " + ] + }, + { + "cell_type": "markdown", + "id": "e6eac7cf-f780-4426-b136-eee693b9b485", + "metadata": {}, + "source": [ + "The SFT curves are shown below. With some training randomness, the two SFT training loss curves (centralized v.s. federated) align with each other. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8b3ecf1f-2729-4183-9aa0-23b91645cab6", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext tensorboard\n", + "%tensorboard --logdir /tmp/nvflare/workspace/llm" + ] + }, + { + "cell_type": "markdown", + "id": "0181a317-07a7-4001-bdd3-894c40a1f293", + "metadata": {}, + "source": [ + "Now let's move on to the next section of [LLM Parameter-Efficient Fine-Tuning (PEFT)](../08.3_llm_peft/LLM_PEFT.ipynb)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "762d64f6-cc13-405b-aea5-90cce58a0171", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/figs/diff.png b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/figs/diff.png new file mode 100644 index 0000000000000000000000000000000000000000..e5d1b5f980e0cdb8227858aa4a1707cc81630bb5 GIT binary patch literal 233527 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 z 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%0AlUrer0kuf&i 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

c8IEIL71u6bJg!a)?jX%iuOo0+u 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 bhwi+-I&12!W$wWrAxU0Z2~Z+w;QK!SG>v1I literal 0 HcmV?d00001 diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/figs/diff_fl_1.png b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/figs/diff_fl_1.png new file mode 100644 index 0000000000000000000000000000000000000000..4de6721e17fa262a0995d282fa1575cc659999db GIT binary patch literal 48498 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 KvC41X{r?{)du=rU literal 0 HcmV?d00001 diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/figs/diff_fl_2.png b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/figs/diff_fl_2.png new file mode 100644 index 0000000000000000000000000000000000000000..45499b307c9abd9beca0bcbd2a44b331d658fd65 GIT binary patch literal 141173 zcmZs?1ymec6SkW`f(3Vn1PG8IA-E^F6Wp2LZozdRxVyVM6WkemaCdjN!EN9t=X~e> z>)v%|^;%=Qt9SRV?p?c{w}OAjiKC$qqr7_c3QbZ%MDf+Dw<51z!Qmhwy!grRz{5W2EUAqtnE#098TfE02$w^+u}PD1gPAPe;R$~dJ|mV@oyu~ zumYd&G1vDR?qa?*HyCgKXC@K{S>d0Lega~LKLd&uIzCE@X;6#&`@f%XB=j5NKL`0u z>A z(RhS$5HYU{?Vd_&1D>-d*>Y4I-;S}5h{bLHrVG?XqW=u0oEA31+(kwBI@%#2) zC63lP6?i6e<)duog_R5UB`*Tdje}-LW%5alomo|(N>nrpX_$;SAU@ab|Z^ zWXKQY9Dlx5=nilfKGhv{AFZHuQWP}52r06C8${xWdSQb85#<#H&0(8K|3JF38+#ZF zJfYyODY|_9Ys|hozrqBmFu9xR!iL}G0EXO%jM1U@9jggw2!>g8O$hEndxgZDWZ}xeNEVK81=dI-F=NY3UAXMcceIus zF7z4?{L-o?Sv7JUq=Io>k=zNv^gF$qdK>SQz-PM{xuLK?#r_?d)jxcNq>&}rsW^lz zn<6`8JKqos38Uz==~kwx z0W`#t5hTz*rh8(sYRO|w9rWp3v#)}f^Hd{^#^zqT%rIub6nOtz4r~JeAmW z_Y+i7e$U-|aK60_3`ga)hpSX}R!i81&mHYm391{}xbzY1U{DmO+uZBzZ$l6xiTXg&k% z&pECf;(d@uwSO~Ldut_7i*DS+=+aVAg#sp)J=(j|W+OlWyT@WA+UO6B(Np*B54CN9 zhAyo(t9R8VPs^&4worR5zw~`;xIT7+3C}S9z7?8bw)9wE0PP#4x3&>)D&+)`@n?qa z_Qx@J20AC$7+Ur!W9MbG{m)y7=tHI)keJwQ#3Md(f7cUNV1#EqqRZb_P9Z}LF>l0N zdb_Bhh9K$N%68jfxze0;QgG-#`ZRS}2X(H|CT}ml4u=VUM$svIc0#mgO2a)j`FFzU z%%;6GaVZb`%`b;gkoui5KZ`Ber#PBM__fEhN<%D$!IC!}b-QV3=O+>t;#W&s)&pHE zClX>@vfjaT&3L}loFl3~iYv-!tZ%u#>Z5sw4O$Js^=D0D}YZxr# zXhc?tJhD^PQs0D9pKG^YSs(Jr1`Za3+Hgo5By|#~W0N&P7EYxavG%AZK`DtHV?)48 zJVA(Enp-@*wUgX86eJu{Y9uGspaM0ImBh%K z4I`w8Q{tRJS$!EGNtGcLJw3i;4Do|J%E-4eP%jO)q;1j{;&4K=0HnW91`IW@E!4jh z!1XfP3WXrhPY)ICTIIs>1JIzD&H*|oo=V8d$+|V_S1|##9(Zq3O+ZFA8enC)dT_W` z3ZE^$$5ysHbid7Rx(--+H@HfH5-&YdX(jz;Q~E~f>6lm*+?Fy80vQiOMA;ll;985H zbZRM%5wm8Zo}WHCsa=jum$Co2>_RQjB%qn&1vZbS*GU+bkg3hUG3dyOqbBAJE0b{O;#dCQxB_+Snv1G%{uSRAPuN zR=a>P9n-g_#|WnQ+J4sfPOaf~>dVzT=chkSbHKh!5@*~^ z^VvTh;~%watuN5vYkA7rrf_8}35yn`uQ1=L5)w|Q(M zQUn~WWue5gZKAOa75%_NQ>NqqKKNdRPalPo40us*4bd#%Zf`DXWP?ZP3&TFxc^hO3 z;xzdvio5zmcXO-eePi4f#gEz1M1QPCKqn$@OL?`EmtlU9!2|>fK{txy|DbdFM~Z%p z<#H!QZi)adI@QI`|8Y1tmSipgsvXNeylz?yRjZ95bc$?|Lvew+F?Kr@KMMTgOPCK5 zK_$3Pm*}gOFQE&efR_<#$)adFZWkn!VJOn@`NyNO8v7byLu7uQE&&sOn3YMp?>6Eq zwK;oyty5Y=0Vce%$_&mTbGGc5rsG>CcRuJbt*NWFvbzD{Ow!VLi4>g*sf?wRb`ev_ zp@D=G8~-Pf!fZp@{9nYvu~zGwp<@D@7Cb=h$vc>`>eQx$RTL51U_god}nCvWD3HOyn|Ody zLY!4mf^YPVp>4TTJ^rwZyPV~9zN(Uj0qx3s6~NL_1>TMxT28Ku^N9Tlg0#Vd^xp8n zsrG68fPkqj=zu_|;o2f3YjRfYi$GznS+BeIz&VfpLl0u)&Fz(AQKaDaJ>JWE;!h5= z29vp!65oVA!5f6om8kZyasDw-^Y-eB{SDygLqLs*nCVi|P(~Yb+)_oB*THxqctoUh zY%yHPb!%eAM5AsFVAK$gX!?1_!`A3C8agz`0t;*S2oagD-~K?vnP2}ovyg{q{4 z{RuKxWggdsC2Vw$@k>0uT`AT!dGo&|$CX9Q#e>|wM!?x~ z)gP`X_cGV6lzs00&^@{zK&Oh;1JL#NZq>s~rx+F`qS<|;agM@cnTh9MJ?MN?=v1te zOFidrzXweTw0&m%veq|`8Y*Yhd&<1^XulJ!N6}tp&$P9K^FcBIE+zC+B9_Uc0C|>v z&Ch5H`gx_ukbpu5GC=>Q*jv>)~EcmU5!GG5}X zppDQefz2+z{vY!FC+2V^ktN52>MCgxs>TMe&K`maFTGI;F@gtq(4+q>xo0fk)9>*& z2U0~v^)X3vE|ZBeNvPIRHw{Rp0-x#`jbt^5HIi8*!X?SwTX3pRa;iQ?yb1KHFChj| zy$)PLgHie9;VSJ>$P`SzYD#VK=<5qZ>m{i0&=FR_#m&zM`NUiqp*}TpY(d!WM-@1B zHfX_jb!p*ITl!kE8i#@+4SR~M`b)N%X68;#)q*$c`#Kf(L$)c-pHYsFA-{72kiNjCD=k7%ej#y)O2k+FXter`dg*QncCKv!f;QcL* zpKnx!Sy2kUjPZfn*|TF*&QW)X$`;=ArV{QFUJq^qLw65hN0yRS5@aR^0nXezr4xG~ z+-bwj)`{*-=11oDwXQ{qlSe;d3d#%cg ze7&rv#^V1NTZ4;Zha+w~2knwPw&`?r!PgKMi$Q&xpXHrS^4LwD(Uasqk4l0N4^H?o zd+YBL1Ne4=kGESB9mZ{d>l@R83o7mg!KYx@q`!7I<`IU>L8ntCc=RzPXL!s@*}p;5 zHbrNRa>R|kZ1^dJ5x&MFDLLE6io>{d##dCw8(xcre05q^uj&eDHaMbF%i$ z)5s-q7vo6w#g$Tvo4{Px@)_Hmg-+j6&JaK5ydAS>b&(B6rsfW~jD@gRcl_LtQOF|| zjIc8aH&{x}m5@&OPy|#P#GtZ#_Op5ZX8ro^t=RqOpz+auGS{It8)2C`$&E=w5zJ9= zMUzZ=$!ml%EbD1~1EbJlQyQXnC_HO)L>2h;(N6(&aIAPvJ(#oe`kgH8k4;e}_8}r+ zqDIP8JztsU?hQ3T0VCp=E=RPMJwI7YFS4ayQpA%mPxHt z1NjsTM~2Sbk&K%=KFmPutGO}X!1Y&c-B(1VlR{a&z6&mG-@enwYr??-lbW+VzGHr< z9c9LRbUo|~rx;AG#BMu2oQr1*sLs9Fl$Xp3DY@rTys3?wO6$Mme#@YF{vpz{!XQH0 zpgS%(%yP+FuDl|T!aV1W8{20|sD=y=L*gmm znPQL~*>}3X-44QHVY``(2A}-Zgew#H{OMWi16DkjpB*j5Xp%benWD;eFH5J-R(}T# zB-UuUxZ;;uO$r+s}|e5J$jdM z5Bkl-o&4DRE;;%`pv@}Nv|!A=e*|;Yy+3x-zxQm(=i*?Z&i0T|*XwAhxFRpZVIH4; z*_*__k^bG^)j!d2Pg4)c-1Osj1G3DcINPwK_Af)6yN-kVJ#a}S0H(dVt7f7 zr}W^QnH_=Lnz&O?F(Gxt1#jv&r7|CsOt`|!$Sajj0IVUpi1Ikrz~kQbfI?<;gnVP8>60Jkek7T zGXPy`n~)<*$*p2X-MGEEoi_!BQCTygfT}1=Dyn+dP`rx26KB{T7*53#O0DQsQeZ?o zk;%8tZ*h~T?5btwUNzvI9xa=0{AM8QaM!G$_T##PqsLpEg`L;%T34V-s84!y4dC{+ zg-)1lFr-YR3?@2K>dp#zBN_%Zbl5uQ< z=pC@Ij%V3(V18`{IaLAOehu(sz2CEEa;u8_y7pdk=$CcK2d{Ydf*R4RbU7(khSu`x z+`TOAtg^e6gz6_~9$mlc>QK1_tz7HLXGgr2ihdoLhZT!Izc`(dMw4Qd(T?zZAouL$ zyobiZHm>77SEp#)x8#*n8am(9E76;_6=WQk!};f-I{i&=xUsDpV&>WnPsOHgz5nr5 zv7x*@WjsOU9YJshZ9z`?l8A;7zOa(^(QR3P-yKnRD&NuF2gK2r{0*7m+~Ljkm+e7s z-P0TwD{jYWT@N&rxlM^w7Qb$;V%L7_eEn|;--U##7J?`QgQnt;+DK8Iy_UN&M7dqW zO;`ff!G5Yz!Xr@=nUnI|I2 zNyAyn#Vl>ITL=hI{OsuT6W5^6MvDYj)FY!=IHQSZ<8A8@`8ke*f!m4JIFH8gfUzJ{}70uC;Ue1J{9my7gW#+6oovFv;ICU}j~+_g=(Ei)=f`Iz4r zKkxq_Z4O1}(MYX>Y;Zq;7bU#RHl<$W`gU9dt}TsXz*_5oV`l2r$=blh!(udN?nOcS zbv|w!@qH!^o0=#)ZDJ?*Wu-PsB&_}X)vi)ywQ1~B!c{GJT)(l)mlZS&PTHwdLW~3o zU&VcLYxjW&kAB=shiX9{6WvU9(WFmtP;_-Xf}HI5{Q?fF2)zGST@{Fc!>ON1kwJPi zS9?dCl31;zx6>tS_GgEK8LPrCoDD$J69SbzUBKCMA_*aJ|5J7eMJB|reEOjJTmHLx zQrWc0IhUoSgNBeE5#Lec9&9imGb6ue`^-O>+rA^2LTkageQtZq) z7q1G)aZ{C%n)6tm_~d@3#pM8%dW3YiJ^$`k)JA@<@jmY|H}~EJPlVJKxJr|91DU)5QikMqwSBVkl%D9L}i6EhF%erx&3zavjPA7VudW8L;uc zDKR?STB}47fCcB7%Et!v%Rwp&2`3ejsWnLEV2;QML`2kLDWq|Rr~$9gXmqCt-&};SrDz zKDwtprNyT40yQqIQfBnqM#dXesn|%_1Bk5!epZ0#4KAM8Cv>9W&E7+npZ075*;*X> zQ66l{x00l$LggifHz_2(6sWi%Yw`jr+whQXb@ig{B~ z@>=oiyAak4`15ukejBiJ761q5qK)EK69%$w3rQs*~DkO);H|kp$NEPDjm?=gm^^-B>Q9HpjSW6E#Ni8v-8_LG$s^>~+pYS$AP~)zcywDp6w7Xy$RG zR{-w76#oOveYu#k`&HHneZd@#yjuXCr!z~B&zp%rb*hzTjCmpDn%@>mSpF8$#h+$_ zN62OF+k5>YEIaVG3(Y;>iyjWzPA3Ot@&$PXBTy?J)WzpqO21#3`Q}Q_Ri&?RAyrgQ z6SK4$rmNaPu7(TUkvdPrS5VEtVzvHKTwH)OfY`6!x#px$RT8v;mLIq4&SHX2&7WHt zf$&a2LEzIYu3}A{Ux;FO5mxc#p-qTouWHf-Rchq|yz&bfG1cL&aAOTtLWo&12H7C{ zDr7rZu!tUB|EOvt=4TI{qoe^nOSPbNsG#)%R z99<8#jE54${irEOiV3HQo1K>+C){V~z`VjB05-%lbG2QDNL|OJN)Cjejr5{VDYcDD zPxXo9lq=p|zbmYY*+6+`*NbxQwIJ&nEH~7!_R9*n-LSW4N};kgtmIet9!qieOrlbY z4vn!3gR$`5p%$njCvWVVkP|6BGO8VZ99grSq#QOj)o{@D$`kT+U}TX3vdF2uTkV^Q zPg1`;yJbjas;Z}#tF`{R3wEmm#{Zx$zCOly-?81>IgNwuAieQnp~9D6E2WF(}mVI zOkB2x*&FT+pKq@2kz2J!BL&Mnke$=H66gs8hs{~~+|gtwZ#2aelJZr@NbC#)~V&u)*MfuFE$Owqo<*_@%UO?8%a`!Ju+(-M{SDK!xPX*#_y{%jjI zYX@7+G?ArUSiV`&=#Bu#qNS%NO%*@gTx{p}6H+5f#za{bo|(%wtRkOHP7~#6-_syY zLS$D1cVqar0hUL_!2nHqfS?Bz=kg`C1xCPOMf#uPNj=!32W>N z=yQRp=OfN{pdFmuSx;h5dy+k^n&SDzp@z+=&j<7J zy&)u~tk+^)jlr0HjqIy;x_VKHIH~6JFXj3@=K9_^2tnOz*%o^jFv$2WLjUoP@Rwk^N(+}xiB(frem zW;oD@QQcB<#eB$C<`JYMPJ?40uYXB|QJGV>?b2|K4Dhl*V|a$BZ$#d59(uBk{0P@O zQE@(NCfX#5&gk8nq*&WcNm#|07FgTdyx54MLGCxC)w}1nG~*8~s(j=IHYUHFgs^^K z3_ser-;Wb?=2MmIbn9K5DsK&E>0Psw9UKy;o6DqEdpLR?gAR(l!!N}v?P(Yd?E}Nu znNm~a!j>g>mPl@^YOEFmPBPCnLA!g(EiNeNsc4YXm0W9>U@|FL+A)Fib(=@Z0NDR2x75WK%4>GorN z>WK;IOQd$MZyBN}#CShGABlBQvEFN6)z@HdcY(U?Sr4GTddC=ZNOgIOr1D$=LCjKP z++4pXjE*K1*9Pm6I*#W)DlT6?9q3-sy`x2?q<^0t{J{S7fgW6o;MI6%t7yCeL7*7k ze7=2dPQBQb)syM{@iF5Hpjy)gPVj_)NBPn^g6+7J#O;^ZZK_+RVYL^tYI(<{QPoE# z2a$ZijnHxvVzFO9bRbYE6udTbH-8ygCpMJ{e*0HO@UdXsGE;kof2yfG4?U>Mm<>BN zMh4s3yj2#Kuauf7-DdPES84c&pSk+MEG1%m>{`a$Kup;tEDILnDylSglQR3S>WC7_ zQz?n&g6AY1r8?uzhkddBV!eN{asQMVQ4n*VNucHahR*~Xo!>l2zkG&@lb}7|A9?!i z?U=Od#ZKy+(-IPTZg&Lf~h}#oUqwoEHjOtT6Tt&-mM+jyNo!{gr4gtmwARE z4?YcvM((g>3K~A@O`p%#nn1k|WmZ-+MW^fPxpFsF(>ymh8&Ux{KkcpI>Y0~?jYg!0 z7Tj3X_}a=VVO_QI)Z6v8&mNpWH=dBgjHUpVgo4aZM@0i0D>k!#7@d)UWzDIA{4Lqg z$MpqBr{rkjI@E>Eech0OEDZlf2Q>d z#_DxQg0gtub!g3V^M|2ZB#q~jAW(wnGgo4u#mHK%V-GF}7(e;>bhAZ^puQ1o-5l^=>txLaD-tX*V6(0ZrKs>2>1B`95p^nkI2)E$=Wgvu_L09fKL7qB+~T;mgdXm_0J5- z^La>IJh&*>KAD{PwNo!Sd-ProUx@Oz`Jo9sga`o}0r+7`i2VBRw?t*S=by=6g{hY# z9K4&U(!b(xa-ZP7=?A$&H&A)h^QWQnR!){xSk21;%jEu+)dufepkfYBV?&cRCjBY3 zWgb@_5FqK@K(#WX?P;d${JTERjVJ?sk4 z51#}joS%nu5(R^g7ib%eWey|T#Cply8Q^8SU6EKLWN4i)oZv)e1geVd@HHOft;z=l z3o1PxOm}4EKZ1(As_1WjyT=&AVRBPT%r{86dz56K^Nu9V3LbN;8!8h{9F6>q&)Jn7zUIgfvK{=KJ6IAKID0LEXJa@{FnDL2n;I8w);;*j z%gKhZJDtOCNFBM=3!`-AB)cvUhna_rUsXoISa82Pg#xy_nNjZYKDoOFYVY|}g zm4Og*&f(*|PiCR6^4$3Ns+z0`G?#oteXM;cmL%y0QIO=4`;usE_FW&u2gGAsWL^w)o2IbkTwkDcpvdn{Gs+E_h`Q+lte z^&ak`n<#9Wa18?@tpac#ztPbA+JPUfKAxd%SZ`^bPgX+^mft?%)ZIEiI#Gus*m+!6 zPs_{WR&WwGh&zywk*00+HDh~!Nb%*T<@9f{TrB=IaUWPFRviJJXgmwC)`$%ePwZP^ zEwEUp>DSt!>o3G(j^)q`xrXcDyvJA4putYBR;hL;6p*ffF(~<4)zbNEwHPBdSu1W} z&~G$^$58c47?tHLH*aD{IzmRqT^L0JWD9)-?I*94y?(at%_OoHK# z?$7X_oNf>;D_(2BWtP6N>Ug@$@T(bp!oWO>znBr7_n0q%l}!rQ08($=xaob~-tfte zq|4h6_B_Am1d|4&4UsYPcjf0c?>-SpK3t`J+02C;g0hRSdsR2PzHZUUkxksQV?y(!W>s;)eH?Kg=Trc zeQv8a^{*OFm)ij+E{U@2KcX^c`&l2wlnF>waX%PEZ2Kvblvr?<&RXd~*5?f+jE>5J z-dm`WirvcM=OHHcO7|U`1euTVxLcVzw}@lsH2E=)dgFtzd9}su``NaLzCpKT1e^wK~y3OiSwyW(JR1PK(631bF{l zHj^qgoh5Yp^~ZGHF5|;_+f--m`F7q z9`A192^Xlto1C69j5-Y!%}lTMl@xHPV;fU5l#SX=$9}{mT(!0>o!&X9p+JVoBKTu& zh3a0SgVZFaRfL4c@gA_r@J>ZDa4Qed7<2NGghz^U?A8V0hYFqs2irA3zFuDbQ2#OJ zwZ_bEif>vlufus0ejYFQ!A7s4alVvUj#eZ7J5dy>bee^?ZdZs)XS<_>x1I7WKuc@{ zIEGdy)Ln0c9^T$DGTOLP7B!SBL~RWcCa7s;}2)6Lq>^AJ=w$G4f}nM>g`H>j;`0VvD;bC;$J*ilgf z?MWX(O7Omt5~nEkq|gXfqkmIUU?}D%jx6z6>OfUc!GOixkxK@dQ_izn_VdKNK`4wc ztM}W0kFAY`phs|G1^O9bG&OEFuO)uym0=y_`>FrqL2~&u5+Th!t<(c_Op$DsgwuBB zxjf5E`AM%m^c7*DZb#{9hFn$IpZ!b%`^gBZWSfP~(bUl5y6~MF44qLk1It&Y`JY|( z?+b4Y&9KOGW5YH_*FnzdLtos#{4r9?=w&=@^o2$?`k4su27Z@a#VHnTaUG!0U@?Bp zlImrw_MN>z(!_4r_g!BI^0VAGjk`AlfYwk2s#eRDgmx^5X1`7g(P~R+sJA-J2~o(i0uM@Uj-<2v?(ic7PkqCy(=}2D^kuL$M^!cYH=n$A zmYFYzc+9y9f)>QZp$}(ntuFAydnf`R0?=q=6@`*1wL7W;9&bOr{pOV$^lekicf%ik zu+I>lpy>;Ba0RKe!D9(JAp)zp-$>Ps0QGG7M*o9feWG$*jgEZeY6VlW4Yg>`&(a|y z@jI_N&<<^uIbGYUPdpjFZEjs$jRwg}%I{YR#%{pR;Tojefx9#^L*v1{Ga|2B+D_2EQu2}O0y(8rNS`B?l>6t?Qvo*~*uYTO4^T-eKih~pt zZ(cWnoeu8Jahp8DQ3ZdYj1LNIFM9GZluX{33`YXYh$ve9Ut}Sgn*=&d zxUX)k<(^Z)$HFT-)azV_OajYSZ!`G>VvC}8p<5BmxB9aL9@jI))b$IYbBUm@Cr^Q{ zZu6fXFZP!CoM+YI%wAnFz;-=72!I05i+_UTz3zE)X9PRCnDIHD%2`;hIxzlPXn7vP za_^zYQ`F}51GhY|F*CymSVI6?(TOO*x6Ikh;RY0~k%Hl?-4Supw>PY`IjxUG7BFvJ zh@NU6m!|ts7?OAFmVcWUh(}lLeACKi8XJ@{FgZIF#eKd-*rPe@&*2a=)-ODS6 z)?;gdNS;9PS&w%2Rh_}JZH%+bM5p@jQEch+F}8_7>dV_!@xPf%bdGfSASpRH_dQ3Z zq6bdahs9uW2W;jya@z;1CU0+CSi{yfnkzo+Oy^1lSLr)OtQRa7Li^C{iA`ceSWZu7 z8#u1<=#1$T=g^!zx(q7a=331iQQbY9_X)-& zo5121BX8pcQA0is70(ZOL|gW`VS*TbmdZ{*}?cm@I(jXyoFwr@Rz~8aG8NB3#2KSz`&mY-Yv{0RzNGrLF9N3nmXD>Vn13Le@}VnshR)eUAI*YupSx|f zQ3~?XgWj7TX=sGmN>CF0QD>Y>d8#IK^GI;D)vxmVe()1xT-7UI|=MPjX?U znvd6AhAYdEDrxBF$r=X8E-yIn$zrqrnRR^CdMgCciti#QywHX8&zJas^6ta^)S|tcyN6ye_Y$ua_F%=*aNusY9}s49lw||R7N`GgW3uo_+qY=p zk=CcAm9aY|z#`y)3#JgAJd5{$9KRbendTn6J4w;gkt4pjzCJvL`sZ8kcFa$zKkcTs z*gKszgX;?7JqHCx!C|!LRksg^6n0mB881pl<(U`B&aA=q4PGsIe7LB!Sb^wzka^=m zq76uVTyy0-T&{k-rv7pke->YgkA@#7j%j@~7J5KDF6^+drO)er^s0|T{--bS>OF<9 z`w5LMzL((|6g^?6l-rA`O=tH!k%4qPm`oEcbGhFP17jzedBnstRZzEs=AvF1mRwJs zve1Dl@6VsfKM16F%xKY!qDGoh&Pn&+v{@Bw95%C#(E$Tkyf+$_|BiDw2TmKHQ1vEs4#FAd$gOvPg1QG5`X0klACw z4-6M1nZ}UB%ru;imx7zWi;2tm(}nQB)t;1rW^@O_hpEL)-nrmwJRugjrgT7vjo_Hx zWm&uYqvs`*Pd88R4-M;m92_cGoK&CR*e1)I z2YW(SdZ+97eTgam&C$WP>@c7a%vX~Nd{Ih~kmG{3uaTL6dsdteE|`8}?+63zhB1sMp-HNJ7jmlB#Qx;@72R-4UH#&kbF1m`R%+&KI|n;JhLqgHHY;1um-XkSiC`rp zcm1>WNX&L^`E?FO-<}=H=heID90as-jzsy2wiI67KAY-107wfmxceM$0RqVM+nh)J z11Y&&fvp@mC5mOGuTkx^+O;W;i4za3YB}%0rt}`WZC#4!0a!f$scc}M_s-n}8lC7ZP4gmzrm zcL}sBhh+9pSOW$xru}{I&HvDrN$6B-m7Q>CEdP=^>-X;fec?VnfA~=#NbR9IQuHi z9_F4eu!vN;hIQbh)e-*`Ntz*Pb(CPHz@KAF0sx~?qd%_eC%aJQe-LhVxIlB@zmXE}4}~Bor)N6Xc;-!GC68NWustj7%kw|~%=gu{nFCjIc2jyc z?YKTUj36GF3F=M%qMn6$z_WH5S^j%n*Sb;vt7{gTbQoJ?3ZAWL1wTA7-eBVSwe@~^ z($84&fc_QAyf~#F6Q=urq!_EDBv-n|6-nWNAIpgTcIxTmP@4{$_X;pQ%s(Hq$56)p>)|igpZqt~BzdVab7^Y2Txh!#8N zUvr**Ip%%G*cs-{VWtm$MJ`D(|0q0u+ZJK{KXutc)9qs~7&z6VC?b2gS&&6oIY_zA zwbKXvUu)S~{{d+sifl8=dUxl-{c_eE?A~iuksk8@9jEjC$KQTMavdQatn3uo>Bbr|HqztFNm6L zKiCiP+eTPptreD5tE)eA-Hnn;gV2=zi8n?6KlRk6QlHKLf9k3C$$uqO=*O0BlW6p3 zjD%?j2PN_d$f_0d5Ta%$3{fQv{d*=zfh}id((?oP7!8UnWfv8 zrAi6|l_d5KQfxLDnLAxtka63xM=GrE>pxO~KVS#WmyEt);QRP82Y(%_2-kV-xUjJQ zv;egNi3hg~JLKfyicPWG78kZd#BxA*%K1LNUwpagvXWBB#ep%|m}zJWOcV=`KWNCJ zeu8K&LYV#=@6K$R?iknbNE;ZO>qMc15`MI8zg(RuSqnqf2CF7fnq{|nLS+(lCwO$0 z9(dwX7UOpnR$r{;PGS&-9Yd{`e24Uw_?!3kEE=udQ|(gK8eK1jG>z3zGG9|kTO)4m z?K|uAOj)q$XN8gPq;}&tRx6g7yn*xi zM!AQ1q~V$Lg)cNO0YmM|t>oayaEE&1OKR|_1l9kc>n(%gY`U#sNFan@!9sAi;O>y% z?(XjHJ_L7no8a!Q1Hs+hg1Zksz)SA?dCoaiUwuEOYV_*vtM}~Qz4uzHlekO0kv0yA zIr7Qa+PPI_Wfqv4IECwp1?zJxYfN;BK7ags3Hy5VZsKoFk9w3!0)S6#gyIA9By82@ zNDq`gGAXzSOUiWtQnJtKiD7yPz826Cv+M}g{=!E|Kt&C`n7h-GUX)JYhUHO7RaMc^ z3ht0iqGXY6-S0qbfg0;=QPZrNxThB|cOQ;liriUe_?_a*u>YEi@)t#CGlA37~<=D58_AzN`12W8UJzv+o<}fa% zgVI4u7fm#+d6I^uwFmq64!i$CUsM@C%WW-QICy*gnGThsX~Yy8SD&Lv7g{|8YE$c;E{igc;J2n>gtDzlrs1Ya%d|>}I zZe#9rhYyxi~%sLR7nws zgC(&QyLjZof(JN~O(>?W<-Qk#|0W1QZJyaKE-XQpGeMi?4^H3t_;ELj6P6M{N^T3s zSUj`qJ#ZpMQ1kD7{0IX_fPmfMv{+-#=EQC(p6u?4bRI4Bp*n%X5r!)rIbCw@c8pd> zReUQ@H{Y$R05Ff3aSgdI^&1qT{RNLu(;}V`N67Q6DQPQUY?Xd zs>((i&=T};r=F05yIUGYis<=FUDz6s8A+iywJrDx%nv^E3Ov5^%Ta3c#?Lse`^w18 zl)e(VLPhd@_v($es~E6RMQq|~(iONvHQvGV70>S$TU9}9$*wO^gGFL6%EEEr{9GRd-|;s5%CNe%gqa!uvPStmv%X8hvyQ(B#xXc)q4AN}R$~8pbqC_E^LT#F9BjOZ1#6wBBge+Ap}|JNw`amEzwY_mYsS7m7W67 zLy~KDn7xs*wojoEh2+Xl7d&{J);V=P za{OK!&t4<2WHB{v%6-#j6*ukV(4Uq*@iXoSj$}nZ`~l^-xSET}qf}l=yO(;-eVEQz zdX$u_l1?N28$s6!!{R!N>DfAY5R5w^*=3q147m}E&!7JPtz?;WMLIsjj{Xo+D z-cxk&%j*6)=QZj5y^wBr6lI||3$E+J=Jmq`+)8Up2~s^2w206poWfKI!(+O}_3+&r z-Y6RFV*Bk2uxyi<5Pm{_z#qTi4f?&#Nd6C%(1BJ%Xqxg|^^{ zg|ydA+Q!+ESuEfFCnuU)PT?^hY!;HPg({!kU>wZ3ws@?n^e)(m3ac-~rE255c11Cp zy&Ir0RaEY8f8&B5A-}9}O27EVmK^7RTw)CkCFIvIl4t*@VH^#+RgYPbGOZ(0p#PAA zKSn6KEc>Lg;-*kK5Qj0tRN2u_cTI`4QS+x@OnXi-hMHt@Hy$#t+Sa z)m`SBO`Y#ssY*Yb$hJP*kkRxAZe4gkW-n)q(tn8;z0H|$=pUD#gjao>M8{t&n0yUy zi<~wTAB_@vS8qw!{gtQDl^01e359Lk_G?;!+QY}*Mi1(c!W(N$ zvLvTdeG$`_ft4lvuC!%DpR$cPi?5pz9@_P_H3uF?(qTDZEe5h}gwhP4RS2*&|myLTrkk^dS;V!__vq z4VYmWvE$5f8=-j3wmCBA@xiM4kbGOfHi|Sp` zR;{k`fMLWWO_pEzdKgmHy1RBZ5`0hQv{@NlVjzlW`@9eA$FKId+iei!D(a|iv-R!- ztR~KlS-l@q!S6mDxhncF;&uERso9w8s(ORME20nzFufZ!ey0gU!gnZsWi8Lfv|6~h z`35^gAf^iB-{B!A%7zdx(dxA%SItwW8fJmNP6yEA*9(ZvIg^1^krmhk0}p<*$x9?$ z*d8zB_25Sp z3~RK2dFGeMw#ateN%^TUe)#^H^1inmqdLb6c#ux^z>vU52PXb}DIT zO_jG~AUJ&^Zy%oH`@P~Shpa_h27%DSl*K5%px<2e&6g*|_!*P7WKk`;#%&K}&W6xy z((!bzN3-T)VGp4Z8M%rp%-ZR{`l0A`MUPPDSgSC$8CHKL`%|YM5hWWZdKy8chw&I> z4`ZJN^c#z=SPGWF2NV!m$=aenr3oqYK*5x(Dh5@k>BVmeeW>9a9gD_gtkn}nyfOO) z=(#kZ75HobL4ff$w|J#&S?WUvX7{6u!h+979?$QD-aUP<1OVt3nk1_kA79z4+twnH zKWIM-_;x%3WY)d>K^&%pJ?L7AAFuBA&Vp1<=Zk0`5lCfy8eL`!o)LA|uRiSK-*Lho z|32rdi-U*Ko@h$)(j+m!9JnPbU{7*DvuM_UE|7! z*y-`@B&2yKv~Wfe`GV&Vzq+@>_C+pLLm%#xp_@$Gdx{#*!Gl@|7Y5i3P8i!(;2C|k{cwYV?yU-aCcDg$<0>XdA@1NcI#B*{(n?ONUJ z)MX{aO`Rf6-3$gY>Hmtx;S-pQ^0R&B>Tq+z<;^ml2N?Xl_ zU;t&`bc*)ol%GEKaxX{u+h9rb;~53DO8SQ*nS~>j<$1J`&fDn&I+jB=2ppN;B7wzt zn?HA32+QKp5xNPdO%vbwg`Urd)enWDoY-P8=HK6!b|=r}15_X4wv}pF7poNWDh>H) z76PN=<4%wjgA33|MTqqwUl zMo}z@(8<4|Wk&FNyR1P!#;zI|UR64@EBiy>MITS2=!S%?PzF}S0CdYV3!J)V!O;u8 z3aM-0c!7QQV>a|@@Slvr=a11)7?qKDjRuI zs^P;4^|Fw}c+KC!3bvNdV_3_~$Ks5|(l?uB4F+~q*?u8(EGcysGo5^?^2Y(@@zIrm z#5aFvYW0)%E5=I+$TCrb@Iw`)l-I;#ew~K*KJyvonSpm&k@qLAm6{^#j$( z0{hg#ggZn8PfzqJ!~*b-w?jqE{g3^ML6v@S{Fbg|n`T!lZh1(1I{8sSheVztrv#0S z=Jun>kW#3NsJPU$gY!%Y4ZDH{OuWv(ypGizefLyEYgy!QAcp6Fzoxcm5=+oJDoKgv zRP}B{R#L34PK;bh&r_cxj$C!(XukAUaogZPU6q&#(*avM9)J-S8^8CWxTP6xZS9N@ z8~o1BNk-E~W}LrO4?DYWGp-Jt(gN>?Xa|xE^ue!Z`SuTo5Pmaw8S*cqzq5`oZXhk-ES^r8nXj=1D z;eY2KtIf8e&QpgxcSK~N?lzt9HYZyHYIdab{EBpFOgafQV+@9KUO3|H)0B6lG+de* zLeU&b{`gKKv_AqKNtgBDMa1@&V(mEhuHeP&>Nd<#mXCVB6+ff@&;<--T_5dV$B_hl=|L_0e z@ASAq&ZX*L{PQp*7Vd5}lKyf2@4CAS+H>O*cY1~BM2k$N(txMgPy=_-c8y3E3uA0B z7)Y_z>Exm}i%u@P(XIFx%3tuDHBiJpXol2$s|FV*Z^|zKjC1}xcp&xd*@6kPLYu$r z>gOndEctNTb-psPmna?SEU?x;S~kAQ2XYq^t~rSyCcL%m3}fhu`A@ozi1VGiGN1E< zV8opeYVKx+yOwY$LH*xE(+{V2D(;dpWKmJ)iLx!EDHkHrVYlPh5=AOXS3?xTDm0gT zZSfhT$;W>;_zLyu1|l%Q{~6R~Nbb>~;&GIK%v;jstv)doB&1BlNv5u|BjbW^NgXX{ z93M?6J)QFE8^`yYBTI^EZ0g>SoFD7y~o&5Z(31 zkm7$E`1$8Rh*S7)F6{4*pfD<2V2ZzN277$|AKUlOL*M-Te@57$0#jVd_6kZ8QWg&V z^k2s$82Hw&f6|tJK7OBZ?)j+T6tSWu9QZ%Ym<#`(W@g#(bH@aX;{W&a??iaH@49{S z6jkc|$&aNj2`v?1p8TqT--$ROQ#Y~KbtW*yg<&lJ?J10Zaw?MATkH=(7CTXAWrQ1(2)96^P~(*pCi= zz|ZOad1#HvqR;zo8jiNgwllRF%pG++F**P`@zH(hglQ0IP#h&1?>HlCZBS2i=DjI@ zIUrZv(;gNfjVC!4t1^9nW4P9F5ceGNK*ZAJ${roxBH@TXj~(2I_HAHR^?v0WIT@It z6Tr_f7~qXHKY2W4<1fV@a4EW7WckI@=EH-IZwYceablEjyx5Iw>CNANm{n)4?2O z^PH`8=Zp0t&-aRz?kb>f?*c-b<5<47bU2)w%r_%?U_X-t61k*!Ej;@7EZio66p4kP zt8tQu*S4m9 zl?pB3_v`c$6eTPV(vdqn7*|b2dMyY8dak4c!>Y7!bJ(9g_nuY~GqHr3>QvUPG_g$O z+*%Fr!$EhnzH8bdQTwV(vb*_|=Tc56v}HQ`0sr<}-0=UfvNM;qiY>7eB=4{3Muq|t(s7L9@dOBl8m3j9j+e+1qwGcq8p_@F5xZ7IyWDp_P`JBA* z@%{7BE`s}Y)WgX<)X)$UmhRAfA#th;&BH}O5*c2Ctcn5r?mAJ}Go{nsy=^a)=H%%ue#uNXF{`y1;wIcRlpY>FBj4LeDS={M&tcN~x|( z=8ELMTwz)Cbmi7Du^30q?SKA!W~#3GUC2;)Z8W;1%|*-t_HdyT09~TCh3NLT1fa(v z-k>XR_FOU50)MC#gb%x4aFNtiL_h-oXMByua$C^dC~&V9a6~$#YtUsP*wP|=pE1wZmj7p;+O3hE0MjK za|1cr)HMVB8hM54EnrGQk^k|2zXGOw5?>kvr|YK>kG{RbdOd@#Y_No5x817POqr-chu1lPITnVWfHyFB}Etk zrHR42cLW@44%D10+P@BFRhlokt{t!=-_0rgP!aqj;Z-*|#oNrs&MYdUZ`}br95LeB zZda-XU9$(ZseB+yFQK_l)`(ye1v-%LlUpT1?xBG%_364;h@?NJOEj*LoIc@YGM;|8 z%JOCRxr25ZO~XSc)I6E3d`*{2*8B=MV*^Y0U-c1Qj&no|J+E=LvMzb*ZmQZNOD~Uf zS1L6e@pLkb?X{ON5cL!)wA}J^t||sL#h|Cy&hn$H(15lgWZaGTyjF~wmj`c4xV&0S{G&IY8Ex;~QG?f$%DGP9V?)onw0S4eLL3&3cG zY3(b9l{K&xY2)J3AF8KNdw5Bwyg9|Ef7A|8pmmpUU^=`PSY~I^4)R;7q9ymy?p8~9 zy)?t2!}fjZ+36O?$jm(SvbwgAoZ z3=YR^_c`7k{Wfg6d_BN?(4~e7owOz}u~$^fpXn`Yw}tX3+uxJY)d<@T?%GbJ*y!p2 zWPC5Yk(VMnpyIx(RazNq%#zqK)%rnaqGj=^B~77)6be%=C`EtsuKnsaLi@#P-RQkB z>7~*5W-l);5^~NjGkC)G`*#fO7k?il=k%*GRD*&&I-3;gSEN<^ue=i>dMusG!qqLY z?jwBSmByJ~c``uUCWbVyGP=uLLvMZodf9@a4ps;YF$Uio3p2L)r_tA0@(&T@(UFLh z@wuNhJMXEM)dhe1#epRnNiwzu#iN8ICEcaj`73oRr~qvE@*?;f8;vU^%VtZqBD#E?TH{=|NTui7XWJ$5yibCgEyN z!IR-8N&0ZB;Y%6`Mj$d`PKzt+Q0CGmTgpiJbJtuVbvzQA`IW5eB($48qbEXz^y!{T zvneU{rx6zg4|K$m?Bg$VP8&mkcoQgU3~0^mZW{8qlX`vj45kceg=G$L5f8TU#GMNz z)U4(3Pe)nQ3bqw*d%YYV@+~FBYL8(DD?iQQ+P;;cuUwhZPOOhizl*&?%|Yf*W`U2S zD+A`s6cex9T%SI{WWoQfzJ)H;q;u)f|5=%H zF$Rx(;**}UT52r`&xgE30vX?!zlMt)hVMDEtAYQ;0%#p0w_}7ChU|UH)7sk&z$GqS zkeaRFP-vNz-o-(`Nt!BP^DUGtv}}-;KRLm2hU#7d?1K|@%_0u;LIVG!#5k@W+9|mg zgR9OA&-j`gB|tM=batPLVu0Pt6f;}IDOIRJY$!T?zCfz zo_)fxu`Mj!oxYI$>cddq7mr^9A;L$8O;;zGZP@LP9xt~On6nL+yxR80lVPO7Kgy_b zd*gaO!zC&I%nns)hZlDRq)Zd|M1q?OM39)tJHGE-=L3xYbdDbgG` zbo9{%oKx_4IPm`@~us^E4z>8BU^CFsT56Xh4m%j$yVvR1^uqn zvAf&|3LIy1$!B)|EkB(O`&spmYW&xFwRLT%G%&Fdwqiphtke9Tic%rDgQ5F6StI#c zJdvkwRM)++={SzJzdRk(`nMd}01!j6jm{A}zc}AJ@weD3YGbJ=h*Y6RX|pe#xBT*2 zahgfnd4_4z`27>>A_$Oh?Px>od_1QuJ-1!RO`MeP%B|P-_}0s2llTvTdE^Qui>Lj2 zj;Jbr#Xq0(YN|5hB^943z8$$}_eH1|p!_)rmXgdp0ilvdD!&jQHQ0cto{CUUm;YMn zEn&b5YBZuO!PchJVMC`2g;tWR#XCNK}aCuknb?-_#5pWA1a|+g-kI@E_nS6QM1^_{~I?@OTa< zs-*00{er+2%$%S!T>be4+2f^wB<^@pOlJSI)VkT3OF-(hNT3Z~nDaEXUcr3|%DwB2 zZE4v4499%#yhlm$)`N3&6iXhb0LGkuH7gJ|V0BwCsB8vTufl=>*yFJXhI)xqYtjdhqHr27<~o zeNF&>p{l%P-(vp4ht;)R*_XC5uIfwr*Oo4M;j)%>GN_a42BoZuC3>_rq0RWiPE(tG zo?+mrKB?>y_rRK{K?h-Z`KnWf)vGw=0`oO41NXTX?;q zMsz@Xz?PJFEP*&4-9rIAl(y5KBEO|@fLitIAkcML3X0ovdjl?cvOrRC=cB?ik=Ppn zs?U;h+L;VqT7oEp4VnC@eu208wvew4&J+gh5ME+vERSKB4u=XwfE^D=bJgH&}$v>C}+U#eT0#Z z>RDj^{ZF>7+v?M!+)3xJNh7cAWaY2G2caqsXvP=_9|OqeOG)tK#`x&9ox#u_Cx=R% zuEH(J(SV7~`GN5qxqCN}ryCBNK;SR(FfbzAe7{-i69cf;_yY%|B-Gc(yL{!qaEA;o zNT9L@@LPu0eSpZ2d~zh^`b^LC;(?)WR&(aSX|G__`y8hBwcZ$J%6@DD zHt^l1&<@fHWg4COsD5qwtpiW7?DMeP!-NlEb^c%sy@oip2R8@gnB+rSsHA}AA=>)m zh}5V?bpUr=Li^B6^JO)icp+hT|HA+FqO z{$eU%Chg`~^;-d%r!;TKF#%^p=lH=g%C|mHMUUI42%?lF_nVG#@b$#Y7|Lhkm$7~O z*O1Lyq}FuoI))r8p{L*13%Nh`Q9-7}T1_6+cq~x@{0{5cmr6C6Zf73Za(gML)to~> zPo$<-3&cZWvA`T}R2!$SQ7BFu3U~?38FMrN(uX9sV}x)1LmdyDYar`;RkL)zjyG(I z9&;E$4VQHD&L{dTv>=v5zBq@WQ!Yjfl(Tf<_^e-4lJ^Dd^myRbPph3|ijgRv3v#)C zCf*8ym&tYl!(7M><~o#>DdJBO-|C(BnMhjabn4PPre-A`irX*P>H-&{}2e~9FDI>YMuTyW8>j**H$*cxES@Yz03 z{}`$3UlyvmPio5cNYNV+Qu3(w#M)(ZRa7*L%e4T$$p-IQ>ninD;^U?Y*wM{Vr0KiJ z)RDqR?yw|oG-b;HebQ0XX6s$9v;sfi9H=eGW5SA*`)ouI}G z!AudTk0Z!suoYj;QCj&JJa|jV{WLt3-|C%651jaBLzH)Abf^5;|0?PP%jA`H zQW56qYouM?i_xS2&ORu(T5U`!Xh2xeQ83}kxpcv|Ke7n8-LbJc)<0lip!f3+QQlkO zyfDYYuQKFi{zrHi`41N*b;STZg<_hGZuY9EUUwEnZ#3#eh3Wz{V6J>!UFcZSrEwwN zy*!c#l83I=YRN3bOM!dPEHvHOx&_zewh+ydqLVV>dPIN^EQ76feGoY_sS6yt z`i_xHW0l7A0-j8sqNu8?0!sO?$}Dt@`Z#l!d2096y{(Xs2|e((Hl75cbO;2VkyVC; zm-oNjJj})`z}okO+3rqoIA~!?g!+4}8r~PPmzfeQ&wkS=mzr-g&f3Grlc{>p*=|P&{(SzcAyz^(aRQ7h*LSx8o z)lQL^uD(Z8nTP3R8}F+)|N2cfpVO}&Pq(;hBQY@`*`ekq51&4`QEdq4eo@tC+qtWV z1xWJ?=~Mgb8W4Ni0|?oxZ83l+AI0RJTIci z6YV{w^pi(@xPJj?s@s#Hh!`;55uMPQHpz?WjyFU$AY7?*t@@{11o+q4^v0~{gs^Oc zDTAP^PI6;(2DWd}GERPf(mxzOK=)+c-{j# z#)Gx?!MU5H<(AXFf{*K^_YDI4^lid7$4F+B1TgFV*pfbSc)%X(I|4G;=Os)~t*JHtv{B@sxQ)ItJ^es(q*mm3_yn;#bH(An{Q~Np5nwG5 zIOx}zA^TlVE_%!4w%sh@^KdnA=e|jaS;mq8*`6@fzAMbhrf3CY`#IgFwBHlL>PQ;) zil{Nt8_8vz9uEXsb(Lsy6M?xpEV0iSWOB8f;X-j1fmEkfq~vVTIuUMDM#I$37C5b6 z-`tS2*RGhsTL^7tOtSdzMlaxHit&~0OdqAyVS#M2U}?;byE7dJqbvcOxEFoV)6@B` z`-kv#?befu^$lBljsFC@6z6paQ;yoFfZ(}0!&972-2$Se?ZKm&5bvTl)ndkW8X7gM zi?Lv$LUqcu5OARtoOPB}vq@*i$En~hP*l9QskzcPCT43obGSgCI-v@QFSm{(7)p9Q% z+JGF5b$>wu)vd2jCvZ7BU!bCYMskf0-31kx@33Y!+2FE9{*;BU zo^evP!>e;sC=^V`;}U>!_1F$h zu>2ry)^&lQyHWo>=uAE8NMKL#<>o=z-)dujorh2R{_-5#5_V{ZXbQ3V^-97OS5*Uf zX~00Jais>f=T;#?qX{jTmocuLjD;~VL3T)gd|Kvy=yy)CV!cZ42CzRC+sMB%FO8A= zXV44PO8JVL_=Jq{FqGGTFr=a2+X$1<1Uu%!O}#fcGs29)m=)K& zxG3vH0AR%|U*J|4UX#woyM&5|T3utpPirFIt`+lOL<0Z{i9}Nyr2b;^I_I3Fs#TZ1 z7sL5_@&qAzUrX++{A+G{J-1Id=5q64WIAh;G|Riz0}H2RP!hE)WeD+gAyvM$jv=AQ z_-YFB4x(ljbVDf7=~`;N+}dO6EiR6B&}tt-P~xl^Qp{$@oU$MEdg$^Ebc}hqUBS4U z^&83Kv}okesCny_>9jDzk@E71%9nhwB(DY1-;scXhsb$HA=p{mo#|$Be+sYWWU_5$ zoarOCmD@?#pcEVg-ZXQXyP&Yc-;id`^r3W6*pNUakK6Kcnl|aMs7k(Np@lW_7`K$c9oC@VU`qtuZ#PfyE!E*w`?*^5KfrL2*~Tv=qW_e>b$mZ{dJ5 zwikI*6Sc4NGY9i&5(D-M!|;oR@Y5~R*9`?QQ2aS>4zghC!CXShu+<-DgJG|}s7AyY zIw%_~jWZJK8gDbi6^WNIW17yz6kuS){B_=88an5r0)EL-viqV_aB*O?M6$&e3ZHw^ z=rP()5^RtLIlYkGi(hR8D<~4AIWVuc(H4^CpZ7877m1N&Z9G$&%)yVK0JqnuKw1v2 zZ=&&2Y;<@c`PELhQjq;HLhAB&68`!ou*=wESaXgEEPye})jl`@4hWXc%Xox8+6D97 zt<^mp#9kHUu{t^tSsKRZYYEb`mtkN0)Tx_ z(Ajp#{DtiZM6go4H)f&N`CPyf6*H+I7Ek6Ih4+uqeXriQZ>-H(lsso?C6R@g|2#9j zNNaun&*%h=Wp6GRN8!mw;nQ9$zB5Ce*&D%+Ddc^vbm07i*JCq|eC*vY zwP9d1jxugum5Pi|yb0O&yO&|?& z;viI;A9A}M2T*<$lTT9|9Q_IG)ae?p8AwoU$b^k3)3@(|8%-^zr+9fHx8-cyPR`Jj zx0nSLmdN1c!xg>w3`7oiyEy-Y7zV&KO-#EWro5UVro5-p{j%zX zJJH_mpFn_X(3~KD_wrlnj-Ko^tmu9vQ2tftdD4X!$K`cx;}-jHf)gY$uW;x66K%CI zT!frHm@q}e*^O7Km)odhWs7|9r%S8zJ;V>YjxBBI)gwSFdPQ&OZubvZmX*I=lGhg9 zAYL|n@1c$VAxP?p>3@J))Wfq1Ju?psN) zds9+=E^bBrjIG0@(*%6b9r{_NF{8L@A?&b__`Qrh}IGTl1Vi_XZs51PEtcBS@@3L=07l0gpjDr5ALOWjn=n@ z5AqUjIY6kJbk};;54lTLs&BfCZOv=-XoHt`s0*$ zul(@5OVa%_^gHN+IiL47xnEeEj)EW$Ag$sBC^~VXU!)ls0`lf+0NXEciy80#9@a_aSpmd9Iz}G`JU0c=-ZWN#eX;?F`yqv=Gn!2kAD!sm2U9_AyC)K7E#Jm} ze{#F>u<{yyYY92X4&vpy#Vgdgg75RXhBTl@p_n|A_|lyB`Tc-;52Fwtx@?O=Uoplx5L>vzScLv;H6N~_<GJ?b_AzJ^42;+l6Sj_vSF7>)(pISeVe6xSy0 zbfdSt)O3$!!kr#bTHvX@m|)8Bx#KIHSI6?Ylb~N^R_tB6UY}PzM}mvWx057fNb>=^ zh0u2X)B#TGK53{;*O1sJT=bX3{Iej1Ev~GqRly6)P!IfA6o%)c$(RGtB1a zvw&~=o1@>48C7AE3Fesbjvi6I9M!5dZ^p+gEivN_yW#<~-BGs7|7PKC%3F?n2)H>4 z$C#tNoo9dfmmb2nG3+;3y$tm`0XHu<%ubuORdp`8n{*$r3+VEOt@D~s2im?Gt^Jb) zJ3U|DsWNlvqH}qA+v#IjRDHf8y8Vj&Oi`&g~&@em@}d5JnHf9KME-{QAd;z`nXeBe))JyFeICzV07qMy!%-=OnB=} zhsL3}?=v0-_lyGTzn~u&|Bn1Oz>gHJ0pb{`u+|RG$CPjXP>ue=d9qOdPY>XO@pDa; zj!)g=Y}3@(YR=XE1^j%scP4oQ@BE833SsK*|2TDEm$on^^1tApVv+v={~#OrOAa1e z*iZ`AcT+AqkF>v)*G~|{dXTRFy+Sndzeu86td8VdkGgw@2z$nlF#Z8@LV?9|)4@W@ zA;E4LqM_jj^8YsYu6rudKJ*j!*C3mXmlVnkO@A?tm-_G33kNFYkPB>GNNa4Du9+&; zi|+?HP#fYo|E*kwH9ZQDJyqdNd+)IG3(4mbbVFavKy&-h<VGU6uj6CL0jf}}p6&UM{sujfO^i<$b8kBK|7cg^nO&Oh@~f8FZf04D#iAOCCXqe_LB|w_pjH0vM&L>?XkIDP(ievge zEm917G7Ha;<`V>4LeupSL1d%Iet=m)-awV^FgRfw`wl30+bDI0X)|6mHFE~}Lic3+ z+GF-&7qn+IkiI*b{fw|tUR799rckyt7XA?N;62XUdGA1XJn}xrxgk;ybe|R@VE6bK zRJEXjKiP?@>_VogSDN2wpU82O@-02|zre81cpZ2qWo|z{(olBDORuqTOLl{>0gx>p zdI^nZ?pN=Lp^mb4?F62DQ!NX#!BLaaMAw_hg5cS%q|85W`CAcsGW>5k#Z4qdz`n}3*#y?59mk0R( z{m>6>8OzSrA;LTVqr(5;-LlTS)gS|C?~Uvf$J-;GCU8e4b<>QITK&wDXIXs(88Sxl z_EAVF*^*|uT`jP{#S@rOQk2l-Re8smut9N~FaX1K;;ZF+fJeJ1jDvXLrnOOkbekvx zKJEq(-_X+YOlXXZN!Ki;hO*1WZG~kU;Lj9-=rkR9T>dC@rsKeqwe<-h_uN~$uGXk+ zhMX@846a1Xc>L%zl9wpAHi*8h$aOOguz$!`Ks<^YxUhINP*8e4g`|Vz_<~n4C@bkZ zumNjQv?#|_khwa0Zahwg$JnDJ6eg1KpFNvIm-+itwz;5I@(KHwYCOe+I&9ym8OWE4 z7k`B2Hak3IvEtRmeCJl(XLy<5ke|K~Z(kK|CS9o7=r z3a0+joM9Tx>l+0&()Lxd)VSF1ZWa|7mK0DJlZ)Gb9lW*@dZ2v6$B*;)QaxyBAX;1N z)n992)LXp{UWu{1a=ghO(k|9LG8(vlk)X?(@Z?i*ab>nuR8`32FuoqbY zDywzu6S^s+w>Anl&81)T>}lqhI3)bcWd0QE+VP}gbVXP6uoN3H!;4Oh7X**_ondzk zv?|t3O;|T}Tu|#w02qd(EeNa#)=NRHEhLO=+y@Vc(dm2K_KaqG&>aEH7#9ddo}Y9& z|9>do_D{p^=am@`jSEJ!D?0tdhj~;w3aw@c>(H?bXg|+w$pz?w=o5ej33op9Lz2Yg zEMdp+ZwbuaLY)#DZ^WB8D zs;!PhP_4cjeiH2Kc5{K7V~-PTv3S1j19j5Pjr&HnB;?Q2Lq%cszXH1P zX|gdvW>Z2qQWcZIvtIMzga5&f)#jv?JGssU0b75|kGrcw!m2+iBS_x023X_~*~WwZ zTFqxcQJXI{87Ktau}BFUX4{pDl1 z+?%_H4v?AYRR^SbOy;ok5U&j~v3j-jL77aE9w<>V&{MfEPVD=KZ?lu&e+p3UBlI2ki!ZXX==!fD_Klc`Rz*=?!)(&mHern7FS{X{yfx zE5V&^*VFM@Gs3!&v4#GAp9}$~hTZF%WE>&_#_Lvg-?@<{UY?pz>fqF5-V3Y(XKx^hg zfZvI8tx8uzk_ioF@Us~!oitHM)(Dc3R-Nn6w_zwo{TeC^vr4nwW&9Jmi2YVK-`(vD zEj5|U*#@^V0o5fx5C{_h5HNmwUWAk(_f&O$+f-wSiP&=J*&3{MX~&_I6>q0glGI9BVv3$kcx#7y_tOnms?>lxMv% z>WmPAjg34Qh_>M~O_9(*Kk2b_#hpm?CH4OwQ*RyDMz?(rx0K>mXwhPYLMiSJ#flbp zio3f@io3fPDemqmF2UV{yE}n|H_vnL{r=uRlga1IOyj`2)7igmkYW)_uaN05R4+D_!vEVo$ zJ)r`#LN9lutB$8Chc)-!Uh3xY8Tn0iDCXyNlr-%rpu51~iW_+)P*!NUcffrxZUcTn zCxa!|pN3>W6}7Oa###M^DUE4f@$UEhL5_i$@vZD@gF(4L-6jLdUAl>R+W3F1p{hB# zd&Fj@LfWvr-Q9ZyLkF-FTUmecAzuQ6(l!40>K!Qc7QV`!VE&0))_mkCc z(A(XW`}3hmm{Ore(nRrHrJG(X5c_hWbQxNC$c~%1`Vr`xsa50f7HG@#xk&7z;T(1} zeQp#K_i`*Swt)0bexdttI=sR9ea>6htZ;o<20johySRmH`B3S17Z zVKy*fz5@m^Xb8E<)I}AQ?xLd8S~}CPZKm5VDZi>iyz78kXndTQNeSw<5YXO_5D{5u zG{*fz8;WQCV?VU~`8lqK=xNKlQ0R$FVf`7`lBhL~CBj_DGk1j#;sukHQae&8QwDhN z2@OLh02A{O5RmXMuDgdL1y*8tWvFKzs>aUU9EGiFbPtF57C*>)CgAsZy_MTPfyS z0pv;z>5YT&_HG{**kQzH_CGQ*CW?}^q@;BT-&sqHfkDfdD~ogGc+#(v-AxC(13$7z zI_f#t>i1+3h921k?htd8tDA^}Qaxi>y<}+Arazw?qG_keDAvWKFsmd~8AvIyesyJh zLrvNpxmr2QR;o-H2n*0ZPlC?&vpW z?3kW7z+yU2SoEsJDa~7l*$!aK%I(X8z>b80#*>|ljVK?A2dBc+`aM;XQ++Rx2_L+U z0PTvhWvn9qP>wUj{88SvT7N$?9DQzL9rob*Q=XRZ%>0X*TD<97)X7*M5Y_hn)YWzJ zGlu}$TUr1ZoI)?JhK;n3Ms_L?MRce}j>}LWL<1_;%m0qSnN5x~GZneK3c;lTIH4xK z{@FU(UM}oDq1khLuBXb#s-h$|cUU5yDhahrzD>Os#5>PWh>T6SS__Wm;9BxzN(z+bhcCgmzJ0RNUA;mgJEJ!FU6EwTsShU zU{daN>$4>5ydR3JJ74-;NTM3@w?qoI{-Mr$;zRrs-zQ09o*V8NCg1EiRt?r$kRx>t zS#;sQkNd~p!|9hT&5*kr{=x2ig)XW_P55jZYUj?K_+W46e7~X8eC-63Lh5Ys|AeV{ zy7*DswC9`UFt1?m!~Qk4rf;A8Z7`8r9xuwhP1$ehXY&sWn!VK(pK&5@vz*vnxNO2orMo7Fhk(y@@SGafVC z5WIqIr^VVOo5d{R1Ev#r$n)8)oQHBtLEAK6pbYb5j++8E*kD+Kh>;B2_g7>7y?-|A z_?4#UK$?B4rlBRUXU0e5Eq4pBz-wV8e4ki6|?wU6HumFjqgQ^vgnAcrKzB%aoo5?;e z)-;uT)TEWd#YakjbResJ|EBRV+@SY(lf!K{M-w_nsxNpnxc%I~f2%#Az|2qCdftl2 z@DdKbj=9d~ompWfztcB=Qa?X!F4CBI1%q7J!gv*B4!e^(csFIp(z4HV?D}<}A1Ce@ zbv<_i!-M=>_!XX&HZK3Dk)9?lKrSnNZ}$ot?S;s|+AA+s_svD33mmGI0!rn_XMkgh z$49(EGD^XA!gi3vVV6VmKV=CYllJybLMHtv1Iw248os{ z!@lzE;S)>ld)3L75Pvaa2K`-YcS-dRz(hi8reE|WHqhZQb;&j*Uwm9LG8v%t8};lJ zzcS-zhE|YXYyEDzeR&uaQO8Et_uU;-$kl4$lQW$fxw`dT>*5V7>EqeL>$$`mcK%nD zEvEM{6DL`;^`ud2Rq+PlO#u|uf;S;HEA{Fx=Wb&87u#0PkxoQEp(sLO^pwf*XYe}c z0o>QUE72ze$D_;^E9kb#W5^kjLbf5&>hO1Q7{$72QXCdq#EW};>4j#w-c8F%^8 zP5sU<5c^!(&0arZXGFt{#bq$i1r3{ax5zZeEutc7ZSbKmjouS-sks*w@%isW1ZNF5 zvyJ61WW{|aGTZ&wIjhe{MdWmPPaJCq6`D}~i$%2J`+ue%@xfwMQYvyk-trc1SFCSD z^VxA2g48>Wi=Lb7MHhGefHwsPE;Hv3&Cz^&i<#rV*kNCFM{Cfl=L&Vvs)vzg=~#I# zx*%CSSlr8^8OtZSgtRmFddTX6=lwX>?mmExTQ29@Mk+xf4!)?AoI=hzcgA6CmVP5` zK@}BU2r+qK_cs|Qq8TQeA99(XbOQG9mmgA&3;GY5IliUjndAqe^+S6%A77AR!|{Xj z;9sZ$j%UPYQZW<^97zY(K;AA$IeV83Qz&}>hNGEBIr+Y@+!3z4g~gY#!HHrIsPjz> z%4d!al9&7H`lhvyz4nuWtC=_Gc;}h@Ww;3p9^@28zcKOYH|2#yg6l&|lm#ihzUrk{<3Z-)HQ2yT+4)(SUgR^5LMQ!AyE z*ZMXOP==09qT-X0(6pZ$3)$(To%G!1!eTY_$>eV6TK)gkJaj0EbJ4Ay&fS^ZMEKMb zNJ5{(9%8+Vm&N{!d8azTsQsOl{gizGa-+KLk>T~Mx6NLB`J$^3W4)s`02M4+YXyZkYmitvtjXRkqy3ankLx#W% zGo5O?zuu9=*ldJBhe}bEQymIHi!RZ>5Ic`Nid`A6A2O$ZusM-@kPUR95@yYoTU*-E zT=96#5l{Ha0q*Z@zfF)TZqPr3!{ktu_Hd)rJC!HhU_*;Xrbu*M8*Df?K<{|wez~;I zq<%WJ=&l%Q@upe8$jpTv+|2y1nV>_y{0r?p&B&xR+A)@b+ULhUqwJ=M zV)Mncr?017V#sHKbboH2zx>Tv5R)&zK5_;X6WHv8`~k+1Ihl1-yD#g%h<1iS1Ur*> zSHaeqNya*VM<^X`zMoPajV9EV^1;FfX#}i)7RfBuQDZPRjO0|Cf4}zn&Pw>1$>VRV zH-GjbWPtDux3JNM*(){k!zi|*m)~s9LT`=V*y#TwwuqR=1outMN>4H&gBI7+7V!Ae z*?L@5XAQ*UyU9qx`?ZG(2Vx2Wb`;952PVsEw{hgL^L=rN6-&k5kjf1Z)2Q)5#yebW zL~m51DA8K(z9uCfJ~Yn9z1FSHS}mP7WjpxHcK4I?v$7;8(eaRPK9ObTmqWn&^cdR> zC|K8|nH8tpYV_rA_cAq}f*u1oG(h?44Zvmk?qD(TGc_s(Qk-jaHCc0QU1~6$vFS61 zL(8{XIHA{fwM(zTe}cwmXy8EBu`Uy$kPuI+-(ZvU6*RhSCPCvn-_vgTJNb z1_=c}*TY!^F+br7s$bR>iGHz81p}l=L87h*#Lze$pu1n01vN%a*d|TWGheKC+fvwitJ&)gbxBeu zrjq5W$~*0UrU^&hKB(L`FI+>i9tef@hj z_fd9&+K=1jRT-?Su9E{WpL=ngsqXK~;yN)*Pse}ti)rY*kSx)*-MNAZ|066qj=0tC z_HTj-no*QD+_gSWA#PK!BCD_S?qaJhh&1_+m}-E7I{U{j=e!O#1mAAKsOCA@$hOg> z0S?60GoxG3Som1Pajze|C6$arr*4vB+vyfAJ~SU);>PV~$$ldGuZddLsrdg~L}!x4 zcNrw+X6MCJ=l-9!WqWwge@@q9SrPJ>^lE}E-zz!jpftnqzgC$z8DIY=mdX=N7tu_X zuRN!*C-L7=5jPqBPX@J^wLtm$TyPMFGmI=>^v!|E(fT6gzwZ3eRQ)fv0|h-`9Wnt} zF!9IRw)+p`Uzo6%a)(dI7gSuDKIr1e^AUKbb_{xhD|Sz;v|Ub!gjrqXG`!je79T{-nS8I@ zvvPp*Sanyuv>s1$6N{b*6M$kS*!HHuZelow4ME>8HxY~jrw@@4#QZdyJ5F^@$~tYU z%_T>Vx>U%rXOUD)v*L0k;b{k~qjUn@Yh~UKur48&@LF6Ft503WR24TE-}r~f*~#`> zi-pZ*roh}AFa@^34r>c`1GJSgeejX|`bYfnAK+Ox!{NT4S4h_{4{<^yE5 z8I)YTwmXu(2nk_z>0k3V{M9bVF{`Rr=d0h=^QNsLufB}Kv1?rt-h|rFY2{P}5ODya zrnPifS^j^;eFG?FONMF1gCTS}5Tx^S;*mmv>Vn3`+5$^Nr;i2eYh%w6)st(Bi`^XW z@dRn@x0^9}88V&_ZLNv;<#}|{30WAU1)tEDp!X(xsQIUrV5K_KuLS+b@i=(mSElDR)z2cLAl;s*OvrS(E8^+0`)vTW4V-+f7q9i1)I3ETzYi3q! zgcrC#NutQpq-2Z`2-YDqBtCxU+QlYN{#AB{bf)1;7pu&^x7-~=SG~!{<;Z@<5X{cQ zCBB3^TYi>381|AfSx&~C@IVvUXgirMo-BUMewH(tQER6UcIeAq#&vNCUdj zP$yWa&GsvIQHwVfR_hrlmijDU+%q?Z>8Ul$2K0bHBqD~Y4 zmb~uta9Q@j`Y2ia!F<3*ueFYpz|D4ij$7VrmvX|`*nFwL@k}p?ZYrw7jE`upsdB%1 zqdXLetw<74%*$=#PIBEy*8hC>9+y-{?`&X*|0NdttTir^S;}nmfF!NHA5PpYe?DR| z(!g7c}mM=CPr}FLoyq_l26u%kG=Y=6=*q2alC19e0>#go^=9jGv_K zma_ac6bvo%}#T_Vt(@N zP83x8Ep!5y|6ChBX^$J}wO1kQZXsACFXn*Ap&1PM>W1Ow<9|Hw0cf}k_&uSLr{`cpSpjSZom*S8{m+rKa`upqL^ z-^BzgcN}UVNw#>`H?=%q-jQ{7xymd+Q!~Bb+I1wLNABlTSI&l)0jypk!rGYRV>qkj zhgN4|KHrdkB}z=VfExnUTFeoLF&5glln+V2SfUmkT5sA?IVp@W;c2+-sW>3b^na=0 zE9X>DiTI+Lem$LFXtcd<1YpE?bS1m@?&c%cL|3#X(d6~x?xD`(vIjqT^|;aL`OHDf zkqB9OE66wjJ-g0@ptG3K1a@`X!EM7`EtlT!hxt3Ke`0m$>8-KH_PN|VGc@R(WRc^< ztne59wysFsbi-9)d)nhvpzOKs#MSH|zi+0v*u>@_K`y231w+dDkl9R;erv%JT9;0e zUYFl%ppZL(aIoNjdnR_B<C1=Jv(&kH0kKisqfAs1xvR2Q7NO0_z z-rxLUJHsY$@2D0qbPh23d;7}U<6)a4_JITK8Vxlptk4U9fAi4~03~9Z$n0+}uhXhI z(Wna79`6yJo{B>uy5>aZ-l;KI{`1i29d*C3d$AJo5#P^;WX#0(+1`VvQ~Q%oDhie1FkX!QnH)eH5zsJl$Ji_xO># zYOz;x;*Q#05-WMa&kQ2>TSp~=!V5;)Gy0@rM5|!qZ>E;98B))x6VPM@bV^8 zVfUU+>O_mF?T6M-XyiP*>!jT}z!jf2qk30kNhfYJo1A$!66|N@GE5KG*N(KmRoTD1 zRQJ>0E@#fJ6J&zG+*yH@O9PSwr(6BQx$VG<$!jg1Lt&r8dmdrCwZWp@^GVA?(p7Hf z=BMX*Y_|Ky;ziCKoq3a;gbWC+yCm~=X2C)vws&)**(yJJ+UhlVQ=ee(e(q`jWJ*qb z(RUHh*_=BVAkEDJ;_=tYbXtPk0}dOm$j-d0JA9{{A2)}e9{DdN{MH&{`>XFixjW07 zu5o|!adm_}Qb3pcCOtKYR$kum&3M z!$jSDSVDfqybLmXqyIBrRjWI$`whe`A%kqZ7idyvJ0=Gy5*jN^FC82x7RMnvE2ID- zUe5c>s8<)3m~_A2(Z3F*8IG7b>d{jTC@L-K_vA_bCilJc%oKCTgolewtz3ZW^M9(d zO_#!L9*3~3C}r08KNKjKRfk=pUr1QfqYV;XzfD{USgO|uu|__g7X#91%uvoE=IuK_ z_E)AgdI@qRTp)kzS%$L>P~%?-FW;Xg4f{O|9;Bvo`X46j^YkmMF_3$a|frm1pzvy~!4{Na7m*=f!Bph=&Q) z_xg8HY*J|76W^*Niz2gcB&#BMz_mIx5P_zqdDBQsE07~ME?dAMXU`~wE z**4&DCKo4@^wjv#m(oz4H+Q`I?7G@@qDqs%G@)GNeOHn`W$BAzKE^A?!)eACY`60P zi9ut$)xirVpN?cO=+cr9cfKl+ms=I#S^98=^o5*#u~?L0MYC1i@NYwWU-~N*<|@ME zp-@>33nyT-Rs{5hRE&$UBH)WYhmZf#%7rks!x{{DcABwwONgEY9H^!&+h@buW`S#n|@B9+ZVO}VM1F0=rF@bOkY8Rx8Z^{TL%s}g*@9Z?jr}a7ur6OD0acm}O?a+b` zo!l)a+VW^moR#Qtqal^?)y0fAIfPde1#EuF`g zR4l83*!H{MQ%%}W%#_k+P3k;4L@dFay|wn-k}(GqPlnOq{S@$Gi-o#$k1W4zkd6+d z+-O>SR;$dM_yrW_m?C2^&)Q$E8Q95Oz9FSjqr-_{+{Z~XD?Zp{N}H~XzU;zcypkl- zRQo~tbhNQcWP8~4OKRsaRsx_e+^!CHHSaCsGNg6Ivofs0vweuM((7z^4DY~2@5@n) zYEuQB&>e_H7EwC2iK0YkP2L5$HL^L*b+Xfyjv`_w{_-Jrwu*rXi+QOh&+Jsn?py~t zC(TH)9cqK|h7X6pF-ylf>1tuYaX2hJ^?zJ|Suh=auKX~>_Jk?2z&LjU?*LT|d>bpt zcw3yB(9Re6Q-wi<@wsHvO(Rr=2uOiaTu_vWb2}2=Tl(8WAf5RAn>LWv?h4iLHYw}& zZ|(>?v7^_alYoWPCPC<&< z%C`GZ%0i1J$t1kG7X5h_IGy!b(@R#(>q0sqsOoTVfFYRup|*Jl0!Hq5Iw(I`$wa@T zYPE%|{7kU9%=;yO$Cxo{Z9i(Npyc@+j64z}kEZKHhE%{vJd{G-(5>YfP zMdr@cZ}9Evu~H3FEm28eB6bf-w79x(>q`Y9TM<6T?9x|+k*6~6rpE1z;QdT6;rMY| zOOVn0Pr-I8AS9F^C^SbH+5v(je`t5#YS_PmK4390o%@yW%GBH>uP!vd?>?YAueUyG zm+OSSoo08=o3o%9&l*YEOw~~H9?*D9Q@&V$5cR`90Tc1!kRG@-Z;yf zWX77;a`*PPltfT}-#h2kwQtAMIG3_RISZjDp1wT8Y4?bD`Wd}35qF#~ua->*6BXlG zJQ`k{Do=qiRqZR)kQ9KJJumZ z^9jlI^Unpxlf~hfkMw(q*<({SNgrf16QXYwLJ+)4F*B_w*$xn!<_UMJtG7#mD(XU* z-u`$hJK;1f$L+Aold=>@;&V9ZdOD$&h|n2@yy0^IMOKRZc2LQ?0;NBr+lNbk+Tt+2 zlY7}C41I|R0U4vIOAaXeO+=Y(cuGHzYB2E4PL5{ae!rg7Dey%3n+(bioiLyF2C$wr zqO;{JdXwJqykAD0Z9+0S};A$Q)NEqvM(;syL!*Is8{j;0WkBIpaF)j&)av5=O z8jrrrU(11)&CnB2(NOa}+?lX=_8J|HK~t-Iwj9j}C9TiUzQ+#8rG8YJmk4Hcng+dN zH+IME&IE_IDJnB&nE1f(KI-u@*mCG}kcNdg&#|z9;i%N&;wq`Kbp5Rpy%!@47i`|Gt zlKt$|Rsam#gJ$9~?_Odv?K-~Fz(PMi22t5VyVdM527_KpyiP5@$^-8@v?cCgHoQPs zjaaOb)a`*$H0}iqbVCUtdyanXWm#Za{;tGI%B=-QUcoBt#w*C=b=UUN6M!1lx$DEl z0mmC(hL2pxolLhDIZXK15=#{8*Hh4ME_EbikGR_InOmV;0?KG}3RUQk!wC zX=arcYrxs{R%DJWU&iDcmo4d;^`hO#Fn5t_qqO?Qy84(dK#S}`{mqcqK4_!KB7WQ?t!J|?9c z4T*?|=dL7_TwDQXv)|i|Qdpa_mg%#^$74CkP#yBHjCL}vNp}xm+?{-FowSD)9Fue_-0RGt#M- z+bv_dsZ^nC>MM0W-b7d4KS{e)n=|3uSF0VP+g>Ge*L&9oykiGkXQXc4Li#189sGR$ z<>of|;%_+k-HJovqDs;ZLi>WXmR*&MD6 z2VHrx`0DbOe~c3U;_L4 z9xglIk5?S9Sk}OLV#f6+6uN|eLJZ5&_ft5D}R1{S$jP(n-}F}fh9>?_a-)vw6l#@ z^J@3!^glQM!^iDjaR5-W=Id9^?{VFuIndP=Z-l8x`Y*<;Vkxpvl~?fy5#!ofqFaIr z>ch0Z$;7?~3psfGgZEXNYiRNv%Qo#UeOUnpEeZdbIm@y;yOXt)Ug6>^-yfqYkn2z3 z&GnRxRV5RKvo!)mQD)f}+jvviGBMlNls-#4ZMpo!;&+38s(*&mwKt#hYRFYS*~dX# zYaffN7nAmTJRkJ^w6mlm-E3DELihe2URmhZ`koBDFTvpI$7_p*)~+V>XB(i-{V6kM4E{?jxT@9wz7Ct;YQv zJ&WE=`|jyq!;>S?{52nr(gI^oH}^({sBJT4EPz-pT>`-=@mzP6{80tZ)9xqZ6oM<_ zs~6)g#P0))ftbcMj|aWE?2L&~a&M-@_@_+eaPuS{lfK|H_z4N2O5+}U4PL^E^8ank z*VGyOWlW4kmTw$^B%{b!My{}TF{Z(;%E{u|(5B7)(wiR0PL;UEPjU4euJC(Wq) z@uz1TF1#_o!p%kk4JLg_?RGb)XG2}5Ap40?;{BebQ2%}<%qOBc=E`$>FO6Fv%LT;Vc$-NB|$iwBaU7ga8ub@aN`Qj}E!M4J1Lo4x-;4)P-f zwD6*}mMRxgqEKLWRHJtOdUE>k*$=D$uf~Gw7WoFtH=!D&zoo7j{oo)14<>H@K$p7{ zV4_jLPafk`M`FtJ`J>FMLL8>7XR_24@XqpCtqYL{If<9t_O4EX&vOW4T(^R6U!{M2 zYrVZQ-+gO}C!C%MmJaNQ)jyP8h=1S9+R3cA%yL%Cf065q+KKzga-|8o`i#7P#~axI zv9Jc@;o_vw^99|zt{v=|_q-C}k4@0loZT{=+dDJbUhI`aF zpsz2KtWU{W8>O|-Y~1&|{DJ<+k*uvtEEy92Y0zor!2;I7o{rxBoweb~KnVN?;^5Pf z1YX!KFsnH`k4_4vb@*g2KBp&~hmi{*(s&Q-GaaUEICDYO=apoKU0q$0B%4(E86mvZ z$1aw&piTM$GT4oMU(20gy1m{YWd279?on?!uE;?5AiOg3eaj8@?gSYaZ zq_cSyy4@r{Xr~_H-o5$VXN6c;g4lP=!~Xu{E`y69tFb{j0!s%aLV5K?$mlam&wZ07 zTFfU$gZD+fyoJn*eMS0gyvnNaV^g)eE0a!lb++T*xl&$iV6?#HRBOIF1QJAT*4!w8 zNV8sbo4>@W(6bWdG^H?Bx|A>ns=98eZ!JWsW2Cw8EujKLYbOj!7+XSr#1h8d?})z4 z$et4;x$vnVBzm|EEKm^-QM=|rIT-hhtZrQN3iOP6g{<4CQln2py^e9Ph;~Al$xZcX z^%pf!O-RP-@dGY!;fkM5ioS1^CS>v(Q5>r_=Gi8iPpL*IW@c}IeppcXY%t|0HkJHG zE-Ev?aOgwFTyE^;oVJj?OV($sxNbF*Uc&{}&kYo<`iLKz38drWWNdk;+2_9zX~oCW z?h% zN2H&w%iw^72xI@Bk6`p|(V3!MroBe~^R1cDCOHoP)^pJMUEJ z9KTI^$n}H$Ir~LR-8|-b;b8HZ(LCpyMIYU-Dw1z!osBW8f7D+!I38lJuAIIewYS?$ z&WrX@l&4(CC~mb_)iF_SqjB;EVB}}p zd(y&x-!fnCMQX$^{x+^a<76%QW5Z94J2BU2XGY1jvzSMVMbN(3wgCO0C;rR-&M}$b zt}YPdU?GIvrVD5bqS75GvPPsnP#g3ww@m+0WI>>*(ole>$m>Sr5Y&Pa#^Y+Ju`oQF z{UfF^n&2B=SYGY@x%32hB4&;aRV>4BWM%Nq6-gUA=Bm=&!nG{BmtJ{^p0n5(h-0!Q za<*0ZFSD)50S!$v*RhoF2oK53#ad!52cO~ z-%iqLH7Bo`Ub>u+M4d4D+3zv~K0|6`MN&jWMe8(CkLME>3D3nY4_cROpc?se{k0b5 z&2f0^F@0%1Zez?8J}%ZhQF>d~>VU2R4V_1y-vs;h_P$IKo?IuNnJ%zt?7ktq>rR$b z0LdB8;L{Hi^cz$%0n%~bxuXKf z+pdH$$lWNWp=G;frG{TF=pi+BP*YQ;)QYBTtI;?W-jfss=y#83i>&h@*yD+u*o~u3 z?y*V!U1e6-OMG`CTG)br22ouTBxe-)F#HwMI}cq&+*8mq60Gx>?RGN$YKzHEYzOsb zTM6B^1);ZQV@E1Jsx=uw!gR56_vs2hw;6KH+NBNJe?vzR)B>a2-CW|icyhx!cgFF% zx%DRX_9sOUi#_c;Ieorp@)JA;-(M@Z$JP>?R6(?4T@9Goy*`pVkc}6sVo~tSDz@6$ z{^LcOgs00mj-Nn2_Vn{RSe@4R7gb(2o%GM^+b)_V{@FxIQ(Sa+zro~pTZthKXmqw| z7ss35k4Vud-p#ur28y$(S|B{j3CWf1I36#!lK!y&eOcqXv0SdGUbMX+BUWUUUq5fE zPFg^1%>N~?^!M)YqO5CFwCgbXk0^12Vh_=Z*L}K^{&xfo6pO@C&%vZVp=2zw(7Xp$0K? zWcwN)n0f_5dOr-;fkfuxtD*`Wdho?=4er7)IFwH!3LJ%tdg;`nTH~~!otC`*AV%&{ z4dy$Z7<{l2?$VP{bog}&E_9**cLu1dM^wsn6DzxhxS5yYNh7GuO{%UHKyTgt3GRBg z=VH*qfBhY>M$pvc{ApY!An_*e`=y6pla!Th4hc%_=`X!cQtQOBW`=KV24s4q;QdQo z;lU(&kLqk|9A?$V-}^;c%_}TtdOZ&wBsJho?h)Lo1D0|wo8<6~E=5w={O?*ta#Mb& zqkMbnUujs~(-q|f<77n?5r<*F&32UuaYe2iZ&VXSKUuDqMpXcs>$;I-ehcSiYvZl- zKrIRhQViL3YCJq&e`jto?3J_lHioLS027En$@jd*J3v4oH2Q=hJ7%(<_0feiDJA?& zqX|=_?*@g^7wX1Ufg~n{^UfD9tU_u-TP)1VJ1C0L@iL?0UOmofeSgr#2tGDcg?@|qeueK&nRfffuvdz5mW9(&Ro-mkMAeJ)AfKS{O2RQOY z>HY7s>V%!}yVbecI_{d}BRALhMN2}zi1cOQeIIC3<*J%lTd+@^@9S45k_O&A{F9Eu zS;Z%mfB0f3vL_;O%TV-`x}G?~cR_H1ijF4@d8#W3#iup2`!%ikzL1eRsa8&z zf5r={52TCg1N9Q~qy0;rvZ91k{KqFa%P{5+C;9INoY6BUA^P7&B%J>-oHj8EJmo$x z=%xQN{y(QE`~MxW$}tY}g6ZAh{A*+lKmLDAr(Ta8vH#;ZiIjZ*pA+;ri<);>1nYlq z7_mE1>c5uLS%w_3K_wy`Z2w^qQlO|2%G+>8>lZx_gtcOEyi#Eo(s99Bc*|j8@t}P+HgvI-x5`uJ%Jk!gGXofPXdj3C@~Dfbr?`585KH=bZPt@S)Vtmc z6U%|zq+PtkE4cTo^=dO#7edOsW+2f1^)EEOwb;_s0+a^X;2uZJriGOj@Jj3%IqUW!LxHi#SmPl06$Un z_DrHL-T8!uf4f~9M&$!>FZK4-Z??xOHgzIdZVIPV zEc4X(&ts#F-jTHN!70iR3LSba9^Af|s~a}9o9_W&3jIMT{vh3=lAyTsM^!Yz1Y;mk zIHX7;o6tHbdX!IG&pR=kg~w;)uUfSUrc|*$B$Ify~6z z9G$v*x{n~ZWlv?V=-POmnN?-CAf5NS)S7M0{mkMCco$AZI0e(fC&1wnDv&u5=Du<@7m{LHuge^wggu*9S?^ z$^L-$nmzXVF_R*Kx+plLU41?+IPiQN^}52wPXdw^@v2pQb9wZSJn!R4)3M1)eQ zG2Ehp&o5g|4(S!DcUb`FYtEOqn-!lVoi2;PtWum?<~An=8E9|Ta-Wh~6J2K;cm??E z%#D#}U54NNyy<2@Dx;~O-F&WPe#zl&_jVY}?6t5HbZf<(3(PFi8(p?GuQ%~A|1KWM ze2bV@T1rLv)tDo_6_(yTdf;e8P~Gaup^_78hW5+K{4D9us?aeGx8R51*4It{iC2?p zH(Ll;hj}jH*nb&bZ`L-TjMDH=NGSh$C+2`52%jxH!p!V4D2 z8dt*w=U3ZxHX|5$4P<_d_dkdvNB}Vd*MN2i+j2`Zpy`ZiVmaPg&pQdkr_)(=t_|hl z7On|R!s2Q=Ln)pJ#-GNw?-#mxodal~e@%NkB|{6ghR#*{Y?wR>re5x3DZBige8gYR z1u!zw6Hi=0^p`vt{B>Z43T9*jIT}|(H?2qtF9jN&mhiM6M-7n ztE*Sn#oL%2kI-a}`xt3a9gBiltUy&w0J z%gj7tPtJ!#thr+(*->FtLMW{iDAAR7T&k$Q^^v0`gimQ3RV5j}?yQ+>IP{-veHG*} z1yG525s|2nCh>9DU}35uHyG)4dAOT& zvMJYpOM;;9|Cqi0t6AIrQW?0juMp=IbaP^K!bI>V^e#T#o`z>~yHvh85e|t^H|(g^ z#s*vbj|(7xtv1SnZJU_4ogzFA2#ReR}7#``&N4K8_qr9Ht-oaxq>N%&=M? z_o%ObBuM0yG_wATAC(Iw%Y{7=7eoLzZtw4t&VgyB6P-Qeh6~qwMQ@*5ZdjGMy2g&l z%{}{yjVBZjAxFm@&Cl#PBu%O5D){jQk1bt-G3e$UJ=tqbHS~M#3J$ zx;mbK4XJzj1v25(_1eDByeXebAOvprc76!1cbE|E@~*_@bU#yB(n$LoWDnk+F;yX| zIX8ygOFu%qSV1{{Il0g((}A}0^y|T1>gu!s+1!)cT3F}IX)Q*&P_jyu(r~2dh>EE~$5cbP?g%eii|@`2sHsu~7qd^9?F=`9kB3voT!_+! ztO?$#9xhQx^j+OVBez`o>wCiIv_^pEStLwgitktExs1Uf=mqCG2R*eAClz<%g+|hV zFXq%(Snm4~mRr&pL7?ToMXtk<<)e*eN4Q{9guczj;2JKaZM(ByCJLWBvvKb{Bg68J zXdNQXLKUg4*jK#xCSOY%=Ey<~XY}H&Y{EGzN~@cbcY*63iC-;o@8pOCmHRcJGv>M= zRa8kKLcCiQW%nM-p5)>A+ezP%VCrCEiUwqY=&;RV&eFy3L2Gj1rC(DRgrDQyW0>I; zZOgaNHwe9a%#V7->LNrNkj`K47#|k?YTSF9WW4-A#hMm!i)rWx5#h*4y_bq-aX*tU z#zC!#bFpKae?%Tqqm-%g_I<^GFPDJeXU5=XMqU249^~JfD!V%AzXE#>Ux{fRdYM@F z9UN53j(m9;wONoUXH2lws$1PzCt1S`JS++!MhOb~hF}T4aZ1cwJ!igOx3@#1rpEbuv-qB*CYtg7t89vmY)X@bhPG{HI+(n@+)TFJDUoI3>2`!(vw(e{*L*z2 zEmv?~PF)Y`{(P$Pdd^7XZH2E;(0JcNe>$u=%5+`JE@y2aO#ZV~SxhB~+8%W9@o@W$ zjsfUHJyEeF@qBhQ`f$M9wW|Aj(N^S0R@niN>BMowUO{8TIFtuN|^ON9wfSq;$tEN-ibR3v1R{GWft{N%}@;K{U z>pTCL?(MU0(qp9(PP#l;Wh?((4Qsy#@C0N4k&WxYS$PW!!;iwgQP=e^E^^ze&JkL^ z8z_7qN?J#9EuNfi$#v^3DpQP~ccnLUjD;enJE7mFG8p&@wtV3IE{6 zaAJN`RD92ZA#H-f`u>)mPWKY>Fb`nb(t*Mff`i?2$GX~Mg;d!92or*wn?d8%80|U2 zCtY1Ylpq{Y6iRv0xv`T-e75%3UM}#Lac!QmiLJXUx}Vy9R-*hVc8`j3RtursGi^V+ z_{AudigCHE->2Nl*xb2euxP6#m<9GA1Rc;wu>CPQx|1F-;WDIFDZbP~l!*;Zp^J#f zJ5JX!pu+-Qun4~lTUqt~{t{<%TcAJ3PM7bN*=?%_`H|x zAq>cT6Mu4xZtyey{FZ;}Zubbdy348CJ$nk#`D*UqXSz=N>YLj8|BtS}j*4S>9=PEE zArRbyOK=PBumsoO7Tn!kcY`|wcXxt2!GZ@}+})il?mU}&@Avn<=RD{6W6z#5yED_% z)74$|sp=|c1Y1M=T)K;-Yz(o>u{W-Z}Bv(_?9xLfma&M z=~V@#3`mASYdbs!dcA)PUU**DgYDH+Y+q6o{{&iQTL8K*_==h?+sq8M9j=Ay`Q~;F zwiUa+I%7e51lYKF{%xQlvx>aTbLvKC@uHTyA`x| zWpTK!I~jy#@8>I_0gfD8wr=n5yC8gK=v#8|i!*rYOpknZ5qXvS1w(nojSf223whqL zcsh>1x?Xl&OM0!cIYc{(5;- zB#-N+yT^7=XU+0);aH)hAMkYwI)&j3?r^2>A1n=0QdA&O`YvY~12v=iq z#rP@L25}^kK+j(9wXgH(uP2?@%sF!ALr;-hEH`)S>jsCvBGh*0bf~MsHZJTv|g`tI2(=+HVI!KMJ$kNPz8|^8DY1_{b7$y>tT~c8QJgC zY0e0Gj=YC7kP4A3hC{_(V+s)gM>7KlAe6&9v->7xZfYSf!O}Cfc`f>iPT0*n0zLa{ zZ+B@7=^8lF;E`Y0yP;F+N!Ex0p0gGt#R!pqbv2-vuz1oI0<~_McS14<`CUIN#!KUu z?qwU`)5XBrLW$RvMU?}Ci!-GJgIO*urz;8;g0SJ~!(0JYyk9c7-p}vWA?D?n4H#80 z7~2{p!$D%}_WDfa{t9 zyz0ysrITQKdTB0w9;m(_Zhy@mV~?_$_sgzc)r*tyTuOb6c5s>I0d_anZ8%3V*$=RCL^h(~vbc7_EbxwOi!V(Q3W8@~uH%Y3B|%ucX|$`D$nX_!Z|%<5osvl+UY+M(obF`^+ErcPnI&dgLm~boLrk3k)ATr+JU2 zKlSJ9y{HCF8_>T}Z?8y=eLJc5%|1PD1G!uoSR;U1N*_@UZd*VPCWZ{#OSGVol6~Bo zjSPeRrE{UaSG@-*ZF=+3;Q7nO>QNcm3YY#IK6`;z?JAN>T;OX55B??D2;DhXqng9w z?^oYrvcjrI?#R%QCnbuIkNw|CfWgptk*%-sPKO?Xi|&q3h7>9$bHiPfV<}L%;$O}B zjLCz6u0&Ewy0QW2GuPU$qlR=#@rQI*y}C|vzxY4eKN5xdC~DSc4Qj4G(?nLF3VL04 z*go#4KKmXZRdQU*V|0Y$Jm__tC{yU_D$t*J!vRQ&sUQBf zO)8K|{GSVqlc#4ZgfOhe=aK*KlE34j=NgAiBhIVW8(CmOy3r&MDFAQle8SSb_r(71 z76g0UXyiO_uRfqXbvHg9X2OXjDs>@Kv_Dr6>esF=T zU~0i!dFzAjthgnjNX?kAt2Wkf6T%#PBi6enKcC;@$ac$Dwe1@Zq$;RjU(R*US&*7v zmd3h_z$(6;@_e-Qez&^3Qqye5str zqxw8RWZPiyD^;eqcK5Oe9I7$G2!&=NNjaug!Lm2kjc!*YE1cE%MeC2fJ6A$fIe&E4 zb|Hgn6s%{mfHq^S@U#94%MdTY3M)?Mj=29E>2(;08JSKfK*MEI8jhZ!w zp1l|Tjw~DfYaKZ3?tP(P6hE$i4$SjqTwcsZz_RclUpelG%qTg&9S}C$xY(4XowSdv zZX-)473g>E1NyBQ4@cA-&qNTyyd*jEZA+dgDlF$4cgXH-F}PX+9kS*E{xjTGa6coF zl#0XS;^6yj5u(@r=kXS@=18=UrmBpvyGPk4m5EtozN&GPF3H~dvH#_4@Cs3Y=cJ_4 zmwg0}SB7i;0(kw&Hkc!ABdV_XXH3k6m9DcsOxfOZMP*fGYR3yy5wb9BZ;V)FMp zsH4u=o=<#5Pqqfvgf<>L(|L#Z8e|E0dfgJLD_*)?I6elxVYBe_j`Q(xhwJlbmvaSR zN7M0BU7nF(XHm#LK$zLa>tvFPcoL?6>?z~i*y)zOy|qda&3i)e_gS|I_=y~oYT~Sz zJ%N183-nv+@e}u1Er;18oqH!)UZswx8uWRXnTlre^2>AalG(glvc`t0f`YBtMW{Yw zuq626jm9J68c!^Y(AF2Whc#tILp2LYs?P-)Jrz#|1^^!pBm zMVXMaWJl;cY-tzlm5hYkT*SFnKX2kgXIk`sR3Ai&ZxkJ&rQs2m)fSLc(eh92(E?{J zX~+!7X`YjV_jkLk33+d<<3hI%^yjMAdp8|n#pnwwtT_Qh-$Z7}$oFnG$0m`rClnXd z11!jg5QU`4EemY-urx9a6iR1@GDkj{X^03lN==bT=eOx^pC50@w_bb#_`I&_YOnPy zJJ?g(7H-=t-!L$G@bT^!KYDJg9jD5n-BCUsRtv+ixhUM?b>?S`b)oHpL_<`!)uI{?!@+~U<9 z+<-6Aj(>#Bz|&oMbI1jtYQu?eu8J>yWo)^RQFQLT!pWGor!>6MvEz39`0^$^F~sHp z3E;JE(PET7kld&vFtR`Uk~w=dE8Mz-;LZAZg0)616YSQ!)=yiVCNFjEebZ#I)OByB zaRyb8GS0W53V^9(MK(qIK7u#ayT zqH?s>Ox{^D;FE7%Y?AaC7`%5HP({N3y1BK$H#$mIG;VU3L}ea{6yhHfkROmdHT7Eg zEpC3jd%MK!tQSw_zlGdMUhI*w2^a(yDx~(QAwUD{UyQ|&vX^r9M#=Z`t5bBETL$-^ zt)EF$E2>(~_rdaz)Hhk*gg@6Aam7A8oQ|_6Lq=LJwaNViz%#YW|C~OkOhjPkx>AZ#01#@7sR++_wY)=$OtAP ze^CX2Q703iu00PK9zwoz+p+ZqA@rBLP^r1A(r`ChF&mt<(=YQ@T*2bRb zM0%Q8UHmROgs&*Sf{U3k89pfV?N;>XHDe#LN>@M5BR+^x*{pA%zjh#9;n2&`CxE+> zskeW|N@N+zz3Jz0GQ1_D=(8%o@xm`YF?Lu*Tq!`7zCSLra$^IUH#ciRXx4P}q&A-p z7ajq#GSjW0meq%+^v_o;X*~Qm{6}i>%J8=D=8#aOoX$-8rUtRnOq!GlZ}O)}JO9z+ zc*Rf;gG$e;3%c1N7SSeRXPkcXMZm*7Q01v;@ud_cxqMBxHd`L1z28q~Q`2LEiNvJd zh*fnTkJjZIOS|`jpFKKAI+;Lxplp$ChdW{t!YALk+H5=sf;EbeYEd?Bdp{r~@Ae}% z)bf4lUGWDCENTAAZVmafLmc{Vq&;Mus{8_N2IV3)rMQvvDST)9VZst7+uKijdQ*?- z{IY3{rf9uCs~N^mdih*(7ZYuLr+=Q#TQeGs2H7^>S)t)6X{Rf^%MV6u;D>nEk39E6 zOFOJNEvBXL=Y2-3x4@IQHyw>aeEAmQ5K}GXHOoOQB)|*4*{>>&eVKG=K+nn*4w~g@ zt`~x4NNipa7IxwN?D9+UFyI7OP3WJD0S|C^==~!T(dEFyUbl#3%d)9M$K-ntx%GKbcz5dm#_4t^+oydG6Vw&O z{p3$Y3GPVyQOpOERon>L!|jb!g}LYyzN+-Qm2^K;*9EM>lw+4L%j&ejd@6GBjn#J+ zNKyN$ERT2MKEf8?N+00Q9pMYt_6p@_l#QwME>g-JZV1)0bfwzF)1?Ga4KPEa=UQ^9 zZZX)ZaXtR3#0uO5NMeQ|rFj_DTzNiRy4a{!-(`I&-(PZ~MwaE6m?Gul1=ApF$QtWfh8m)BdPYnxiN><}s3% zK?|&?T8unTzR0sDe3yqI)}wAbQ3aMYe+7)Of&K9Ti;15ULw(Z)8pcag3+6+JhHcHbr;o;c1Kc_YZ=m&PJfDRzMi!Z>&lZGwP0_t9Z zY9o!YZ@WJWoDBn-KC-G4xT!Ze{R#kD&J2JjuQOyd)*1iHdWvH`Mtnl&{HLEyT12{e7e-r z^HkuS7&P(Hog@F3&j^!_2-$3D#|h`pc`PA0%tSWfgKGMBr}n=-%k>1glPGa)w|$@5 z{FFHkG@c7=c>7C$em$*PG_2e3(gxmH{xCt9C&$BxDk0LluWe=_Y!<;}lN(-i2E-A2^G+>A*a&uMq|x(6_QE zgbb~*X~#*55~v(W*Svjg6otPYHG{Ai z;z&fuPx589{pspb?RF`bV{J}Blx+fwUZqSeHjVOG1zP_S6K$>$V?JIS&3+@v;&o{4 zMjk|u6fxP;Y3IgCx-^#V(S*%bMn!2yB?E3pu_pD^BjVgbPlG(@dQm4whvj_EhXbjG z6~^UP0)=J~BFhN(NtFH_8V%?|;^d|S+o#(w%Eii~LJmx)=R)HSWZs#YH_ZhH3eaZa z?E7xVp6;|70Dc?%SjLwgL3#BNe2Jv>s-_kaq9?E_EE-dg3FjbjsJekE1;}==rE^yr zMFIlrY5Ce1cnO}@*Y8nDn3FyxSr<@a^zORnq3%5F9OZBe`v-+$8SEuALhUKb{$sgY z#kJc|0M#*19kp6_T^s4f%j5Yz^t0C^Yv~YaG{MEOIBx+v|9M`e&=~j>_ffDPiT(@O z1U`mnrfx`80W_Qs4PWx1tF4PJn%8X^t!H5}>bHUh51*3&<;-kj9II>oSSa6u^0z^+ zBldjnW>ncW;V?Vapy0K_ROa4?_z8Ly<$seWRaa>8Ras$lSS@&BJKmR*< z=!P7iC`-?1sBUNVM}G>w%eKBr8Q2{>o_6PnCQxb=w7WYr6@GV|%nOEGeoF6=`|&yo>MApG zi#45spRcjBHD@Stfkoc4x7hUoUyR1Ejq3f(bZJa#zdzxZJ=Y%&GyJAe@IhkfJ*tU<79a`s8<`7;~de1^0I8=h%i&#QXKp zkybzR2Ls6szwCtrf)AEn!GL?8<0FNuU2!Vt!}BP5!((BZ|DI#c<* z;eG2IB*TKjhVKJz(NSGbDw3{UuCzt3kNjOC#|Vm$L3M<~Ot9K3EBcGen|WF(QdRVa z_qd6!fQ^XMoMd;NZT=kAob~6;h5VCyL!M$Q#?+~ z)OyEbRK^L-&To9FY1Qi`u~*x(Qaq@YN&2I*`TQ?bQx}f;#2SuCxew6Qrf|0q)S^^S z<<32F26@ftr)AE#Fah%XUi|vKg5q5Rf0RPcGJkW&6xXfpcP=s4VbvHc0!^?%BuyCAJClT(gT8D&bAE=p#7gH^ot| zN-BxttRV?T+zqz)nYk#z;+3+a6ZFJ3xeRYEhW{XtDm2RalkkHS!ZRSurIl z*G3_3pLDRuQ1su3QN8q-4NR!TfdkU!*|lxn^~JkLn5G+&!4I?U?m zIsF*PAi=K@nr12xn2v$_FNQ;dYWhwLbp4}k_mQIlaV`rbHCryK3L4_jp8>E_a(mNh z67^-v@m_w8SovjvH2nN1NCU^Eu6x#x!sc8`Zb?A~-8*co`3&Lia4w6voFe}SZKgjJ z1;bwu=oB*HN|}M)KV!#lK8zm}%0Pe=ZVs=fYWd?Vy~>&(rRVT$KXowIutKDvTm0^g zAy-Y}@j~Wcso1}=zfJ!BKV%;?XJ>II0xFF+R$>*9=w?H|@nGtZU*nTF-~NXj24T=j zX3x|AchBppD9czOaRjUX0YGm5ci1xx9fSuOmo;B&XO>aLK)uw)09aGto%2YLC58bHTB_lhM^z-d6-NU zKPqu9J@fwzvN$u+lQ+&Bf4Xih^wm>breS%;Y_d(0PMrbV;OO`tmgONSqz?ki{3uHD z3lWm6hfljqSx2XfjvZ-7JZpvp&#(jXgfYzf;g^Z!n?>uD`5Wm1U)AOqZ*9dFuCC6# zIsnfw68`~9XS(9jYgV6iU{Z4vH^zyy5upeDt*!v76foGz_Y&k;?1y=)%l*>Jy7BHT z=t1f)LKbD{n@h>ruRiDA)b%7X&g<|cy6gMikZ^N16#QgN1r7DZ-_kj(%b?+P}k-hb=aG)r%JKouzm!>mQ6BsoqL-v zY~Ojb4{%h!CXf_VTZGgvvy@Y|Y}|mIVyyd}>*DjWu6^)#`39uOio&23FMhd-m0Smw zc;$Bw)&lM5!p^^rF}7tfwO#8E;HE39ila%`d*qQq-a?#?A$AI@bv*UIHxsOqSJ7FR{@imLu}^b|B&>- zG=@T$HD~)JXSKbM=mOo=guzRSM}f%4h{$4|Cbq3Qs}^S}md4y1jS}=~5+%DknkbWn zVz3(;}2t;UH4Sz=T;xB8O$Ce7`3$3Q-QD*LqSME zft9>|`nFPyzxIxCTan_q1uRjNNIH^49QW7 zw+7=km<3MLfA1luN)}9W;xOe8O z9hr50EPo!8=#$qxxDo;!E*FGPD_X1oX9;(^-!MddTMk>yD>q*FC@q0jF0p;ZKvOaS zEbJC1rA`S{`WuIr={}U&!Anbe^hVicKe!JK@lM_2# zHq4v$JGw_2zf8G7oeMe#3oad@_ z?8PQ?^`)dL{QSBfr5ShD+-dct$`_KnA^Y|S5zot!_Llkni)G;vEjP02=X+cvTF+Y7 zAHs1AzqtJ1bK0egAXz0|-lrPo(ZUD@+>55-e-Kn!EIebhMdv#&akHO)^=usj`c2Ur zDttnOp+Ia{k;zzme6f%gA7m|D4XBG3dOv|hQkb}Zf2xm*gYM$c67C{9OAJx3xjJL1 z;q8q4@bU+y*WqXQJt;g6e1gnaf#17IfAVyFStt|JItie($=*=3X=Za-S-eZsJqqa~U;fC_gZJToAl7y~xv&*W+;>n_y*CkVE-rg%b6}N7)Rb%O%_VWrUuDT$wxT zl3NxuTy#$*2E!TfqLB$8yBSqffe|*+e7`nsuxldsLZrCA@gqDeZkdrygbb;Ala!D?g+P9{F2Ed3f~{JTDFIo697fHI^Vx< z8I>;wv<_O1s003gkO^5sOY2)cUE0goBml~`DvBHjm`90~zriLZwJN8pn0`PV2!zS+ zEoSvmduUly{-(!eGH%h~GycNYf4zT_LwwDcONS)uE9|p8LU4?fYa?Q9&8()Uk-3kL zEsB_jMkJSXA?Q$*BeVDw5YKg9w`T~yZ{TwEGgg&ZKmh00%4y_u_loe$fzm;#s*EIV z4S^OTCvm?QaU^{oS}D~MZVjQ<2p930h)u{rf{5)hDH>gpe5y5NlNk?cGMn0$Ce$9E zaGV$QWlI7;TDa;(<$A8)G}?_@BFFQ}mTFCxgYf>mc9~BT%w>LAVoGMf+SIa@XWl!% z7gvUnovhUj-bsjC`JjctER7}5gy{OqVHdsZCM%8pSAQ3Bn9SdI0D|irSGblgl%cy| zj6wE7XbgQ<%)3E4bAivoDuWdJba)FcDRBH2`rhW={MeS2FXW!IQzjqj{^KM1KAz<$ zIUU~@H=`@`TBDVCBR0#8A+wsS(L{5K+e6dc2)t^Zt|+IvRcLKtGBSG|-1MIIf<~Vzwyz9)c^WO>OunSv`?4kyHR=!{T zv6*g}8>+Ts^Ch*!N=u$EW;S)Aols&MHL6CoVO)Hr%4p=)FPB$H1v4EzmA-W%CLU#a z#jwD2(GV(FLXuKi-GE8*!I^M6WdiX^AK?K)W4*L#;Xv4&`P#7E#-@?wa8PYg5cRn z)Ew2z9%9v|P>y3Yf({~Iho_GsRd_IB|HxO@{lQdKS6>jUBHarL^{%fZ{bc` z7-QGU4apx)a#Zy|RWCzdyI^HAmpa%aS*{GNvZ*@fknRo&fH{=qqbMc zsDe+H*!u=b+;k2#DrrggNQOsEroai((!#*487CADXndR(3KIgZic`(PBVREc6&J~% zBb+*nAU}6kn)q9pFf@X9b{V&hh6agmHI~*EL`9;$yB9pAvx z=<~yd!uDw|JnFug$HgYtd;9zg*kt=|oi+L<86Ogad;Zm=3_s25+=R+%Rd=wv!;T7t zv0pV(rr)-u?wm22YIDqO=G;n*k*>Ibvpdd`e)@wC+acyr&a#{fC&!P64R>yPSRBUQ zb@Z64((F?t>70b%}5i9Ig&rc|qbqU0dM+IFrZfE*D>im9eIWtGgR!{K3-pDiGlM zmbMEr?eMz$vug*Rj1~_%>Rk!)-z)#izm}Tmbe)9ivqHTt!*3l8d{8-%FLmD}WO*Gb zv&GwSp86)-qNT7og!4R0jZaK+k_5VI!?NkBe`-)e65}#b!TpPYAbr4c`_}AwIVLK~ z=!S+xdU~hg5yt<>R1|)a;knOpc&enM@$DT#p`b4^6~4N zI}Q53#H+S|TBUF_*Ys%6wzoLMN^m7aV~1+8A(&^q!lJK^F?Dxr6@iuzbW^D%MJ_$6 z7hK|zw54A73123TD4r-d)(Ac&`YwgSN0vno z1d7Cy(#EB8+48FO%t&NkIsL=FINJUMbvm8agA|;eTOVjGo{{Lv#AayCIo3_KYA}nM z#)m47bpttmKrO)#PB@{Wf`Yumckc+x5<_ITjD~?f%!0nYZh7kT7H1$L&4-9<1@Y*B{?fQhd$piwu zZ14tmSo1*lZh!KaL5N8olY0nX-Jl7+Buw!1fYN5rU5wDl0#GoRR^ibswQ?C{+-I`0^-Vtati z4bWh>FxFWKlpU5b)KsVeenuXY#{F4SQqRuWz8c2nC2ApB{(4E&C(7pN^|MORMymNZ zVelGtkPQW4yv&5oLv%1%@8oE%6IdNfN{h#~nW@qJ+ml4cLkGXjm2*$i0~77&4rvzq zET^y2`v@*rO^*w+WR|?MEBj|4=2n57*A*z{W|yt=AxY|9Z577a9Y3nYkU_m4W3eUl zndrGC1$E@!?Cob?2a?8X4F;BM|28Gsb|du1%1@HoZ3Y8xYis-~j}8dRpXYN3*3$`& z@0{d?z2A4Om-Z08fcZ>9H(SGr~&u~2c+xveLNQN8vSxZ zxSQV!{#Kt4CenlbvSg;_A>5!=Lz8t;NNv0`k=lg4OmzP(-stN1@NM>P8i`42n=9YG zHKW(%qFdrxM`B%W&Rl-2T2uS)Ui1eUToj9o_>r#@HXUsyqbSAp-qBr8-f-!Yl|UQZ z54xV;l75hGrmzrdMUFt77skbVaVYRg3al>?S8bR9{utcVJkMi}9#Dxf?%O$RG4Kpu ze-RAM^VD)2fkptSx+KpBz-VXb3oZ%|50Q6sf1ngBep<317-^OJp>;oJ@^pbpM@P_G zF;Z=6&)sSI?XwOWrH_L;EBW*PsS9NAluIEd3b^+0gzvH4M+;?>)!$R}O*VLEKUbG{ z@??J55bE|=*P!pZQ#dn-Un8WgGsCS8p>cT>^q5D;5gg8%VGZ3N&ChZTzJtLnQ0sM` z;pXp%DSKxw-=~8jU-V8=oO;dPkVpvMKN4`L{1GTnwMa8TDGrMkv9~Kw4%H6cS7cX- z`{T*b{+W8u8>nRiYqM(^!LbNm-y39XN`{{Nab+UJZG}p(p9V)B$FpL>dax=!dsTft z#$?NKMFAHDft#pMSMQph2EK<4D+1LZE@FvtGrO>nCd#e{M^NIB`z!I@)yuJ0Q_bIz zuDz`#sjjreQSb&W7WZlKGRN}y@bX8$JCkJ89+z8?Uy^+0*6$S$=PU!-x%6LqjUIC; zw$X76&L!9tUNdA4(IzD7i>D81ZKR)5jvuIu`>@>G?Sp5a8wed>@ZzbA@oW`#3$GOG zgkZ`O)Tku$CnDTzsj&ymWB2)?q7Zldus;X#igwh)mz$-M^*y9s2?h|oy%HqbOre^> z=+{>dA2frNUx*x4GCgl?(22#g;n;S(MgwR*r$K~%2290Y-8K1C&_$_|91FW4gAZ4G^7{P!@wq-Bz5>@Kf#wgGTQ9g8Y+sqT3R|!22mc3k>|Tf(QG!7?ANuaVItS=!TlKDa+l9)KsjhMiKJ4QO7F9|RHTB(PiWZo+kaXE1SZ z6C+#9WQBttT>>CP+7YHcBmy=GQGyN&Y{mEcA96aJDVkTB|KLkKusD!<(C3iMpCUAs z=|2COsRHfW=0x}m_ph+;9%Kvc-M^#jdf{{XYl!GxXV5y&exPxGaT%U9FdTN0q71^Y z;8mrb2R<|EE`zIa#&o-zEzsImh6>#tsa8ye+5ysXd-YH_nkBnZp{!+o_4LcL!lc~a zFH=FPL$(le0yi<A~=@+7;r3()GN_v~_PsaeQwRS^2KXHWqqY#$AkP`=K`PHsl#lv!j97o0b}bv$f^9c)Kt&~U@ZP17Wf|2^ z?^Wh_EJ@=!J6K=m{G&|ReSQW_?i3Wx(smbqM(`E-&nK1@|c=@f8 zLBg7F!(P!FNv7hqfQO3dC8B%!|g?a z$TD6?DyoGtcOec*o|@*pr#D#H!~7;2GX`tc7UeiD^*7Dew&Y+B*@19cWcn^+*rJZx zaIlW>K6sS4zU}AI2lSGfWK%3wTJ0{<0!rh2Q-~{r+;SG?gcP|i@KoAYJ>MtZt^3nb!-lW+id}8^GcTgj?AL+4~wvidjFB6kHq~1 zkKAc%UW(pT73eR3tsgZo@3AGE_)F7P3>t6Kx|CwCzCJI^@|-x2)hAHF3)KJ#Yn8Bc z%7nm=g)w=DR3G18ZE7^^QF4+^)qtU?UF*Z4C6_cIy4S~S#&)ZZQY8gk{$mO2*Wj-p zG`>N_symleg9~3jQimteuxY7m51JTr!u~q2ZyL)gh=+%XFx0nfHuB$BLYE{HcB8VEJ&Ux~8-wz=T@#ja{vU3KH2MYVF z9Apd)zaup$-97-*dk66gpAU|o7HJ)q1<2mu7EtCJvW$;v2~0je(A+sW*YzmTp8b<2U``jb(bL$F~oK z*#3AasIy_%HuYPMA`HY*jIM`&9!O;a><7+{yb5Od;}0VIoRUmwAMvESfC*o|G!WM+XMUmEyMH)odwJPm13KRr#5_XFU!UJ?eGtu5 zC_V?hKCYj?1X@aE2Ee94Zj5df#;j#Cduvje#Icpvs&yz|Nq==Q)7&#CLiJZHFHpP= zUHK&8tde@La%WZJh`;AIrH`%;aO^n7TnuF`qWLn^Yo%Di75RLw6;MaI8R=3!g1op6 zDshq&n27|X_lp_vBpPVP|!WKoGCZm;RyC82ze4h(VbfzQ2TY(Rx zOV4P{_lo+W}GJoNRPmpBy4KjPfk@U#dwGPfRXY8f1FqUbWZ0C~fp20#orPc4k zM48ZX=;0~A&wwp9CMxSdcu!C2OT8yJUTt{OLZ8MSfT=1A!RxnLOv<+mLAM}vh>OM( z4$Y`cyqW#0#6nBq(5Uo|{NZ1+s)D~Y{h)0}X%~YP5?zy@Y4}RVja+OQHh_vsyfh{5 zqK9ImPK~?GVZsZYVVhXyufcM{yXr(S0dM`st~qT1qIAaHZHAn^a`Ij=itzQ##}D2} z#tkQvPGeWf6U?;bOX^rqmayO%Hc%PODMgI(qoH7@eEoc1DKgsF1-a3Itgv-5*Yd_v zGTE$3QNJ1tJUVZ-BaGAI8~9J&i9v%O{#t%2CdY@1vcCO+xUsx4+DjRWvkh4? zy(Po1WP`6c!uAkD15v00WcBxB!ishJHZclSO*+na6~oEX{n{V>qvc}+wmObplw;i{ zD!|?ELB}HkSKGy*vkAmvmMdbD9Cs(}gLglTzU5&rcjO2)D$wY1JAd{mji6xi@oRj$ z`GspHST42QcERb>`3KQ3bXu9P?C$(=Dl_eaB#?*(yUO4v^1uIw?juP2 zpBwNE{p#oLU$$plVru^v#qLV`KX2o=Vgf@tt^af9J^$Teu+>L!I@59Kp)voPiHaf! zpNXDC>DQc3Na@MHu1*SZ^mq*Fe*bx3uOiv{=L!51tss25=!aR*ZVG5o#Pa(<^;&xb zsLdG90(3zEz^@>OHfN0t(b5(G&2mSx~`6gGtD~1icdSh4d2^)@-rLW}j zTwKI<4bOMJAaK(tDwBwb#&a2D4X1)TWk2qp(|BIj7dY}0^Ae*GXU9TH=B6>W28}#x`Jn0B2#8)Uxe0b%kmzeQCRIk)j@MR8q~C31 z-QEvI9-1u2^zq@|Kiab6{F<^0DYZYqFDaD#*M|sb2N*9#nkX^eU0r@z$*fZ~B{?w+ zL1G*sU6I?MeOIc^iF_$@`D?`hY$pbY`Ovmt>-TKhk{3)RQLfD7jc)maKKJmYj1+KG_i4m|;z)&mSC-urjCC98?O%799OwEI{k>{AwNO zKVP#HQ9g$_YB~r+;4aY(>{S)#_KQv1yRs!*-a_EZg`@iVI2)c=afh_t0iz?cZyu-yaL$PEjr@N=#PVzHktf)7#&VS%F;B-k;5< z@f|aqwVsNak98zDJdFtHy4^5AE4yT5$rW5vzV1lzWcR1W8w*mFxE!YB7%R3P6Nh&w zUbISV(?d&sESatE&Qc0lgyAzgT)f%~g_cJkF$|3Drk_6r!lm7*Lu0sBV^+-p<08fr zO%NGC?Ul;#;HNy>73aP68v%{SHscAx`9BWLt9=W@mvkf`>JNZFD6^+&CS0*~6&DJ? zpVcajqw|YbU%N>H&;^RudTxl33s1nVqb{yZPEBGGdZ5sL4*Y4lR7y zblsdOLNY;9NG@D@lLQIrjC% zK=@sp8@lu5N}OlJDo&det6e|3X8|8}U{f&JDE(H5u$0dX&q>vXFt{PE*HD=Rmdz?6 z7Q>5%SV)q?YCp9LP5ptu52&gOg*7Ff2u^YYCJacm%clz!=81yK)jS>J=+#*gU-M8y z9blndN$YMg;0}Q78{e((4K_37_L%i*=ngHCy%N~Kts)aORKL>C%M(e))h8ap{p@;v zJ<0O$RO`q41-d<3U3Hy0;KJ~Bm?}j67{-a2Lx=Rhq^P_}0g&hq(EBWbsicH~i6c>1 z&Z!(RGvR|oHIw)Soa}S{XrjtZF=Q{z3bTLf=WFZkewLZW$Ki3-YBBEV?58=k2*19VsNuXn->KL&(B<7wfp9w%($xJ+wDzwECl3X?GlQegV2_M38myMo7(FTYb!gr~Juv{Y1MU;+Fe%uGmTlG#U{Ax-M7aGX0*Ze?yvq_F+{W z#ml8GjEn&T@hzcvyOZD2xhQo>*@(-p3}?l}2SGiB_DLNjh%wsx$~06a;M`ufXbJYc#b*`=R>sm==SCz6Lucb%6pv3kXX z+XLlH-?PbjLRRl?!nMP@4+u7$!x0XHdBDzZl^PcJ!os&01_U{VyF-zEXX!E-B#M^@ z7A1ANkrwyJ*b6u>cmm*oy2ojq7T*KswHx)-9xzzww`M1}o{v4b-p69v{1|k1yTXw; zRFIJU`_^G>Pi-BY7qc}OdGG^k?GiA#B6g$`$Y7)Im$ggrYCat<%=bF4#!r$tRDWrr z1S8e~K(WCcDtD`0>;fB(lxN+c97tJku~YnwSxhV53PgJCK*;Ofa5o-j9T80U-kX$P;1lME~rJ%GrI3vPBU}n zKXmQI{ux|6?Hjb)?}@!t(z)4quwO!uUx7dzpvP#vHPDefVq7qiu*Q9anJJ9tLs6nL zdJEwf?^hdhvis1h+rfJeiuK!l?_Sau14g8a%N=43?+a>tV9z4J+IVL|V zB-(cs8x{Sc7e#i02HWU(l@V7hpz8|90pDk$X@OJn{5j4!rdOV$Oz@)db*?YZ0yjWx zn;}o_SU7Sz`5a%5BXMFvsk{l4ruRv5HUBuLFXDYcngcQTCq6O+GiSp75ri*rY-)es zKYX5yGbc~j?>Zvn>meA|hdt6F@M3r7=LF|P;~^kLDdy?643DMosc;^W;yJv1KbCO3 zA6I+%B|CM$*Tsc)ib3-$ai)T8j}OtU%SUJA*rLuMY%j-Qeq|*zE%Bj?yj%gmqtQ5jbL^=1bWm3Hc*X5M#3R-n?#K--Lv` zbyuh*Lhf={YE8IZr(G&BPch2dPjFOZ)-y=I)c&lZe;#+gTH$;z6R#Y_PM3t}@HH^k zwquH8s-|@9?h(g3PbIIsiZuVzJLNC<;_&k+Bbe^V4wKTjaRgN`Nh6@A(F-!gh#Ty` z0=nT)96%@zvv_co%Qi$#pW5D@Ny;l3I1)?cyL7udXZZe4k=yOfUB4vu57@Wtz7Q`3 zCVUw`G6IvNGn7O1w0a9;XEJ;E%x;fgv>cB z13Ms=_jt!+gx)=@@q{hWd=KPz zeyw>Bw_QE{2(n_`FiVz+6dKA7WXl9s^mI&HaCo>fX9^vF^<3t#ArQMDuMvE)WoL_K z@^J}0$EIrCthNx&-GlV?aAp51p^BB0-dqaitO0Z>9D&=RtJV^!rYB~6p%y&M<(TBp zz~E%RpU~0(>DE)H$qeFIf>Qd30`Je~NDi_7AG+Q#y0WJ0_wG(QX2-VebZi?PqhqUM z+qUhbV{>;pwr$(o(LU+xy6@*a?-=L(w#KSGYS*fowbrcp`_C$}&l`V6d!6|J=! zCtKNrB%!W;A6%-#eU8Bw$wapEvf3P2+P>#%G}8ZPP9Y9ryPQiE4)!WcH-#{E&$nOQ zon59e*H_Ds36a`_zC|whL&~zhIM@L!``owz?oQs69s}|veEd+CQjTd$BlAw>Q_l}) zzc{FD85lZ~w%VyzABgL~QVs9nf}M`|)c=D2iJ zR`t{&-m)Ldr#F{zQKW;B0xB*CtNKK_d4~C?XL<%k7wU-em>d9e?o3mYpO0Zq&xrDe z%@Wpl@3`M*y4OdtCzrxI2U3L|*lQfk0AiwgEgDzhXYbYwj-^$*PQfv@+gnp}(`$Sd zoakDzf4`v6(!UW1MO0n5q~_VS;3fzsfur(!NQ_r|R-;@A@bzC7ir~y2llJZ_gRF-< z1yfA0brF1mbIS5~>g80C#M%G_!HoO{iU zBrEC`k>xz4td}q8pmP1oAWAF;TdA+HCBlhBXv<>#^O^ZT_R>&^-v*sUE~?YG|9DED zYb?@Xiw=VhQs2pLmz;#6NMVVaFSSBO#{bnl%$>)imVO;rMl>2BJ*xOu!4+$~1G z1|P>73es$4Q^}yTtKB)^i=6=0$DiVb+svN9@a z+PvpPiI`5~p~ONHfjQt*sJ1g(b(Fix{$=<`@MmXG8@((w$@g`*dbj53;W}#P;8J;~ z86-pPo4?BldeyCdBpi-Job~*9oLuI$7)zoA z>RlJN!14#*MdKKm4HKdi3S1i~h6Rx&+S$GilB%0d6%Za*;ar!OF(*O%_3c`v+ALDZ zJa&HOc;1mGAa0<+o>K5uu+-TcI>N>y(CGz$C zY01U~9vCHFObdE8 z?9*6!V$T>cSLVpWV#8w)f~blIG;mjNgFkcpSwsa!*bsi$Q_<>6=+aGEaEs@?=}M-r z{ox(K)8WCC!_v9?u7WeZ?_!5{HD_@WVCp*6_nkri4T79Eidg0k7TDGh+0G`d1}|$& zHQdP$$tYuBA#)HN!GMaZ8)>1#$+F4V?bAE05!1_*$#&P;YUPftv4dE-YWBl=MPcb+ zosBvU!pX}aHlH&tCKBOxSs{fkw`xzznd>OHJ3&Fc>eb87yTcj5oB6Bi0#d0T&@;>f z9|6`Ms`iLJx_>Z8jcymNDyw-8KOv}A#+28y=Az{pu}PI_`so9|OT^`#v$D9kH{2GN zq1f)+9*dt~;MKd-(xS`@7boCzms!wdXAM_FioX`B7BC!RWn#HsV3W$#BRR~`(R?^S zMkYbx8A>IZ3Z$rm)N?EW`-^nq|ZPj>uJa;2J6?xZvhFxYC0zQQVSGbEzu5^Q{&g>iAmHt1&j+#yMDYE2Y zkgib&=@x+&N2k_l=ewcTH%QnM4_5a~xi#2w>sjF<(I&@ngG=+4U*08LP@Fknj6#)o zIgP%^y{;>e#PH=Z+X|zNXPP+Ni9+kzK9c;LRr=K;w>3U&bCoFAQ6FoUlRG|AWhTY? zy?D8{PBv>LRBIWITVf89A4ecoZm=HOQXq~$-*5DDhKKN%&2Fa5`swshE_#YkxKc5;2k_?{%dMrC){s4aNfdJZ|4N&fA4Y8R{Z)CfOj%5Ngv6DQ_ zcDXiiY-3O+(-6YVEhhYLNX}Qm?zYz!zYTs zeZY;=#`9G5aqJZ&=A|aR9tG=a;E#I4QgT zm9dVRYl_5lc;~SoQDrt>pRei=4q+FqWO3UQI=1@GZg&@H^xml7_Um&|-FfumG9GV; z0#cRHeD37z>z0|<(|p@mQ&#*0t6D9;C7-~=nR4HX%D$I0mI6cWV1bR@^zN&=SLapM zAy51-_BQW@7vAXy=+a7ovDI88VeQ5cXxzNgo5MLyO)IfY{Ja- zZVV0pN2IqwEc4|Q>;Yggsi=^lZttG0SBMeeWx7?&< z_tSh=BICJ8y1jm4f=%$OGg)B5<)<&PabVat8(;)O8U4SnMpW${(LA}mht?=@xJOfk z+g{Go@3^?q5!tP#E~d}Fa3GCW0?>wZFB$jV=N^yL8H1(Gl%`vaBUaVw-6ub6&{m!LpDv-r&_6U22^6j=h z->kmt$DX4@GP<_pji%}+(VNoGJ#9`^v&mB{r0(u5P#c05d#M3f*DH>9{CtV>+uldt z-aiS+1y*M;d3nSh9$)FZZKsoG(MVlsy6z56r8&&z4EQ<-RJ^!tqt8W9JM=z11@kZG zl<}`U9B|6QK4tVYy_K)I(_n^p_h*G#dT^8{&p&mk6%mC@K4pZ~oBq_c@eR=89jKnj zt^MIu$U-CFCv?eZV)z?HoDH6g$?Ka8jhx}I_M1*NPek&E^GdyZFGo*t)lWPBba0Ki z{A0N83w5J;LXT&c)mK$BXj;q8KWAoIdnfvJ=PD~e9*JtJPC#pa?|X7Ztlaq4BjDRKxTQymWJs;+I!jp5a-)CjC%f$Kcu$D)jZo%5{ z0c%K~@>T*8=8m58HNVwb;0n~NPY^1l7v*sMu4q~XzB!3k=X0KVkqxra&i-mv*VAo6 zn;0mHPsi%L1laB#HmyFnpCdl#(2?$Tz;Pk2hJZ;cZZL~FPLusIGr1ednB`%^El&us z^G4{WNOTdOc$`5G6&(-)Pd8yq1$YF zEJyV*MM+qDrpk&aweS{kC^35Aj3q+rV>ipG2GZ|QehOIUb})N8>5)0OqFtR#Qjncq zV{X8-_|7gS9_uC5-wr8-U#D#6&V!)RuPdf=K7&`HHa!+Kyv;?5N(e{x@Rc1K{nn|$ z_3rroGu#Bfcl(Xy{PIE^opi`w?pUA}lCmZDH+y@+-ev}KB>zF+pd zu7v7S;49h=Y61*GxvJg`v@Ocq9qGPSx6!7xN7Qr7QGg!w0fATZrE^Hy1dHR=+w1-1 zJzdu-DQf(0h(NpBwaJzJ=uYMXCH_d;KesY!Ecxrv0K$>FQ*!Q&YZxVn`#~$FTT+0j zC|9MdSn4GGiPXmFg8%vM?vq6$;AImuzIJtfN_{NAT+-v#+08rL$urW&32Y}>pzL?t z5ynVWIeSVsSy>o`i~5?3Bj%*#1cZT{u(_UQ(QFUVPxDE}r2SMO`fPw*Tppq6;=|FT z%xo;Zh47HbqD7B>`E!kBb7n3^q}h9)QV=3Op9ovK9;SS6)Rl;`)u>tNIN zB)3s742g@ogJ?bZV6J+i#8eeoI?n~a>3o(`X4;2qep&0?v7ff?6tRS?^=BsJob>_T z9mxix#{4-JMeppb{!5FU6SYXE*VoU?bCYIMj<)H(T-O40nagd+ND5Gx3tl}EtUi?7 z5M(fC!!YK7n|mEd+y|@U3D>)$QCW*Y%j!1izFDNZ-Mi7-`~*qrd3S%^)q*h)2Y=N0 zd>x$~U*@8xt?AF$F8DPNj&~GP+Pn?U15&q%Ari#Yx5r}@-fkuV6(_wM?-xNMmNScI7cQ${`H=LMCYJY76mg<7mlCLCAGpNGvl=P8k~ zp`i5L;}%EhFuW|36w#vTQjdk4%Ou6gJxfEP=&V{ll8=Fg!yh>U$j`oa>idhf_V)LuV^}#N6P&s=C2Pr**Ox3wwmVCn{=nja`h= zx|h}0vaG{@W5dVCFC+%dRO=XpfyI0SPfzFOzh^TZVyzv@8yGbvmp;n4Z4g;t#>%#U zYzR7!2&xWDAu(v#kL9DG@>PXevi{ccggsS`C+@1F}70-Lluz@s;nqbS_`hY6-LsNkY%@3-j@Iv^_GY$!^bDK!p8--LKc` zeNVU~nc(O7LSk%`A=09ThW} zHQ;Z=iUw;ZIp^=i7qA|L7?Jt-I3|ocWco>!1LsmD6&3Os#64`w=zIJp$YZy~+2LO!C^aFw;?!SwSDk=?8DiF6qGu}Dm+ z?TCjxh;~;(gZ@Q4PEp_`wT43aEx_U}J>DD3FPDHPBG(wsuPY^l-nd`d`vL_1tX)jj zzgout?V(~rd?7+`qI9m&6&mS7UQe^KKCLM#;?!{) zHBpCCPs>qO9D!R8;K#FlVm`yU+WMJ)mNEJFmlrPMOrZ;eK=H3;ZrQIJ91Ca+5;>$C zto~6Ej^h7zn_n0)O{;v8E4)(AeLs6h*+*Z#;)mT!U7zu0hpgwT)I?x51>Tat6G-w3 z7O>ahk$1S4L0|LG2EYg0NUQ_=D8*~GgoQ%CJF4fb0>Subd^F7e2_=r;Usvn?GcNvH zkc0WhM<{8@0h_75@JjxIip^LnF-9BH4*T>W|BM|cn>w~=@MCZu9Wp`CKhEVV--m7M zy5?{hE8p0qGhSj9{B!$bn1#ed@hZsvg?{$nyY^hcV)3*a)VKdr4or@={I3>3Oyq-0 z{99uCQlF*H<}6rR1bE z@H0LinA1&_ysZ%7qfkLbHqPg#*6Dpl`hxf3O2C!Xyw~-casWIHVf_nnrYo^{@!H;TOipRv-O&jSZ2*ZDjX9xh#-7LGE<1$>*!FgO7Jk~iD zthvUvBQ?!GWdw&I_|_UaZvb*Ra)`U5oO>*g=at}r0L*KV{|v&HfImuW?|*z!-YC5+ zyj__R=p(>O!7UsWh!R$#7>4Fs@BJx{=`xv8sCLKwvithjXy&2+Y`qVn#{q=iGgSrd z=037@dgfr-<4IM})iEt}&y>E9xSGth zVs$LuBM|I|oYe1qWPU`a$VzyU=;xz>Jg((bQA!}yA{%}3u4kcS<#JFrzPE!;xap!l zYTBFD^b}*dwlaN3e~0; z9N{TVHag!36%11d?qk99}iX1h8b>hMA7 z?j4o(mv7lH$v;!3v>lTpzdiQ74}>g-#u`qSA9%o{Wa=eai&U2g1ani+?~u> z$4m+cFYFh6E7LmU3dHCS?-PH0X1jF7gSzcq7U-f?X{;~Fnkg(}&4UYh~Atf+IE zlw#K8#thwib@vP1avx`LJA^wGv(Vc)#&5>|o~FlLa=3v_uL!Y)&3A|uT3cY7YS6&2 zz3oT@d-Q@{6YaZcK2gVQk4$(#W- z#=6_~Gutc-=eaU6Dm;kG4{wDgRF^jpAjLa6hV$joVj5lZ?=8aJ#OO&OmCx#wz%5&O zPxfLPQjqY1@5Ri=H2cjOagEHbSh))AQQ|#bS33=F`K5HhXFPr2ku}eMx-e2EKfCqv zIL3VVMe}`JGXPw-ls0>>k4&|SW!|nWGUv0s`wL3hjYXmfPT6r<>sTA}P=mlKI=$}DS^Sgf5zLub(8!3lxCf$d z5QPvj;6QXz z%h25X()%w~7-xkiWlB!a34#esSiI@YA2GHNa$*?jGHR}7IeKP)G8LwCZ}O?N#TpIX zIKb1xq7>H?oRtF96v!WhOp$BQTd#j@g(VyFkfbKCrsM^((AY^-yswx$bAsvuBct=| zHky9uZLDyG6GAc0TKcWf!QKu}FdBv&y|f89Jgf?}a8uv`TRSP0w-hGfn2+_taAj_0 z%v$YC%Kf?j@n4Pca;cWgJKLURwhNN{?eX{V%o-VAP|litF8-RO)7z!cTf`}7CMH&S z^iWT9Vg@6houz<3h^9u%Dw>kO_W#f`0-6 ziKLXPALYmDsJygXN=2$ZMMYqK*S@+6l2~mTL zmneX-o=qMQ68{|*TZ0KH<-*@!OrDU2E<8U1$)Y@aU9OC-&zC(1F8~H^{0LkhVI;CTe>M^+;tsh-HBPO6I!$8A~G+COFlP7#l>uQ>_d0X4odpBIwSD-xE zbEj!J77qOMu~;+-=pq+59k~WeR)O~@NKW+Paq@K zEc}+OmstA=UrvsMWku30Pt zN)|U?fwbHSzR2H;zQ_+2?M^w@?@WG8#}kZ>>%@#bs4#G{&`+KE_08ohyfDwv)?&)J z*32%QlNNXxhrX>y&Zk1M+v>mP4Ld9WZ%lDWCTUEdT6^XUbd?;)KP+CI)AK34cO)L%jrAuMc7GaWKgaqu6E8|r?nn?8|gV_!M)(_ zpghmBi>|sWuS@1Pka(gm4$HOVaPh?$UbDdD@q2!a2>5jxH8ng<#v70R&@e$(zXXXBgJgBF)}8f>BQBS4813pc^$HP)!VXEAx(ay zw;{&GD=4}if4FE{*!}G=mN3kH9*ZT>oaU)U-!8Q?V}kkSFe2qW3`rJ#Qvi*AA5)W2 zSbSNWyndaoCDT$2(Bj!-u*BFwH4)C}=%Zy1Rjx6pb|NF!UN%#d;eX&YOEJac#6>}{_N=*!-V(EoA+J|J3KAHeZ_i{QIod(L9HP8{uYqpl4i9{)gyB#dU0 zVcB{D4ugwVV2eO<*IPLryc8~@eZLke2&JYJcCh564XN<}%wKEtO_5yp4~~r>oEnUI zQK6>sOd$f*`1h!Y7*`pnjZYat?8*ZdrABe}x7i#&lG!_jf2(yjA*=qF!{U*7kHdNk zM+y`sBg0Oz(otE9`ycjfqGlKVaOq_DmYJ6mzQ0UdE(TKNU^Q35^r&RvC%;a=Mm~K& zf3+{+i}hWJT_F$+`nsmkda5sy{1D}Bh5RD!JD4J&&0L$ITJZOkXfN@#MLyZl*Jm=M zH&v=XHy6Mkkzqjm{pRq{*)(=1^dR3rN^*ebTbkExmL)N9f9zIXR!3=aQIQjvH9} zC10em@mjE;EltH0q<`;V%GOSM`?TB~F7UyC;qEdRn}yfZkS8Xc$S4+P&9H>WP7;-M z&wko#_90NpdbM2+k-B8`yLW)HHYNddJZ5%QTBp?9m585K|!5? zF2mWuL*sf*UG3&T*_nm@r9{+4^`@FADfvMDjf!!@T-lI2x#Dn`QQcDVu?OK8F^{7D zw4)y{Mmpal%q%noJ@c<^Darn|Er~b%h-)P+;M%c-#VF(D^>(w5?Ve-gmxSb_S!*rtN1wf#d&o$Rj_r)`e{TpIlXq7uSEY9ORxB#xc|V2im15f41(IuN1tS(G{@fbpp)gmzG*J zX`_MT@P6_uC!E-~YREM7pA;Q9Sl6-2 z;q4!1-Wl$-nMJWV;8Niki;1iK{*e%&k!Wd62nbB|Rm|RRsTe%8I#FCSX*Ct?WA?t` zav3x?PmtZ0pLh2V*Ifzm+g}xldbUG(INRh`=7IcF>_E3G+Ol zIz{;ZjiD(7qm8Hiu0{FpNox?9!4YFRnPXB>LA~|q*^;0K2=O=9R2iVYNh$~}G8M@c z^i%0y5Wapu5D;U%Yb=W*y&5l@!IfaTj5NGbtV@^0oBDm#a9B0K+B(-f`!27aQc?ay=ALAJrR3hWYxxJFlQ2rlQ`mFy#AX9Nf1Me=hwDls)bdGtAjm` ziPTbZl1QV=x72dq`@MC$OX9IF9y>zaA4lM+U3LbHqe&JTAYIS(O{gjf#%hnlx&A8L zuaCuEpDPC(pKk1KUP3VMr%G7Z&J6QDu@cGwbGn$-oQ5TjKDK1)>i`uvU#PY2yl_Gl zTTpTbRc~Jj@qkzA@-aooEbfASJWnAltGwxLJiCzHgQOT`K9=+nXw2)lW$R6x2yd2C zyWOE!tj`=cPYWfBN1?60d7%S}x+2cs?7z)~DU6X2F@2if7M#!Wf%mWkld?o6=0Z}; z2>uc&Y%l3|J3yPxL|xw3-)HpwylCKje=#|eD?8`$MKn@a(f%vc8I|eSPgqRGqWpq* z_v23hFF*!(I5!uPW_OQJniCw%a56MZ(vY+)2`y8Q;Igg{(w^%_T7IJwe0|LL_V4@( zbAtBU4m(0paS_N=28~)PH57SkBhY>Z8_c7`O_5KIlv3mC#fpAH+@0G5UikJ7FwuI` zB}p|fijRG~?TM=DOsG9YR5o%!{fa9X_$;)%$Z5z{%`Fx_w@rR5 zS7WK#LF@CAHlU8Scjwf}XrH9!*Pn!h7bl1E{H~HjToaBHYOz;D;78b^gMxuqXE*CZ zxqVMz&G6RW1`R;3}mRQy!QrvP321AhAE0h#?M?bLiP z!X#OdovF2RZ)(ScB~d7ah;y&J$OwMA#>NNbmDC=umigcvUcA$gl3~0j6Umh6t{Xl? zz-qS@_K4fp$JV6Ea4#F8`}M9aTzt^FvxA$GEdliu!u&P~^Y#FEH9(JZ3;1ad#A)8p zdSp20?bnO)up_&d^UE1%F1M>+Fk@?YsM!h~Pc&~d;=W{v5&_@K@Wxu>iPs14o!$8@ zBrbP*aT1eWF z1&Q^MSXPm02K|;g;JW$5z3s%zuCAtcEjy?pWvm8ZQt;WoP^*^vL``iRvA4fs@H%pw# z!i5G)7X_~jzTa81r<#OKIqah$DL+bsrL&3=V|_Sd#eBz&?N>Oxp4?SkfEL!CyWSj7 zvD>pEryBQ%w`>AW*n~dL6Jq@MR#DQKNc_ed4>j(E`gpgcZzsG_Q;Dw&86T(XdgqvX z2F=bB9-L1?<O&ACLPisgzm&3`h6;nX9q0RuvY-ML zR+0Pl-PO6)4hfwQEm69jq%BiH4SI8f7g{mR9c#BnKMlXiop{_%Hp#s+=x$8 z*<9hNjhrUvg@u2$!VvbC(5`mq3@M!_x{U%Ea}^4EMEynjpSGq+%>^4^gu=o}8qIu> zLun2hT#6qZ3MwnKHC|YfJ4z&;EGlDmktkd@anHo8e&qP&nx)3C*H2hw3q=tARtv0; zq)_c>qJzGCqOm6zmyNbHc4Z=^{LvaOFNXZ`1(@c_u|{WF#0D+BUog-;_CbSgsD_V&$z?s zqvxt5GQ!gJ2yM8C?f0sO@e)3r_Snj99Cfce78htL`jX%sIJ_=osX#U@=0n{IgP$RO z^OB&joG*pd8BjKB`l;*0-R(zu$Lq!3V_3n9UaoRBLpqPRXHaqbp6 z-FB!xzv$Q9@^H4j>s1MyODk5MhL~>uE)fykUok_fJ&6+ivFmbYc?6Fi=ggn5o1WZO z+^n8MJc%rN%kh2L^$FPusRHXQ0NPF^b;QJt%r=GZ^15D_u%bvxV{+su`Fp9WIfpVi zf<9_lIE#gu(<@FJmLfy|iDCwJIxzmLXHm8ktJ6iUK|vQ-!e=l9Kat%dYV*f5;Z}hy zyB~t;;q!y+zlPfMP7mzC>EE(*dozdK>}J~vgp(885=;p8PY=}%d<-E;Gfg)0@tE;8 z7-!$tZB?1U{spiCwgUe5+Zk>$pPjk@l(_;;DeIF>B{I*1ug$bkpKIoCttrunXyqp{ zRG6_pVDz`sf}ut`NZMVeQlaKA^R)F|g=chtsm%JIVY+zi)PZXe$dkF-on}|HjnxpGRE8W^n3lrhY( z>1W7+z8Vv3lqXaZGx1)`UBw;30#c zM6)hhW;D3|zX#Sh^0);+&M2oM>{lsZM^5^#-Chrv4|3*GrFZAnD=MizjQdA5pA$7H zPG7O*@ve(qvMcV=A1J)xl5~wh2OsIDf{T$JDoRf6|MF}GA*;U{JDdc)yEBqj&1u8s zdVBKFSvZ%j^a1d}nVN!2N~)$;9Q9^KQ%x^Qg04yj82G-wXn~6HKhS%_Q1#n{&cu#a z!t7(WjEDdl*JOqp(=`#yLeLEgJ<^AMl0LE6>M8hmC>Wdfd-z>1i?j-Q!)WyKS1Tfw znX4Mj_tE*35*NJs^B0Lx#f^PTQ9cW!wMy<-Mz-1hd8R|f^!qgQ7dJwRW(ObV|Dnhb z0$JJz`tr@)3XH3LfeUXc{`Y0*~eK2 zR~GS%mO}ZxI=dO}YgnSVzFpmXGnGtC19?=w>hV5+kDYKJyg+TT)P;hXXT97}6Kt|4 znZUj5s-L9*C)(~?YTUC82p4vD?~P!o0a$YyDTwa>rQCkfdv}A_zJ_c96Q{x0|38Sl z0F5}-YCUpv@~;!Bbl{6iIPh?ykMdEpz)fANRp2xEcojwttRXhFYb zg7@(y@@`~We~|d#F5?fRCzDUxb7t^c3gy%5ZqD*E?l7l_-qzgGGH=E!BC6|y*+i$$ z9-@DmjTes*{Yy;$PvQlzrjDzvK~(IeZ)O3ECBC}T3|G5+3SXH|Wb|+~hvbF+iF+u* zqp#hNw=xVh9kD-1c_g>sElQW~t4m4C5)-j$z<>IS>ei-=SF%EcrATs_(P)0B0Ikms zjq6qMH=h{>6hnjVS*Ye4kdzaK5z`fYe!|)^pn)5=xs8_NNJc;*TI8e(MyemM-uknp zM5PTa65w~G~b-buQ$H*Q^mNy zHB(Z4kE}!!k5QSlUxkHa{TY0h(eg;vVYAA8;-p)p0uc)$V)qSEjhUO( zR{k z^Mw;u9A_PfX4I~R0sbGpiOq4C;vVR9U-(08iRChkH7>n;S4BYt^5-M@@c)O@?`cRo zO|c(>fc$V@a(={FEFeGFIU%RRE__MWcR31TkEcH7^0s9Tb-aXn+y*#7!ZjE3%&V4* zL~? zF{s1(J?fJrH)Z&h-+N7G9gQd}T@W%qE}@+8Jzd%>z%3zVE?0W`6lR8}K8>fFD&7#z zkO8rnC{4fOUUsIeUud=aui$5FHJMSq&_U3AoY(dLLj7m@BJ3HB4~IucR^JTj60w_- zopEQO?J!g9&KZGEKje88tUJ*&=R?-@5}+$-N*8VRlEnJs-IA)b8I%f@=t~VgvH%s? zRWrd6@U6}Wn$P7QczvuaZp{iQ&y7tV4We+m5*)>Bia8e#FI>g9Tf8vfT#Zj_@;Sz} zxRxGwU3OU58k49NNwv}fFZ32~Un ze4VMlZ(LXhPnG+hK45d?*6L)9Z0U{Qu){AOX=Kzzl%MNkT>0=Ba3o_CB|E@Mm=MHV z4n}$RUcFaLVD1zA_ugR3F@r&|zeULY$_?7qP0ePOoFD}(_T7w76EBl$Mtfst_G~HS z;7xmP^=lc$p#G)(=N2+<&G-25XHO)j_$@yluegB-KR?r(&&uW>gne$L8aA*(Om2%U zW&I(_-7BCv%xkne9e)hp$`j~5I9jczoIDccKGL@U<;qg~o(m7XIhlaUy`+-1>WvHj z=iT@z=nD%{|CRYuF1&edrn{#z)06$+E&KKRHj(QhC~-CO%;ddaF$ae*Z4y=SSSt~V zr|3p#P@s+UOaW5BC0#bv$+5)%zOp40sKWX6&|@HHPvKErf6wLgQPxr*tf$M=zH|Mr zqCw5&CBxzoKpk}hil#n?Vz!=ACxiFOfq4u%u<|WFwI?{U4?yBZ+>5FG4DVy@*nJZ@ zD5&9vAtSNYtBbFvLmo=gE8lS}r$GRbD?R4m>G2SzqH$jhw{o~xfoX1i(1+{4Y<~Ts`LsiW z*DEp8a5C>~pBlp+JQ{x(7A;LA(y9275*bWn_#eeU*;N=;fqG)n#g_fA+w1ksQNhf1 zAo$>$!*ML^Hn~TG;@2Islot4X-S_bjmkUaY`&Ge1XF^AXv?~>Acs?HieF4_M?}sN$ zC$o!f9VW{Vd{nT`Ag~;)t^ha)U6yfOciTg_J0|of;^#@xSlxcOz~(83{g~D{6b5^*gQc+ zhv_h&0eVpbeRy2;!n~UQk$S9cxuxGQ9`~ua|62y;pJyHv; zs#QM27a;C5j~Q(|zDQymuESOT=>b;Vrh|-vrjY)&Fez;_g>h#j*wGfy5g$(uBZk5J zUn$E+XE|6QH@$fgB6!N~F5R!V7wa0{{sLVO%+-7|2FRO0SK~IbBTaue-`^bY?~P8) z7_T32^&R(2J(^V%SX~tn2;P{hI?3ByoU(gyS+O46Sk5U->ebIC(4fDd$8oHCH0&;Z z2g2tCdmmz~hn$Z{p~&MCQS%oHDx+A`4N89Vj_XTfEh=5Dp2%^CDLQgos_xaXVA#Tk z{hvU1NBXAkPDgcK-A`5{Ck*=0lOE42|RcIOjoE5ch;|D(?V6H-DmK%SR+@?dfX zO>TIHN+?w!;ejT`n(qjpVd#UVz{#BAXrQUepqJ1`k?wz1VJg+R)+}y7oGmG$l}A9A zp%n+~iO}Q+-F2Io`uG~7atPMLNlE^`b_e)pVZVvsi%oAh72nO;C8-Iyx`T-`MB9b+6zta6;U zTCZ5Uq^R;+?x*qyTYNPpA)$+tH_|)sJ8kRm@N<=bWOaB>T;pix1m|foT^hGb&~VxT z6PbL^ZYjAV<}ZQJ<#s-NF2!^xvlI+^d0JD7t=?+Ne3`D)#meS(GX3LC*icF1!=s&Z zEcH4bdvjuwYJcpf@r(dSpKMm=`+WM|Him^Nlgbef-)_jwD1R;u3RN?3ZFU%5Kx*|g zrdu~EJX#nD>uDwm%I~9Ilk|8fpA4Z z3^w7H%c0si`og9rAO~B}^Pt{d4sF&d;NAvB7e|7pJEO%8TQJup-|1{F@RZ;!1}#-c z$YaC9bdx>k{ahqG`n(T)cEd=f+scc(SbNjpB+k5Dw1toQo%^!B&j(mIJm_}CeTC}XwA+6i0RcD0 zgAt1^)@R;hJQ@IXoIenvjE74DP1XuL>fWNIKVBd?E*UEhPd6cdO21^8UFRNJN`^@X z>43nYV$kaCCcb!e@Q*O22Nw}glUelWimD~gP|G+lZH*`YTIvi{5U0GJQE|+Msq^x2 z-vK>Q?=fRgqSyNF;(6F)_J}k*s>Cm{u-;_LD#xPL->%Cr=W-xIl?Us(yG)BO*uIC4 zvgB@0$7b!{d->$Jlj?g4j1{bhF4tXvw^y|#dJ1q4g?DP&T(_zGu9P(1SfY&%?J)I(+)N3d9pV*y0EN% za(q9gXuA}d*1dJnULD^c!y%d0ak58d z-@eCURKFFLJUFa&n}z^##x~~YH~`Ecvq%KhB5(I#DFP0-x1FV|zE-fAYF%=R&aSL) zv&N}6H0ADfyf^DOYPK39&g7uoy5)|F=S@b1jfs{4o+l?2NNpd_!0dAS0qej)_sBZquYzBeFNg^+;(VkguS88=?`H%o4M35H z&ydp`e*h~AgWBDW;WZcuV|kHLjcA1~5JKM+=BTM@G?{=eb1YTj6%QRjL1a9+Hj~9< z3WF#(0R_wUNvmax=HL;@D&mh06iUhCQk_=0QQ~vD zkt2e-H@Ip2Na$M@ZIifeL;Zc^`q*%DPiwLNLAvpO4}H$ZlOMMqo|AFwM<{m9`{FiR zu8-t}Sn4PWG1psUCV7X&lf$tAdoc7h>YVW8=dkf_Yb7eK#*gjW5bYa(L8y1WqSolF zVoaLNQaj;zFn|4M2P^nXkOLLh_^uraStKXMuwP3H1f!ju@yB&2dL`vKDK2w5eVQCg zp6aVDzZ|Hn+?&^2f7!fx$u|0}5b@si%*3(aPhPl@g~1oDT1<7_m6YsLj*dIRVENvx zRnF!Tg&N73N5a0(TT2o(aVDfCuCx-y&9l3d9eY_yrZt23pA9ql@M zUzqlbC*7=dXj9<~z;y>?3sn39>VQlPxNA{k6;G!N0*ysnI*XJ!Pfzc>odrqRc5->@ zUc6l)eEm2)Kh6e?d=KM)7S*myZYWS}?@PVF1Q0=^%lf1ZmHw)Q$H%X_!!l7L9NU21 z`kmp3HeUmwX9{DzjD|ldUNvb2CROoGokvOqo46uXD(T`o)skV}k5pkzp?z=^lU0O0 zI~U+G@$S@m5^4}!nD;QN4#)qY>#d`r>fS$46(pnuq)WQHV+iR6>F(~%kr1R)x>FjY zq@_W+yJP6?ftfqL-@13L`&(=OvDeHwXP__b(y!lh z>;Rx?RcQ0*3~lr4BaIc!TafIj?)^D0w*417f)_|*bk6NN#)*##$`ebhYt#!U(VPMh~Lez&NoxmmL2c z`;bg0Wy)hLT?JN`fD|-XmaTJ0Y*@Y|SBn~79+OwWFXDAl#Of1CYiRHr^5+31Hb16@ zw5urV7ZN+VUwGF7AJqBYhViTOVbXcRb61@A;|7#cn371>XyE341N5Zy0mH1aDj*|_ zaAmOf&ZGWTV0I&R%rh$&vX(SA?!?XQhny0q))?B%l)KnTHDaBiI|sG*e4Dyvrhl^?i<<(*91Rh59Gr;dl|So3`?3+k5(1A)Tyyb=>XIT`rTR z*1DD)NqmPdkSgSM1aAsB%h&T!cmAP^ljd=oWGZ^Q^7GXd71bERYj#|_ugh-qjJMrw zjjU)bzrGchRa2D9Ypdfn7~?{FOT+rJiJ#*s-+oO4Di4iHfWKm zP9jIT6<1ThXE#o!j*i6A;c?CY_j131yA0Vd*^h}E9)@8xLJg-J z&;B5bW9N}<^rT{R3@QVQ)a`AhK#WdXLA2r67H?+FMaTkphoO+5QWjn~bWdSF#yAip z%E{i@Bj6h)Cq8Kx4h2dsn(=a=;&P zSfgA}5A07b_q2!09eaqEw8Kz^jQUx_FCi(mRS*b2};IZw8xj|DD&K_NIkV&&o!#0<3!BdRt{gZ`_-n*+G7!KPAMgt zuvg#ZSY8~MQ-29+uzSp3$!zzv`b>hrlU#rN2;_8oaW;f*2^1*if$aB=rPp^#JqGIt z;11a?Jg(uZ`61DFSWvVl*e|# zviG$uVE8v)HMI1FuO#jnTkiK54-d3dwI|m9@Wzc#y<> z>ufOgI;yEfh73f}76543Pt%O<$b!u)1S_l8u@#`x?eJ2|I%A7>Z`CkQrSykqX<19v z^L9HSwhTrF5Hhv1=SXSRiUXjZVSAF*Rms)kNB~=o7&yz}IT*CsStBPB=)Fgklw}av z*iM|e)*CdIfz~D=(YQP{Nk8%M-2w>u72P@GvRJ7-GE%n;D*&)qxu;##RDP(|$l+e3 ztV~qVWhm+z5vf}n!U_psR|lO47jl2NZKobRSc}d{64`{cWEzWA^oW2?%B46BUHWS` z;|%O6cMRmsmi{WR@{>{ozim;xh``YB0FcExjWjx)=Y4|XV$^I4EBLIeJ&&I&C@0_Q zh4JwcJh7PIR7~|qyZ@po3d+eIHGh>~=`{c!@ttT^&L=&EkM%;f zV)NmUg-@UPohaz38Em_iI$MEEGa|6GHngd=k^MT(jUY-d$`Q@dnQ{q*{^96+xXU$@ zwRWGA5!TX^1F})045>jw;o4XD1&8Zvsc1&PPo|pm7K6OkkCf`(o+hnUw3?)M8241| z!5@)sh3OCVFx2*!4vH*4N}xD zMF;vRgy>Y-$%&y`OKVa;xFOq6!*HmNCZM>h~4u-n}j{AqJ;qKO%)F z{P{TVmDu#ebJi87d}l{b07prroMj+m$#`Zz2-r3LYn#=_tjW!WJyHjJ0@cR#$38#P z9w>mh5-h;r_DdV7MmGn$47oV1b>sO~R&P&p8ZzPLET*yEt9cm~A24ur&J9l%tNC*K zM1D%ds7Di{G*v!KWm&qRumzdwT(gfm>i5L$i>kvwKcyu{3S~;-htO-X4QkR{md~JI~bL!&UFB(bqp1TcbWMy=6IHqOLRo z^8kqc0$j_>(>e7BexXb)&$$g8!{%GT@XAck)qp;VR+mtOHFh3e{oOD~=O{qfi54>S ztT7W|0A+x_Fkz#`Q-*UoYO{=Vtw*4VvKp`56CtrjUzfNNU6`>y;d9gWGMppCXC@( z81R{@x1_2^kmjTh{q)$UNtxW?b&%#prXwEAUrH=4HgpSFF zWX#kl@*&?d7rGCk=r6Nm#ZGlRo|pLI-+p}SIy^(>qH$@bPqL83(m{Kf{lejxiFrH? z#4$Dd^Kelf>#qDO%qT;6w7%J^+FSqKZepn=`_vQ5!eDbAINon5<${5AoXVmDz zH8_y%g*$m~R9xBI%F2Qi>|AYc&~}vE2RejkF?OVF&pPZr%1#+QZXl-tSD=#M}S2<}u-rBd5n7rGeFb z8z5X51CV_6l<>oLgpbDRyG7r-2$C4rX!CD5$Uc8~zNPr&J&Z5j#_lXiu2VKx&WvUe z8&#^82e19@kSJ;)p^#Whn8~TmI1z_w-4ZsaSWzs)tG1(DmGRr$Mv8ekhva)EPh4|| zMWSUE?m7B{b<}F{6||!*&HJNe>&<3fF((P@OxEuhFY731`?s-|J8-JS7JqK&^2}=R zj~G9Fn(X;ue0M%pE7OG?@~%O_(_*-9kOs5*i+SwU_JJjp1&!a`)135+ZT=}Vqs?rP zgXig;p}yuoXKJad&W$#UqBn^$OHu{>$`~F7bC3VRi^9=Kd!%zw9j7Xd$+l=u3^}5w z>FnZ%6Qkyu0!ShN&JjPj)0&nt-7n<49U+i zppDq6IU6KWBbaA)Z}Qh?Hqdp?&WbzTa5#*VPB_Ba+m zZ$s0VsU-nt=PrPz{MGi{5hFH(FxahbC9Lpr-}jQp>qCLILTW0&l5ySM*%|wVN$^`u z$l>$OW7Q4ZCeQv^7uqZ7?Rx#dwQ%#SXYOzxROpKzj~N4?YSiFY;Jj2dzyjlJc~B87 zAk8MCY4*5$d@lQ}b|9&cS)XFVeJ0Vf`slgtbJ95@!oW*3-jQ2blMfBT5&BX__Uy8J z=LLQ8nFkQYR5}mNyk2$52*2~bnX4v8>SQ5pHK-2HA&DIE%QK)Q3Q zCy_@9)@}0{C%EFshS;~tu34<}Gq2mKC@VyHB`OFYVB`C96AM}pd#Wg$jUbEfxEgy6 z(XnW$MT&_v3yxct=&s2OT}A8|C7QcexiSYJN~er&jV%PyHrAR_wRDgpI_Td<*)I4o zNkpw>)(jqWMp#%Y!_=j@HPJC#{rfS|!wyK?n#y21Oi`!TR$WrXcs zSQY5jHS&w<=5-3oNQnpJU7~NCiDgBJEAJbaj*&-G!Upa^NYIOY!3^*Ci05MCGDfuG zsmoH}j`VLQBGZMZ4?Fp*6T-@6>8kGwZ02Hus>%T)1}|%69RUp4FfvQW+-T&b0_)}+ z17au8?dVdR1!4m;@SGgAYSib6BJ)8zOiS0^X5lXLTY2lBVeBWO zBw>jX{khACd1Wd(;`j4RY@hg?il_uZlLwA4K#^qp+jx@B>v8>`-1d-bp|0(LO!U;o zXN^oa9n~ZDx|+h}Ic&Q%JHkY_F?nGqhKRQ_$=JG2E?yYo#~ouZHt%(GxZUkSqkpfu zig}D{s(Em^4LK3)6{k*3-6WBEG1tJmUVh_!(28m`OwS(cART`TXOR2wZNZ6YzoLS4 zGm6)1@_d*N6o86~uRsy)dyR5NpdumgdopE;x9_4?%jiP!(d20rh~=0Xe3NX(TQ5Dj zIhZ6>ohCK5X%{CI5xH}&uw7XOwDpvpE6gZqrFuZ6Lum8`mowm_&Lv)Pry|B*Am8M3 z@skOb$_r!{LdtOCQ!6%!jLY(Il@>%eA}b496?E!K&y9QYjo%}DQuFcK$vCdb8^R6v znP{t}d<2B?#>~UlR+zwOO zCq{hdsxHfyeIr>aTz~`j&mZ^p73(3rXUa81ek`gw6rmlpT(4>~L`{ZtXg?mUTl(=MNbZxGF8}p=pk#lgi0yv?ZKB z*`eRE(d6EpIq=oz#FUqTAb(uxGe?MaK=g+?Rb0IJ43hBj{AFV618ock)6;=B6VibB zMt_MU%0nCkz56PPIom0XF3$QVB)%k!^(U9adXzdCO0>Pja`kK{K(slES&Yj-E|0r2>-K84tgAKlKb%h`0tf2nN~ z4}EuqUfy8jN~8bNy|uc7jfnDcY16~kCyKv*e{!T?@~qNl=F-6CHS>9lIcobsYA%fe z@0AF?m??DsVE6XrgJerOoT4D}DfZ?$wxP2>+~UIUTjq&mc*5LCv*8KZD$k^PW}d6@c}mDH@x@0DO$d9#eMl^#4mJ! zK%t&qB7KI27h!JN zFF{^OmxCY$=uLo}Ij~?w{}0J&#fF{O)!_u%Wu~{sQdgRj`qUoJD%YV;HHv38Lq#6f zqPr$#Ku$oeh{tMV{?4ZoKy8p8X@&%}_&$E~d@!$+jQSG-?En!B9wK~F zk+@Z67EGA1Wa(kwQVru0G$5Lt6rx>rqV(ap%x)vsH@=FcntSqA`J@mN zcS^4ZCrVo*X4QD!nt}%}#zzvO`zPn5#EpL)$2;BJw^7*R^QWMyFUe+N7$46ojSk9qWtt%-NHt`=l|!|uVK@R z$6h>lkbnOE<@_xa<`smU1Katd`rj$g_|PA6Cd)iL=fYu)brLo>9#v34$ICxA^-NJR zit^8bNaClcXSoGQ*Z0lO3~}2>>p{du_h=PkX6eb8-)1i^eMEN-$Z{_whK);NZM>e| z*$gftqzAg9Jfaa%y-E)=l;EmEI3{M@cR>L_C|d5wln?JshT4p z8Rl_QP%Ib6m4l4o>xMIY3DEur^N#2jJKlOmqnhRDUF`nNd`AY3~V%!1)kjNF zS~jGTiGV@Pvx3JomWMXs z{N{wl9gM(@hTPsq9#lwzJAo^5h}m#dijliap;0-U-tSAdj(xOgb?D=?(r z|CeTgo;;B>x>b_inS3eM4*v2up6tkGGyg_i;}HMR_9kun9WD7IHsFybOmg^O={OzS zFYuE~^j*;ccQad2e}u*x+cgp6m9}^tqTZJMu)t6`(L>Mui6zAqdM)g8*4LV`j^OOM zO!P$cYLg2)*@-R6L+ri1&OPC8U8ca5%k#PDo>>WV&W=`LR%juV{s~umWOEag<$bA}D zY`n{t$S#{1Pbvo-y0zxuw?y1eacy5K#JSL4AS5WzMOpCh<%7Jy+ZLZ6y7*WD1Dt(i zKj#Db7mmHK@YYf>`6Er5*G4dpMN<6RzglV;Db|o9Qz1*1$t=s}2eh2!tXsd0@P}Fm zbO%+-m$WW*7A52+h2zXdyaI7Yr({ax(5J!YMgo&h491o9qEXYG1xZ54{dO??Uyh}8 z*M!pc&or%W$pe{LQ-us@scgxy5xjT!o)O);Ha@A<@bo<%;5b6R7Ry4STQ7|ozGM8{ zUfFwS!YH|#E7N;9)-{A}*|RbUZcg+stV)7K1?d?n-l^ zSQ0-}>-%|fJQYERD#GW`XZTUR z#=;7c86^IkH6S=NLf;4Qq+*BQCJhI`65OeV`CC7WYvRz2QyRT1aLdE$rEg4NOpKCz z#PNNLZSE24 zbmK>u zLJ+62Iz&g2u+xAWb@nPvyH)yMlKcl^Bh9DAEG~4P#-eaMYaJWnI~!Ric?W8J6i`w& zBldK^MTe>zox^hOjrYPU&<^2SpSN^D2A4^%I^tJ_Vm1%Rt%4OP0lZ`qD)*6t*|Y8X zkEEHA^#RPeSwazcv9#MGBg4HoSzl`4t@+LO`1pKpTiUe?J=eGUJ5z&^e`kA|9IT}@PJ8d zU!ME4(DLHSpJPW;>9waw``NYr`Vt*^V1|Y2uQpN4Ar>utNU>jO3CA6V75bL6S&I2! z`3gB?__N2)y*ZZ1eVek~??+tMc4?!ANrWW-=T6{n6w&`|1S01(4gM7tf9d$lR)(_N zyz(&?SFoP0C+@4!i4^l#smztx76<2!unyq!7;VjB+{N!d6qo|k(ipA^u~Y)9$9+4m zzsIc%4YSbHO3Fy8*oyFf?Sx0vh?QwfOw|eoMj5AOVrVGX(bh`LHQcG&?1FGMafuZg z)0fYhYH;JVb|q9a{4LC_Qs3i{=qJ2lnxNev9Nwf)I`Emt<8JJCqGW9j{jDjM#nYvm zqHNegcAEXRakY@x4L%Xm`>KB8C&YufMo>jVP_pGKpYD^{*y`xMIina{h_Vsc-^b!p zCV2Bv1|L7jFmZSpt-m4auHu2oeYf683)y7tV|G0y6_u>C)9=?=o*l?lWaUA-guWMVa2)mx2KE;nDAV0WY`%9 zEe4w|N4%nzQv7JN_#69b_L4H4$mtgEZ}46vh89`5IcpJ0W1L&9EwACwbKDh}p4_TJ zTT`@j-vjcPse!=Z@cK@3A35a2eF^7Ue6qg=Q5Mi$QDTmZ3vKc?a>D>{<-usv#*q)( zV9-*USHU^)317_A4b|{aN%KiIYh@K%M@wCa5o2iU50`=#W)Gs_?zQw2RukdcnB-c%>Pz7eY;Or71pU>BTp%>% z9;(9rwkfYSj}d=$-sgk8y*$)}oeAv5a$X64KFQ-w=0|YZJ&ru@(}ME=kNw$4)ntOQ zW-)hPta=9zH^b{hZZk3W@j~%$Zrr(DDI1@7f$@lth)#Bd?&EAUe|zIQ1(E?bki}`j zs)Lb@NbXUk9- zeIUU}%kBrn$?asUTqTjyLz1pc3m~))!*3ZAV2|@MXYqVFBh}+b6lr8?a8$F<@uGqh zK{OKMf(txsA$nk7|E5y+<*ym(+&OgxGw~4V_r60H2MR*o2a#+OEO(k7W?c0SpD?2o zD$7TW&`ARXZoZiw->p6W91PmVmT4nBwKfF5JM&O;LY{gWU$T8a?@l1BP=!yT+miVT zjSTxo3r9bqhrgLJm5QC;e@R+=`Zg@<7xi<}VMb*6;X$mp8bp7nro}}cz@H=P!cO`3 z!{?u5y%CpqLns(cb1rj;b@$=K;e@SxhT6W2W3M&LNsr{w9rU{V)36eO1TF6M@JRl` z-6H?&7|sOxO3DCAUPRwNwsEjCh4pXtcRhd%!`~@BxwQi7k{sSxt3Qbf2X8QkVWD~O zV%DPQ9fxDg%~nIn6#^ntEW@yj7H$+&tnp0h;c>CD*3AEkE>f7qJ;_$|>V0)fT$J~RQM z<+3aj0RDS0b?3iP$S={XIV9D(&gOr;mkSKa|6PWL+rseQ;wFPG0TcHh_V^{r)%Z6c z?4RSs)v5k_I8qzSe+}he!T&*&$*F#%e}Nl#v5PM<|Mp?%zxu@SAM_&xTNCeHM`%MM z%7d_u!-)+17MUom-?duG`5y^l7Z?u0-42uc*NVZL=``_}9`T*qaE4GAKP7N8^Z6ejXv;0$5i$r{J-%r67WyOOxK@jAc(Vokbc> zbki)xDdm5-knd9VJ|97Nr^&iAAuqD^oj1yQQm9ySpjr!J36b!V1w@te?bmFs=jaUy z_3n%`ITD!!e4xA)IJEk0H09c0`_;kRe*O0m95wArS75}yp<*z(cgbbF zl`9Uk`2l}{kLe+uP%ur3T-x;*!$v`1M$%e#EHysIFkf?4Z;E7P$_`e z&?T-DgznEiYsUI~^}#^hzZ;@qJ21oyeJV{&YL3zf;`fo1CDcfq-2R%re)ylk2iMXa) zexh~b$Y5kUSs)tFyo8HWer{y3i{LHBweJ;gmgt9JH zbqE{@r_vO*x7dn#J(L(~enVkCDSzTZLS1qO+6xRs&Ar3dLOYTTAChTR^v_0P>8-ul zsqk@86JqY|L5e!*Squk$tU3yObGB2Fu5?$LihZyrx!HJ4zYJmO2bc+?#K32^5t^6Zy(YQWxkKmP;H}TD`e%bGoi4QXJ0Mr! z@6uJGhquZx8$IiPro3!fqWl7T!Z!rUPo18a5BXkstQiSSxzOh-8V+F_XkNUG&b2SO zk=6)#{<5`gYYql0O;a8-pTv)j4GDDRK)Ln zm!R*sYKjq<660~gMEAE=yLT^oqFV)Eu*nAT+~YCJ&tC*|7$GRb>`s4P5+Jk$K}Js` zgXDn0PJ8j+%dyI9HtN&)LqOM`X7zJ&YO{CWq@FHJi{$-09Z+sj&*&!^i1RU?)QtUn z+=aUWbXb-W!RN}Q{Rq;mFq)DcNuwDvUG6jg)9koS(93idZ^`a3q~t10AVMtptJr5;=+JdHoRWanpXK^|5AYbj2?V?Fa7Vs0V=4#m8b2cLD<_ z5cP-Q$cMlEVKTR&-J9RUOq28Ow6G}KlB%L?r8pXOV>Dxo2jdMM{Fc8(#L3WPs7$cZ z)3hY(pS>T1a^VNB+EEdnKk8y*!(deG^|cf$rG=4*Nw(hmN-q0oSTM)Gq%@3O!$N<2 zT4Y9{r(>jqRzy5_0J9YPEt0=~SN&y5SXC}}m`v)<=6**;MauU(*agx1CVwIuxz|51 z!-a7pOr-m%QtjPcK+hLu=vKAYJ-?H<>Hg2SdOAi%%Ht%LG-aajkdU`)-ewvf!FQ~d zrUOZ#t_FyCUVZc2+iOc# zVgtDygq@AS;1ALCmU#k zPRHIKpWRC0misTC%|#-gvk=7rR=zznY88oLjdL4v+b@hdb|-&?ZPdLzj>&ba^m?tF z@IIe*%|ERDtTTIh7dRGYd9+U7-sA+2yrh>de2qyvZAmDdI2NtvM_F@9CU>$P|4EkXe2N@*1IT*YPBB^2d!S_7mcaa@xuMx#I06 zi5G0~yEBI%+yCrqeWQLa$!kRm%j4vqYBaEl3Fzo*cN>}vA?(OnS5aHU7u zkiF7?C(>-glQ!_$v&3J^^++)}StTQ!Z)k$iW^oT0W-|#T@-i1Zvu8iAQHt&*7~Q6Y zQVrurJQ#aPC$FVj!cPDag+x%Mhdxutq3>QP ztNm&(hrkhdsnT61&ZtIIX#z7Z8?>~)1pSI)rTGI}hs|FUpNv$d$`|O@s)g^Ja=gc=+%cC<9Qldb}qv4q#~>{Q=p|7rjhR zfy)Uby@A0}oSSJ`(d5NSDWxNIrOF1zk8elCKV|U5b^f&iJVgz8|DbH@MH~^)9;i-W z#*3PlgiIBJwNY+pp!aeuyN*P%5m{{U4V5ARHEnYu_qe_e`%AaM^BLbe*|lBryJ40F zJCj!#?9Uxh$cmSqEtn}bJ{GV#%kR~UaXO@Z>wSP&gh4~FP07Mjtx5TMZC?cc@zL)PDDmx1x;Rt|Vd3fsGdA&f6^zHU zBlt{TkmJo!KfEopvmSu7x0j}+3R@p7dwZc;!~?#F-;=(6fM^a-jD_qNF|RaZi#CEMW$Wvd7Mn<9)&IkpLy^hl-)4)iFQR!Po?hU7 zaa5!M&BDw;f;DMElu6Z{>a0({A_4Q8+e;`&yP<(fKp9HI^TNbTQAKvpFH*lU?aj`+ z>as(gj(OfSl_aI>N6(d0G+HYzPz3H)tx68mMY?eg^JXoM6kL?uzp^dM)fGL8Lmc)X zDI-ruBAWV|vV|)hPAM2Q+PAX{TAoC+d|sBGVi?Z_&K?>0(hu@lIIqsWfoiVPfu3S^ z++VD$Ww5oiE`+A?YI0%SM`G51q1T4laUMGSQ{0M$AZKr)1R+k))!a(8?@6@b|y{_p*@51bOT}7_DKBa9G_b)Z^}OYnR`hijG~*#o4Zq-HOl` zGC+SO{oHgp^MkdV(wYZA>6SIDG=XIUPl8Zd*@Fzj9c%2RJ%esGUp7{)`HkwW+LXGj zFsDV0xj=MW708?|2d|u`EtX-HTtZh#qw-}S^XU%#aPbTEQez+}?D*J; za5B3WE(`eU)SQqf6BdSV)XxFB(H^Vb#%0Yefe5rBBvOe4z1<>{MbA_Bmv1^jWsxf7K+|XzJ$UbIHa)ntt1ik?PTqTMfR`UnhG%;(1H1j zTP;ow*%7wst3*e5?_rt5P%5#5TGG%1fu;b&#nip8En+|G8fTS}^KQQjTfg0;qx1_P zY!P9${&3`PDa%s^G&eh#N*V8EiVsLD+Q3M_U5Xfr2dX$F_*=8n#3Jd4IfmNiHbJw% zY~w~~PftD;h>t{utKk&C=_2 zQr(59$fquj$?L$~1R!(%9If5MRw5hDNo6+a;BE133tl?RY5VS;Y*4P>xQ&y(BCX>P zy2P!|d2?fh6qeSc57AhORIQHXXo76SjRpuOy(Vk!`4V05#TJmDqvk-E*gaE^b`N&a z=7>}s-2X{adNc_WFt=`F)D(<){7O`>NQR{sbG8*zv0gh$|88|6Q*L6#zV3>62B8y; zgt3?oUrQg{*}{KFt+Mv4D!)H2KT9NcIJk4^h3P&QN*}>S-=`^#Gj)!AgMewIv3d#< zmEUL6fu3!#YTV1Z1J)dFGc&zDg56_&AsJ&+#Bf1kBHQqCn2>zb*H~1j(W0!w=BHt% z%aY*ySFOE45`%kO5;YHoKZ4eogp|JJRPZvQQIVn5IFrb}p_Um;qleWBoZ5_*_^(Uv z#x}o9hl3|1%xIE7Qi~5xpnnA;KCl*th$d3&Y|AMZ#9wwCB6rVRx_j z*>GWzw&c|vHB~2ff+`JY%e2(lkB|T{QndpP38g+kgHURLq8k_V(U!kOZ<;^RFic{m z+nA!(N>8_uozh0qoJnEZd#~p_y-jxYoeWKnv^xdEYEp=3u{o52<9=UVx116To@sZo z>ycm>51nl=_dL6!@pN}bYOKa`Ernt1$OfWRGBH29EtT7$wY$oN$ zhw)=luT%9L#pxb~YIocXzkZN;;azYW14%PYiSG0IHVltZ`lIeoSNjvB!$cmxXx|(mPh! zg#3mtqulAq&Gw35B@qpmAN>p^vCKADrv{h4p^sY*qte$Gf$to_;cW|K->2ci`5KzP za8$&e>_}^iGiHPn39ST@fOB(zp&bCSd<8fv&MitoY+QzHwXR7^Lh8X0x@H38GhD^b zpZ6dRIQGi4oMrf#jy*LUWJiIFmr4{_|A__Y-Z4e;=vXY%7<(CXfI6>qp8fKHR(L?q zVwLX)ovO}_M9x+=){eDG@4YZ$S(WdMR*peUKx8PsBd2O&9NGF3;!>`LoR1H`W?ML; zj4h8nI$HQ#Y^0LTvg?P+F;9ZD;+CxVJ%!z&myS>}ncxjrwao?6aiSnYwV^`O%M+0_0#U?rKEgYFmjfQ<>k`g%4ajFnkrx z#EYbdRh)~8liH=Jt&Od=X*}~iwE4ra-D(eC3CzD?e9&VPIo3w` zGvz_B!E$=Q)`UVLAbJgv~@}h@rNngM=5yI(#ab)BNZq5G?-{~y40y^I`PDGQoeJ8l@1Zxin<~#CL zELx#son=SN66hISgV@2ElNuRyBvV!-@*7(AnNC&&SkjGUOqJIgXAS%%>uY%yk z$z$#)L*ql=lZ(>j->WcdwG3<4G3g~%1vYy%3w8m{X6L6eAhIbLJ08p(9lWGEiqm^> z9?2zYhSW@4w>2s++`%H%sZ}8XMqMN)8%fqi)gZ6qE`uxDJK}01V{eerup(mi2=5?* zO*Df)3PkF(9@K_F-LUHR>*cJuWjC_S+<#7aE6`}~nf@^wYw;jlpYzuFOlO7E&^ss~ zm^YX`sA9ul+zH)TQ0znL#x;EL#^dYoxb`#z%G?g6jtsx?+~qDTd%1i98cSBQ%8u}W z)LfDa4VKI-dG*g*@~)v7DQ+~p&?5tuHd|IH{cW?^)oXq)SHsk2;OMQN2g&RE_lW!J zZ=~2afB_U;%d9QCrNo!Qoq$W3>b5In%jd^8G;b4EFvbSg?}=r{yhOGaC^u-#;pqfC zK7g`v36u*HhTHY5em?oJx{N8R+OLtjL5H*MfranC511dZ#_cYS05&00xtLd_?1zbu z`VWm%(!(v;K&u)56SsS;&qfgT`{=;9cwU*yx7WWO{4Q^0J^fWJS?kXPGR&$U;sMG} zp_XrOvK#O=+NIWCh$poVRZcjaDF9g;{zbxj_lzQ|CFoOOB__!Uoq?u7AX8t{3X!Fe8Io zHe~{@Px`2&CeP?X3cK1c0IdPYrCGnS5S-qHGcQIVDqYfEop0S#fK+@3)zATs6lkq+ z9q6q4IXfY1Pk4SanX3OClSTpXpNCXgZ= zE^>VXDI2=D7iq8{6rC`AG)pi=QeN|7o*Y11Bi!p-xoIG%)`6g*FF}*zG-R89zQ-^SE z@wX-^QDy3RVw(3i+a!Rn;T^$=JOV)D$GAuo!-chA)iDa3@5LWIMsYNfy+Mk85IRifmOF5=( zik}*9t-*=~wjvvdK=a3kwqSo!BxCcKM!Lg!BCB>0A~o z$}LkFx5)tn6n^X4(}{$J2WkN47RV~ZXCOT#PR z^LZll_-c7=b+ryGbjA|p;#C7y-p`23d=kn{0puR%8V=nGpPi?4_OZJv?J18ud8Yzt zHl9{e{VT$E$eh8Z41R0uzoTn}fBPBv<&D^Rxk?P4Iby`5_syMqDr&KUt`&Ctl!cUf zB~x7wM3kLRu_xT-h_NT>JC9*`0Dj|>SJEy$GghwRtSceXqg!qA`!}7yahceFD}~ro zlT4S3pw)Jv%Utld8ON)cmU0(gHISFcWAciUw%po$f-F>{hs# zCE^(m$K}e+%H_(l2#|hb(?ZFAM`m@SErL%#e1EwBR;XFIJ-mL<{UTxXA`tG$4;doM z8eId(Jo6?vy4|^V-d1$@iUgAFg;-Hpr3OUR`^ggh0%5!Y@`MZfpoic367$l4pikc8 z8~YwHA=N?!6L&Qh=GQxd=sSZQ4in>BPl6c7#Xy%IzEAJQAG+(Er`Lo6R3x_T(#sCqM8-_GHot1v`l=v zQ3d)^4*Cj==}0Pcwlec~_sx;i6~`JLf_EQ99c)UdG0i{yOrrjrrjjI;_iJQGSozD% zRK!L$TfSwIsO7e1e_gGv!ZS$_b4;@A*WIT1pIg1YmGk&TmMY$h-mY!WVwz(nAIo0* zzZjg;CWrv*nkhxix%I5O`wp~lDOOP-b=j|%okPARoSpLsc!DRK`r`nt*AJS7Z*3y3 z+ftPM;gb_!opeZ<9pKf+2@|}58{mq4wiE!Vd2I%hKfg#VA(C{!E_{;H_m2IID*rWW z0n13dWx|L7fAjsPi@O_1b-lFOQ(CWzDKnVTVm3;B_9&W>Fb)lWWpm(hTWQyz;!aX^ zglVX*w3LXy6s}-yZkUMMUlmB@_CiFmd?c^afWyVb1^lwg?^9$gOAM4gHo}`4VDn^a z^CRb+>h%Ab4T|H^Ik55ZZg_YUR}=`cIVFje_(jplC9Za9-u$$q3rLzUid%gGr2gfh zxr6DzwI|l*tMdZTadY8|-!_}RK;Mio?v259<_g!lsu&KIJ(`f&A;B@n7HZ-E}C zS$WLrz#m?RLL;aiVOOtc@*j( zXBTaInBf`pEr6jn66l3mD9P7Mu+B z{XpnY9$)&c#3_>IkVKxmGqdi7XeKEYGTRTbdZ1#$ba^Y(Svr_Cxia8Mb5O7SYs5uZ;d(GfaR^|GdPuaI24X%6dIUQTc@o zIL#P%lK^bKQoKVQGhxal1G1_|EyGE#6fK=T%g%OnUX^Av)#i&+T1i=&>Y-Njck4~= z`Tn3Ai<(z6JT1NW;bMMEIeDTBSgi)m_CzAk3)#H)pb(usU}2-K*9RL(#{jTEmpnB4Go&v^bl)2tPnTf_T{z^i{|a9-A+!%^?p&W7 zMcRohX0?UZtsP$CE2hM!zB9j8fAFV21|+C4RlG=Fap-r5-M;}ozY|4lQ=)Cxk>J1F zFl+6(coi7FS^57E_Lfm`H9@;*5)v%If&`b~7Thhto#5{7?lJ^-f(H-oZo%E%-Cc*l zWnj)k-tVk)?_Kx&o84 z753puW@<>t2pPQcER3`pApU-MsM4KdEzgQ%wcJ^yZ2IJwmvfA^bVP^0Orh2IO~(0U zb@-C)59fD$SnFpC0mCJWwdCS1^0y?_%bq^7?kNcTtNL#ZM_1a0p0K9zJE> z!3!Yide{oNJT3_khMeaXjlUFp$A=VVtPgumRq?(ys6Hq;xNQ0T`r!>QBC6 zh$$BS@Q0EtQ9$LDF`z=5&3$-nm9aXKbDcaskErIN2fMW_Ni!9idw5_G#13;ebsEQ2 z7!BtoTjh24>BBKakjbRRhc8(n*5x_i$hsN!qMfMroNGDi_|uIAG(j2mHCBe=;cIzo zeY%3-rtd=^SF*Rq#Ut*frw20AzgS67hiB5}Wpg7&g$lF+GKV}X1U_AN*LgSEokAYC z+ch7h^Pg^30gZ|6!@sA*R_%(@;ZyLZ`EfRYLA1e&)3gi+lFP>cR6%%>hbgRTaS4fs z$tusZM76hgLU^95tc}Tub5Whte53O{U zCsz(x+=wv*Sf*18Sy>AkyG*M_68g51yMbF;JNem^ZM z0Q=Lh&vNFT@Ergzf_pqU$r>=DKIPGFYjLSQ5-)32i{ zBt@JAjv0hSdKhTeyr&RB(`f!iVf%^nttWEskx#m~2$Hg{iegP}(9+(Edqc39&AuzG zZW$14{_TZNbMg{HiCTMZRQ~tOm8bsinFJL?F78|sI_UF{hU)3mXX1 zut;>#Av!u*{CZ4oYmr@=LqGEgA&!|Z>8CCX>&S_;LqVj`PAzY>Lf1^0gLr1TkmMB8 zB>$fk7OgIa&695CeuKHrr0V00cD?7M)&s+?!n4i1+;ZphYMD2=<8xbirY6*nh$zzB z10@50+Y+phwl`@b7mj~=1W5^nQujsB?wKvdvUQ>>ayY5(3CJ9kXTUt`NWiiX#rnF* z>q2m@C?jz3S`2DXJVB8oKvO@7>o?l7MH zY}rY4T-ZL2M$M*$mQDQbnnX@CkbzKB&1IU+L!Z(yCmWEYT8HYWc_7J&V1H1t>Aw}4Hhy>WW7 z;pHdi*%oMmO%io4&THwoTE8mk5es8QZ;UIYFp5ax<(UaqBn^uqt6_r?@p0e$(Q4y> zLR8Pv1CO=m`{?_nlj*xYdZ|THRdf}7cAtlr;X?$sw_zloCx0EF^Xo1~Xfcrl{jz>g zO}aUrxd5wT**&~eJs%dJ&r?6`sCf@M}#nf|MTpLt(7 zNuuAubm%)8(@(~bVl#DIIvV|ceEHlw;dV3-^P)^OV{0>Wd|^8uQuOft`DRwCaqB2a zZIQJH*dh`v-Ydp?m0)}Ebi9_Yk=VIr&Q2e3akV`59Yyjq*~&uT4rQFbJtdLE!`tIn z_tC1|gRtP~$(+#h4i)$L94>>Q&eb~mQQ}qhAH!LekQ)A|TVm?$PRo=BKd-DX_hYOu zt*q;P73>g)8Ajw-2lfW*yItQxMDyANTvi|bsb4e1GYf5BNNWdjH!nX4ggYD1w2jiN zS}e!*urrkk>oz6FMY~5-XL+}?22f|q(D#T&7;cH%?%y(7yk+p&B`fzTG!c*P2yb)X zG0gzKKCZug=%;ai_G|!o%muDIEm^0rm3RW1y8G94Zagyhx20`-_-p<9i#AV>%Xj<^ zDIFC+p=n-s{&eBJci^T9yWf{;ULPrCo0BW z47Fg-k+A)WCrw~m32US=z!Gr7y-U!W72|r5umZVYJE_}20dGaUU#c$vxMJ9@eb_2x z(d0UcSBZKy2_SDa2)VMEYWo@{ml=7!FJ@Ru=;wVxxDMgbyaRUzyYHx0ROJVI@R&WR zHtRgj8!c5xEcI0FB(vY3wzqjMC58M^HH_G}-cs6RJe~U3u`zCwwSGiKFO}|Xg9y7# zZeP-P77iaW%PI+pC!B4QD^{L<8H-)D^%Ny-8f^NUGwj|wyyznMkubCucC4k$>1JQ` z&3fQgEfCi+Qa@WHZ6kS2B2=YLix0|**_vf9enazI`1;|v+tl4;RO{2@6H^58I~NPs zjMfXJ3=gJ(Mh1ai5GfMtY6mOQuU)2$W(oZyzHT?Lt1oB0F}0bHYHy7;oTm+g^2E5|JU}gGooO=PXLjm7Dk2 z3kmVC0h*AbIiJ}+B&&#>s`esg_6dyQN9pF;-T z%A|)m{pk1`KQjc8R@sCJCKL!>-6P7tkV9_={Ol%Q!UQM@;&2kV*#C)h6c00KP%DeQo|2kwPLNz?3U*FhIZSNKU za{11+>-IMafCJ37)YhRznlJXgsh;0Wn|=qnK#WvD(bH|C1)iBgb5kg4>TPcvGi&ZA z`Ak{(_;k$EA~VmEET2c-5-FiE*Zi>SdZXojOf4;$N6IIl?af%=k$4HJwy1w?;_%Ej zZ})KGd;DBA{ye}@t}z^OsGnFiu=#Tv9N*gb?)aeOZkNpy#f=9To{=Fc=@>U0Kg=iv zaDOuoa3Z?}AA5zNTauJC-QJD*tzW1^8QYuN8fw{#u?h7@vUPzy0%Z47{xRAp-O_qh z+x-#&xlL1`_8@;-+l>kz`s~?K)~mQn4wKq zqJ$%}s6c1nwmef#zn^=IJt32xvIVI6BQ6i=9Arq>{>1Df??OzOLVK6%1QYp;ljc2- z!s%?%;!{X3bw4kIa=wykX+Bf^!*1qK%VXPIdiX>-yzW5V=DGWAyb|>Via-+4N~13| z^r$(s@IuOPv^OtXwC5|>`P`%#TSP0S`BsO<7w@?id8> z!T8LeGtfq_!Kz{VW|XPu#c8|N*tb(O_X31I{BczVzO<@jTqDAP(EbnS&<%5Hiwvfk zFurk}zPK6n4kZuIPUCba5ouZ#Cb6)H7@0k{!TjogYX#kJ9)6s`$qaNLEwIu(yUs{0 z*=>@y5d$@ZQx;*XXX`{Pr*}tT^671Rlx@^EeWTfc;QMd|z3IaYYbL-OKZJztUD#Yj$WL9;7wwVvqdR?)4J?nFNgfx>MZ6v%j z@l4E7+g5lu?vDv+g|fp3D9ou@XG?AEZJQZtxRve2tL847r-TAX}q z3nqtv(#8lf+d3R80GWfCPl>vGS_wTE3Xvm@w!6J`73W<<$dtwps`t}Js&2-ov@KWT zZ_YTn39RNgA~vTei8iVrtKHP}fb9m9a+$RDKopzC@P=xJt=n+jy};WFLgae@D`}SFL`0%az(2<|iucyv9J3`OUz`@dB zY|TFZ;LMiU_-%A`yT#=z%a#U1`I>CpW;cA&N446f!z!3`^eC3{6H7i-ht?Y!aSyL({cBg1jCcKaNCS?-R})*^L(uwCHj z$ue-~xq)zNU#x7O!L{W}8TvS|paEJWn}WRiFrT?I$l=q~LNDjgU-4n;{#q#_Y{==8 z*O4vwGLneda?}tE0vRpA_3(qy*OzX2Km9sbSK?^xPlrqIcXu4$1}hR8iZCm7X>YE-WH65uJBY@tOtcwpu0@TGVdnoF+A`ztZVr34~c0n?v25DB%^a~?p4@} z%V#TaA(hfC3a`aiEzhy=`IlOyCiXE7j-^B=2>&|hVjpWE8G1;>Zg2S&`DVYtz7OSN z{F1Li3*=`nKpMrBH+IOe{gT<;JwUhhz=;-M33K8dmZ3`LfgIK{EH`MNpetb|!ILU2(W(PwMSj|_E&nqd0B|o!;p4^NDxk9(!*3k9RoHC1o$5(nP`dHa z2lTjYpWzlLg7%s1e9V?G=cqFCQf*L?_vz4<)3Zlm%`Jx_^x2u#yH=N;Hg!aees)%6 z%G#500RB(R9n!{$6yMVLqmDqzIq|#;H*$p9@rMBQ8zgwuBzfE+L&&5i93N(+GM@P` zp4gXI4(6vxlLqU@Y=eWyQYY3@M1lm6WtT~zA^zO4R@CmhsZ}H5eKh9FoNsb*%-s!;Y=^~nj$$PWFz1w&qp)5 zOQn&{SwOynuXT#E=7PLhfln{bA3+wdxp(IUcN4FCysF@1jOL_R3NFiBb#^qLMje{> zI2zp{x|jCC-2xB9ZJ+`}oJGxLc>3*%`8HqRMKhP@gG$4ixu^Z4Vf!)U{u&M6;|PP$ z`$mY}>k%KZ`>p$J`U^M#&KzLse$*5C{QC0nbpx$>8~jaMqAH5ef4Kl4s<0YqJ)$VC zHs7RnwMY?tu_uT4?0%7CxP!arWKR}Sd4aIoRN;+;$QiEh`&N3r*{CcPyb0!{Egh_~ z&G+1h#nsI#cd62xH+Fpm$7Y9*c6TJ|ExL1c&(~usw}L*`Tk66|e1q2lD_!p6KB?X< zKXG0%$2NMpj_T4}p1G&Fp8`=vHh#jexD^%rf%IYcm%Sx zo*(bVTFNv50T=y%;ca6$&}uWqwOu2$kSbo1-^f7r)^5C&Fxyz($EjhftGRWA?F$R> z^a}&zVU^A_+f-?3n5lPB0&3g63UF^*w;vuTa7QG=A^i4DvKug%wveaL?^ly;^~+K9 z44*l+N!K5aR4GpkdoC@GM$(bTD^xkjm<(heAOy#dm-I|S%g2YC-6@t3<<9E&>E1Sc z-+58Ka|Px^GiW^tX9KJs z-FE36tBMh3()|Yxl=?qlX>bka`2@ZQ&GXodtINAXkzNTn#g)UIbjHjg*XJY@nz5~TW#EPT5((HK^IKD01X?)G>T#0(XSzA- z*D2-ZR_lt^u{>>abw2n${-vNZA;putvN{ z>c4>Zgq;$771OwLW4V*1`D`%LazWMJe(Oz>vV44NG9&R+vaTVD#z*ge|}=?(oZWI;CAFmBYCE;DD?sMb8l zDdqi1`PObp0#A2BqAL(F8y;r#bnI}OUq4bkvA?cv$GF-7RI7}6-DcQ$H5Z?3rae#C zz)y0Qt72+()2%yEV|`{8`Oc}46fCxGKRnx(y3kE^Mses zhp6gm4i_3{qVYbfT~WTt@LhtS$@2tKkYDK^I10XM*&>V`$>se6hNdklBDF#v z7YGk@bN3C|C0*97Cdgr{z27NtGt^eGj9bOpVA&-7hr;R{A*TNbe)m=D#xSLBK`Du! z@+Emin^yN-V;txuHfeH`D(-NGdnhK7yrAX!?ky7R5(&A5{X( zY6ZHVtW?^;uqC)w2B}G)vt>LSwni$S?LFbpMP#TE_;(dQM4<3nOZzZxMx6mEn2etV zj3I|Nms$Y7ECiKREWt&ttA~}h>%wD?*Y~+k+ct#vKkJs^8E3V+3_oZOMwh$Cr92~_ zPE~W=)g~*oTwyZ+espFP$S&>Wt6Sgy@I>ZIcVuwO7$S_Sc%yJ`z1H#Y&~qJ4u3#Er zvSdYdKd)J88KO5EfBsOy7Lm#lGiKAFch#l{0^Bg^vR?5zSoTM)!`1jw*TB%ZACXI1 z(w8=2wzzGd@}5ui`K;bGCq(1XW0M6&mTIqs7>k4m8-VJ{Rxb(+dJGUL4Yq%VltTQX z`7KvmkUG|e#tUjd*ZuB0t1>Z@cXV}u=MnOQ8}@4fCtBdSd*kF0f`#F>1GGghuoqxqLU2&+5oquf8s zt>sm<`@e!#{U(la@(p{3>ItCWVY`mVgG+7zl@gOG)hzqTrIp|N zT6fyC|87f{sJd;m(_CX1P~Moh4ADr|!abGD;6LW|xhP;3l;v%Y2@&LIhAmxvR6S^Z z!rksyz$nW$=tkAv=l}8#YHH5q5&3&5KcIDJXU&E^P*7O%YlNqEFr1wiPZ9a&SPpHRww|Q4B*If zY#V1w!XUT#cr)2kv2na9-leuHKrq2yA^U2b+L{G>0?BIc!rz7Oy`zUgWa;+9_r@mm83N z=ra|&U3XUU!ceg!z-#F7NI$cXWtd(HC|dam!4 zmr1!>rrb30=J_FCWUHcC(6_8xd>U*+tQpuFi<htj+QXSz64<7@>=A)PIdG7D@Mx$vC<3Vnr z$AS?#tRtA#=qe9eH$vu%Y;#N=x@z#1vU0J7I28;WjQ z8=_vXqe|b}O^Lfsp9=D{)^$P3BP#D4B24ay>u*SuemoKx*&9!)a{woorGYe4CsN>( zGFFE`rgfmt=78x*#i?9~oGimBPkowL6o2}gX{_@E%V#mO!)+}l$CqzSyi6y=fw zh6)D(<3`!I$6DS9q_ZG5hC=#~4LXZN2JQ9iV6rr!^3Go)igZzqsq^ z{I;q2y286bH@H4Uw2O7}m$4a~FxzY-W0AdHo_X^NMMpIqHn95GeGbd**AQzYqzMkRLrqPxf2P{Jl82MNS7o&qxA~ZMWVzjL7ik ze#v#GJ!uJ|4rjh#EYJIGFJKyG+Re=<{`@vfUXM%6GhluxA3&gUs`__mV_%S=lEE(j z?f4It(EZI_xD(Ak9ricn5W+t@%keQ&y-9PvI;w8e5{?Jw&rO0SE=IE7eus=bHfByo zX42m93?pQJF#R4>@_#hJ>h~<1ojV7p+W-3x8S!rh1->eE=)xE}W&Py<7EZ-KEBz)o z3^)=FNAXX{{*oP4%Q62nP^SCz5M8QMGj?6Ks@wrT!2K8h7QBl4ONTI7rMo{M@Ln9m zt-kun*Sa@^pxEj}!q9rf523dfbzU@Tu*%-ak#h&_qPQKNlG7C-Y}W$kszYN@wwIqT z$CxFLKSK9ifj7^neaWBwmptiC{jY18G{z`fuhTS}mlGK}$8i=pC(|Ny2)oR61JQHN zsngPj`dIYw6b14nd!n>3gMdCbkj&$i%cVNCW=!t^RTgnw>`%F-B1` zBr3WR>W19tCj9~in9_tY*OXFj?g{>T5{6SNAS0CX`N^V;EFM9nKvuzB?n!neFGSlI z8Df~PxLLihDz(F@t;n6p$RAE8FNqm z>^e}OD;DXm#`tFc{@ovH4i!cUy3^NhOH^An@0@telH=QWd9(WqMl{x7kFk{#3Fa$( zkVBDQ9{$ps^dKYMlzwNzy?(-X=gl#<@Al$)50KjHG2?yb*cWM9%YWHBx$uQWhuVjHl~LhNKZ4zIW&rE zv~T}_wIZk^VhoN>L?u<2+3>ZatNBoK@@{(f*1!kcIO|2FB=%j;Gq$jc)WbSBMKtri7;>5U1loE%qVHlp8w7t1 zl)Z$7j-_=@yb9L|hrd}GK{)>-6)US(Q}lNN+>>0VDGX}Qe(^(V$0nhphzyut8eu|1 zGwDgJe@1ncX&m+CUvYfb@l&Sv{>+a7*cSi1G1I#{r*%EbFMr%_$g1Bzju{&9Jm``C z>478&_uo_B!SD+~cd_`>8uRXd8vjw-s2{oh{lbPdf|BolK5^`Stg-&*|M@3ALP%4X zeB&Mi*kkromUS5R0G>L^>@&LHZs{NGNMBn*>5sR(eL()B78q-Dzsh9x3MAe4x8e z=o5;yf=G};^$A=qQ^8xdm()$p?~)rvcPmYa4E1xL;L&uvOx{1^iEcHU^S(h@6*HeF zAsEB;x$Jly*L&UsA0T*AcjJ*ynrW5jBzy$%u|XL@&^ zG?^u_L&8sk?Oz)sl#Mk9j*?S=uFHMxkf+1#7E2h`i>?w?kkSx}J8;Q)M#r52PG!C4 z_PHVGuGEQv@{^k6Hqx$Ax(^-Sx=s((Te)oxT|hdy>T?21%Gp-2+|up3Jv1kWA-F1z zlF3-%r8$pjWhm5ss!XR9u$AN2hLh?;83g~E_q6Lz@h_lu+|!{TY;m-pw8pM?%Lz5G zp05*+@$TbaRvKFZc^X_jnZ3m9j#Z;WAcSB-x#+bRe`_fgMR+sVlzF*g4&O70PFu|Y z0^fI7Y06!GHyobG8}T46u%jb6J38Zw)$=VFu&6D^CyE-%v4VjY5k>f16iEWJ%6cp@ zFO#)1h40O9E*n9;C~#X=zis<~lAb=?%lb{puvjb?d!U*(xpCy7^bhN7caQb@8aJeh z$5a;*%qjSUW8r1ibLtim_HXY#n+IUwscFLvdl2}s5J=}uY8ardT2v`}2grED@NPPo z$p<8rXA9zW=N|HRg{vy;;`H|v@w-F=4>8N$Ce=_V4UG6R!|ETcL}dUzzm3lmp3mRhQB<+oh^r#W{GdLN$pb6L>xH-VGFQ+{ zy!-!^NbmKhN1VW*uCZ>R@C~Y)BF6eIrzRv@n!JPGNj# zCU8pTdzq7;NxN)3ARU+^OYA(Hg?9`h07K80AC5OsSWH_1hvXI@Yx z$Yn@PSU<{2oOQ@&eYhXItYE$=Rdusb_yjAz25=|b7hAdgGs~EnnqV-x>bxv=<|Fc& z>I&GZjlw%`t3Q3G;|(8)>ik*C8GeVB6)A!)(6m)_Gk167Vj~F;*~>ev@lZe>lGti|!ES9? ztlsYRwv3)#L-KnXmmxLX1;r~-_A=TMHQlApnyG?va;7^k4F|_V8;A=G=)*sJIfD2qQ^_xMixerHVR|*K>LX;LrevB|3;Urp`%XvG`uss~;yGITw#KG9L zL@hVGZa+Skl_IIm3jA6VS>Z<4`wDn3^n*m)pq%rdF7rNc z_(d)eW}dk`S3-$Kr6TjdaCl`AeROiLso{%aeVDuoE8?9ODbZlVe8u?XGDWYpDOc5h;D?Y_kk>Nw??M;Cp7qLuy{%Q+i9)IB1T3*4PfM=rHt7g@nT5v1*M_53MVDXgl$f|W-JTzcGtcgio4uA`npLJz z5s#oBn~~+0;vQUF*n4|?gDNWaF!634aI+VS?3aGLkVSa|Nwj)`uS}&sMAYV)kjB5` zXq4ct@9n+y8R7|C-`x~Q8+W_6{2lnbEipL^oZ{sgKE{yum^MWP=5(^DaG5oSGlr8V)`@zSQUD`u^tX@#kS^&tMg-r zM!!&GFefWjbZKZx+&`FKmh;- zx=C_j({+4XV`z~1;m!jlGqZ)jGB1vGRzQ!u$jE=^+-V6%)%)b9sH zr(|i!l>N=r-|S*ih=`4Uq#ex%O)HnO`0W&seQ1kd3#{^2Z;eiK>I%^0y<@4**|wwW z8ig%l3Bxu{s4>b528l)Vf`N!B^_)Aeos%98GXxqgLO(bL*u`}R9@;6kRuDo}RcRdY$Kqpk0`VeQQWXF0gfb48t1 z9AQ00S?+!av(#+)bIk}DI&>m{&G=iIgl7ZD3ivQ*W+a>34VIub`~0gZV(Kg5WAu%! zqVik z^YkPoM#7P`=JbD$2_Z~=iC#N)I5jy3e^3NXcoz24Y1!^)boVkvWEJwyXG9wnlTC?g zQ(hUdK|~)OqF`8$AeuHcIM6U)&Hh)1R9O9|4k`3Sk>>NtIM5_`sv?^;vEls>-oz0l z0;Lz3CH=kLmw)rQCLR)UwZC5wV@De|{U;5SXqF(0_=#Zu_5YI%7> zoj)j0V8$+M3>w z$?`StdGrM%k*%-u=rj;X`%V*o{Moz=u_^W0+g!rQiA-w zNQI*k(j6lw*rHO=w^P;J`K_)J#GCopZD_61$}V(W)`VWr8aFhXh%H*_+;Mt9`Mz8q za_%~!p-77ng{W(BKKqgt(f#u4wXJW;zY9U~C(FP&SSqaIn-XyY$A`laKLRmtVp8UO zQn0gv)J*RiP5Aip_!voj8~Ukl!k}LyA#tARV}rm+K4Vp_6Lc$a5>S#=GY_5Gu98hR#lN5=F2bN$=2 zb{FjY(HhbAhL}w2>I!Rm= zj<^fqn^hA!RHQ934~dR<5ws2vg8epfCU+NAFEYtmX${w}l76iSwJ|oY!`3=-0-@;M z>HYsH0g2%l1UIG$NGxaiAjt%pX)OzDo?@}bTTx_Na#~N`#oOk6t@k$+gJ)ZGn{J_6 z&hp-9)V$A0#ZD2i)P5cKvA!|tf@4l$IhT73MP3kzDLzbhy@{IuP;s~_$qCi?_ccx9 zZ~^_bxHFL&g0vnv2blfsZzgh0DB9`)nk}!VNal?6;b1~H9M`X>*#PCc=%&HhoGF66 z4{^WK)(3H`N&hBz3q#zI7=+2_ zdl4np++`x0@r&Z~q<7)d+w&52vU-^0G8wGX&3Vg94t~HjG2wO(hEnxxP`#%MQ1g6$ z{!DSN$gbE{8!>x4HIT^@u@{)hS{b7vkH zWW;E(PBG_Ql@^PF>A@&I7a`~{#nTX-R+n5y#9$nANLt2;seMe_v6yawh<0-idi3-Z z@{;uaWkdcp$+c}I-%-8AEk~HoKd{Nu3-=Gu z5d?GZC-LWgY#d3&|4^2&|4^2Qe<-(LNSnkzyc-*Y`yXKUcMJJL&yr$$qgeW{)qKj_Ks+H>#BQAk0r( z{2>yXS zQ8E>$FaC&o#9gCDgvXvAldvy@WG#NYQ%~0a>+1_r00SOJUJcLsX9V+&bu7*6`jj1| zbT?@^BqEW&!Yjrp{RNto`aW&VUpG@l9uI_|d2{8I`IlZ78Qp`IU97}&$UY_4h}{^P zQF1)8=g01r8E7KK;nDtuv+b>7|H2l95#~R|>VFy@!)rCk3Tx3-X)w8qu>TO*!4jD$ zHs(YY>0Kp@a&tusE?!dU{!@?u7FozU(QHkNG*__}gCcJQ7!v=45w#UpNHP(A!_T(Z z8@Q8V^HC79(wYtf15>*D3bG8b+g-N!@mp}=CsU$@jz`s}bj>rZ4u*TmO@R+20fk~@ ze_4Apx{{D}c2oTZ8ws!bv>a0+0iPNu=NIMhMPsoZcWdHEG_X)R`)i8~DbO^%g&vpO z-53u;lKp6>oc^{(qL&IUTOly^6xaRO=|X{3@~uS)TsFl$7H^-s=_ti4FWW5mJ8Sm%ECdu#J2(z?Yx0Mux&!1?E5?g(pCW3jrL#ty>D}BK z00(+yptMCL>g*L^d%W~GJ!G<$_u+1v_SUh}9ip$gp>^*4^ln?UWwb)f{mTiAF37H- zO3SMge@eR2G3mU=*IvuvW-?Qk&7WS8!mjtp(%<9BpHP?7-@Soh;VzjjUp*MVF!izg+*Fh28aat zYe!f=7WAn$l?Ba)vI*0F?V?*@Vh?+z5g$n^)p_nK1c%X~a-Lu0wu-fB&yMJel;7Vs zIV3vkONGZL)q6#PFv;CB^qhPPOdo6kLeE+6i{;(ZfQDM}d$JwU#y10+60N%|ggB?! zrGjUh{*aCCLvQL7M}ckN1A?xe?tFtA-iy|C`j*>4HG>mrer#{jl4f1OnZ#XitP6Z= zKj&Enf7A}_5D3^F#^unLqMFQTPnjMoC9${`@@yLX{lOYBV-wxoK*6;uf#v}Po2PxV zS5tR?xhzlk8n<_%@AE;7;R4(o%l>onh-OUWJetH~c!lX!D@^F@msx^BZZp})xbyw3 zg%(1uC@O>P70zKme>RWZ>n)zSuNseY7tcbrm3F%}Vb|jC#Ul1Iqa(6qsC+x;3T(C} zXM7^28ZM{n`E~-G3|4Af?Y@`-Ovd?t#Y7Wj^n#Ta-O^)f@h&-R8f%D{W`B7oJ5wSj zU5-a)F+_aw54*o_PG?>GE|bKvRP8VpGwL$t!S7~-mxMYCwRG_i3-|+t}1sZ@IiOsHX4uWUw1=dPn2LJ6`eN%k|cl(tI{t+SB$n z6em+}0V@(VGw5kIm2Tu<$d`RC=J3pggf^eITlL|MwE7JK5#6uk+`0$B0mN0lq{^aW-F1EPG%TaIL|FvkdX#B zoB2*nd>ld8`;ySLZq>DXk6swX`F0&S63lmpUScinbdTG9#yBORj>1PIkeW+HknO?b zpoD)p|LhmMr^Mx{I&1&yMewVzpfiUpu+VjdXetTx5S$kwImhOa?w9k@@7@<(oFH33 zl;IINi_B1uK9bD@ZXtIXyMa=^T&=GRmn$8BCvbX#EhapOyJ6Xl0x2tPNh3#(l{{n! z4uHBwVxkcieWtYa+l`;vjzA@086!wi`;Oh2)@zTF`D5+ax9C{H!zfJirI|Q3R z7=4=)3KL>AejZ~FzU|jN2bZaT-!?qIB%QZkj5A&zFe3vy(#T1h5gLhn55=5MS_20| zuA38IUzd;$O0xKu;blGU=#z;aenuI?#p&EnwvdMp^!700GLI6;S+}q-I!B zva_gh^RWGY2Larc^Eb`M|{+07&8BK$RCEE~H z0WT3Uhr``uBd=leP;35!3YqU~W&Y+k3IP{Hy5d{0vZ;(T9uyqf7T}N`7L=d;{D@u! z*_Ic9U}7rQRi1_|pI)4%M`DAvnpfC=!hY)i?Lbm{RwBX{nfF5^M%=9jK&n4gI>(;A zaS*DBjMRQ`a}p<`flVy{rx%3zulh&rMVKr&+S)m`7xxvMyt^kg^*6)ih%El*C@3~P z8dlM6?pF@?zMQ7m!cY^lAqnv04_vRJf=;~hX({0-jfEcsLj;5nS1Z|-Mey6tmXg6XXt`cZzNd}+k}f{uUA=*1*V2V{psawPE$&~b zBlsvf(H}A^yk3nP^>_7xQ!EYer8ghRAI;{<8wEg)f;YM~PqwSD>_uKsL945T#!T|;ua;i2g6SCABgCLVz@uo5(Zj?4bnGsm!Y$2Q5UFjkkL7xZPj=i+l&haFCtcl z(U*eLy|@l_GCVkPy6zT&RhYadu={kGq9X(@SEH>B+*;lEzGdq5t6F~PFR$FJ0WKsQ z_tv<=tb5fZ&GE&(ssPZHy|-4tDl@VT4dl2KODv-)bb&xC0G`t)vR&Z>=SsKsSDNHG zpas$excAI|)=t@k4u!~)qf<+7p&^e6ty;d|W)P`4jac>tJ3ey1FVVam<&P!~t`+;#PCXOmC)UzU@&^`@^TWX1aw%L@akV3uJ_i{fHLvRuRoFYc{sFk;$GOVSQ?jHa zMe!(~ngW!0kPL#gMpx%4(>k8!^&ko2W8nERt0rd(^w5qv>uDB9g_0Ac~K9GD?Xc;-v=JEWmYN6Fab9sZYjf+$j z=F6pk0pf+xZwC%lF-wM783 zYsg7G@;jhJ*QD_3iLc6{^tnA=+uEbutKPqTlT-e3rRKY_xFnBFR^VfSHWi>Gx>x49kA2WTMSEQJ6!4|~l$g@W&_p!wy_0bq@ zgQYZm14ZZy8U8J>z2#?HC-Jiqfj&$s{V?#y&g zcTH7yy;bjYqi?-dtA^jRTYzFt3Czgk~{f&6I8o;sJEZQrFi-oBrO|rZp z@0V5_;r#9R%N}MbvuDkBnFz>sgi0Y#pK94wA3}1ZXur*C`pT%5BB<(h*UI-qa@$Od z^$%&#E#>EUusa`cpE1KZypZn(lx!h2P9}=Bs-ti$QRc?PWLCb*DA+_`bDqt&tNo)e z77#`KtqAlD$?fTl@ut1gogT<079MZky;irg)1zQyCps~;TNbhJU@n>sE`pUy2@&X* zRuzq^aW<#JBXK$qeRF@PsCL=H^nKe798)Ow+P5G?U`|@&BQ4TA6tN=|M5X-;lFRcr z7M6t&=f8-f=BE}3ksophMM|SQDOcZN*LuCDvtgbsQ(Og{91@wH6AR(Yx-m#kx7ty1-TYfjP$%dS^(_j42(T};5 zhS=0GEu@^b=}~&hR+qCn=F@R*(lf{Tx&Xjyy-@8Sn#yaW9iXoF5O|BD?IQRF^+$ejLRM6({g&;-k;`H9Z#f**7&8I=W@TN$?5!yH3S!4qtL*g(D?T;`>JkX?1Icnnwfk zmw^{0irqFam9bcVw^=nq==om9$VfQxJsg?g4O2OKrM+rKdstb5N-i|M+A^ZxJha{8 z+g89gO|0Csn9}^+%7>T#6XdZE)j#`vmw=Kgc#1@{Lv$hj04}212Q6f9hrsJ_3}b0m z&y(8@2~Xt_viYiE;y$CxZ^!VJc`fUiNEuO#QtvhIWwhsF2aR1-UtU5-mcLo{A);&} zQjR0ZCyKP~-Oxd)vf8g+M_Pb8R6|p7vLxDfwC#~1)QexK3pM~>-!Bu=jS%>f(+i+rSjpy}c<)S0(;u_ZYB8$lIs8B-{#TkEAnj7=n z32fON9O18gjw;{ZF|;T;N!eSMj7pkJzEcTFun2R#RK#!V``e?r$gLPU-}=?CBZs%8 z752}^QM%1`KzA$w`C<3EKo)yML>^K^W)jT*;)AKu^y1G_G$dRC z`cW&&iMEqs2cA)Q>u=}mRn8>-=Z6LoHkb%sF&F1U%mU*~vBYPGmSNCy9{|reh~7nr z4%nkr)d~T2X-K|0B&ZJWY$le(N{cV+n3uEvBCzTf%Wr@h63A+N+_I|tk~gM1MUcIZ zo?+2OpXC+RrGLXhaQe&D-Cmtjme-t1$b2z)@S;Qp%C;lvotCEY;Q%MNQDBN#*gzo? z47}vkX@#G5-7EDK+NPG2e+uKkToH* z7}mBt{~D!Y|IK$vs(FW8F6iyC)jEz7iCKj_`TL|xQd54wH!br{o=L@62$NlPo^cK& zsW_xxdF?;Y9BktBTVnUXr%h8+PUccd03AM^y z7s~7YE3NTfsuSpZDOnz~#Hyd&8-La|fH6{fc(8g$Bz+)M5A9uwiFm0~%UCp3u^FpT z$R8-iS9wU#Fut*~3xlnG^Wz0WWTd@LAfir?5YpO4pN;$?HXf2`kO9F~*ab$}5-`ovp(H3RkoFQFDXt3T$*}OOA{lclJu)TWHg&K8vm zuH8ar6x?rDr^sl+3{@g5wBBzF@Ke~q7Dvh0{=xzF;Q{<@YY96JRcCpQZ%1#o7F%O0 zan7i6v;_2D=&JF5hlE|a)7;FHHG~|A+(Xy)VIwkpVzEXUIpAR+r*OMQ z_o~n*P5YAS@V(21`3%Z6LfJ71oe`h0@9(h1eK^1FfADacrImXbMB??);BIv1%y3-o zAUtP!VL9c4!L_K*|At2vI)#lH=YM8>cP)&`{ZZu{?Hk|hh*Ii}@-Bep?OXcYpeSaO zS*4%_GA-A;o4{b(pXe0<=0fAbPAL1UN8g<9YVqc%*$?ChcX^jdEzc~LBiApGt?50o za3RB|eWd;J72R*rxts1-pdbOyd+lYDjD#;|29Xw8owsu8GkuEy#kFkk6x+=0B^pP& z9dV}ESnx}@m?Q+pS&IsvsPxK|;jDGAjuXm8md2fHCJh{!fn-Ps>xy9thWu_vW8xDzv9U49WA@he4)NiaHW3Pa%u%)!9ywD}$>F%d%;y zy$2Qbotia}^p#rNKew4JY^y7XQc_ANm+z3bQ_)^VHNa-H`2*-_DK=M5cYqu^7|9>7bNb<4%Lwi=u`mdr8vpPlpo#hHH zQv2uj$I1S2!GqDU|2cLffWMX)T=e!IZb*UDQZ(SJ!{`Mh0nNE_VinkR7oBBcF*B@N0#?05aYa1vq#b?z1VVi*uQZi!2NoLDbh<(ePSN?N@%ekrv z9*O%qr8|zAm#MqU#US_N*j1(QZ40J_9vUlR%DeGBJw|{~M_l8}%14Oj$t!(*- z#W^eK@sA6TciVsNiC@;?!`PFCVqpzQQYm_dgLkt>ZfPQwB;PTl9Z>Jlv7JG*;j?c9 z?Jr@gj`OL*8y-_yVcWruGovspeG?NPWYEgDP7Ylg>{Y!*n0ZG#sy>;9&s%qOsvgjK z%kADFrOq3 z7>(IU<)J%rexdP{?g03}RDo=tJ&=3CayDK)kN<#t;rujd%>e@8bz@wU+0O>R3aGdG z?$ji^5n(k=79|-y*Yd}J9Ap3z5BILn#LUH^HNiQgg60 zUKDw%5EoM$(V02b)xW9EDj#?OScX;Q(Xn;W=AD$-Hc!tuCAhRI-SO9pgm*Myw4bBUD8P?rFz&5tM8k@bMd=c~5Ivm_z7~$4RImSE+<0hi zoyLNi6E!f&OhpV{^P4m{-R3uFNmZwTb`31ITdJJ7W0AQ7+n+gOe>&clk6YY)lQ>bV zt-^-&SbyuS7)9Nndebj%k1l2{CLGe4T~m3;`;{SqlUU2sv?_WKA<115Q=zEvJ zc_@&!Twov-CoPA*{5-Pi^TS~xaUood9E&NK7j|%->CZ5xUtUI0ve=}nYi%)KLjpSu zJ&FxnEqzx*s?WrzF5k`gt_lh|f*7`Hlmd*C3mq2}oViO{d%h+v!Ufh4LesvoP{?S|uwH)0+8dHv*sK00J zw3Q7@Z(6++FEE#ki1)t*2Pr|1U;^<*6G~~s;v)R#RZg#As@Cyk>Vu9 zSg{@Bl#!YDqVu=DK-3Z5)#-@pNYmnG+KD1P%b)Jo^4dI&25^Afp+pq?Kd}&-d3xXA z`hrI7>-4Un)z(LTlCBH)dZ?{aZjTHAjhAsp)`9HFT&unF;AO0p+ZR;yX>TdgS6hXC z_$39SbBX;6k2ULdRwv|d$ZA!m6Qwer535(_De60XeVgNn+XOhJAXP#M*0CN`g`D2R{bh=d91J0&=~?tQ=Q8NviL+wf@9cG= zrZQr#fh@y4-HP-mx&qc*Tv>BrXc>PJOVtmC7ff+CUa=a6A@MlRsTWJH(m)`^t%Lsh zefMkdvQXM!^=^ZXj@@GA)OECBul2_-y%)XOdlvayhf}W`r-G-KDX-2dZ5NkM1=H~9 zp5igY!L`d<=a6M5!a9e#dM@3+2Gqha4vu%{>Wp?8l`}S+g^?6yi>0FzAAAoF*6vto z#6oj!nsjZ(X`pr@xXsTm&K#2hsnmr zw;ikXXi+#^A|Ppgy5pSvZ7aa*dDWj!(2~)`#EJ-rIVqux*lzi?((s%jzVMBX zG|`%v#xl(K`vB(ojLvA9uvn8&6z{8~HqLVwUDDllNJJta^q(Kntg9Hlgt*3u9p`{a zV)a-mBt)MDO{hilxGu{0TW-Zlj3<>p5IV|>(SZ!cr$IK-kX{05|w-x9UUy$=3ryQF4x~vR@10L4uECG$Y9%$X4eA8 zHR6n0D2oYm9*nMsu8%aw5Q*7U+Q12)li{qpV2cn7i^x0OFcAhMQST$?^l*U9ydL^8 zz{huGF}#^FJ_ljT=QDuz5^p;JC`1Kz$cKwM-otxX5yr(C!N>C#{4|D364wF8+n&>O%@BciztkO|P z$XOzPrVc^APIb7#nyE@LB!Q_V0I>?Cfh2_y-!m4a$b@?#u=uA77vrabue@BkoeOD( zFz>f}RaH2vhF87T0EoWMNIdo#QaN#VSUY{ zpk~{zCupaGbiV4Zr#d7$`{xYf8MZD>xD4e(7R27 z7Yy0h@+3R?F?x8;mL9)EuEXC_i$P)No4gE1^z5Q<&mYg>)6uPEbpB0eh~T@7u+pQb zvHRIwoBKGTsa4w$K!QS*Z2?$m>si*+4u%t54P82@8(RWbY023%uqh zr}0q*A3x%Kh4W)lFnt3IEh>B(v7C7{C1lBQE(yywf0A;p^-1VlHJoyCcH(-Eh3(50 zunJ^2n+C|_{BZu5WfX(&usM@|LA!?TA*~Cq;c_8*NsSLQiY+~BagZ%wp#|1^Vk1%+ z!6J@*h?6SV&tKy{5fM(CnQ2huSUnEaS3mPWbF?>riROtpj|ytQLHejCjcm2@_cwt; zWsjAc$|os?4TkYUjU0kN#;%&`wy%k+{erCja%zUc_yik+e3G&sC~`{xZ11)YUz=6A zFDskhGcqdk8~NYiDcgzK0n|aSxK>0DIQZKaW({+ET*LXKv(HrH30LW)ilglQ8Jw-Kp|s`akfiJ( zTr8Ta18_buJ}FfiRGt8!!8(CfPH%W8sI;8GV+l_G94tO+!rY`s=vP)G>J6=*=9VF3;bdA z2uS-x91pgZEEOn_DL}e!=)rG#IoEF$x#$*U#r@H(C=mN|@&{l>0f1dqP$w#fUjV z1QV_;t!cCL+@U7os;G~s_?)(7@Tn6>W?fx+gFt_7kIrX$srN9LdfCG<_QRFnV)Vrl z(!pUPGesa?#rqH664Vh62kK$orF5Jy_cOas&|0EfNpk~xs*|cf`irh8<1A0`+1`qF z^;}wUKA(p3f{u5qm&2SNx&jhzpIcXW9#IhMr9asDFnPE~Tn|+CI=g3ow!7byW!5wJ zRLpBlt|GLdrQ5p-N_sW>apX&urFAy!&g_cHRdh%>EUx)Z8YU23|R&uJwp? zXxPJd<$5}j>ent$sVM2 z^8Bn(GzW;#0s*Fvz2TLd_gWg8?jxI@axSL0KKf{`j^lT-NC+>M+3gwl!#F-qrR=cH zy9_+TLnNUsmaC>+iJ&Xj0}bniA&A@73zSbBYy|HK+xt( zf5RXCf3E~0#s5j91*8A>At%~@cY^*Kz_?6)WBccpuQqA^r0>EzU;N*p4y=Dy;=X}! zqh_FuaO6ok!aCiEM}z6~SlCHm z>*b~+$iw)M!b5r%?8`(Z>1R?Cuo!T^Q)~2I&_<0X)tm*K(0uK#0!%*XEW&t?B|Uy0 zyy%#vbT*PYvTQkbu(<;^{|&P{KQ6ruU!p>!vZ3!Vh1KUC+oL`U?etOVkQ3u?642$X ztAWS%O{MQ!M3gI&4f1SW!lcQnH%7~D-!KK{T9en8WE=T$cRYzlFT_%Ex#cE`xI+RO z_mk}2F@BM3eB-Y7aWP&ekq95=UkGI0iFkdylMlPvvA{a0NI%iPesi}Ux4)VrOVIx~IG5yc{dRD+ji(OYd$3XPDSPo7XWK^`+?f0%4?bmIAkJ~7)#VOV z@)UD;0$!^p7Wf_{gkye^^8-U-9yQrT9s-$5jK6`?hdcGfLFemVbgdlPx1h#Q81~X3 ze2NujqmWVW6|OceZ%Ve4&F$v9i#=g16%}C$6l;JJ6qtzfle-cXYsUbINSFbeu%hGJQk^;_!FT)TN2~zgQ>mSA3BO1jkrCt_4-=+?)k8#wv&o1e z(SxKcFirUXl~?Ue^9;~~*LgnbyuWL}sN?G-7CKt+i0zP4c0P>O6cmAYwexSmXw~j> z>1{W|te7%XljZ#nj#U;z>s~d$?6zz5%CA`3Tx(!{|0lKdKXD;>R$FdT`hObw36OF_L0O+Go;v^7(`>R-0VyD|P+;I|V^h|hW6DQGI{D6ynDKOl zy?c_&q9TYMDjj?{xT92S?I%%s$q*ST0cH1s<~g)|q(_!aTN;(n-#?_VBTeEdAEH}Y z{!jE%fgh7mMA}EfsCvABQ(@M5->&bs_92TbG8mFB1a5#ZNg~ECzFl^n7he9Hl5X=J zLnqLxH$W%=5BU295w35C8_}7}4ru`sqCB@s#5ivpHcvw;FT~{tvkoywfSM!$iUG!A;x21wU0}|u2up4sRcn3{9llT=w|w(eYZuBxig!< zk5iiAAYe2X(w`D=1Mz~1L+)kBKZdm^y zH{UYX*0|>2bHi&_Pvju#W@q{?$VS^|x4=vs9AhEBdw))NFT*< z>Um#$T&hIa_rsJeLcHxTR6(N(CY7T({~n8Rd1IaQTv%dAi958^iD8Hiy0D`xEsR@#FEJCeCG zA0dSKPw^f40)oM(?r-04ql6VeQMW)x=5Egr1!zYpsuJ^Z4X|O^A=0Onyryf`<&(-wfO-4^*D)&)czNDiJD}hQo-ru(fGWqMeL)y`gTpM0j|bv6(nP z?ytLNL9;sjet>?u9-y%=V!Y3|eUD^hthkad$Y(_qA!fmB=d!;t4s7+$Efc~*tls6yOzvecG(BHl+YrKSnDv#Lc>;FI8Mp=C4u8`k6qq-D)ztG<22hw40lhyggr!k9Xqv1!i_U`XVt#oq~vP5iI0mAH&+&LgnWy>{tfiKBH zz`m`g#hIrr(OCdac64hDSEdYB#|_OC`}KrR^lq#b^EW?FCmlz#d5@8{Yo;#_EL1g* zi^vYh!6b|RC%{jK0UFP>bc;YEL*EOtxdN{$Z1|YRjDb-#mOQ-=8#e;~k!0@eG*GFo zv2As>C6ae;x~~Y{?7r6j2)^+>nOIMKRv=BRuWhm!? ztL9uw=}BvB2f5C?HJRJE#!KQY`92=P!<6&QH~LFh`?7}_bwk17xyxhZT1tbnz4g&c zsyZqIv+W|MRV%_Ky$q#+=V5J~Co8xgxvIlw+MVs=pPrPE!|N_IR%y}ok=u27%!ZdB zal!#^b~zLR?lYxqYLxc}emdg1N4oBO>C@~ALQn&ZRdy;#HSz`_69ql@dWE;@9NEMR zt73B}E7CURNLz94H~O=;d3MhLccduXa}+z4 z|IF$4h#x@fcaA1C_DN&2TLS2>+cOk9Yh52OP*j6z!((PsTwF)4dzj7pATI>Sb$+B9 z7Tyek)Vi&XnLPDhVmo14_>fw8mm0Iw_1h``KDZ$sZ4s)is)zYOJ*mU<-)Z(Syjk7X8|=r z>SAab-Q~IX++Psz3|;2Lri)EL#buw#D5G62jw9cV920>`IB`Mm#H7UB=&Z3Zzx?Hg zXf>wq!EjD_;4kp1{xo;c=FcXK#n$U?6r9`V)AYg?*Zpbr-Il`o;StG1hNnWPQO_8h zmrC^bn7ObJ>ws(v!pJVzgJ2&RbJMW9Z)n+9ZneX2)jN6+A4=X)R1f$GlJIAbH-bp7 zznE`F3WVv6x20r>Srs5RR5S&-Y9>CP?Tvf}@2v&bbV>-%ft=uz%T-uMO?}CTo1G5w ze>!IrZIf2k0J|=dM0-F_+hsYuBw1I6gSTHF0@^CiFZ%=9VmUzc;{ht6Kp-Xy-*ZRN%?R%ReSJwpM~H^&I11Xbp#pM*QhkW6mf+%wZN^VV;Rc z*B544d+)pNJaQd`+~5#=1_=9%mk%SvYimw~6tET;=9v+}6z=WFvv>&Sv+Xl!p-h0E z#an~gH{F?vs1f71_^M7M*>=pRA0RQn+5;zaFK1X1c%%Jap4L}d#FkvM1?gF3;m>xk zj%krHJP2_}>i*v9g0S2bO79Tp-+zkP<&tf+!1aM1CR`FGEO)GGev|$k7G7JR{9~K1 z%4^nZ1zwRKJu=9;oPf;{5+PUY7+t|e~G_r#+>`qxvL(eKY+A|pF8 z%TpVUr}b1C(TxeqO<-qq1FyF|a$^IcuX|Wokx|N5z*cDmfhNXKx}}=Er^}-K+$`>o zwKm&+*)xi%C$>|OIQ(MKjvK5Z0Y6H?_Wd#P1+JP%S}}f_iGMJ9uTijsn{f&a582VM z3LTgIcg#}?HO_z0<;ZH327huEs$Qe?Pg^tA&!)ECC_t=a)#i}0&cgUAd?r4#qR>x@ zx1CekhTL2WkomSsc4V~3bBtd`!F0@4g12ob8^RNbvaWlAjs%%2XDGt6O+4}sUlZ+O zzN9aKE^ylcezC|YIf`hu=5)WBU9#_WCzkbS{Iq2~R^?9Y>3YL_tJ5wkNETH=$D4r? z;X-Pm*t;#<5#Y<%YUgVgdB?n$2g8InUo2!I7Gw0L<3hXN-M&YW5U09*b@#)>r&W@S zyv`09af?eE(>3;Cbmtzs=n7r$E$4T=HgBht_SyrJEdp`cx(O#aCXb#)U zPg~2bhmRBE&5zOK3Yd)d^iZ`0Wc4fa6;fHK1y zEmm5vqwmEyub+roU;G>`OIt_8UQXdjT7VaLJKfx~UE2XwYIhYk^wSms8yK!mFbVwZJ*Byp1z5S!4Fot)()^d zuPs)~_}8A@?Cz#%2YPEMlw5uFBPS)9~$<|FkQ1adU`ch z?hAzF&`pMB20tdMJlRwpjO1jnjhLI9;<(p$(OPKBe$+T#H&L6RUb-Tyoaa>2;g1h; z;mNV7Dc{2<(P7O_v(E7J6k@5=kF7L+<=5(TbHMLx>OP>gGI${o2eaC#I#M;IuUYOD zv&&@i-QM>8_)+;%GcO3Oh1VZ0pJHsE<5o|ww!?&Ue0_xcW(zDYWv~q@78S1dd-9po4SE_ zNYA-Ea{Oc;PXbzQPIa@|FXVmia0y=te8i%`ab*)K?wTa%P07Pze7e@>Zsg<5iD~n_ z_GsE~tZy9;OtpNPChqi$Q|6L{IEhz;K66RzK`A`>e} zwZ*2z-Q84@k0tnY4&JQ$r5C{Po}=%!foc`Od2SEthQc)o_mr01d@}Yf(e!F%Y1${y4Ih8pck%(BV~o{I-;=d zfUCbJD;1soZv4q?9z*`4o05p7;Fq{UWnrwdSJxn>*{4JPpp9N%IJ8I30#-+Nv!iggN0w)XW^Hhuw0?I?o;)9|nq*o~ z^T7y%jn_V1$8+zvH5Vd2zbAifq0-s+(<21U_B(rK&&q9{EKu_00_?uZ+UesR+h&vP z{nm1v>m6x!PK0<#=*W(MTCAJ~Bp>=D+%1rXzYXWKy46|h3=B~}z0Va&PB%CNuf?8= z-A3HP@@@9oYJ=whzITnC1+(-fKf{NcP3*i%zy|o)e)S^#ZG2i6&GSMpUEtX)Mo?CG zOO)rwz}DgAgzt-y91Bm?GG?*>mM)8lt-#CbX&c2R&++D@eUkLs2sEZ!^PhZ~4Claj zaK|qDSnHU~VedG*y|$_hm5whT*k>q4P;Z)KW@Ni$vlGhp1+LH?VFe%cD(V&A{g_^H z>a)lS?`Bw?b|139F?k1DZB5kdd*)CJlT;s4jd29ri&@#bi)nz>)EB4AGo9{^%02At zq1|=8A?5R?NF>LrckCtvg<9BdM6JDYBm%XrAx|RB?kfP}8>@a&aB@KQLcY>TK2%5s z|0BVd^`Eci6};oO%*M9rR6|;Cvm5*lAF>_%NIH_o=|q^i;3q@n*B5N}HQI zixpgma)RHvSkp_!2o|h8*q1o^1nU)mqH!GY^H_jzwakPmN1btj8ioXWsK##t02@_^ zD=o9gL^>IWiPO<6FCeQMDN4SYteXxb^SMtpnXi3+=ABD(Lvj9s-=V3Zw3AUvQu3RY z?ZTR*jjV|>&vsQ~jWgBF=0lc4zllwoBc{9=Qf&*0P&>Hi^Kx6-zN1&XNV{s#`IZlG z*WS#hjl6H|fpfkS$a8ephFC|dyv&&LtF<$Z( ze7ORgYfMRFu1^JJTIu~pxNMz$+-tS*jJ8W&dt84JMpOi)0Yy~@8`t>&^ z2g#EpWtUZ4E!Xb!_5G6s zWh3&E@wi{6T+^GAC87T7*N$3jY+8_e9nR zkA$>1{7(&om<1;EuYXN+pUeJU5@k)I`m4;KJEG*T*4Kn*ak9XV<=YHC`c@U-7Z#@f zYRDDc-wMbPC3lU(XGgXtS&Rm6YS(x2w3QpBA?3c=zW#kD^Uo6a;VmXX4H`I#NJ5-U zjjwo4ce%&_6YPMd)%u%nH28^`9x!O_E*w~2ghnz&^cB*Vr|lsf!5GZ=+mS5}zc5Rg z3b7A>5VaE(RyHxifMg(blxOa=Em6?h%m}WoZd*4Kp+oGlt3iGLyXKT};n8%x#9z1n zu_^IKf;fEU*tw{9&8XcjxIHgR7y4|)$(JrMeM)6Vw$T~Smb|BYJf%s;6Q}AK^)Sy5 z3x3JST;adU1{UpZReq?7o*7q#S%wAZC&P5)Bx zsssZf(9b>p9>Wv`nuZHQL7oE`(o`S$;c!N8JmvWq)5AfY&1YrV|4TNu=*yNH z3vf(P0+u#cnp8iS;Ckd`q)ghT?`$A_>#@=9-EM3XHQg)EnA3`=he(!4_^)zcz`(>E zl5JA9c*!vHVPWlHW34-ew(sZHjmEyI!fFex;{9qL^SeK`VS#!N*xP+&rsDY%foOSp6diK%K5^tH z3*;MOD-pj%fH7$KSJ|Wg=l}7aHy^MdQkInT{&Vg#V;-XFufqR(ng3bG|NKuLO-KfZcB{TI<~F49?AVh&nz zIw#a6;CEsBGi=C=r=e^7%AVO$j%ffzblh7aN*K3yOB$4nhJ^ zaBS|n_`*Fo&S&gLNHXm2?Os?-!+j|qGMQO?e)TOayJ+o4WWI2yBI#S^(HW2%@$%Bj zDz_~v!4MGOy6F6MKn5c1f5yMV)h>DR_U^QcE8L;R>0#k@^ap1GMO_{p&;BLPWiYfG zGPxV|+($`vWLQ@HPuo~8)rRv36}G&XHP7W2C?`i62+;DaUb1eHHO$`ArOy%esB}f3 zqvF4m_Vy|8Gg$20%=^iKMdxf_S~_3jZHpd&+r!<+B;JdvqC2739wQSj`ShA-;+8m9 z(b?Am9-QH<_gxYNO;e4*q!l?En*#HbXg(SYYmi}?bw-msJGxumi8diQ>sFC@6@Dyw z#6HIXl13tMx$(Vm3S4M66e%eN#-Z0=T~lck(H?}Nm?zUdGsQE;ZuR6Lq|7}*lsqk` zW{Z>Xza%*(`1srYVTrFETH86yhjp2mlzx#`tiz*Tue3?3-Hl=*^u|> zI(a34Gk4*vm=e7GiAHpiO`@1+iX?WiuDm?Y0kV5RQB-QW^5Tx%LlTWLNuec$e6pyG zQP`Zl2{{{!UxB)+>gP1l`IzatZ)u~)y5Y^jhy$+^!hv)@#&;AVr!V^D8Xlvn+(WD6 zALEm)EvX|T3l0ed!{(AC(eQ0iOJgdX93`64h0yP@X%aOM3@h5 zpO6~)#5s*I4eLU8>+O5X-%t%`vK37OgTJV&wKl*C6vo%o;OQ+7;d$t0&ve)=%?0%C zB?;KrSF^cX(WY{y1Y!@0PNIZFctgMZR6NRMZ?T$T4~!)o+=%3KeJ$XlZscItor?z5 zBXeI@t)57DIKTq{?c=J%znUg+p<|3jx4FTvCIx@QRW6GOhDq5tTTqfH`UoWfI4mTL zM8AQ51*b659-L_tr`&QG!&!A!i2{_~M}KSzajK2!e9x*e@cJ9`#9e?tWI_p?7AS$3 zQ)J z%%~V_=WLB!zTRKv95ouIW{A7WK!#SOk^8nt?qLTDlRL|(w{3A?1-E_w_+DIqSnf&z zbC%0a`*7-fhYgt}cu&f9vB_FMYI^9>q6++c$1;n`BTc!pMY=YND3|Q1_Hy8MgHW?C zWgB@3r_$oI<}@+CpQfQBP0>x5Uc`el#1e}upt4)*D1zfzd8wxY_WBIs!7uu}29shw;-VpJsdkxR>9!JwYO1C@WYA(n z8t-GDcV4-Q4A86sTShU+E9AfWzUb-qaU$fdJ-R99jAj1H8NYc?Motbd8KC>iBQno% zs6;;>kTS-8q0rLN3lknr%J;eM^x zCeO>~*yjKRj5LWKm5S@*;N5y#<(#7OQcoxtxb$StDOWDya-fC!;3MeoM0{TNs!;Dob(hq8W9y1w9{T#PW{7^b1w3BFjajm!mvkmaXId$jQ0 z%o7@4-*OWL1Zz8drP%oCeW{DEg@HRF-C%#K=Iwq~am_G=*S*-1niTM0J+C<$m=w*I zQ8$m5tH`!nf!Gwu5?%R$(ESij(uj#JRp=Ht_bc{ z;K13Vrf1Lo)@2~gI9c@wB?kO4x@A*MfJ0F#P5_C8zmo^|H0DU2WB?PXIlacq z%av0}J*;@WA6;r;?{uvx_m}Nltd~0?sfN3|x?a7c%T$++6{}&d#D9W$ZiOz>rj1NB z3gzJZrB)YIAy(?fUDGHeupN$6ItpIRiv~)m$bL=+kb) zl7Q|KrKGsEH?ToV0aX#)eG!g2<6(p^lrbcPjS`6-D>!TU#2Ila%nl;KkP6Z+JLyRRGfpg~GiIpio-3)_{_<*hn=bur z(R7a&rfM#RNb~)vq7u4;!UieckD~8ms8Vlt?#M(lh)1bGWH_8*5pXas+ddLg5Zq#n z9H*dhPT>t0;ry@zA3n@?=+dX4e7LDaq7na=e+}dfPB#TcscmD$pvTOzHAu_#;1 z%FxZ@+W-gO>WE-W;NzJYeefSqvwD+c{hmMgUk6&7)=Kf7ByjY0zg)Rh;edGE!eXF< znR%5$3P!GO2iAxaR;SFJJA~<773?}Wez^N>23o{Dwu>^hXyMX?@5aK|R&kp}0Cpsa z)dFu#q1w=ge)Csn!OP*nOrNBRX{ypm7%AjzC6IBGdt>2B_U+8|XuY7D?J;s=vyFYa zcBCWfQeGl1Ga#b8QEFlfJYJG~FH(@xO{o$;$d`?L)CTLXz$5pqPp0YxW)7{pAuIM; zYKmoaLnv)*<_c&o^o<|=Q|hURB6w+WlQ(vAi<*dNxqI})|8fUwZySMJAeXm7!gm;l zpy7dT1^X2toCSeaKzJ&glAfTKe2a;xjmz46t@J5ECt*dIr1|=&*TMCm@siC|UuPMdWh5 zWWBWYfnVf>peGF6Wm-8WoE$B~=?5JmS*28T#s%=&t)K5ybR{9xl-Z zkH=lKOj@!-(A$8|+aNZ)6I_=_QO+<9M9IwaDH9y|MdPw-OwMeZp=RVGE;;@d-zQ!q`Z zzb7$f9h>g$stUTlp@Kcd_9)g{TCOzTQYAMbZ9au6ZjATe_NcmS-(|c64pt8JaZbVO z7q3y3-qAnfO-kolel%qnjIU$8Q6W->dZxjxKJ3w8G<=bj3oWNiAg3c1`lvkJ>7E73 z|EZ(avm{~%Gb{63fyM1)Dvpv|In5y3RZv>h1sIlt^fDWs8s&ZuYIQPo!{d zKXorcSz<;MnIX&AA}$HZF3c@gku6u2LDsCJ8Ig4`*0D7+W?{@2<~Pdsw>-YT-{-&2 zKj-~8pT~Kh^M1Zw=W~vbl^D{Afjn);Ikt?u-=%d1f84%&k)PW7dhgfUb*aoP@1-;f zsn{N_*(d258||-Sg@Cqij?gPOz@Ral7I4O2(OMg5(SAfwRx`mM=r!|)8I!Ql#4NQuK(M+5f=y=jX^zH{ zs%UR8FkK0Qa~$dva;v3sipn7K3Ma7=rTJ;Mn}1~3`u0y!OTYWu;d-JeB$8G(nQ=f< z7j_R7jGKS1TT&0WOa9YO8cbI%`6<5qlm3I;{*QM)rKE!hR}LYwBqICr53%IG8*TEw zdl2RSZ^8wC!0Z>Ke*Nh)+ZR|ycP{c=joK<<2*8+j@p*Z9=+eJJ zaoCrp9J1>fMXxJwAn1=Jg>Ldt@Y&wq^;k)rpP}fF)@hmkS7PUty|tU zVCzWRsadT2(xVn2Y`(9mAtULgpI)3)+J)zO!yy8a7XATxmEUg%Q}lG&EE56ygtmDvrb6 z7C|1iioqw7Y<@7>SmvA?wIA5_4KwHBHG& zYXvpEJ55;75};NVuLf%nriy8-cPiy=6_(MqV%Er>Z_yaLR2I>&I1TdKniWUegM@=@ zG&l)$;iu|iA+}Mk@Wn-S|2MvJqrk*76DOVazhQoW901Azx#m%KN1?I(Y|AvO!M$T_ z<7F#X=$actkp>_dk*y-`Q*w3&7ydL2>e4-&Q#GlQr28V2RG1N9$e&bru z4m-xz>UGUkgvr-#c6YLr+j9J$cIP33<2&6=j$y%^BWGye#P#E|uDp^`Lawj5Q9gr6 zE39lBdBpsR%xA(o;j?{{fPLV&bZtTK9yeGzvY;Iu3i#Hy*0gM)!Pu`Z>vKI&lR@4QP+JA8<~H3hMV zH=?N<#64eS`>tmIkKt~JR?CE)d%G7pZx)dv>ooxyi5n+%t?0CW*xZTSVHvl~q77;# z9y4n);oJeysmAr0X=7vF>U@y5SiAG5+96In&{x#!P%bWB14}cL-&s6RmP7~FzS;BK zLK8M2{K~_az!k(myR-oNMVa+0=Lvg4UMYA|KBHgUwq#lW{M84YZyciobA*IO1%cfq z=931UB#=;(^weisLNgLOEcn@l8g z*&~48T{7wfGwbG5$%vxKUU}Pmv`pxq`CxUyL?EHvD1<)L|3M$kg}D#hT)IyvSFree zCJZ)~rEEbMPch#-3-NN`_Bs7%pb~g91czeb-@e;vtPO!sszl*+(Ta=I}q?n+XON7a{9v|qu3W*BV6}FDP*}a`93c8^fHBI#|wgjNHYwy~{ zKVIBtlJs>`lRUn+PA5Kg2GwWijYp->L(aCT?=R9drgxVYn%pJAfd~CQtP4D&%Hc}? zQ%^trzxDJyY2oRfkWgASU6Heed^#*#_!58iOt2{Zqj%FWKz21?B#NatUhskl?TP-MxBJGbz@5iq?DxlO^t=NF4%hdaVa5aGW0%n$fCeV zZNMSMIxYR-l59R;Wqh~ma>vR7+@dFv#b7ng;Os-~9 zUixvr4M)GBhR=XKk624@ zXOB@hvCM0b$B9Psd*Jh2xYF%2?rs@mbt!#^yCO7xDa+kZV^b9oK1&vm&^ zT9GybC(bz=qybmr_0h6+7Z)#Ga@rgOmHGugGL;JT7*i&)g13mGtqkN+)7Asd%?7)S zNlVYsU1cAMj>ck;#z;A*5XB$*6gB?kr-Fq%v4RC+8QI-3`C?%{&a1D372IRykB#Io zDM9b&u{ce=3W?=+l&tdIO=6atH$GQwGd{33)VDIuKH_ptbb$0LBF6~sjm-1&zia*1 zdqwKvgQdXXhd>w@H8LGBEeI}(;ET2^=o2?~tLg1dN%pwy1i!TIa10VjJrawV8Mt^) zbuKmuH%C@cfNtrCynN!`gT*=Sp3n>4){(BA7p}?+(s0DJ#J1SSBUTa7>w;jx+wIl< z(S={q(HkqUfwK*d9@8XSbr`N_(4Mh_O;gb;c4Z_x>QW@#K^4t5Xs|WTkzCnNAdIwi zRj6eX$BZ$STF3_jzEKh1gN%VRX*8QD)&G8DNlrZ|^eJc`CC*@Fk~muytdR2qulWLZ z`Q+}fQ{;`Qh;fauy)IV`{1Hwe@SIirOn(Zz1fvu-Kp&$>$iU8(+^Fv=E%~~60uTqH z%xZRYf>pK3G6ALP89u64pv976MFpFCt!pbUkX`P|X;9^DH_AiKaRz+Kyl;XJXT+~9 zpQ#7hJKvlb`JyYYSJmaR6|fbg75?eJD59@p`HJ#F18}-mpf1am1;VX#k{#%zZ&jS? z#q|qCsR&<{r^gpfO_o{Aw<=m(G!nS6r$8pq!7$&=zep50*yQ#Crg3pQ#A9l7uysT5 z^2v_c6^>CiG%WoSHwO<2@|;GI1E10BY%xtjHIgEX4v#i=Ig$GOve`zi($4ZTrSK+6 zzR!+fhHU08Do^`;%(YYY$%z(#pH}e8WmdM^lTr&ijUzOJ{nN~|@AqztMyz#<14d_0 zUgM27N3_=%%?NUO>yZD$WWIo6*vtTaxX6=-o@n+){ZEidq?xh-FH*byLsTu7kBjqI MUbi)?GIfvrPen0uKmY&$ literal 0 HcmV?d00001 diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/requirements.txt b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/requirements.txt new file mode 100644 index 0000000000..4a58cb756e --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/requirements.txt @@ -0,0 +1,7 @@ +torch==2.5.1 +datasets +tensorboard +transformers==4.48.0 +peft==0.14.0 +trl==0.13.0 +bitsandbytes diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/sft_job.py b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/sft_job.py new file mode 100644 index 0000000000..ad73a4da8c --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/sft_job.py @@ -0,0 +1,194 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. 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. + +import argparse +import os + +from nvflare import FedJob, FilterType +from nvflare.app_common.widgets.intime_model_selector import IntimeModelSelector +from nvflare.app_common.workflows.fedavg import FedAvg +from nvflare.app_opt.pt.file_model_persistor import PTFileModelPersistor +from nvflare.app_opt.pt.quantization.dequantizor import ModelDequantizor +from nvflare.app_opt.pt.quantization.quantizor import ModelQuantizor +from nvflare.job_config.script_runner import ScriptRunner + + +def main(): + args = define_parser() + train_script = "src/hf_sft_peft_fl.py" + client_ids = args.client_ids + num_clients = len(client_ids) + + if args.threads: + num_threads = args.threads + else: + num_threads = num_clients + + if num_threads < num_clients: + print("The number of threads smaller than the number of clients, runner clean-up will be performed.") + clean_up = 1 + else: + clean_up = 0 + + num_rounds = args.num_rounds + workspace_dir = args.workspace_dir + job_dir = args.job_dir + model_name_or_path = args.model_name_or_path + train_mode = args.train_mode + message_mode = args.message_mode + + # Create the FedJob + if train_mode.lower() == "sft": + job = FedJob(name="llm_hf_sft", min_clients=num_clients) + output_path = "sft" + elif train_mode.lower() == "peft": + job = FedJob(name="llm_hf_peft", min_clients=num_clients) + output_path = "peft" + else: + raise ValueError(f"Invalid train_mode: {train_mode}, only SFT and PEFT are supported.") + + # Define the FedAvg controller workflow and send to server + controller = FedAvg( + num_clients=num_clients, + num_rounds=num_rounds, + ) + job.to(controller, "server") + + if args.quantize_mode: + # If using quantization, add quantize filters. + quantizor = ModelQuantizor(quantization_type=args.quantize_mode) + dequantizor = ModelDequantizor() + job.to(quantizor, "server", tasks=["train"], filter_type=FilterType.TASK_DATA) + job.to(dequantizor, "server", tasks=["train"], filter_type=FilterType.TASK_RESULT) + + # Define the model persistor and send to server + # First send the model to the server + job.to("src/hf_sft_model.py", "server") + # Then send the model persistor to the server + model_args = {"path": "src.hf_sft_model.CausalLMModel", "args": {"model_name_or_path": model_name_or_path}} + job.to(PTFileModelPersistor(model=model_args), "server", id="persistor") + + # Add model selection widget and send to server + job.to(IntimeModelSelector(key_metric="eval_loss", negate_key_metric=True), "server", id="model_selector") + + # Send ScriptRunner to all clients + for i in range(num_clients): + client_id = client_ids[i] + site_name = f"site-{client_id}" + data_path_train = os.path.join(args.data_path, client_id, "training.jsonl") + data_path_valid = os.path.join(args.data_path, client_id, "validation.jsonl") + + script_args = f"--model_name_or_path {model_name_or_path} --data_path_train {data_path_train} --data_path_valid {data_path_valid} --output_path {output_path} --train_mode {train_mode} --message_mode {message_mode} --clean_up {clean_up}" + if message_mode == "tensor": + params_exchange_format = "pytorch" + elif message_mode == "numpy": + params_exchange_format = "numpy" + else: + raise ValueError(f"Invalid message_mode: {message_mode}, only numpy and tensor are supported.") + + runner = ScriptRunner( + script=train_script, + script_args=script_args, + params_exchange_format=params_exchange_format, + launch_external_process=False, + ) + job.to(runner, site_name, tasks=["train"]) + + if args.quantize_mode: + job.to(quantizor, site_name, tasks=["train"], filter_type=FilterType.TASK_RESULT) + job.to(dequantizor, site_name, tasks=["train"], filter_type=FilterType.TASK_DATA) + + # Export the job + print("job_dir=", job_dir) + job.export_job(job_dir) + + # Run the job + print("workspace_dir=", workspace_dir) + print("num_threads=", num_threads) + job.simulator_run(workspace_dir, threads=num_threads, gpu=args.gpu) + + +def define_parser(): + parser = argparse.ArgumentParser() + parser.add_argument( + "--client_ids", + nargs="+", + type=str, + default="", + help="Clinet IDs, used to get the data path for each client", + ) + parser.add_argument( + "--num_rounds", + type=int, + default=3, + help="Number of rounds, default to 3", + ) + parser.add_argument( + "--workspace_dir", + type=str, + default="/tmp/nvflare/jobs/llm_hf/workdir", + help="work directory, default to '/tmp/nvflare/jobs/llm_hf/workdir'", + ) + parser.add_argument( + "--job_dir", + type=str, + default="/tmp/nvflare/jobs/llm_hf/jobdir", + help="directory for job export, default to '/tmp/nvflare/jobs/llm_hf/jobdir'", + ) + parser.add_argument( + "--model_name_or_path", + type=str, + default="meta-llama/llama-3.2-1b", + help="model name or path", + ) + parser.add_argument( + "--data_path", + type=str, + default="", + help="root directory for training and validation data", + ) + parser.add_argument( + "--train_mode", + type=str, + default="SFT", + help="training mode, SFT or PEFT, default to SFT", + ) + parser.add_argument( + "--quantize_mode", + type=str, + default=None, + help="quantization mode, default to None (no quantization)", + ) + parser.add_argument( + "--message_mode", + type=str, + default="numpy", + help="message mode, numpy or tensor, default to numpy", + ) + parser.add_argument( + "--threads", + type=int, + help="number of threads to use for FL simulation, default to the number of clients", + ) + parser.add_argument( + "--gpu", + type=str, + default="0", + help="gpu assignments for simulating clients, comma separated, default to single gpu", + ) + return parser.parse_args() + + +if __name__ == "__main__": + main() diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/src/hf_sft_model.py b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/src/hf_sft_model.py new file mode 100755 index 0000000000..fd84c3f06a --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/src/hf_sft_model.py @@ -0,0 +1,28 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. 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. + +import torch +from transformers import AutoModelForCausalLM + + +class CausalLMModel(torch.nn.Module): + def __init__(self, model_name_or_path): + super(CausalLMModel, self).__init__() + self.model = AutoModelForCausalLM.from_pretrained( + model_name_or_path, + ) + + def forward(self, input_id): + output = self.model(input_ids=input_id, return_dict=False) + return output diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/src/hf_sft_peft_fl.py b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/src/hf_sft_peft_fl.py new file mode 100755 index 0000000000..96667151bc --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/src/hf_sft_peft_fl.py @@ -0,0 +1,250 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. 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. + +import argparse +import copy +import os + +# Add deterministic seed for reproducibility illustration +import random + +import datasets +import numpy as np +import torch +from peft import LoraConfig, get_peft_model, get_peft_model_state_dict, set_peft_model_state_dict, utils +from transformers import AutoModelForCausalLM, trainer_utils +from trl import SFTConfig, SFTTrainer + +import nvflare.client as flare + +torch.manual_seed(0) +random.seed(0) +np.random.seed(0) + + +def format_instruction(example): + output_texts = [] + for i in range(len(example["input"])): + text = f"### Instruction: Generate Output according to the information and question given by Input. ### Input:{example['input'][i]} ### Response: {example['output'][i]}" + output_texts.append(text) + return output_texts + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument( + "--model_name_or_path", + type=str, + default="meta-llama/llama-3.2-1b", + ) + parser.add_argument( + "--data_path_train", + type=str, + default="./dataset/dolly/training.jsonl", + ) + parser.add_argument( + "--data_path_valid", + type=str, + default="./dataset/dolly/validation.jsonl", + ) + parser.add_argument( + "--output_path", + type=str, + default="./workspace_federated/llama-3.2-1b-dolly-sft", + ) + parser.add_argument( + "--train_mode", + type=str, + default="SFT", + help="training mode, SFT or PEFT, default to SFT", + ) + parser.add_argument( + "--message_mode", + type=str, + default="numpy", + help="message mode, numpy or tensor, default to numpy", + ) + parser.add_argument("--local_epoch", type=int, default=1) + parser.add_argument("--clean_up", type=int, default=0) + args = parser.parse_args() + + # Dataset + dataset_train = datasets.load_dataset("json", data_files=args.data_path_train, split="train") + dataset_valid = datasets.load_dataset("json", data_files=args.data_path_valid, split="train") + # Print dataset info + print(f"Dataset size: training {len(dataset_train)}, validation {len(dataset_valid)}") + # record every 5% of the dataset + batch_size = 4 + gra_accu_steps = 10 + logging_steps = int(len(dataset_train) / (20 * batch_size * gra_accu_steps)) + print(f"logging_steps: {logging_steps}") + + # Model configs + model_name_or_path = args.model_name_or_path + peft_config = None + + # Load model + default_dtype = torch.get_default_dtype() + torch.set_default_dtype(torch.bfloat16) + model = AutoModelForCausalLM.from_pretrained( + model_name_or_path, + device_map="auto", + use_cache=False, + torch_dtype=torch.bfloat16, + ) + torch.set_default_dtype(default_dtype) + + # Train mode + if args.train_mode.lower() == "sft": + train_mode = 0 + elif args.train_mode.lower() == "peft": + train_mode = 1 + else: + raise ValueError(f"Invalid train_mode: {args.train_mode}, only SFT and PEFT are supported.") + + # PEFT specific + if train_mode: + # PEFT configs + peft_config = LoraConfig( + lora_alpha=16, + lora_dropout=0.1, + r=64, + bias="none", + task_type="CAUSAL_LM", + ) + model = get_peft_model(model, peft_config) + model.config.pretraining_tp = 1 + + # Training arguments + train_args = SFTConfig( + output_dir=args.output_path, + num_train_epochs=args.local_epoch, + per_device_train_batch_size=batch_size, + gradient_accumulation_steps=gra_accu_steps, + gradient_checkpointing=False, + optim="paged_adamw_32bit", + logging_steps=logging_steps, + save_strategy="epoch", + learning_rate=5e-4, + bf16=True, + max_grad_norm=0.3, + warmup_ratio=0.03, + lr_scheduler_type="constant", + disable_tqdm=True, + max_seq_length=1024, + save_total_limit=2, + # safetensors has some issues in saving lm_head.weight, disable it for now + save_safetensors=False, + ) + + # Trainer + trainer = SFTTrainer( + model=model, + train_dataset=dataset_train, + eval_dataset=dataset_valid, + peft_config=peft_config, + formatting_func=format_instruction, + args=train_args, + ) + + # initializes NVFlare client API + flare.init() + + # Train federated rounds + # start with global model at the beginning of each round + while flare.is_running(): + # receives FLModel from NVFlare + input_model = flare.receive() + curr_round = input_model.current_round + print(f"current_round={curr_round}") + + # Update the key name received from global model if using model def file + global_model = copy.deepcopy(input_model.params) + for key in list(global_model.keys()): + global_model[key.replace("model.", "", 1)] = global_model.pop(key) + + # wraps evaluation logic into a method to re-use for + # evaluation on both trained and received model + def evaluate(input_weights, mode): + # Special load func for PEFT + if train_mode: + set_peft_model_state_dict(trainer.model, input_weights) + else: + trainer.model.load_state_dict(input_weights) + metric_score = trainer.evaluate() + print(f"Evaluation metric score: {metric_score}") + return metric_score + + # evaluate on received global model + eval_loss = evaluate(global_model, train_mode) + eval_loss = float(eval_loss["eval_loss"]) + + # Load global model and previous training states + # Since we perform iterative training by using "resume" functionality + # we need to replace the resume weights with global weights every round + if curr_round == 0: + # First round, start from pretrained model + trainer.train() + else: + # replace local resume weights with global weights + resume_from_checkpoint_folder = trainer_utils.get_last_checkpoint(trainer.args.output_dir) + if train_mode: + # PEFT model small, directly save via torch.save + resume_model_file_path = os.path.join(resume_from_checkpoint_folder, utils.WEIGHTS_NAME) + torch.save(global_model, resume_model_file_path) + else: + # SFT model can be large, save via HF API + # Disable safetensor for now + trainer.model.save_pretrained(resume_from_checkpoint_folder, safe_serialization=False) + # increment num_train_epochs so that the trainer will continue training + if args.clean_up: + # runner got cleaned up, set num_train_epochs with curr_round + trainer.args.num_train_epochs = (curr_round + 1) * args.local_epoch + else: + # runner still alive, increment num_train_epochs with local_epoch + trainer.args.num_train_epochs += args.local_epoch + print(f"Increment num_train_epochs to {trainer.args.num_train_epochs}") + # continue training + trainer.train(resume_from_checkpoint=True) + + # compose output model to send back to server + if train_mode: + # PEFT, load PEFT part from trainer model + out_param = get_peft_model_state_dict(trainer.model) + else: + # SFT, load whole model state_dict + out_param = trainer.model.state_dict() + + # update the key name sent to global model + if not train_mode: + for key in list(out_param.keys()): + out_param["model." + key] = out_param.pop(key).cpu() + + if args.message_mode.lower() == "numpy": + # cast out_param to float32 preparing for communication with numpy + # otherwise do nothing + out_param = {k: v.to(torch.float32) for k, v in out_param.items()} + + # construct trained FL model + output_model = flare.FLModel( + params=out_param, + metrics={"eval_loss": eval_loss}, + meta={"NUM_STEPS_CURRENT_ROUND": trainer.train_dataset.num_rows}, + ) + # send model back to NVFlare + flare.send(output_model) + + +if __name__ == "__main__": + main() diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/utils/hf_sft_peft.py b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/utils/hf_sft_peft.py new file mode 100755 index 0000000000..1fc7d53826 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/utils/hf_sft_peft.py @@ -0,0 +1,154 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. 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. + +import argparse + +# Add deterministic seed for reproducibility illustration +import random + +import datasets +import numpy as np +import torch +from peft import LoraConfig, get_peft_model +from transformers import AutoModelForCausalLM +from trl import SFTConfig, SFTTrainer + +torch.manual_seed(0) +random.seed(0) +np.random.seed(0) + + +def format_instruction(example): + output_texts = [] + for i in range(len(example["input"])): + text = f"### Instruction: Generate Output according to the information and question given by Input. ### Input:{example['input'][i]} ### Response: {example['output'][i]}" + output_texts.append(text) + return output_texts + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument( + "--model_name_or_path", + type=str, + default="meta-llama/llama-3.2-1b", + ) + parser.add_argument( + "--data_path_train", + type=str, + default="/tmp/nvflare/dataset/llm/dolly/training.jsonl", + ) + parser.add_argument( + "--data_path_valid", + type=str, + default="/tmp/nvflare/dataset/llm/dolly/validation.jsonl", + ) + parser.add_argument( + "--output_path", + type=str, + default="./workspace_centralized/llama-3.2-1b-dolly-sft", + ) + parser.add_argument( + "--train_mode", + type=str, + default="SFT", + help="training mode, SFT or PEFT, default to SFT", + ) + args = parser.parse_args() + + # Dataset + dataset_train = datasets.load_dataset("json", data_files=args.data_path_train, split="train") + dataset_valid = datasets.load_dataset("json", data_files=args.data_path_valid, split="train") + # Print dataset info + print(f"Dataset size: training {len(dataset_train)}, validation {len(dataset_valid)}") + # record every 5% of the dataset + batch_size = 4 + gra_accu_steps = 10 + logging_steps = int(len(dataset_train) / (20 * batch_size * gra_accu_steps)) + print(f"logging_steps: {logging_steps}") + + # Model configs + model_name_or_path = args.model_name_or_path + peft_config = None + + # Load model + default_dtype = torch.get_default_dtype() + torch.set_default_dtype(torch.bfloat16) + model = AutoModelForCausalLM.from_pretrained( + model_name_or_path, + device_map="auto", + use_cache=False, + torch_dtype=torch.bfloat16, + ) + torch.set_default_dtype(default_dtype) + + # Train mode + if args.train_mode.lower() == "sft": + train_mode = 0 + elif args.train_mode.lower() == "peft": + train_mode = 1 + else: + raise ValueError(f"Invalid train_mode: {args.train_mode}, only SFT and PEFT are supported.") + + # PEFT specific + if train_mode: + # PEFT configs + peft_config = LoraConfig( + lora_alpha=16, + lora_dropout=0.1, + r=64, + bias="none", + task_type="CAUSAL_LM", + ) + model = get_peft_model(model, peft_config) + model.config.pretraining_tp = 1 + + # Training arguments + train_args = SFTConfig( + output_dir=args.output_path, + num_train_epochs=3, + per_device_train_batch_size=batch_size, + gradient_accumulation_steps=gra_accu_steps, + gradient_checkpointing=False, + optim="paged_adamw_32bit", + logging_steps=logging_steps, + save_strategy="epoch", + learning_rate=5e-4, + bf16=True, + max_grad_norm=0.3, + warmup_ratio=0.03, + lr_scheduler_type="constant", + disable_tqdm=True, + max_seq_length=1024, + ) + + # Trainer + trainer = SFTTrainer( + model=model, + train_dataset=dataset_train, + eval_dataset=dataset_valid, + peft_config=peft_config, + formatting_func=format_instruction, + args=train_args, + ) + + # Evaluate + trainer.evaluate() + + # Train + trainer.train() + + +if __name__ == "__main__": + main() diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/utils/hf_sft_peft_iter.py b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/utils/hf_sft_peft_iter.py new file mode 100755 index 0000000000..5584b63a88 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/utils/hf_sft_peft_iter.py @@ -0,0 +1,193 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. 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. + +import argparse +import os + +# Add deterministic seed for reproducibility illustration +import random + +import datasets +import numpy as np +import torch +from peft import LoraConfig, get_peft_model, get_peft_model_state_dict, set_peft_model_state_dict, utils +from transformers import AutoModelForCausalLM, trainer_utils +from trl import SFTConfig, SFTTrainer + +torch.manual_seed(0) +random.seed(0) +np.random.seed(0) + + +def format_instruction(example): + output_texts = [] + for i in range(len(example["input"])): + text = f"### Instruction: Generate Output according to the information and question given by Input. ### Input:{example['input'][i]} ### Response: {example['output'][i]}" + output_texts.append(text) + return output_texts + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument( + "--model_name_or_path", + type=str, + default="meta-llama/llama-3.2-1b", + ) + parser.add_argument( + "--data_path_train", + type=str, + default="/tmp/nvflare/dataset/llm/dolly/training.jsonl", + ) + parser.add_argument( + "--data_path_valid", + type=str, + default="/tmp/nvflare/dataset/llm/dolly/validation.jsonl", + ) + parser.add_argument( + "--output_path", + type=str, + default="./workspace_centralized/llama-3.2-1b-dolly-sft-iter", + ) + parser.add_argument( + "--train_mode", + type=str, + default="SFT", + help="training mode, SFT or PEFT, default to SFT", + ) + args = parser.parse_args() + + # Dataset + dataset_train = datasets.load_dataset("json", data_files=args.data_path_train, split="train") + dataset_valid = datasets.load_dataset("json", data_files=args.data_path_valid, split="train") + # Print dataset info + print(f"Dataset size: training {len(dataset_train)}, validation {len(dataset_valid)}") + # record every 5% of the dataset + batch_size = 4 + gra_accu_steps = 10 + logging_steps = int(len(dataset_train) / (20 * batch_size * gra_accu_steps)) + print(f"logging_steps: {logging_steps}") + + # Model configs + model_name_or_path = args.model_name_or_path + peft_config = None + + # Load model + default_dtype = torch.get_default_dtype() + torch.set_default_dtype(torch.bfloat16) + model = AutoModelForCausalLM.from_pretrained( + model_name_or_path, + device_map="auto", + use_cache=False, + torch_dtype=torch.bfloat16, + ) + torch.set_default_dtype(default_dtype) + + # Train mode + if args.train_mode.lower() == "sft": + train_mode = 0 + elif args.train_mode.lower() == "peft": + train_mode = 1 + else: + raise ValueError(f"Invalid train_mode: {args.train_mode}, only SFT and PEFT are supported.") + + # PEFT specific + if train_mode: + # PEFT configs + peft_config = LoraConfig( + lora_alpha=16, + lora_dropout=0.1, + r=64, + bias="none", + task_type="CAUSAL_LM", + ) + model = get_peft_model(model, peft_config) + model.config.pretraining_tp = 1 + + # Training arguments + train_args = SFTConfig( + output_dir=args.output_path, + num_train_epochs=1, + per_device_train_batch_size=batch_size, + gradient_accumulation_steps=gra_accu_steps, + gradient_checkpointing=False, + optim="paged_adamw_32bit", + logging_steps=logging_steps, + save_strategy="epoch", + learning_rate=5e-4, + bf16=True, + max_grad_norm=0.3, + warmup_ratio=0.03, + lr_scheduler_type="constant", + disable_tqdm=True, + max_seq_length=1024, + # safetensors has some issues in saving lm_head.weight, disable it for now + save_safetensors=False, + ) + + # Trainer + trainer = SFTTrainer( + model=model, + train_dataset=dataset_train, + eval_dataset=dataset_valid, + peft_config=peft_config, + formatting_func=format_instruction, + args=train_args, + ) + + # Save base model state_dict, which will be used as the starting + # weights for each round - to show the weights are loaded correctly + initial_model_path = os.path.join(args.output_path, "model_dict_base.pt") + if train_mode: + params = get_peft_model_state_dict(model) + else: + params = model.state_dict() + torch.save(params, initial_model_path) + + # Train iteratively by using "resume" functionality + # and replace the resume weights every round + for curr_round in range(3): + print(f"current_round={curr_round}") + + # Load and Evaluate model file + state_dict_replace = torch.load(initial_model_path, map_location="cpu", weights_only=True) + if train_mode: + set_peft_model_state_dict(trainer.model, state_dict_replace) + else: + trainer.model.load_state_dict(state_dict_replace) + trainer.evaluate() + + # Train + if curr_round == 0: + # First round, start from pretrained model + trainer.train() + else: + # replace local resume weights with global weights + resume_from_checkpoint_folder = trainer_utils.get_last_checkpoint(trainer.args.output_dir) + if train_mode: + # PEFT model small, directly save via torch.save + resume_model_file_path = os.path.join(resume_from_checkpoint_folder, utils.WEIGHTS_NAME) + torch.save(state_dict_replace, resume_model_file_path) + else: + # SFT model can be large, save via HF API + # Disable safetensor for now + trainer.model.save_pretrained(resume_from_checkpoint_folder, safe_serialization=False) + # increment num_train_epochs so that the trainer will continue training + trainer.args.num_train_epochs += 1 + # continue training + trainer.train(resume_from_checkpoint=True) + + +if __name__ == "__main__": + main() diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/utils/preprocess_dolly.py b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/utils/preprocess_dolly.py new file mode 100755 index 0000000000..5c02ba9506 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.2_llm_sft/utils/preprocess_dolly.py @@ -0,0 +1,93 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. 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. + +import argparse +import json +import os + +import numpy as np +import pandas as pd + + +def data_args(): + parser = argparse.ArgumentParser(description="Preprocess data to train and validation files in jsonl format") + parser.add_argument("--training_file", type=str, required=True, help="Path to training set") + parser.add_argument("--validation_file", type=str, help="Path to validation set, if given, append to training data") + parser.add_argument("--validation_ratio", type=float, default=0.1, help="Ratio of validation set, defult to 10%") + parser.add_argument("--testing_ratio", type=float, default=0.1, help="Ratio of testing set, defult to 10%") + parser.add_argument("--output_dir", type=str, required=True, help="Path to output folder") + args = parser.parse_args() + return args + + +def split_to_jsonl(data, output_dir, validation_ratio, testing_ratio): + print("Preprocessing data to NeMo_SFT jsonl format...") + output_path_tra = os.path.join(output_dir, "training.jsonl") + output_path_val = os.path.join(output_dir, "validation.jsonl") + output_path_tst = os.path.join(output_dir, "testing.jsonl") + + data_ct = len(data) + val_threshold = int(data_ct * validation_ratio) + test_threshold = int(data_ct * testing_ratio) + + with open(output_path_val, "w") as g, open(output_path_tst, "w") as h, open(output_path_tra, "w") as i: + for index, item in data.iterrows(): + context = item["context"].strip() + if context != "": + # Randomize context and instruction order. + context_first = np.random.randint(0, 2) == 0 + if context_first: + instruction = item["instruction"].strip() + assert instruction != "" + input = f"{context}\n\n{instruction}" + output = item["response"] + else: + instruction = item["instruction"].strip() + assert instruction != "" + input = f"{instruction}\n\n{context}" + output = item["response"] + else: + input = item["instruction"] + output = item["response"] + # write to jsonl file according to index + if index < val_threshold: + h.write(json.dumps({"input": input, "output": output}) + "\n") + elif index < val_threshold + test_threshold: + g.write(json.dumps({"input": input, "output": output}) + "\n") + else: + i.write(json.dumps({"input": input, "output": output}) + "\n") + print(f"{index + 1} out of {data_ct} Data was successfully preprocessed and saved.") + + +def main(): + args = data_args() + # load training data + path_to_train = args.training_file + train = pd.read_json(path_to_train, lines=True) + # load validation data if provided and append to training data + if args.validation_file: + path_to_val = args.validation_file + val = pd.read_json(path_to_val, lines=True) + train = pd.concat([train, val]) + # randomize the order of the data + data_full = train.sample(frac=1, random_state=0).reset_index(drop=True) + # split data into training, validation and testing + val_ratio = args.validation_ratio + test_ratio = args.testing_ratio + output_dir = args.output_dir + split_to_jsonl(data_full, output_dir, val_ratio, test_ratio) + + +if __name__ == "__main__": + main() diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/LLM_PEFT.ipynb b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/LLM_PEFT.ipynb new file mode 100644 index 0000000000..acdbef0911 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/LLM_PEFT.ipynb @@ -0,0 +1,162 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "4ef8a52f-f0bd-493c-ac70-32d5f7e5b87e", + "metadata": {}, + "source": [ + "# LLM Parameter-Efficient Fine-Tuning (PEFT) via HuggingFace Trainer APIs\n", + "Similar to last section [LLM Supervised Fine-Tuning (SFT)](../08.2_llm_sft/LLM_SFT.ipynb), in this section, we illustrate how to use [NVIDIA FLARE](https://nvidia.github.io/NVFlare) for Large Language Models (LLMs) PEFT task with [HuggingFace](https://huggingface.co/) Trainer APIs with [PEFT library](https://github.com/huggingface/peft).\n", + "\n", + "We use the same model of the [Llama-3.2-1B model](https://huggingface.co/meta-llama/Llama-3.2-1B) to showcase the functionality of federated PEFT. For PEFT, we used LoRA method, other PEFT methods (e.g. p-tuning, prompt-tuning) can be easily adapted as well by modifying the configs following [PEFT](https://github.com/huggingface/peft) examples.\n", + "\n", + "We conducted these experiments on a single 48GB RTX 6000 Ada GPU. \n", + "\n", + "To use Llama-3.2-1B model, please request access to the model here https://huggingface.co/meta-llama/Llama-3.2-1B and login with an access token using huggingface-cli.\n", + "\n", + "## Setup\n", + "Git LFS is also necessary for downloads, please follow the steps in this [link](https://github.com/git-lfs/git-lfs/blob/main/INSTALLING.md).\n", + "\n", + "Install required packages for training:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "32803fa2-ac9f-4e9e-b5d0-5ad5bac52bd8", + "metadata": {}, + "outputs": [], + "source": [ + "%pip install -r requirements.txt" + ] + }, + { + "cell_type": "markdown", + "id": "8437dc31-3073-4502-af79-7b0e981312a6", + "metadata": {}, + "source": [ + "## Data Preparation\n", + "In this example, we use two datasets to illustrate the PEFT training.\n", + "\n", + "We download and preprocess three data sets: [Dolly](https://huggingface.co/datasets/databricks/databricks-dolly-15k), and [Oasst1](https://huggingface.co/datasets/OpenAssistant/oasst1):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eb6c216f-057f-4225-bcec-1839e6139c4d", + "metadata": {}, + "outputs": [], + "source": [ + "! git clone https://huggingface.co/datasets/databricks/databricks-dolly-15k /tmp/nvflare/dataset/llm/dolly\n", + "! git clone https://huggingface.co/datasets/OpenAssistant/oasst1 /tmp/nvflare/dataset/llm/oasst1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6ec5c533-b8a5-4380-be49-2eaf717c1712", + "metadata": {}, + "outputs": [], + "source": [ + "! python utils/preprocess_dolly.py --training_file /tmp/nvflare/dataset/llm/dolly/databricks-dolly-15k.jsonl --output_dir /tmp/nvflare/dataset/llm/dolly\n", + "! python utils/preprocess_oasst1.py --training_file /tmp/nvflare/dataset/llm/oasst1/data/train-00000-of-00001-b42a775f407cee45.parquet --validation_file /tmp/nvflare/dataset/llm/oasst1/data/validation-00000-of-00001-134b8fd0c89408b6.parquet --output_dir /tmp/nvflare/dataset/llm/oasst1" + ] + }, + { + "cell_type": "markdown", + "id": "c258adcb-1de4-4ecf-a733-7a91b9ab1dd7", + "metadata": {}, + "source": [ + "## Centralized Baseline\n", + "We run three centralized baselines as" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "acfcc12d-999d-494f-9c7b-bb747975b4f4", + "metadata": {}, + "outputs": [], + "source": [ + "! python utils/hf_sft_peft.py --output_path /tmp/nvflare/workspace/llm/dolly_cen_peft --train_mode PEFT\n", + "! python utils/hf_sft_peft.py --data_path_train /tmp/nvflare/dataset/llm/oasst1/training.jsonl --data_path_valid /tmp/nvflare/dataset/llm/oasst1/validation.jsonl --output_path /tmp/nvflare/workspace/llm/oasst_cen_peft --train_mode PEFT" + ] + }, + { + "cell_type": "markdown", + "id": "f55dd56b-2ca1-4c80-8b7c-68334079528c", + "metadata": {}, + "source": [ + "### Federated Training Results\n", + "We run the federated training on a single client using NVFlare Simulator via [JobAPI](https://nvflare.readthedocs.io/en/main/programming_guide/fed_job_api.html)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "75cc3483-311a-4858-bbb2-c3f236bb5421", + "metadata": {}, + "outputs": [], + "source": [ + "! python peft_job.py --client_ids dolly oasst1 --data_path /tmp/nvflare/dataset/llm/ --workspace_dir /tmp/nvflare/workspace/llm/all_fl_peft --job_dir /tmp/nvflare/workspace/jobs/llm_fl_peft --train_mode PEFT --threads 2 " + ] + }, + { + "cell_type": "markdown", + "id": "0da8848c-b1e6-4090-adfe-8398e144cd14", + "metadata": {}, + "source": [ + "The SFT curves are shown below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "07c0f887-8e43-4206-9e76-101c060854fc", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext tensorboard\n", + "%tensorboard --logdir /tmp/nvflare/workspace/llm" + ] + }, + { + "cell_type": "markdown", + "id": "b91d614e-2f12-45dc-9997-825dd696f421", + "metadata": {}, + "source": [ + "With SFT and PEFT examples, now let's move on to the next section of [LLM Quantization](../08.4_llm_quantization/LLM_quantization.ipynb), where we will see how to make the message transmission more efficient." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5f05d5a6-df1c-4e23-a928-c0542bca05b9", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/peft_job.py b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/peft_job.py new file mode 100644 index 0000000000..5e6fd99f4e --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/peft_job.py @@ -0,0 +1,194 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. 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. + +import argparse +import os + +from nvflare import FedJob, FilterType +from nvflare.app_common.widgets.intime_model_selector import IntimeModelSelector +from nvflare.app_common.workflows.fedavg import FedAvg +from nvflare.app_opt.pt.file_model_persistor import PTFileModelPersistor +from nvflare.app_opt.pt.quantization.dequantizor import ModelDequantizor +from nvflare.app_opt.pt.quantization.quantizor import ModelQuantizor +from nvflare.job_config.script_runner import ScriptRunner + + +def main(): + args = define_parser() + train_script = "src/hf_sft_peft_fl.py" + client_ids = args.client_ids + num_clients = len(client_ids) + + if args.threads: + num_threads = args.threads + else: + num_threads = num_clients + + if num_threads < num_clients: + print("The number of threads smaller than the number of clients, runner clean-up will be performed.") + clean_up = 1 + else: + clean_up = 0 + + num_rounds = args.num_rounds + workspace_dir = args.workspace_dir + job_dir = args.job_dir + model_name_or_path = args.model_name_or_path + train_mode = args.train_mode + message_mode = args.message_mode + + # Create the FedJob + if train_mode.lower() == "sft": + job = FedJob(name="llm_hf_sft", min_clients=num_clients) + output_path = "sft" + elif train_mode.lower() == "peft": + job = FedJob(name="llm_hf_peft", min_clients=num_clients) + output_path = "peft" + else: + raise ValueError(f"Invalid train_mode: {train_mode}, only SFT and PEFT are supported.") + + # Define the FedAvg controller workflow and send to server + controller = FedAvg( + num_clients=num_clients, + num_rounds=num_rounds, + ) + job.to(controller, "server") + + if args.quantize_mode: + # If using quantization, add quantize filters. + quantizor = ModelQuantizor(quantization_type=args.quantize_mode) + dequantizor = ModelDequantizor() + job.to(quantizor, "server", tasks=["train"], filter_type=FilterType.TASK_DATA) + job.to(dequantizor, "server", tasks=["train"], filter_type=FilterType.TASK_RESULT) + + # Define the model persistor and send to server + # First send the model to the server + job.to("src/hf_peft_model.py", "server") + # Then send the model persistor to the server + model_args = {"path": "src.hf_peft_model.CausalLMPEFTModel", "args": {"model_name_or_path": model_name_or_path}} + job.to(PTFileModelPersistor(model=model_args), "server", id="persistor") + + # Add model selection widget and send to server + job.to(IntimeModelSelector(key_metric="eval_loss", negate_key_metric=True), "server", id="model_selector") + + # Send ScriptRunner to all clients + for i in range(num_clients): + client_id = client_ids[i] + site_name = f"site-{client_id}" + data_path_train = os.path.join(args.data_path, client_id, "training.jsonl") + data_path_valid = os.path.join(args.data_path, client_id, "validation.jsonl") + + script_args = f"--model_name_or_path {model_name_or_path} --data_path_train {data_path_train} --data_path_valid {data_path_valid} --output_path {output_path} --train_mode {train_mode} --message_mode {message_mode} --clean_up {clean_up}" + if message_mode == "tensor": + params_exchange_format = "pytorch" + elif message_mode == "numpy": + params_exchange_format = "numpy" + else: + raise ValueError(f"Invalid message_mode: {message_mode}, only numpy and tensor are supported.") + + runner = ScriptRunner( + script=train_script, + script_args=script_args, + params_exchange_format=params_exchange_format, + launch_external_process=False, + ) + job.to(runner, site_name, tasks=["train"]) + + if args.quantize_mode: + job.to(quantizor, site_name, tasks=["train"], filter_type=FilterType.TASK_RESULT) + job.to(dequantizor, site_name, tasks=["train"], filter_type=FilterType.TASK_DATA) + + # Export the job + print("job_dir=", job_dir) + job.export_job(job_dir) + + # Run the job + print("workspace_dir=", workspace_dir) + print("num_threads=", num_threads) + job.simulator_run(workspace_dir, threads=num_threads, gpu=args.gpu) + + +def define_parser(): + parser = argparse.ArgumentParser() + parser.add_argument( + "--client_ids", + nargs="+", + type=str, + default="", + help="Clinet IDs, used to get the data path for each client", + ) + parser.add_argument( + "--num_rounds", + type=int, + default=3, + help="Number of rounds, default to 3", + ) + parser.add_argument( + "--workspace_dir", + type=str, + default="/tmp/nvflare/jobs/llm_hf/workdir", + help="work directory, default to '/tmp/nvflare/jobs/llm_hf/workdir'", + ) + parser.add_argument( + "--job_dir", + type=str, + default="/tmp/nvflare/jobs/llm_hf/jobdir", + help="directory for job export, default to '/tmp/nvflare/jobs/llm_hf/jobdir'", + ) + parser.add_argument( + "--model_name_or_path", + type=str, + default="meta-llama/llama-3.2-1b", + help="model name or path", + ) + parser.add_argument( + "--data_path", + type=str, + default="", + help="root directory for training and validation data", + ) + parser.add_argument( + "--train_mode", + type=str, + default="SFT", + help="training mode, SFT or PEFT, default to SFT", + ) + parser.add_argument( + "--quantize_mode", + type=str, + default=None, + help="quantization mode, default to None (no quantization)", + ) + parser.add_argument( + "--message_mode", + type=str, + default="numpy", + help="message mode, numpy or tensor, default to numpy", + ) + parser.add_argument( + "--threads", + type=int, + help="number of threads to use for FL simulation, default to the number of clients", + ) + parser.add_argument( + "--gpu", + type=str, + default="0", + help="gpu assignments for simulating clients, comma separated, default to single gpu", + ) + return parser.parse_args() + + +if __name__ == "__main__": + main() diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/requirements.txt b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/requirements.txt new file mode 100644 index 0000000000..4a58cb756e --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/requirements.txt @@ -0,0 +1,7 @@ +torch==2.5.1 +datasets +tensorboard +transformers==4.48.0 +peft==0.14.0 +trl==0.13.0 +bitsandbytes diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/src/hf_peft_model.py b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/src/hf_peft_model.py new file mode 100755 index 0000000000..b2545864c7 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/src/hf_peft_model.py @@ -0,0 +1,38 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. 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. + +import torch +from peft import LoraConfig, get_peft_model +from transformers import AutoModelForCausalLM + + +class CausalLMPEFTModel(torch.nn.Module): + def __init__(self, model_name_or_path): + super(CausalLMPEFTModel, self).__init__() + # PEFT configs + peft_config = LoraConfig( + lora_alpha=16, + lora_dropout=0.1, + r=64, + bias="none", + task_type="CAUSAL_LM", + ) + full_model = AutoModelForCausalLM.from_pretrained( + model_name_or_path, + ) + self.model = get_peft_model(full_model, peft_config) + + def forward(self, input_id): + output = self.model(input_ids=input_id, return_dict=False) + return output diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/src/hf_sft_peft_fl.py b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/src/hf_sft_peft_fl.py new file mode 100755 index 0000000000..96667151bc --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/src/hf_sft_peft_fl.py @@ -0,0 +1,250 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. 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. + +import argparse +import copy +import os + +# Add deterministic seed for reproducibility illustration +import random + +import datasets +import numpy as np +import torch +from peft import LoraConfig, get_peft_model, get_peft_model_state_dict, set_peft_model_state_dict, utils +from transformers import AutoModelForCausalLM, trainer_utils +from trl import SFTConfig, SFTTrainer + +import nvflare.client as flare + +torch.manual_seed(0) +random.seed(0) +np.random.seed(0) + + +def format_instruction(example): + output_texts = [] + for i in range(len(example["input"])): + text = f"### Instruction: Generate Output according to the information and question given by Input. ### Input:{example['input'][i]} ### Response: {example['output'][i]}" + output_texts.append(text) + return output_texts + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument( + "--model_name_or_path", + type=str, + default="meta-llama/llama-3.2-1b", + ) + parser.add_argument( + "--data_path_train", + type=str, + default="./dataset/dolly/training.jsonl", + ) + parser.add_argument( + "--data_path_valid", + type=str, + default="./dataset/dolly/validation.jsonl", + ) + parser.add_argument( + "--output_path", + type=str, + default="./workspace_federated/llama-3.2-1b-dolly-sft", + ) + parser.add_argument( + "--train_mode", + type=str, + default="SFT", + help="training mode, SFT or PEFT, default to SFT", + ) + parser.add_argument( + "--message_mode", + type=str, + default="numpy", + help="message mode, numpy or tensor, default to numpy", + ) + parser.add_argument("--local_epoch", type=int, default=1) + parser.add_argument("--clean_up", type=int, default=0) + args = parser.parse_args() + + # Dataset + dataset_train = datasets.load_dataset("json", data_files=args.data_path_train, split="train") + dataset_valid = datasets.load_dataset("json", data_files=args.data_path_valid, split="train") + # Print dataset info + print(f"Dataset size: training {len(dataset_train)}, validation {len(dataset_valid)}") + # record every 5% of the dataset + batch_size = 4 + gra_accu_steps = 10 + logging_steps = int(len(dataset_train) / (20 * batch_size * gra_accu_steps)) + print(f"logging_steps: {logging_steps}") + + # Model configs + model_name_or_path = args.model_name_or_path + peft_config = None + + # Load model + default_dtype = torch.get_default_dtype() + torch.set_default_dtype(torch.bfloat16) + model = AutoModelForCausalLM.from_pretrained( + model_name_or_path, + device_map="auto", + use_cache=False, + torch_dtype=torch.bfloat16, + ) + torch.set_default_dtype(default_dtype) + + # Train mode + if args.train_mode.lower() == "sft": + train_mode = 0 + elif args.train_mode.lower() == "peft": + train_mode = 1 + else: + raise ValueError(f"Invalid train_mode: {args.train_mode}, only SFT and PEFT are supported.") + + # PEFT specific + if train_mode: + # PEFT configs + peft_config = LoraConfig( + lora_alpha=16, + lora_dropout=0.1, + r=64, + bias="none", + task_type="CAUSAL_LM", + ) + model = get_peft_model(model, peft_config) + model.config.pretraining_tp = 1 + + # Training arguments + train_args = SFTConfig( + output_dir=args.output_path, + num_train_epochs=args.local_epoch, + per_device_train_batch_size=batch_size, + gradient_accumulation_steps=gra_accu_steps, + gradient_checkpointing=False, + optim="paged_adamw_32bit", + logging_steps=logging_steps, + save_strategy="epoch", + learning_rate=5e-4, + bf16=True, + max_grad_norm=0.3, + warmup_ratio=0.03, + lr_scheduler_type="constant", + disable_tqdm=True, + max_seq_length=1024, + save_total_limit=2, + # safetensors has some issues in saving lm_head.weight, disable it for now + save_safetensors=False, + ) + + # Trainer + trainer = SFTTrainer( + model=model, + train_dataset=dataset_train, + eval_dataset=dataset_valid, + peft_config=peft_config, + formatting_func=format_instruction, + args=train_args, + ) + + # initializes NVFlare client API + flare.init() + + # Train federated rounds + # start with global model at the beginning of each round + while flare.is_running(): + # receives FLModel from NVFlare + input_model = flare.receive() + curr_round = input_model.current_round + print(f"current_round={curr_round}") + + # Update the key name received from global model if using model def file + global_model = copy.deepcopy(input_model.params) + for key in list(global_model.keys()): + global_model[key.replace("model.", "", 1)] = global_model.pop(key) + + # wraps evaluation logic into a method to re-use for + # evaluation on both trained and received model + def evaluate(input_weights, mode): + # Special load func for PEFT + if train_mode: + set_peft_model_state_dict(trainer.model, input_weights) + else: + trainer.model.load_state_dict(input_weights) + metric_score = trainer.evaluate() + print(f"Evaluation metric score: {metric_score}") + return metric_score + + # evaluate on received global model + eval_loss = evaluate(global_model, train_mode) + eval_loss = float(eval_loss["eval_loss"]) + + # Load global model and previous training states + # Since we perform iterative training by using "resume" functionality + # we need to replace the resume weights with global weights every round + if curr_round == 0: + # First round, start from pretrained model + trainer.train() + else: + # replace local resume weights with global weights + resume_from_checkpoint_folder = trainer_utils.get_last_checkpoint(trainer.args.output_dir) + if train_mode: + # PEFT model small, directly save via torch.save + resume_model_file_path = os.path.join(resume_from_checkpoint_folder, utils.WEIGHTS_NAME) + torch.save(global_model, resume_model_file_path) + else: + # SFT model can be large, save via HF API + # Disable safetensor for now + trainer.model.save_pretrained(resume_from_checkpoint_folder, safe_serialization=False) + # increment num_train_epochs so that the trainer will continue training + if args.clean_up: + # runner got cleaned up, set num_train_epochs with curr_round + trainer.args.num_train_epochs = (curr_round + 1) * args.local_epoch + else: + # runner still alive, increment num_train_epochs with local_epoch + trainer.args.num_train_epochs += args.local_epoch + print(f"Increment num_train_epochs to {trainer.args.num_train_epochs}") + # continue training + trainer.train(resume_from_checkpoint=True) + + # compose output model to send back to server + if train_mode: + # PEFT, load PEFT part from trainer model + out_param = get_peft_model_state_dict(trainer.model) + else: + # SFT, load whole model state_dict + out_param = trainer.model.state_dict() + + # update the key name sent to global model + if not train_mode: + for key in list(out_param.keys()): + out_param["model." + key] = out_param.pop(key).cpu() + + if args.message_mode.lower() == "numpy": + # cast out_param to float32 preparing for communication with numpy + # otherwise do nothing + out_param = {k: v.to(torch.float32) for k, v in out_param.items()} + + # construct trained FL model + output_model = flare.FLModel( + params=out_param, + metrics={"eval_loss": eval_loss}, + meta={"NUM_STEPS_CURRENT_ROUND": trainer.train_dataset.num_rows}, + ) + # send model back to NVFlare + flare.send(output_model) + + +if __name__ == "__main__": + main() diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/utils/hf_sft_peft.py b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/utils/hf_sft_peft.py new file mode 100755 index 0000000000..1fc7d53826 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/utils/hf_sft_peft.py @@ -0,0 +1,154 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. 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. + +import argparse + +# Add deterministic seed for reproducibility illustration +import random + +import datasets +import numpy as np +import torch +from peft import LoraConfig, get_peft_model +from transformers import AutoModelForCausalLM +from trl import SFTConfig, SFTTrainer + +torch.manual_seed(0) +random.seed(0) +np.random.seed(0) + + +def format_instruction(example): + output_texts = [] + for i in range(len(example["input"])): + text = f"### Instruction: Generate Output according to the information and question given by Input. ### Input:{example['input'][i]} ### Response: {example['output'][i]}" + output_texts.append(text) + return output_texts + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument( + "--model_name_or_path", + type=str, + default="meta-llama/llama-3.2-1b", + ) + parser.add_argument( + "--data_path_train", + type=str, + default="/tmp/nvflare/dataset/llm/dolly/training.jsonl", + ) + parser.add_argument( + "--data_path_valid", + type=str, + default="/tmp/nvflare/dataset/llm/dolly/validation.jsonl", + ) + parser.add_argument( + "--output_path", + type=str, + default="./workspace_centralized/llama-3.2-1b-dolly-sft", + ) + parser.add_argument( + "--train_mode", + type=str, + default="SFT", + help="training mode, SFT or PEFT, default to SFT", + ) + args = parser.parse_args() + + # Dataset + dataset_train = datasets.load_dataset("json", data_files=args.data_path_train, split="train") + dataset_valid = datasets.load_dataset("json", data_files=args.data_path_valid, split="train") + # Print dataset info + print(f"Dataset size: training {len(dataset_train)}, validation {len(dataset_valid)}") + # record every 5% of the dataset + batch_size = 4 + gra_accu_steps = 10 + logging_steps = int(len(dataset_train) / (20 * batch_size * gra_accu_steps)) + print(f"logging_steps: {logging_steps}") + + # Model configs + model_name_or_path = args.model_name_or_path + peft_config = None + + # Load model + default_dtype = torch.get_default_dtype() + torch.set_default_dtype(torch.bfloat16) + model = AutoModelForCausalLM.from_pretrained( + model_name_or_path, + device_map="auto", + use_cache=False, + torch_dtype=torch.bfloat16, + ) + torch.set_default_dtype(default_dtype) + + # Train mode + if args.train_mode.lower() == "sft": + train_mode = 0 + elif args.train_mode.lower() == "peft": + train_mode = 1 + else: + raise ValueError(f"Invalid train_mode: {args.train_mode}, only SFT and PEFT are supported.") + + # PEFT specific + if train_mode: + # PEFT configs + peft_config = LoraConfig( + lora_alpha=16, + lora_dropout=0.1, + r=64, + bias="none", + task_type="CAUSAL_LM", + ) + model = get_peft_model(model, peft_config) + model.config.pretraining_tp = 1 + + # Training arguments + train_args = SFTConfig( + output_dir=args.output_path, + num_train_epochs=3, + per_device_train_batch_size=batch_size, + gradient_accumulation_steps=gra_accu_steps, + gradient_checkpointing=False, + optim="paged_adamw_32bit", + logging_steps=logging_steps, + save_strategy="epoch", + learning_rate=5e-4, + bf16=True, + max_grad_norm=0.3, + warmup_ratio=0.03, + lr_scheduler_type="constant", + disable_tqdm=True, + max_seq_length=1024, + ) + + # Trainer + trainer = SFTTrainer( + model=model, + train_dataset=dataset_train, + eval_dataset=dataset_valid, + peft_config=peft_config, + formatting_func=format_instruction, + args=train_args, + ) + + # Evaluate + trainer.evaluate() + + # Train + trainer.train() + + +if __name__ == "__main__": + main() diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/utils/preprocess_dolly.py b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/utils/preprocess_dolly.py new file mode 100755 index 0000000000..5c02ba9506 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/utils/preprocess_dolly.py @@ -0,0 +1,93 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. 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. + +import argparse +import json +import os + +import numpy as np +import pandas as pd + + +def data_args(): + parser = argparse.ArgumentParser(description="Preprocess data to train and validation files in jsonl format") + parser.add_argument("--training_file", type=str, required=True, help="Path to training set") + parser.add_argument("--validation_file", type=str, help="Path to validation set, if given, append to training data") + parser.add_argument("--validation_ratio", type=float, default=0.1, help="Ratio of validation set, defult to 10%") + parser.add_argument("--testing_ratio", type=float, default=0.1, help="Ratio of testing set, defult to 10%") + parser.add_argument("--output_dir", type=str, required=True, help="Path to output folder") + args = parser.parse_args() + return args + + +def split_to_jsonl(data, output_dir, validation_ratio, testing_ratio): + print("Preprocessing data to NeMo_SFT jsonl format...") + output_path_tra = os.path.join(output_dir, "training.jsonl") + output_path_val = os.path.join(output_dir, "validation.jsonl") + output_path_tst = os.path.join(output_dir, "testing.jsonl") + + data_ct = len(data) + val_threshold = int(data_ct * validation_ratio) + test_threshold = int(data_ct * testing_ratio) + + with open(output_path_val, "w") as g, open(output_path_tst, "w") as h, open(output_path_tra, "w") as i: + for index, item in data.iterrows(): + context = item["context"].strip() + if context != "": + # Randomize context and instruction order. + context_first = np.random.randint(0, 2) == 0 + if context_first: + instruction = item["instruction"].strip() + assert instruction != "" + input = f"{context}\n\n{instruction}" + output = item["response"] + else: + instruction = item["instruction"].strip() + assert instruction != "" + input = f"{instruction}\n\n{context}" + output = item["response"] + else: + input = item["instruction"] + output = item["response"] + # write to jsonl file according to index + if index < val_threshold: + h.write(json.dumps({"input": input, "output": output}) + "\n") + elif index < val_threshold + test_threshold: + g.write(json.dumps({"input": input, "output": output}) + "\n") + else: + i.write(json.dumps({"input": input, "output": output}) + "\n") + print(f"{index + 1} out of {data_ct} Data was successfully preprocessed and saved.") + + +def main(): + args = data_args() + # load training data + path_to_train = args.training_file + train = pd.read_json(path_to_train, lines=True) + # load validation data if provided and append to training data + if args.validation_file: + path_to_val = args.validation_file + val = pd.read_json(path_to_val, lines=True) + train = pd.concat([train, val]) + # randomize the order of the data + data_full = train.sample(frac=1, random_state=0).reset_index(drop=True) + # split data into training, validation and testing + val_ratio = args.validation_ratio + test_ratio = args.testing_ratio + output_dir = args.output_dir + split_to_jsonl(data_full, output_dir, val_ratio, test_ratio) + + +if __name__ == "__main__": + main() diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/utils/preprocess_oasst1.py b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/utils/preprocess_oasst1.py new file mode 100755 index 0000000000..de4de63040 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_peft/utils/preprocess_oasst1.py @@ -0,0 +1,101 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. 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. + +import argparse +import json +import os + +import pandas as pd +import pyarrow.parquet as pq + + +def data_args(): + parser = argparse.ArgumentParser(description="Preprocess data to train and validation files in jsonl format") + parser.add_argument("--training_file", type=str, required=True, help="Path to training set") + parser.add_argument("--validation_file", type=str, help="Path to validation set, if given, append to training data") + parser.add_argument("--validation_ratio", type=float, default=0.1, help="Ratio of validation set, defult to 10%") + parser.add_argument("--testing_ratio", type=float, default=0.1, help="Ratio of testing set, defult to 10%") + parser.add_argument("--output_dir", type=str, required=True, help="Path to output folder") + args = parser.parse_args() + return args + + +def get_data_for_sft(data): + data_assistant = data[(data.role == "assistant") & (data["rank"] == 0.0)].copy() + data_prompter = data[(data.role == "prompter")].copy() + data_prompter = data_prompter.set_index("message_id") + data_assistant["output"] = data_assistant["text"].values + + inputs = [] + parent_ids = [] + for index, item in data_assistant.iterrows(): + input = data_prompter.loc[item.parent_id] + inputs.append(input.text) + parent_ids.append(input.parent_id) + data_assistant["instruction"] = inputs + data_assistant["parent_id"] = parent_ids + data_assistant = data_assistant[data_assistant.lang == "en"] + data_assistant = data_assistant[["instruction", "output"]] + return data_assistant + + +def split_to_jsonl(data, output_dir, validation_ratio, testing_ratio): + print("Preprocessing data to NeMo_SFT jsonl format...") + output_path_tra = os.path.join(output_dir, "training.jsonl") + output_path_val = os.path.join(output_dir, "validation.jsonl") + output_path_tst = os.path.join(output_dir, "testing.jsonl") + + data_ct = len(data) + val_threshold = int(data_ct * validation_ratio) + test_threshold = int(data_ct * testing_ratio) + + with open(output_path_val, "w") as g, open(output_path_tst, "w") as h, open(output_path_tra, "w") as i: + for index, item in data.iterrows(): + input = item["instruction"] + output = item["output"] + # write to jsonl file according to index + if index < val_threshold: + h.write(json.dumps({"input": input, "output": output}) + "\n") + elif index < val_threshold + test_threshold: + g.write(json.dumps({"input": input, "output": output}) + "\n") + else: + i.write(json.dumps({"input": input, "output": output}) + "\n") + print(f"{index + 1} out of {data_ct} Data was successfully preprocessed and saved.") + + +def main(): + args = data_args() + # load training data + path_to_train = args.training_file + ds = pq.read_table(path_to_train) + data = ds.to_pandas() + train = get_data_for_sft(data) + # load validation data if provided and append to training data + if args.validation_file: + path_to_val = args.validation_file + ds = pq.read_table(path_to_val) + data = ds.to_pandas() + val = get_data_for_sft(data) + train = pd.concat([train, val]) + # randomize the order of the data + data_full = train.sample(frac=1, random_state=0).reset_index(drop=True) + # split data into training, validation and testing + val_ratio = args.validation_ratio + test_ratio = args.testing_ratio + output_dir = args.output_dir + split_to_jsonl(data_full, output_dir, val_ratio, test_ratio) + + +if __name__ == "__main__": + main() diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_sft/LLM_SFT.ipynb b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_sft/LLM_SFT.ipynb deleted file mode 100644 index 64e27e07dc..0000000000 --- a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.3_llm_sft/LLM_SFT.ipynb +++ /dev/null @@ -1,33 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "2ba95511-a7b7-41be-8ae1-1655c7906ec6", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "nvflare_example", - "language": "python", - "name": "nvflare_example" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.2" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.4_fed_nlp/federated_nlp_with_bert.ipynb b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.4_fed_nlp/federated_nlp_with_bert.ipynb deleted file mode 100644 index aa1d5fe055..0000000000 --- a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.4_fed_nlp/federated_nlp_with_bert.ipynb +++ /dev/null @@ -1,33 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "e621496d-a92c-46b3-ab7a-ba693b737a2b", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "nvflare_example", - "language": "python", - "name": "nvflare_example" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.2" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.4_llm_quantization/LLM_quantization.ipynb b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.4_llm_quantization/LLM_quantization.ipynb new file mode 100644 index 0000000000..820743aac5 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.4_llm_quantization/LLM_quantization.ipynb @@ -0,0 +1,164 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "dac4d921-f6ec-4d6e-b509-5d49edea9fd5", + "metadata": {}, + "source": [ + "# Model Quantization for Communication\n", + "In the previous examples, we used numpy in float32 for communication. To reduce the message size, we can use model precision conversion and quantization \n", + "from float32 to 16-bit, 8-bit, and 4-bit for communication. Quantization is enabled by NVFlare's [filter mechanism](https://nvflare.readthedocs.io/en/main/programming_guide/filters.html). We can use the following command to run the federated training with model quantization.\n", + "16-bit is a direct precision conversion, while 8-bit, 4-bit quantization is performed by [bitsandbytes](https://github.com/bitsandbytes-foundation/bitsandbytes/tree/main).\n", + "Note that 4-bit quantizations (`fp4` or `nf4`) need device support." + ] + }, + { + "cell_type": "markdown", + "id": "92762739-4f73-4d52-9fb6-a8f4a3989eb1", + "metadata": {}, + "source": [ + "## Data Preparation\n", + "Again, we use one dataset to illustrate the SFT. We download and preprocess [databricks-dolly-15k](https://huggingface.co/datasets/databricks/databricks-dolly-15k)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "69a9f90e-e7ee-4720-9813-6c94df303083", + "metadata": {}, + "outputs": [], + "source": [ + "! git clone https://huggingface.co/datasets/databricks/databricks-dolly-15k /tmp/nvflare/dataset/llm/dolly\n", + "! python utils/preprocess_dolly.py --training_file /tmp/nvflare/dataset/llm/dolly/databricks-dolly-15k.jsonl --output_dir /tmp/nvflare/dataset/llm/dolly" + ] + }, + { + "cell_type": "markdown", + "id": "e1d7cfd0-dd29-4cfd-960e-45315a6a09c7", + "metadata": {}, + "source": [ + "We run the same SFT pipeline with different quantization configurations:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "50c04163-b660-4f64-a41d-a8b480dfdf0a", + "metadata": {}, + "outputs": [], + "source": [ + "! python sft_job.py --client_ids dolly --data_path /tmp/nvflare/dataset/llm/ --workspace_dir /tmp/nvflare/workspace/llm/dolly_fl_sft_16 --job_dir /tmp/nvflare/workspace/jobs/llm_hf_sft_16 --train_mode SFT --quantize_mode float16\n", + "! python sft_job.py --client_ids dolly --data_path /tmp/nvflare/dataset/llm/ --workspace_dir /tmp/nvflare/workspace/llm/dolly_fl_sft_8 --job_dir /tmp/nvflare/workspace/jobs/llm_hf_sft_8 --train_mode SFT --quantize_mode blockwise8\n", + "! python sft_job.py --client_ids dolly --data_path /tmp/nvflare/dataset/llm/ --workspace_dir /tmp/nvflare/workspace/llm/dolly_fl_sft_fp4 --job_dir /tmp/nvflare/workspace/jobs/llm_hf_sft_fp4 --train_mode SFT --quantize_mode float4\n", + "! python sft_job.py --client_ids dolly --data_path /tmp/nvflare/dataset/llm/ --workspace_dir /tmp/nvflare/workspace/llm/dolly_fl_sft_nf4 --job_dir /tmp/nvflare/workspace/jobs/llm_hf_sft_nf4 --train_mode SFT --quantize_mode normfloat4" + ] + }, + { + "cell_type": "markdown", + "id": "559bdca9-b9c7-45dd-93b8-34da452fd3e2", + "metadata": {}, + "source": [ + "For message reduce, from float32 to 16-/8-/4-bit, the message size (in MB) of Llama-3.2-1B model are reduced to: \n", + "\n", + "| Quantization | Raw Model Size | Quantized Model Size | Quantization Meta Size |\n", + "|-------------------|----------------|----------------------|------------------------|\n", + "| float16 | 5716.26 | 2858.13 | 0.00 |\n", + "| blockwise8 | 5716.26 | 1429.06 | 1.54 |\n", + "| float4 | 5716.26 | 714.53 | 89.33 |\n", + "| normalized float4 | 5716.26 | 714.53 | 89.33 |\n", + "\n", + "Note that quantization will generate additional meta data, which can be significant for 4-bit cases.\n", + "\n", + "## Model Communication with Tensor\n", + "In addition, since the model is trained with bf16, instead of first converting to numpy in float32, we can directly communicate with tensor in bf16 to avoid the message size inflation due to the conversion. \n", + "We can use the following command to run the federated training with direct tensor communication, without and with quantization:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b7f12509-bc5f-49b8-969b-185bb3310e3c", + "metadata": {}, + "outputs": [], + "source": [ + "! python sft_job.py --client_ids dolly --data_path /tmp/nvflare/dataset/llm/ --workspace_dir /tmp/nvflare/workspace/llm/dolly_fl_sft_tensor --job_dir /tmp/nvflare/workspace/jobs/llm_hf_sft_tensor --train_mode SFT --message_mode tensor\n", + "! python sft_job.py --client_ids dolly --data_path /tmp/nvflare/dataset/llm/ --workspace_dir /tmp/nvflare/workspace/llm/dolly_fl_sft_tensor_fp4 --job_dir /tmp/nvflare/workspace/jobs/llm_hf_sft_tensor_fp4 --train_mode SFT --message_mode tensor --quantize_mode float4" + ] + }, + { + "cell_type": "markdown", + "id": "bdfd4997-493a-40f5-82dc-86ec7a224513", + "metadata": {}, + "source": [ + "In this case, since the tensor is in bf16, and the quantization reduces it to float4, the message size change is thus:\n", + "```\n", + "Before quantization: 2858.13 MB. After quantization: 714.53 MB with meta: 89.33 MB.\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "b63d733d-1a4f-4f41-b7c1-62f204991a46", + "metadata": {}, + "source": [ + "## Training Curves" + ] + }, + { + "cell_type": "markdown", + "id": "acfe7fba-5ed9-425a-9b6a-678619a2a759", + "metadata": {}, + "source": [ + "The SFT curves are shown below, we can see it achieves decent alignments. These results show that for the example training schemes and data, model precision conversion / quantization does not significantly impact the training while reducing the message size to 1/2, 1/4, and even 1/8, which can significantly reduce the message size, making it crucial for transmitting LLM updates." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7f0632ec-1df1-4894-a1a9-557f568fd468", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext tensorboard\n", + "%tensorboard --logdir /tmp/nvflare/workspace/llm" + ] + }, + { + "cell_type": "markdown", + "id": "4637b943-76da-4b3e-8e57-353d4ec0d17e", + "metadata": {}, + "source": [ + "Quantization significantly reduced the communication burden by reducinng the message size sent over the network, however at local level, memory usage is still demanding to prepare the messages - large memory needs to be allocated to hold the LLM weights. Therefore, let's move on to the next section addressing this challenge - [LLM Streaming](../08.5_llm_streaming/LLM_streaming.ipynb))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "834dd765-3321-4644-b64d-e3a796579a05", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.4_llm_quantization/sft_job.py b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.4_llm_quantization/sft_job.py new file mode 100644 index 0000000000..c13cbb11d1 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.4_llm_quantization/sft_job.py @@ -0,0 +1,194 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. 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. + +import argparse +import os + +from nvflare import FedJob, FilterType +from nvflare.app_common.widgets.intime_model_selector import IntimeModelSelector +from nvflare.app_common.workflows.fedavg import FedAvg +from nvflare.app_opt.pt.file_model_persistor import PTFileModelPersistor +from nvflare.app_opt.pt.quantization.dequantizor import ModelDequantizor +from nvflare.app_opt.pt.quantization.quantizor import ModelQuantizor +from nvflare.job_config.script_runner import ScriptRunner + + +def main(): + args = define_parser() + train_script = "src/hf_sft_peft_fl.py" + client_ids = args.client_ids + num_clients = len(client_ids) + + if args.threads: + num_threads = args.threads + else: + num_threads = num_clients + + if num_threads < num_clients: + print("The number of threads smaller than the number of clients, runner clean-up will be performed.") + clean_up = 1 + else: + clean_up = 0 + + num_rounds = args.num_rounds + workspace_dir = args.workspace_dir + job_dir = args.job_dir + model_name_or_path = args.model_name_or_path + train_mode = args.train_mode + message_mode = args.message_mode + + # Create the FedJob + if train_mode.lower() == "sft": + job = FedJob(name="llm_hf_sft", min_clients=num_clients) + output_path = "sft" + elif train_mode.lower() == "peft": + job = FedJob(name="llm_hf_peft", min_clients=num_clients) + output_path = "peft" + else: + raise ValueError(f"Invalid train_mode: {train_mode}, only SFT and PEFT are supported.") + + # Define the FedAvg controller workflow and send to server + controller = FedAvg( + num_clients=num_clients, + num_rounds=num_rounds, + ) + job.to(controller, "server") + + if args.quantize_mode: + # If using quantization, add quantize filters. + quantizor = ModelQuantizor(quantization_type=args.quantize_mode) + dequantizor = ModelDequantizor() + job.to(quantizor, "server", tasks=["train"], filter_type=FilterType.TASK_DATA) + job.to(dequantizor, "server", tasks=["train"], filter_type=FilterType.TASK_RESULT) + + # Define the model persistor and send to server + # First send the model to the server + job.to("src/hf_sft_model.py", "server") + # Then send the model persistor to the server + model_args = {"path": "src.hf_sft_model.CausalLMModel", "args": {"model_name_or_path": model_name_or_path}} + job.to(PTFileModelPersistor(model=model_args), "server", id="persistor") + + # Add model selection widget and send to server + job.to(IntimeModelSelector(key_metric="eval_loss", negate_key_metric=True), "server", id="model_selector") + + # Send ScriptRunner to all clients + for i in range(num_clients): + client_id = client_ids[i] + site_name = f"site-{client_id}" + data_path_train = os.path.join(args.data_path, client_id, "training.jsonl") + data_path_valid = os.path.join(args.data_path, client_id, "validation.jsonl") + + script_args = f"--model_name_or_path {model_name_or_path} --data_path_train {data_path_train} --data_path_valid {data_path_valid} --output_path {output_path} --train_mode {train_mode} --message_mode {message_mode} --clean_up {clean_up}" + if message_mode == "tensor": + params_exchange_format = "pytorch" + elif message_mode == "numpy": + params_exchange_format = "numpy" + else: + raise ValueError(f"Invalid message_mode: {message_mode}, only numpy and tensor are supported.") + + runner = ScriptRunner( + script=train_script, + script_args=script_args, + params_exchange_format=params_exchange_format, + launch_external_process=False, + ) + job.to(runner, site_name, tasks=["train"]) + + if args.quantize_mode: + job.to(quantizor, site_name, tasks=["train"], filter_type=FilterType.TASK_RESULT) + job.to(dequantizor, site_name, tasks=["train"], filter_type=FilterType.TASK_DATA) + + # Export the job + print("job_dir=", job_dir) + job.export_job(job_dir) + + # Run the job + print("workspace_dir=", workspace_dir) + print("num_threads=", num_threads) + job.simulator_run(workspace_dir, threads=num_threads, gpu=args.gpu) + + +def define_parser(): + parser = argparse.ArgumentParser() + parser.add_argument( + "--client_ids", + nargs="+", + type=str, + default="", + help="Clinet IDs, used to get the data path for each client", + ) + parser.add_argument( + "--num_rounds", + type=int, + default=3, + help="Number of rounds, default to 5", + ) + parser.add_argument( + "--workspace_dir", + type=str, + default="/tmp/nvflare/jobs/llm_hf/workdir", + help="work directory, default to '/tmp/nvflare/jobs/llm_hf/workdir'", + ) + parser.add_argument( + "--job_dir", + type=str, + default="/tmp/nvflare/jobs/llm_hf/jobdir", + help="directory for job export, default to '/tmp/nvflare/jobs/llm_hf/jobdir'", + ) + parser.add_argument( + "--model_name_or_path", + type=str, + default="meta-llama/llama-3.2-1b", + help="model name or path", + ) + parser.add_argument( + "--data_path", + type=str, + default="", + help="root directory for training and validation data", + ) + parser.add_argument( + "--train_mode", + type=str, + default="SFT", + help="training mode, SFT or PEFT, default to SFT", + ) + parser.add_argument( + "--quantize_mode", + type=str, + default=None, + help="quantization mode, default to None (no quantization)", + ) + parser.add_argument( + "--message_mode", + type=str, + default="numpy", + help="message mode, numpy or tensor, default to numpy", + ) + parser.add_argument( + "--threads", + type=int, + help="number of threads to use for FL simulation, default to the number of clients", + ) + parser.add_argument( + "--gpu", + type=str, + default="0", + help="gpu assignments for simulating clients, comma separated, default to single gpu", + ) + return parser.parse_args() + + +if __name__ == "__main__": + main() diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.4_llm_quantization/src/hf_sft_model.py b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.4_llm_quantization/src/hf_sft_model.py new file mode 100755 index 0000000000..fd84c3f06a --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.4_llm_quantization/src/hf_sft_model.py @@ -0,0 +1,28 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. 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. + +import torch +from transformers import AutoModelForCausalLM + + +class CausalLMModel(torch.nn.Module): + def __init__(self, model_name_or_path): + super(CausalLMModel, self).__init__() + self.model = AutoModelForCausalLM.from_pretrained( + model_name_or_path, + ) + + def forward(self, input_id): + output = self.model(input_ids=input_id, return_dict=False) + return output diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.4_llm_quantization/src/hf_sft_peft_fl.py b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.4_llm_quantization/src/hf_sft_peft_fl.py new file mode 100755 index 0000000000..96667151bc --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.4_llm_quantization/src/hf_sft_peft_fl.py @@ -0,0 +1,250 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. 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. + +import argparse +import copy +import os + +# Add deterministic seed for reproducibility illustration +import random + +import datasets +import numpy as np +import torch +from peft import LoraConfig, get_peft_model, get_peft_model_state_dict, set_peft_model_state_dict, utils +from transformers import AutoModelForCausalLM, trainer_utils +from trl import SFTConfig, SFTTrainer + +import nvflare.client as flare + +torch.manual_seed(0) +random.seed(0) +np.random.seed(0) + + +def format_instruction(example): + output_texts = [] + for i in range(len(example["input"])): + text = f"### Instruction: Generate Output according to the information and question given by Input. ### Input:{example['input'][i]} ### Response: {example['output'][i]}" + output_texts.append(text) + return output_texts + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument( + "--model_name_or_path", + type=str, + default="meta-llama/llama-3.2-1b", + ) + parser.add_argument( + "--data_path_train", + type=str, + default="./dataset/dolly/training.jsonl", + ) + parser.add_argument( + "--data_path_valid", + type=str, + default="./dataset/dolly/validation.jsonl", + ) + parser.add_argument( + "--output_path", + type=str, + default="./workspace_federated/llama-3.2-1b-dolly-sft", + ) + parser.add_argument( + "--train_mode", + type=str, + default="SFT", + help="training mode, SFT or PEFT, default to SFT", + ) + parser.add_argument( + "--message_mode", + type=str, + default="numpy", + help="message mode, numpy or tensor, default to numpy", + ) + parser.add_argument("--local_epoch", type=int, default=1) + parser.add_argument("--clean_up", type=int, default=0) + args = parser.parse_args() + + # Dataset + dataset_train = datasets.load_dataset("json", data_files=args.data_path_train, split="train") + dataset_valid = datasets.load_dataset("json", data_files=args.data_path_valid, split="train") + # Print dataset info + print(f"Dataset size: training {len(dataset_train)}, validation {len(dataset_valid)}") + # record every 5% of the dataset + batch_size = 4 + gra_accu_steps = 10 + logging_steps = int(len(dataset_train) / (20 * batch_size * gra_accu_steps)) + print(f"logging_steps: {logging_steps}") + + # Model configs + model_name_or_path = args.model_name_or_path + peft_config = None + + # Load model + default_dtype = torch.get_default_dtype() + torch.set_default_dtype(torch.bfloat16) + model = AutoModelForCausalLM.from_pretrained( + model_name_or_path, + device_map="auto", + use_cache=False, + torch_dtype=torch.bfloat16, + ) + torch.set_default_dtype(default_dtype) + + # Train mode + if args.train_mode.lower() == "sft": + train_mode = 0 + elif args.train_mode.lower() == "peft": + train_mode = 1 + else: + raise ValueError(f"Invalid train_mode: {args.train_mode}, only SFT and PEFT are supported.") + + # PEFT specific + if train_mode: + # PEFT configs + peft_config = LoraConfig( + lora_alpha=16, + lora_dropout=0.1, + r=64, + bias="none", + task_type="CAUSAL_LM", + ) + model = get_peft_model(model, peft_config) + model.config.pretraining_tp = 1 + + # Training arguments + train_args = SFTConfig( + output_dir=args.output_path, + num_train_epochs=args.local_epoch, + per_device_train_batch_size=batch_size, + gradient_accumulation_steps=gra_accu_steps, + gradient_checkpointing=False, + optim="paged_adamw_32bit", + logging_steps=logging_steps, + save_strategy="epoch", + learning_rate=5e-4, + bf16=True, + max_grad_norm=0.3, + warmup_ratio=0.03, + lr_scheduler_type="constant", + disable_tqdm=True, + max_seq_length=1024, + save_total_limit=2, + # safetensors has some issues in saving lm_head.weight, disable it for now + save_safetensors=False, + ) + + # Trainer + trainer = SFTTrainer( + model=model, + train_dataset=dataset_train, + eval_dataset=dataset_valid, + peft_config=peft_config, + formatting_func=format_instruction, + args=train_args, + ) + + # initializes NVFlare client API + flare.init() + + # Train federated rounds + # start with global model at the beginning of each round + while flare.is_running(): + # receives FLModel from NVFlare + input_model = flare.receive() + curr_round = input_model.current_round + print(f"current_round={curr_round}") + + # Update the key name received from global model if using model def file + global_model = copy.deepcopy(input_model.params) + for key in list(global_model.keys()): + global_model[key.replace("model.", "", 1)] = global_model.pop(key) + + # wraps evaluation logic into a method to re-use for + # evaluation on both trained and received model + def evaluate(input_weights, mode): + # Special load func for PEFT + if train_mode: + set_peft_model_state_dict(trainer.model, input_weights) + else: + trainer.model.load_state_dict(input_weights) + metric_score = trainer.evaluate() + print(f"Evaluation metric score: {metric_score}") + return metric_score + + # evaluate on received global model + eval_loss = evaluate(global_model, train_mode) + eval_loss = float(eval_loss["eval_loss"]) + + # Load global model and previous training states + # Since we perform iterative training by using "resume" functionality + # we need to replace the resume weights with global weights every round + if curr_round == 0: + # First round, start from pretrained model + trainer.train() + else: + # replace local resume weights with global weights + resume_from_checkpoint_folder = trainer_utils.get_last_checkpoint(trainer.args.output_dir) + if train_mode: + # PEFT model small, directly save via torch.save + resume_model_file_path = os.path.join(resume_from_checkpoint_folder, utils.WEIGHTS_NAME) + torch.save(global_model, resume_model_file_path) + else: + # SFT model can be large, save via HF API + # Disable safetensor for now + trainer.model.save_pretrained(resume_from_checkpoint_folder, safe_serialization=False) + # increment num_train_epochs so that the trainer will continue training + if args.clean_up: + # runner got cleaned up, set num_train_epochs with curr_round + trainer.args.num_train_epochs = (curr_round + 1) * args.local_epoch + else: + # runner still alive, increment num_train_epochs with local_epoch + trainer.args.num_train_epochs += args.local_epoch + print(f"Increment num_train_epochs to {trainer.args.num_train_epochs}") + # continue training + trainer.train(resume_from_checkpoint=True) + + # compose output model to send back to server + if train_mode: + # PEFT, load PEFT part from trainer model + out_param = get_peft_model_state_dict(trainer.model) + else: + # SFT, load whole model state_dict + out_param = trainer.model.state_dict() + + # update the key name sent to global model + if not train_mode: + for key in list(out_param.keys()): + out_param["model." + key] = out_param.pop(key).cpu() + + if args.message_mode.lower() == "numpy": + # cast out_param to float32 preparing for communication with numpy + # otherwise do nothing + out_param = {k: v.to(torch.float32) for k, v in out_param.items()} + + # construct trained FL model + output_model = flare.FLModel( + params=out_param, + metrics={"eval_loss": eval_loss}, + meta={"NUM_STEPS_CURRENT_ROUND": trainer.train_dataset.num_rows}, + ) + # send model back to NVFlare + flare.send(output_model) + + +if __name__ == "__main__": + main() diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.4_llm_quantization/utils/hf_sft_peft.py b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.4_llm_quantization/utils/hf_sft_peft.py new file mode 100755 index 0000000000..862148368c --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.4_llm_quantization/utils/hf_sft_peft.py @@ -0,0 +1,154 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. 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. + +import argparse + +# Add deterministic seed for reproducibility illustration +import random + +import datasets +import numpy as np +import torch +from peft import LoraConfig, get_peft_model +from transformers import AutoModelForCausalLM +from trl import SFTConfig, SFTTrainer + +torch.manual_seed(0) +random.seed(0) +np.random.seed(0) + + +def format_instruction(example): + output_texts = [] + for i in range(len(example["input"])): + text = f"### Instruction: Generate Output according to the information and question given by Input. ### Input:{example['input'][i]} ### Response: {example['output'][i]}" + output_texts.append(text) + return output_texts + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument( + "--model_name_or_path", + type=str, + default="meta-llama/llama-3.2-1b", + ) + parser.add_argument( + "--data_path_train", + type=str, + default="./dataset/dolly/training.jsonl", + ) + parser.add_argument( + "--data_path_valid", + type=str, + default="./dataset/dolly/validation.jsonl", + ) + parser.add_argument( + "--output_path", + type=str, + default="./workspace_centralized/llama-3.2-1b-dolly-sft", + ) + parser.add_argument( + "--train_mode", + type=str, + default="SFT", + help="training mode, SFT or PEFT, default to SFT", + ) + args = parser.parse_args() + + # Dataset + dataset_train = datasets.load_dataset("json", data_files=args.data_path_train, split="train") + dataset_valid = datasets.load_dataset("json", data_files=args.data_path_valid, split="train") + # Print dataset info + print(f"Dataset size: training {len(dataset_train)}, validation {len(dataset_valid)}") + # record every 5% of the dataset + batch_size = 4 + gra_accu_steps = 10 + logging_steps = int(len(dataset_train) / (20 * batch_size * gra_accu_steps)) + print(f"logging_steps: {logging_steps}") + + # Model configs + model_name_or_path = args.model_name_or_path + peft_config = None + + # Load model + default_dtype = torch.get_default_dtype() + torch.set_default_dtype(torch.bfloat16) + model = AutoModelForCausalLM.from_pretrained( + model_name_or_path, + device_map="auto", + use_cache=False, + torch_dtype=torch.bfloat16, + ) + torch.set_default_dtype(default_dtype) + + # Train mode + if args.train_mode.lower() == "sft": + train_mode = 0 + elif args.train_mode.lower() == "peft": + train_mode = 1 + else: + raise ValueError(f"Invalid train_mode: {args.train_mode}, only SFT and PEFT are supported.") + + # PEFT specific + if train_mode: + # PEFT configs + peft_config = LoraConfig( + lora_alpha=16, + lora_dropout=0.1, + r=64, + bias="none", + task_type="CAUSAL_LM", + ) + model = get_peft_model(model, peft_config) + model.config.pretraining_tp = 1 + + # Training arguments + train_args = SFTConfig( + output_dir=args.output_path, + num_train_epochs=3, + per_device_train_batch_size=batch_size, + gradient_accumulation_steps=gra_accu_steps, + gradient_checkpointing=False, + optim="paged_adamw_32bit", + logging_steps=logging_steps, + save_strategy="epoch", + learning_rate=5e-4, + bf16=True, + max_grad_norm=0.3, + warmup_ratio=0.03, + lr_scheduler_type="constant", + disable_tqdm=True, + max_seq_length=1024, + ) + + # Trainer + trainer = SFTTrainer( + model=model, + train_dataset=dataset_train, + eval_dataset=dataset_valid, + peft_config=peft_config, + formatting_func=format_instruction, + args=train_args, + ) + + # Evaluate + trainer.evaluate() + + # Train + trainer.train() + + +if __name__ == "__main__": + main() diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.4_llm_quantization/utils/preprocess_dolly.py b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.4_llm_quantization/utils/preprocess_dolly.py new file mode 100755 index 0000000000..5c02ba9506 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.4_llm_quantization/utils/preprocess_dolly.py @@ -0,0 +1,93 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. 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. + +import argparse +import json +import os + +import numpy as np +import pandas as pd + + +def data_args(): + parser = argparse.ArgumentParser(description="Preprocess data to train and validation files in jsonl format") + parser.add_argument("--training_file", type=str, required=True, help="Path to training set") + parser.add_argument("--validation_file", type=str, help="Path to validation set, if given, append to training data") + parser.add_argument("--validation_ratio", type=float, default=0.1, help="Ratio of validation set, defult to 10%") + parser.add_argument("--testing_ratio", type=float, default=0.1, help="Ratio of testing set, defult to 10%") + parser.add_argument("--output_dir", type=str, required=True, help="Path to output folder") + args = parser.parse_args() + return args + + +def split_to_jsonl(data, output_dir, validation_ratio, testing_ratio): + print("Preprocessing data to NeMo_SFT jsonl format...") + output_path_tra = os.path.join(output_dir, "training.jsonl") + output_path_val = os.path.join(output_dir, "validation.jsonl") + output_path_tst = os.path.join(output_dir, "testing.jsonl") + + data_ct = len(data) + val_threshold = int(data_ct * validation_ratio) + test_threshold = int(data_ct * testing_ratio) + + with open(output_path_val, "w") as g, open(output_path_tst, "w") as h, open(output_path_tra, "w") as i: + for index, item in data.iterrows(): + context = item["context"].strip() + if context != "": + # Randomize context and instruction order. + context_first = np.random.randint(0, 2) == 0 + if context_first: + instruction = item["instruction"].strip() + assert instruction != "" + input = f"{context}\n\n{instruction}" + output = item["response"] + else: + instruction = item["instruction"].strip() + assert instruction != "" + input = f"{instruction}\n\n{context}" + output = item["response"] + else: + input = item["instruction"] + output = item["response"] + # write to jsonl file according to index + if index < val_threshold: + h.write(json.dumps({"input": input, "output": output}) + "\n") + elif index < val_threshold + test_threshold: + g.write(json.dumps({"input": input, "output": output}) + "\n") + else: + i.write(json.dumps({"input": input, "output": output}) + "\n") + print(f"{index + 1} out of {data_ct} Data was successfully preprocessed and saved.") + + +def main(): + args = data_args() + # load training data + path_to_train = args.training_file + train = pd.read_json(path_to_train, lines=True) + # load validation data if provided and append to training data + if args.validation_file: + path_to_val = args.validation_file + val = pd.read_json(path_to_val, lines=True) + train = pd.concat([train, val]) + # randomize the order of the data + data_full = train.sample(frac=1, random_state=0).reset_index(drop=True) + # split data into training, validation and testing + val_ratio = args.validation_ratio + test_ratio = args.testing_ratio + output_dir = args.output_dir + split_to_jsonl(data_full, output_dir, val_ratio, test_ratio) + + +if __name__ == "__main__": + main() diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/LLM_streaming.ipynb b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/LLM_streaming.ipynb new file mode 100644 index 0000000000..ec9fe8b31e --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/LLM_streaming.ipynb @@ -0,0 +1,138 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "6ad30489-5e3d-4994-966d-666bd40a13e0", + "metadata": {}, + "source": [ + "# Object Streaming\n", + "\n", + "## Overview\n", + "The examples here demonstrate how to use object streamers to send large objects in a memory-efficient manner.\n", + "\n", + "Current default setting is to send and receive large objects in full, so extra memory will be needed and allocated to hold the received message. \n", + "This works fine when the message is small, but can become a limit when model size is large, e.g. for large language models.\n", + "\n", + "To save on memory usage, we can stream the message send / receive: when sending large objects (e.g. a dict),\n", + "streamer sends containers entry by entry (e.g. one dict item each time); further, if we save the object to a file, \n", + "streamer can send the file by chunks (default chunk size is 1MB).\n", + "\n", + "Thus, the memory demand can be reduced to the size of the largest entry for container streaming; while nearly no extra memory is needed for file\n", + "streaming. For example, if sending a dict with 10 1GB entries, without streaming, it will take 10GB extra space to send the dict. \n", + "With container streaming, it only requires extra 1GB; and if saved to a file before sending, it only requires 1MB extra space to send the file.\n", + "\n", + "All examples are run with NVFlare Simulator via [JobAPI](https://nvflare.readthedocs.io/en/main/programming_guide/fed_job_api.html).\n", + "## Concepts\n", + "\n", + "### Object Streamer\n", + "ObjectStreamer is the base class to stream an object piece by piece. The `StreamableEngine` built in the NVFlare can\n", + "stream any implementations of ObjectSteamer\n", + "\n", + "The following implementations are included in NVFlare,\n", + "\n", + "* `ContainerStreamer`: This class is used to stream a container entry by entry. Currently, dict, list and set are supported\n", + "* `FileStreamer`: This class is used to stream a file\n", + "\n", + "Note that the container streamer split the stream by the top level entries. All the sub entries of a top entry are expected to be\n", + "sent as a whole, therefore the memory is determined by the largest entry at top level.\n", + "\n", + "### Object Retriever\n", + "Building upon the streamers, `ObjectRetriever` is designed for easier integration with existing code: to request an object to be streamed from a remote site. It automatically sets up the streaming\n", + "on both ends and handles the coordination.\n", + "\n", + "Similarly, the following implementations are available,\n", + "\n", + "* `ContainerRetriever`: This class is used to retrieve a container from remote site using `ContainerStreamer`.\n", + "* `FileRetriever`: This class is used to retrieve a file from remote site using `FileStreamer`.\n", + "\n", + "Note that to use ContainerRetriever, the container must be given a name and added on the sending site,\n", + "```\n", + "ContainerRetriever.add_container(\"model\", model_dict)\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "736c3e06-c9e2-48ee-b787-3d00efa8d37d", + "metadata": {}, + "source": [ + "## Full-scale Examples and Comparisons\n", + "In the following, we will demonstrate how to use the streamer with Retriever in a workflow with real large language model object, \n", + "and compare the memory usage with and without streaming. To track the memory usage, we use a simple script `utils/log_memory.sh`. \n", + "Note that the tracked usage is not fully accurate, but it is sufficient to give us a rough idea.\n", + "\n", + "With a simple [controller](src/streaming_controller.py) and [executor](src/streaming_executor.py), we simulate a single communication between server and client: server load a `llama-3.2-1b` model, and send to client via three transmission modes: regular, container, and file. This process (clients receiving global model) is often the first stage of a federated learning round, thus the communication burden is realistically reflected. \n", + "\n", + "All three settings: regular, container streaming, and file streaming, are integrated in the same script to avoid extra variabilities.\n", + "To run the examples:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a152f129-5b3d-4a17-8355-b4627f9d8e72", + "metadata": {}, + "outputs": [], + "source": [ + "! bash regular_transmission.sh\n", + "! bash container_stream.sh\n", + "! bash file_stream.sh" + ] + }, + { + "cell_type": "markdown", + "id": "c8ffbeec-fa29-46ca-b103-02e81ac12cce", + "metadata": {}, + "source": [ + "We then examine the memory usage by comparing the peak memory usage of the three settings. The results are shown below,\n", + "note that the numbers here are the results of one experiment on one machine, and can be highly variable depending on the system and the environment.\n", + "\n", + "| Setting | Peak Memory Usage (MB) | Job Finishing Time (s) |\n", + "|-----------------------|------------------------|------------------------|\n", + "| Regular Transmission | 42,427 | 47 |\n", + "| Container Streaming | 23,265 | 50 |\n", + "| File Streaming | 19,176 | 170 |\n", + "\n", + "As shown, the memory usage is significantly reduced by using streaming, especially for file streaming, \n", + "while file streaming takes much longer time to finish the job.\n" + ] + }, + { + "cell_type": "markdown", + "id": "d3805e71-d929-4d65-9f5d-bc50f799d194", + "metadata": {}, + "source": [ + "Now that we covered LLM-related features, let's have a [recap](../08.6_recap/recap.ipynb) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "248cb159-59a1-45b2-8dd9-88f653b22511", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/container_stream.sh b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/container_stream.sh new file mode 100644 index 0000000000..f16893b00a --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/container_stream.sh @@ -0,0 +1,2 @@ +bash utils/log_memory.sh >>/tmp/nvflare/logs/container.txt & +python streaming_job.py --retriever_mode container diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/file_stream.sh b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/file_stream.sh new file mode 100644 index 0000000000..6566124c9f --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/file_stream.sh @@ -0,0 +1,2 @@ +bash utils/log_memory.sh >>/tmp/nvflare/logs/file.txt & +python streaming_job.py --retriever_mode file diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/regular_transmission.sh b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/regular_transmission.sh new file mode 100644 index 0000000000..dd4ef9c091 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/regular_transmission.sh @@ -0,0 +1,3 @@ +mkdir /tmp/nvflare/logs/ +bash utils/log_memory.sh >>/tmp/nvflare/logs/regular.txt & +python streaming_job.py diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/src/streaming_controller.py b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/src/streaming_controller.py new file mode 100644 index 0000000000..c3f64c1f4d --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/src/streaming_controller.py @@ -0,0 +1,126 @@ +# Copyright (c) 2025, NVIDIA CORPORATION. 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. + +import numpy as np +import torch +from transformers import AutoModelForCausalLM + +from nvflare.apis.controller_spec import Client, ClientTask, Task +from nvflare.apis.event_type import EventType +from nvflare.apis.fl_context import FLContext +from nvflare.apis.impl.controller import Controller +from nvflare.apis.shareable import Shareable +from nvflare.apis.signal import Signal +from nvflare.app_common.streamers.container_retriever import ContainerRetriever +from nvflare.app_common.streamers.file_retriever import FileRetriever + + +class StreamingController(Controller): + def __init__(self, retriever_mode=None, retriever_id=None, task_timeout=200, task_check_period: float = 0.5): + Controller.__init__(self, task_check_period=task_check_period) + self.retriever_mode = retriever_mode + self.retriever_id = retriever_id + self.retriever = None + self.task_timeout = task_timeout + + def start_controller(self, fl_ctx: FLContext): + self.file_name, self.model = self._get_test_model() + if self.retriever_mode == "container": + self.retriever.add_container("model", self.model) + + def stop_controller(self, fl_ctx: FLContext): + pass + + def handle_event(self, event_type: str, fl_ctx: FLContext): + # perform initialization and checks + if event_type == EventType.START_RUN: + engine = fl_ctx.get_engine() + if self.retriever_mode: + c = engine.get_component(self.retriever_id) + if self.retriever_mode == "container": + if not isinstance(c, ContainerRetriever): + self.system_panic( + f"invalid container_retriever {self.retriever_id}, wrong type: {type(c)}", + fl_ctx, + ) + return + self.retriever = c + elif self.retriever_mode == "file": + if not isinstance(c, FileRetriever): + self.system_panic( + f"invalid file_retriever {self.retriever_id}, wrong type: {type(c)}", + fl_ctx, + ) + return + self.retriever = c + else: + self.system_panic( + f"invalid retriever_mode {self.retriever_mode}", + fl_ctx, + ) + return + + def control_flow(self, abort_signal: Signal, fl_ctx: FLContext): + s = Shareable() + # set shareable payload + if self.retriever_mode == "container": + s["model"] = "model" + elif self.retriever_mode == "file": + s["model"] = self.file_name + else: + s["model"] = self.model + task = Task(name="retrieve_model", data=s, timeout=self.task_timeout) + self.broadcast_and_wait( + task=task, + fl_ctx=fl_ctx, + min_responses=1, + abort_signal=abort_signal, + ) + client_resps = {} + for ct in task.client_tasks: + assert isinstance(ct, ClientTask) + resp = ct.result + if resp is None: + resp = "no answer" + else: + assert isinstance(resp, Shareable) + self.log_info(fl_ctx, f"got resp {resp} from client {ct.client.name}") + resp = resp.get_return_code() + client_resps[ct.client.name] = resp + return {"status": "OK", "data": client_resps} + + def process_result_of_unknown_task( + self, client: Client, task_name: str, client_task_id: str, result: Shareable, fl_ctx: FLContext + ): + pass + + @staticmethod + def _get_test_model(): + model_name = "meta-llama/llama-3.2-1b" + # load model to dict + model = AutoModelForCausalLM.from_pretrained( + model_name, + torch_dtype=torch.float32, + device_map="auto", + use_cache=False, + ) + params = model.state_dict() + for key in params: + params[key] = params[key].cpu().numpy() + + # save params dict to a npz file + file_name = "model.npz" + np.savez(file_name, **params) + + return file_name, params diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/src/streaming_executor.py b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/src/streaming_executor.py new file mode 100644 index 0000000000..228db2c226 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/src/streaming_executor.py @@ -0,0 +1,110 @@ +# Copyright (c) 2025, NVIDIA CORPORATION. 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. + +import os + +import numpy as np + +from nvflare.apis.event_type import EventType +from nvflare.apis.executor import Executor +from nvflare.apis.fl_constant import ReturnCode +from nvflare.apis.fl_context import FLContext +from nvflare.apis.shareable import Shareable, make_reply +from nvflare.apis.signal import Signal +from nvflare.app_common.streamers.container_retriever import ContainerRetriever +from nvflare.app_common.streamers.file_retriever import FileRetriever + + +class StreamingExecutor(Executor): + def __init__(self, retriever_mode=None, retriever_id=None, task_timeout=200): + Executor.__init__(self) + self.retriever_mode = retriever_mode + self.retriever_id = retriever_id + self.retriever = None + self.task_timeout = task_timeout + + def handle_event(self, event_type: str, fl_ctx: FLContext): + # perform initialization and checks + if event_type == EventType.START_RUN: + engine = fl_ctx.get_engine() + if self.retriever_mode: + c = engine.get_component(self.retriever_id) + if self.retriever_mode == "container": + if not isinstance(c, ContainerRetriever): + self.system_panic( + f"invalid container_retriever {self.retriever_id}, wrong type: {type(c)}", + fl_ctx, + ) + return + self.retriever = c + elif self.retriever_mode == "file": + if not isinstance(c, FileRetriever): + self.system_panic( + f"invalid file_retriever {self.retriever_id}, wrong type: {type(c)}", + fl_ctx, + ) + return + self.retriever = c + else: + self.system_panic( + f"invalid retriever_mode {self.retriever_mode}", + fl_ctx, + ) + return + + def execute(self, task_name: str, shareable: Shareable, fl_ctx: FLContext, abort_signal: Signal) -> Shareable: + self.log_info(fl_ctx, f"got task {task_name}") + if task_name == "retrieve_model": + model = shareable.get("model") + if not model: + self.log_error(fl_ctx, "missing model info in request") + return make_reply(ReturnCode.BAD_TASK_DATA) + + if self.retriever_mode is None: + self.log_info(fl_ctx, f"received container type: {type(model)} size: {len(model)}") + return make_reply(ReturnCode.OK) + elif self.retriever_mode == "container": + rc, result = self.retriever.retrieve_container( + from_site="server", + fl_ctx=fl_ctx, + timeout=self.task_timeout, + name=model, + ) + if rc != ReturnCode.OK: + self.log_error(fl_ctx, f"failed to retrieve {model}: {rc}") + return make_reply(rc) + self.log_info(fl_ctx, f"received container type: {type(result)} size: {len(result)}") + return make_reply(ReturnCode.OK) + elif self.retriever_mode == "file": + rc, result = self.retriever.retrieve_file( + from_site="server", + fl_ctx=fl_ctx, + timeout=self.task_timeout, + file_name=model, + ) + if rc != ReturnCode.OK: + self.log_error(fl_ctx, f"failed to retrieve file {model}: {rc}") + return make_reply(rc) + # rename the received file to its original name + rename_path = os.path.join(os.path.dirname(result), model) + os.rename(result, rename_path) + self.log_info(fl_ctx, f"received file: {result}, renamed to: {rename_path}") + # Load local model + result = dict(np.load(rename_path)) + self.log_info(fl_ctx, f"loaded file content type: {type(result)} size: {len(result)}") + + return make_reply(ReturnCode.OK) + else: + self.log_error(fl_ctx, f"got unknown task {task_name}") + return make_reply(ReturnCode.TASK_UNKNOWN) diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/streaming_job.py b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/streaming_job.py new file mode 100644 index 0000000000..9569462157 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/streaming_job.py @@ -0,0 +1,80 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. 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. + +import argparse + +from src.streaming_controller import StreamingController +from src.streaming_executor import StreamingExecutor + +from nvflare import FedJob +from nvflare.app_common.streamers.container_retriever import ContainerRetriever +from nvflare.app_common.streamers.file_retriever import FileRetriever + + +def main(): + args = define_parser() + retriever_mode = args.retriever_mode + + # Create the FedJob + job = FedJob(name="streaming", min_clients=1) + + if retriever_mode: + if retriever_mode == "file": + retriever = FileRetriever(source_dir="./", dest_dir="./") + job_dir = "/tmp/nvflare/workspace/jobs/file_streaming" + work_dir = "/tmp/nvflare/workspace/works/file_streaming" + elif retriever_mode == "container": + retriever = ContainerRetriever() + job_dir = "/tmp/nvflare/workspace/jobs/container_streaming" + work_dir = "/tmp/nvflare/workspace/works/container_streaming" + else: + raise ValueError(f"invalid retriever_mode {retriever_mode}") + job.to_server(retriever, id="retriever") + job.to_clients(retriever, id="retriever") + + controller = StreamingController(retriever_mode=retriever_mode, retriever_id="retriever") + job.to_server(controller) + + executor = StreamingExecutor(retriever_mode=retriever_mode, retriever_id="retriever") + job.to_clients(executor, tasks=["*"]) + else: + job_dir = "/tmp/nvflare/workspace/jobs/regular_streaming" + work_dir = "/tmp/nvflare/workspace/works/regular_streaming" + controller = StreamingController() + job.to_server(controller) + executor = StreamingExecutor() + job.to_clients(executor, tasks=["*"]) + + # Export the job + print("job_dir=", job_dir) + job.export_job(job_dir) + + # Run the job + print("workspace_dir=", work_dir) + job.simulator_run(work_dir, n_clients=1, threads=1) + + +def define_parser(): + parser = argparse.ArgumentParser() + parser.add_argument( + "--retriever_mode", + type=str, + default=None, + help="Retriever mode, default is None, can be 'container' or 'file'", + ) + return parser.parse_args() + + +if __name__ == "__main__": + main() diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/utils/log_memory.sh b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/utils/log_memory.sh new file mode 100644 index 0000000000..33f5af5944 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_llm_streaming/utils/log_memory.sh @@ -0,0 +1,9 @@ +#!/bin/bash -e + +echo " date time $(free -m | grep total | sed -E 's/^ (.*)/\1/g')" +counter=1 +while [ $counter -le 400 ]; do + echo "$(date '+%Y-%m-%d %H:%M:%S') $(free -m | grep Mem: | sed 's/Mem://g')" + sleep 0.5 + ((counter++)) +done diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_retiever_model_training/federated_retriever_model_training.ipynb b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_retiever_model_training/federated_retriever_model_training.ipynb deleted file mode 100644 index 0e85096e61..0000000000 --- a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.5_retiever_model_training/federated_retriever_model_training.ipynb +++ /dev/null @@ -1,33 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "0aeea5cc-b56e-4c19-9e2e-7100451fceea", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "nvflare_example", - "language": "python", - "name": "nvflare_example" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.2" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.6_llm_quantization/LLM_quantization.ipynb b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.6_llm_quantization/LLM_quantization.ipynb deleted file mode 100644 index 513cd88e27..0000000000 --- a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.6_llm_quantization/LLM_quantization.ipynb +++ /dev/null @@ -1,33 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "0b6bf4f1-bb20-40f8-a397-47f677ac3c59", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "nvflare_example", - "language": "python", - "name": "nvflare_example" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.2" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.6_recap/recap.ipynb b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.6_recap/recap.ipynb new file mode 100644 index 0000000000..c7950c1afd --- /dev/null +++ b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.6_recap/recap.ipynb @@ -0,0 +1,77 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0b9e326e-1d97-45c5-ac54-6bf581e4223f", + "metadata": {}, + "source": [ + "# Summary of Chapter 8" + ] + }, + { + "cell_type": "markdown", + "id": "bd1c7385-dd22-4d5c-8d2e-7a73f0b3ac2d", + "metadata": {}, + "source": [ + "In this chapter, we visited NVFlare's offerings in enabling efficient and robust federated training of language models, especially in the era of LLMs.\n", + "\n", + "Specifically, the following items have been covered:\n", + "1. **[Federated NLP with BERT Model](../08.1_fed_bert/federated_nlp_with_bert.ipynb)**: task-specific model training with BERT in a \n", + "2. **[Federated LLM Tuning with SFT](../08.2_llm_sft/LLM_SFT.ipynb)**: supervised Fine-Tuning and its role in adapting LLMs in federated learning\n", + "3. **[Federated LLM Tuning with PEFT](../08.3_llm_peft/LLM_PEFT.ipynb)**: PEFT in adapting LLMs for specific tasks, which can be achieve in a federated setting\n", + "4. **[Model Quantization for Transmission](../08.4_llm_quantization/LLM_quantization.ipynb)**: reduce the message size with quantization methods so as to address the significant communication burden when performing federated LLM learning with SFT. \n", + "5. **[Message Streaming for Model Transmission](../08.5_llm_streaming/LLM_streaming.ipynb)**: with quantization reducing communication cost, system memory requirement is still high for prepareing the message on either side. Therefore, we enabled streaming capabilities for more efficient and robust model communication." + ] + }, + { + "cell_type": "markdown", + "id": "4f3f3bf8", + "metadata": {}, + "source": [ + "Key takeaways of this section are:\n", + "1. NVFlare enables federated training of language models, from BERT to most recent LLMs, under popular training schemes of both SFT and PEFT.\n", + "2. NVFlare enables efficient and robust communications, accounting for both message transmission and local memory requirements, such that the resource can be best utilized in real-life applications." + ] + }, + { + "cell_type": "markdown", + "id": "8a32effe-3b9d-4cd8-b53c-f91907de9d95", + "metadata": {}, + "source": [ + "With NVFlare, popular training schemes widely used in the LLM domain can be easily adopted to federated learning paradigm, unleasing more posibilities.\n", + "\n", + "Now let's move on to the [Chapter 9](../../chapter-9_flare_low_level_apis/09.0_introduction/introduction.ipynb).\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e4321c7c-d56c-49b9-89a2-c503290b8232", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.7_llm_streaming/LLM_streaming.ipynb b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.7_llm_streaming/LLM_streaming.ipynb deleted file mode 100644 index deef21b826..0000000000 --- a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.7_llm_streaming/LLM_streaming.ipynb +++ /dev/null @@ -1,33 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "b9c89570-59af-415d-bcb7-a59b304eb49e", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "nvflare_example", - "language": "python", - "name": "nvflare_example" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.2" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.8_recap/recap.ipynb b/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.8_recap/recap.ipynb deleted file mode 100644 index 63414e9f32..0000000000 --- a/examples/tutorials/self-paced-training/part-4_advanced_federated_learning/chapter-8_federated_LLM_training/08.8_recap/recap.ipynb +++ /dev/null @@ -1,33 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "ea637265-6cbb-4e74-9ab3-8eb884991d20", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "nvflare_example", - "language": "python", - "name": "nvflare_example" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.2" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -}