From ac973c667fa55ff1fb2a888b6f610004e4522e71 Mon Sep 17 00:00:00 2001 From: elias-aouad Date: Tue, 28 Apr 2026 14:24:46 +0200 Subject: [PATCH 1/3] Fix default_logdir import for transformers v5 --- src/setfit/training_args.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/setfit/training_args.py b/src/setfit/training_args.py index d6a3aa41..9a6f742f 100644 --- a/src/setfit/training_args.py +++ b/src/setfit/training_args.py @@ -10,7 +10,7 @@ from sentence_transformers import losses from transformers import IntervalStrategy from transformers.integrations import get_available_reporting_integrations -from transformers.training_args import default_logdir +from transformers.integrations.integration_utils import default_logdir from transformers.utils import is_torch_available from . import logging From 93107bc116846fa6377f1e0e398805d2579774d4 Mon Sep 17 00:00:00 2001 From: elias-aouad Date: Tue, 28 Apr 2026 14:50:54 +0200 Subject: [PATCH 2/3] fix: remove broken submodule reference --- scripts/tfew/t-few | 1 - 1 file changed, 1 deletion(-) delete mode 160000 scripts/tfew/t-few diff --git a/scripts/tfew/t-few b/scripts/tfew/t-few deleted file mode 160000 index e26bbce4..00000000 --- a/scripts/tfew/t-few +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e26bbce4855e84065765abf090b5899b3a0beb8b From db184de86178dc635ceca74d429095153b9867c9 Mon Sep 17 00:00:00 2001 From: elias-aouad Date: Wed, 29 Apr 2026 11:28:28 +0200 Subject: [PATCH 3/3] Handling dependencies with uv + deprecate spacy and ABSA --- .github/workflows/tests.yml | 2 - README.md | 1 - docs/source/en/_toctree.yml | 2 - docs/source/en/how_to/absa.mdx | 235 - .../en/how_to/v1.0.0_migration_guide.mdx | 2 - docs/source/en/reference/main.mdx | 33 - docs/source/en/reference/trainer.mdx | 12 - notebooks/setfit-absa-fiqa.ipynb | 7783 ----------------- pyproject.toml | 97 + setup.cfg | 23 - setup.py | 90 - src/setfit/__init__.py | 1 - src/setfit/model_card.py | 131 +- src/setfit/model_card_template.md | 36 +- src/setfit/notebook.py | 6 +- src/setfit/span/__init__.py | 3 - src/setfit/span/aspect_extractor.py | 34 - src/setfit/span/modeling.py | 389 - src/setfit/span/trainer.py | 336 - tests/conftest.py | 31 +- tests/span/__init__.py | 0 tests/span/aspect_model_card_pattern.py | 199 - tests/span/polarity_model_card_pattern.py | 199 - tests/span/test_model_card.py | 50 - tests/span/test_modeling.py | 227 - tests/span/test_trainer.py | 96 - 26 files changed, 198 insertions(+), 9820 deletions(-) delete mode 100644 docs/source/en/how_to/absa.mdx delete mode 100644 notebooks/setfit-absa-fiqa.ipynb create mode 100644 pyproject.toml delete mode 100644 setup.cfg delete mode 100644 setup.py delete mode 100644 src/setfit/span/__init__.py delete mode 100644 src/setfit/span/aspect_extractor.py delete mode 100644 src/setfit/span/modeling.py delete mode 100644 src/setfit/span/trainer.py delete mode 100644 tests/span/__init__.py delete mode 100644 tests/span/aspect_model_card_pattern.py delete mode 100644 tests/span/polarity_model_card_pattern.py delete mode 100644 tests/span/test_model_card.py delete mode 100644 tests/span/test_modeling.py delete mode 100644 tests/span/test_trainer.py diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3a8730bd..eccef8b4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -55,8 +55,6 @@ jobs: python -m pip install --no-cache-dir --upgrade pip python -m pip install --no-cache-dir ${{ matrix.requirements }} python -m pip install '.[codecarbon]' - python -m spacy download en_core_web_lg - python -m spacy download en_core_web_sm if: steps.restore-cache.outputs.cache-hit != 'true' - name: Install the checked-out setfit diff --git a/README.md b/README.md index 60a64970..c6a286a6 100644 --- a/README.md +++ b/README.md @@ -149,7 +149,6 @@ make style && make quality * [https://github.com/pmbaumgartner/setfit](https://github.com/pmbaumgartner/setfit) - A scikit-learn API version of SetFit. * [jxpress/setfit-pytorch-lightning](https://github.com/jxpress/setfit-pytorch-lightning) - A PyTorch Lightning implementation of SetFit. -* [davidberenstein1957/spacy-setfit](https://github.com/davidberenstein1957/spacy-setfit) - An easy and intuitive approach to use SetFit in combination with spaCy. ## Citation diff --git a/docs/source/en/_toctree.yml b/docs/source/en/_toctree.yml index 24123729..4e7229f0 100644 --- a/docs/source/en/_toctree.yml +++ b/docs/source/en/_toctree.yml @@ -35,8 +35,6 @@ title: Knowledge Distillation - local: how_to/batch_sizes title: Batch Sizes for Inference - - local: how_to/absa - title: Aspect Based Sentiment Analysis - local: how_to/v1.0.0_migration_guide title: v1.0.0 Migration Guide title: How-to Guides diff --git a/docs/source/en/how_to/absa.mdx b/docs/source/en/how_to/absa.mdx deleted file mode 100644 index 684b3431..00000000 --- a/docs/source/en/how_to/absa.mdx +++ /dev/null @@ -1,235 +0,0 @@ - -# SetFit for Aspect Based Sentiment Analysis - -SetFitABSA is an efficient framework for few-shot Aspect Based Sentiment Analysis, achieving competitive performance with little training data. It consists of three phases: - -1. Using spaCy to find potential aspect candidates. -2. Using a SetFit model for filtering these aspect candidates. -3. Using a SetFit model for classifying the filtered aspect candidates. - -This guide will show you how to train, predict, save and load these models. - -## Getting Started - -First of all, SetFitABSA also requires spaCy to be installed, so we must install it: - -``` -!pip install "setfit[absa]" -# or -# !pip install spacy -``` - -Then, we must download the spaCy model that we intend on using. By default, SetFitABSA uses `en_core_web_lg`, but `en_core_web_sm` and `en_core_web_md` are also good options. - -``` -!spacy download en_core_web_lg -!spacy download en_core_web_sm -``` - -## Training SetFitABSA - -First of all, we must instantiate a new [`AbsaModel`] via [`AbsaModel.from_pretrained`]. This can be done by providing configuration for each of the three phases for SetFitABSA: - -1. Provide the name or path of a Sentence Transformer model to be used for the **aspect filtering** SetFit model as the first argument. -2. (Optional) Provide the name or path of a Sentence Transformer model to be used for the **polarity classification** SetFit model as the second argument. If not provided, the same Sentence Transformer model as the aspect filtering model is also used for the polarity classification model. -3. (Optional) Provide the spaCy model to use via the `spacy_model` keyword argument. - -For example: - -```py -from setfit import AbsaModel - -model = AbsaModel.from_pretrained( - "sentence-transformers/all-MiniLM-L6-v2", - "sentence-transformers/all-mpnet-base-v2", - spacy_model="en_core_web_sm", -) -``` - -Or a minimal example: - -```py -from setfit import AbsaModel - -model = AbsaModel.from_pretrained("BAAI/bge-small-en-v1.5") -``` - -Then we have to prepare a training/testing set. These datasets must have `"text"`, `"span"`, `"label"`, and `"ordinal"` columns: - -* `"text"`: The full sentence or text containing the aspects. For example: `"But the staff was so horrible to us."`. -* `"span"`: An aspect from the full sentence. Can be multiple words. For example: `"staff"`. -* `"label"`: The (polarity) label corresponding to the aspect span. For example: `"negative"`. -* `"ordinal"`: If the aspect span occurs multiple times in the text, then this ordinal represents the index of those occurrences. Often this is just 0. For example: `0`. - -Two datasets that already match this format are these datasets of reviews from the SemEval-2014 Task 4: - -* [tomaarsen/setfit-absa-semeval-restaurants](https://huggingface.co/datasets/tomaarsen/setfit-absa-semeval-restaurants) -* [tomaarsen/setfit-absa-semeval-laptops](https://huggingface.co/datasets/tomaarsen/setfit-absa-semeval-laptops) - -```py -from dataset import load_dataset - -# The training/eval dataset must have `text`, `span`, `label`, and `ordinal` columns -dataset = load_dataset("tomaarsen/setfit-absa-semeval-restaurants", split="train") -train_dataset = dataset.select(range(128)) -eval_dataset = dataset.select(range(128, 256)) -``` - -We can commence training like with normal SetFit, but now using [`AbsaTrainer`] instead. - - - -If you wish, you can specify separate training arguments for the aspect model as the polarity model by using both the `args` and `polarity_args` keyword arguments. - - - -```py -from setfit import AbsaTrainer, TrainingArguments -from transformers import EarlyStoppingCallback - -args = TrainingArguments( - output_dir="models", - num_epochs=5, - use_amp=True, - batch_size=128, - eval_strategy="steps", - eval_steps=50, - save_steps=50, - load_best_model_at_end=True, -) - -trainer = AbsaTrainer( - model, - args=args, - train_dataset=train_dataset, - eval_dataset=eval_dataset, - callbacks=[EarlyStoppingCallback(early_stopping_patience=5)], -) -trainer.train() -``` -``` -***** Running training ***** - Num examples = 249 - Num epochs = 5 - Total optimization steps = 1245 - Total train batch size = 128 -{'aspect_embedding_loss': 0.2542, 'learning_rate': 1.6e-07, 'epoch': 0.0} -{'aspect_embedding_loss': 0.2437, 'learning_rate': 8.000000000000001e-06, 'epoch': 0.2} -{'eval_aspect_embedding_loss': 0.2511, 'learning_rate': 8.000000000000001e-06, 'epoch': 0.2} -{'aspect_embedding_loss': 0.2209, 'learning_rate': 1.6000000000000003e-05, 'epoch': 0.4} -{'eval_aspect_embedding_loss': 0.2385, 'learning_rate': 1.6000000000000003e-05, 'epoch': 0.4} -{'aspect_embedding_loss': 0.0165, 'learning_rate': 1.955357142857143e-05, 'epoch': 0.6} -{'eval_aspect_embedding_loss': 0.2776, 'learning_rate': 1.955357142857143e-05, 'epoch': 0.6} -{'aspect_embedding_loss': 0.0158, 'learning_rate': 1.8660714285714287e-05, 'epoch': 0.8} -{'eval_aspect_embedding_loss': 0.2848, 'learning_rate': 1.8660714285714287e-05, 'epoch': 0.8} -{'aspect_embedding_loss': 0.0015, 'learning_rate': 1.7767857142857143e-05, 'epoch': 1.0} -{'eval_aspect_embedding_loss': 0.3133, 'learning_rate': 1.7767857142857143e-05, 'epoch': 1.0} -{'aspect_embedding_loss': 0.0012, 'learning_rate': 1.6875e-05, 'epoch': 1.2} -{'eval_aspect_embedding_loss': 0.2966, 'learning_rate': 1.6875e-05, 'epoch': 1.2} -{'aspect_embedding_loss': 0.0009, 'learning_rate': 1.598214285714286e-05, 'epoch': 1.41} -{'eval_aspect_embedding_loss': 0.2996, 'learning_rate': 1.598214285714286e-05, 'epoch': 1.41} - 28%|██████████████████████████████████▎ | 350/1245 [03:40<09:24, 1.59it/s] -Loading best SentenceTransformer model from step 100. -{'train_runtime': 226.7429, 'train_samples_per_second': 702.822, 'train_steps_per_second': 5.491, 'epoch': 1.41} -***** Running training ***** - Num examples = 39 - Num epochs = 5 - Total optimization steps = 195 - Total train batch size = 128 -{'polarity_embedding_loss': 0.2267, 'learning_rate': 1.0000000000000002e-06, 'epoch': 0.03} -{'polarity_embedding_loss': 0.1038, 'learning_rate': 1.6571428571428574e-05, 'epoch': 1.28} -{'eval_polarity_embedding_loss': 0.1946, 'learning_rate': 1.6571428571428574e-05, 'epoch': 1.28} -{'polarity_embedding_loss': 0.0116, 'learning_rate': 1.0857142857142858e-05, 'epoch': 2.56} -{'eval_polarity_embedding_loss': 0.2364, 'learning_rate': 1.0857142857142858e-05, 'epoch': 2.56} -{'polarity_embedding_loss': 0.0059, 'learning_rate': 5.142857142857142e-06, 'epoch': 3.85} -{'eval_polarity_embedding_loss': 0.2401, 'learning_rate': 5.142857142857142e-06, 'epoch': 3.85} -100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 195/195 [00:54<00:00, 3.58it/s] -Loading best SentenceTransformer model from step 50. -{'train_runtime': 54.4104, 'train_samples_per_second': 458.736, 'train_steps_per_second': 3.584, 'epoch': 5.0} -``` - -Evaluation is also like normal, although you now get results from the aspect and polarity models separately: - -```py -metrics = trainer.evaluate(eval_dataset) -print(metrics) -``` -``` -***** Running evaluation ***** -{'aspect': {'accuracy': 0.7130649876321116}, 'polarity': {'accuracy': 0.7102310231023102}} -``` - - - -Note that the aspect accuracy refers to the accuracy of classifying aspect candidate spans from the spaCy model as a true aspect or not, and the polarity accuracy refers to the accuracy of classifying only the filtered aspect candidate spans to the correct class. - - - -## Saving a SetFitABSA model - -Once trained, we can use familiar [`AbsaModel.save_pretrained`] and [`AbsaTrainer.push_to_hub`]/[`AbsaModel.push_to_hub`] methods to save the model. However, unlike normally, saving an [`AbsaModel`] involves saving two separate models: the **aspect** SetFit model and the **polarity** SetFit model. Consequently, we can provide two directories or `repo_id`'s: - -```py -model.save_pretrained( - "models/setfit-absa-model-aspect", - "models/setfit-absa-model-polarity", -) -# or -model.push_to_hub( - "tomaarsen/setfit-absa-bge-small-en-v1.5-restaurants-aspect", - "tomaarsen/setfit-absa-bge-small-en-v1.5-restaurants-polarity", -) -``` -However, you can also provide just one directory or `repo_id`, and `-aspect` and `-polarity` will be automatically added. So, the following code is equivalent to the previous snippet: - -```py -model.save_pretrained("models/setfit-absa-model") -# or -model.push_to_hub("tomaarsen/setfit-absa-bge-small-en-v1.5-restaurants") -``` - -## Loading a SetFitABSA model - -Loading a trained [`AbsaModel`] involves calling [`AbsaModel.from_pretrained`] with details for each of the three phases for SetFitABSA: - -1. Provide the name or path of a trained SetFit ABSA model to be used for the **aspect filtering** model as the first argument. -2. Provide the name or path of a trained SetFit ABSA model to be used for the **polarity classification** model as the second argument. -3. (Optional) Provide the spaCy model to use via the `spacy_model` keyword argument. It is recommended to match this with the model used during training. The default is `"en_core_web_lg"`. - -For example: - -```py -from setfit import AbsaModel - -model = AbsaModel.from_pretrained( - "tomaarsen/setfit-absa-bge-small-en-v1.5-restaurants-aspect", - "tomaarsen/setfit-absa-bge-small-en-v1.5-restaurants-polarity", - spacy_model="en_core_web_lg", -) -``` - -We've now successfully loaded the SetFitABSA model from: -* [tomaarsen/setfit-absa-bge-small-en-v1.5-restaurants-aspect](https://huggingface.co/tomaarsen/setfit-absa-bge-small-en-v1.5-restaurants-aspect) -* [tomaarsen/setfit-absa-bge-small-en-v1.5-restaurants-polarity](https://huggingface.co/tomaarsen/setfit-absa-bge-small-en-v1.5-restaurants-polarity) - -## Inference with a SetFitABSA model - -To perform inference with a trained [`AbsaModel`], we can use [`AbsaModel.predict`]: - -```py -preds = model.predict([ - "Best pizza outside of Italy and really tasty.", - "The food variations are great and the prices are absolutely fair.", - "Unfortunately, you have to expect some waiting time and get a note with a waiting number if it should be very full." -]) -print(preds) -# [ -# [{'span': 'pizza', 'polarity': 'positive'}], -# [{'span': 'food variations', 'polarity': 'positive'}, {'span': 'prices', 'polarity': 'positive'}], -# [{'span': 'waiting number', 'polarity': 'negative'}] -# ] -``` - -## Challenge - -If you're up for it, then I challenge you to train and upload a SetFitABSA model for [laptop reviews](https://huggingface.co/datasets/tomaarsen/setfit-absa-semeval-laptops) based on this documentation. diff --git a/docs/source/en/how_to/v1.0.0_migration_guide.mdx b/docs/source/en/how_to/v1.0.0_migration_guide.mdx index 6f064137..befe34fd 100644 --- a/docs/source/en/how_to/v1.0.0_migration_guide.mdx +++ b/docs/source/en/how_to/v1.0.0_migration_guide.mdx @@ -39,7 +39,6 @@ This list contains new functionality that can be used starting from v1.0.0. * `use_labels` (defaults to `True`): Whether to use the `SetFitModel.labels` to convert integer labels to string labels. Not used if the training labels are already strings. * [`SetFitModel.encode`] has been introduce to convert input sentences to embeddings using the `SentenceTransformer` body. * [`SetFitModel.device`] has been introduced to determine the device of the model. -* [`AbsaTrainer`] and [`AbsaModel`] have been introduced for applying [SetFit for Aspect Based Sentiment Analysis](absa). * [`Trainer`] now supports a `callbacks` argument for a list of [`transformers` `TrainerCallback` instances](https://huggingface.co/docs/transformers/main/en/main_classes/callback). * By default, all installed callbacks integrated with `transformers` are supported, including [`TensorBoardCallback`](https://huggingface.co/docs/transformers/main/en/main_classes/callback#transformers.integrations.TensorBoardCallback), [`WandbCallback`](https://huggingface.co/docs/transformers/main/en/main_classes/callback#transformers.integrations.WandbCallback) to log training logs to [TensorBoard](https://www.tensorflow.org/tensorboard) and [W&B](https://wandb.ai), respectively. * The [`Trainer`] will now print `embedding_loss` in the terminal, as well as `eval_embedding_loss` if `eval_strategy` is set to `"epoch"` or `"steps"` in [`TrainingArguments`]. @@ -90,4 +89,3 @@ This list contains new functionality that can be used starting from v1.0.0. the case it is "steps", `save_steps` must be a round multiple of `eval_steps`. -* Pushing SetFit or SetFitABSA models to the Hub with [`SetFitModel.push_to_hub`] or [`AbsaModel.push_to_hub`] now results in a detailed model card. As an example, see [this SetFitModel](https://huggingface.co/tomaarsen/setfit-paraphrase-mpnet-base-v2-sst2-8-shot) or [this SetFitABSA polarity model](https://huggingface.co/tomaarsen/setfit-absa-bge-small-en-v1.5-restaurants-polarity). \ No newline at end of file diff --git a/docs/source/en/reference/main.mdx b/docs/source/en/reference/main.mdx index 2138f9fe..952f671e 100644 --- a/docs/source/en/reference/main.mdx +++ b/docs/source/en/reference/main.mdx @@ -21,36 +21,3 @@ [[autodoc]] SetFitModelCardData - to_dict - to_yaml - -## AbsaModel - -[[autodoc]] AbsaModel - - __call__ - - device - - from_pretrained - - predict - - push_to_hub - - to - - save_pretrained - -### AspectModel - -[[autodoc]] AspectModel - - __call__ - - device - - from_pretrained - - predict - - push_to_hub - - save_pretrained - - to - -### PolarityModel - -[[autodoc]] PolarityModel - - __call__ - - device - - from_pretrained - - predict - - push_to_hub - - save_pretrained - - to diff --git a/docs/source/en/reference/trainer.mdx b/docs/source/en/reference/trainer.mdx index 674a01dd..d68954aa 100644 --- a/docs/source/en/reference/trainer.mdx +++ b/docs/source/en/reference/trainer.mdx @@ -36,15 +36,3 @@ - train - train_classifier - train_embeddings - -## AbsaTrainer - -[[autodoc]] AbsaTrainer - - add_callback - - evaluate - - pop_callback - - push_to_hub - - remove_callback - - train - - train_aspect - - train_polarity diff --git a/notebooks/setfit-absa-fiqa.ipynb b/notebooks/setfit-absa-fiqa.ipynb deleted file mode 100644 index 67b89964..00000000 --- a/notebooks/setfit-absa-fiqa.ipynb +++ /dev/null @@ -1,7783 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "id": "faV2oTCDWfOP" - }, - "source": [ - "# **SetFitABSA vs. BloombergGPT**" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Rx8Ty07xyZSb" - }, - "source": [ - "### **BloombergGPT**" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "tYr4RIcVpp6X" - }, - "source": [ - "In their paper [BloombergGPT: A Large Language Model for Finance](https://arxiv.org/pdf/2303.17564.pdf), Bloomberg presents the development of **BloombergGPT**, LLM which is specialized for the financial domain.
\n", - "\n", - "The model is a **50B** parameters language model, trained on a wide range of financial data:\n", - "\n", - "* **363 billion** tokens dataset based on Bloomberg's extensive data sources\n", - "* **345 billion** tokens from general purpose datasets\n", - "\n", - "The model was evaluated on various financial NLP tasks, including Aspect-Based Sentiment Analysis (**ABSA**). Bloomberg evaluated ABSA in an in-context manner using the [FiQA_SA](https://huggingface.co/datasets/AdaptLLM/finance-tasks/viewer/FiQA_SA). This dataset contains a test set of 235 finance related sentences, each is prefixed by 5 tagged sentences, which overall comprise an input prompt to the model. Every tagged sentence in the prompt contains a known aspect and a question whether its corresponding polarity is Positive, Negative or Neutral, and the answer (the correct polarity towards the mentioned aspect) is written at the end. BloombergGPT is expected to predict the polarity of a given aspect in the test sentence based on the 5 tagged example sentences. This is an **SB2 ABSA task**, which means that the aspect is already given and the model just needs to predict the corresponding polarity.\n", - "\n", - "The evaluation score achieved by this model is **weighted F1-score=75.07**, as reported in table 8 in the paper." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "MGvd4lhLOY6D" - }, - "source": [ - "![image.png](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAbEAAACnCAYAAACfKGKDAAAgAElEQVR4AeydB1RUSdbHN3y7O7uT8+zkGcdxdByzjjnnnCPmgBkxJ0AMqCgoGYkqQRFQUQkSBARzAJSoIqBijgShoR+/77zupukG2jTBdn19Tp9+r15VvVv/ulX/ureqq/6C9JEQkBCQEJAQkBB4RRH4yysqtyS2hICEgISAhICEABKJSUogISAhICEgIfDKIiCR2CtbdZLgEgISAhICEgJaJGZvb88PP/wgfSUMJB2QdEDSAUkHXroOODg4PJWltUjsxo0bnDlzRvpKGEg6IOmApAOSDrx0HRA56WkfLRJ7WmTpuYSAhICEgISAhIA+ISCRmD7VhiSLhICEgISAhMBzISCR2HPBJUWWEJAQkBCQENAnBCQS06fakGSREJAQkBCQEHguBCQSey649CGywN3MBM6ePs3p8u/Zc2TeKECuD+K9RBnKZLdJPrIPb09PAiLSyBMErqemcleAMuE+FxPOVmB2+ixJqTk8lGkKLOPBzWtcuXKl2m/ujQeUaEZ/4WsZN9Lj2OPtwfZdcVwpFHiQmsxVuShnHpfPact5PuM6BerKFSi4q0vGa1y/V1hFqtIHWSSdPU1CwiXuq/PJ52paKmnpaaSmXaOgSqonBSjTpmdkkJGeRs7dUgW+l88nk5qWRmrGdR4/KfkzPhMKczkTFURg4H7izt2gWJ1O4GH2Oa02kJCUwc18deEoEwq5c636erxyLZf7VWFS5/7HX1SV/2xCMtl3ixFULy8THpKTlkaGiHFGOukZuTrqqJAriVEEBQYSHH2eW0XVS59/PYHIoEB2748h9bpmpDyyzp/l9NkELt3UagyKjITC66ScPc2ZsynceKmYVV8uMVQiMd3Y6OkTgRtpMbhOqkPNjmuIio8nPi6SnZsM6d1jHiFZFQ1ZTwvwB4glcD1+A6P7T8ExNJNCQc7djBAcLabSueVCzsigrOw252M9MKj1HgNXRBAfH8ehEF8spg7EcG0s9wWQl8Qwq0NvzBx98A/0w7TPF/zYwZSdgf54u5gxsNU0oit60hcqh7wgAduJA5hpGUZ2oUDh7ZN4WxrTo74BwYVKsk0/FsiMJm/Tfd5+4uIPE7nfjcUjB7DU5yKysmtYDW7HbKtt+AXsYEnXT/i512r8AnbitmE8nYd6VpGr9H46sXsW0v6zL+hvfhaxqxJJ/XykHSO7GuAansbD8t6zSuqqAWXCAzKOBrK8xxd8VnsaEVkyyspyCZ3fgTbD1xMQc1FHh1s1r+pDBG4et2KCwQrCM0XACzkfuJxxE+1JyhNTCNy5dBRf46Z83XghwXHxxB7ah/PykYyYuYMsGRRft2dQa0Nstvmxa6cpHT/6nBGr/PDf6cmaCS2Z6fag+lf/KaFK+XcYN+WbZksIi4snLjYUD7Mh9B7uRIpCD+6REr+TyfXfpP3UHcQczySvUh2VPDjKuvGjsQy6rKjTRxcCWGwwGY9TCpAUJRHrOcZqApNN93OlGITHGexYPpo5tqcVdaSoy+N7mdPrKxp128INrXfIydgznh//0xaLA6e4mv+ngPPcL5FI7Lkh04cEAmmbO9J8zAGN0Wkhu6d8x69jQxQK/fhuNpcyL3P50iVuVtZ+oORRFiciQ4hNuEaBluKK5ZNzL/sUkSGHOH81v2J0KLvL5dRULuRWNBIxdpn8AdlpqaTnPKDgbhapKSmkiN/UDK49qBjdFT/IJevyZS5rfLOv3UdGMTcvp6rSpJF1PY/SKjAL5OUmER0azqnMh5Tcv84t1ciw6LILvX7sy45KBC676c2wNkoSU8gpXGJ1m6+YG1gxEi19tIP+H/7IyhgZctk+XF0uqSxaGdGL6tHGMEqBp9hxZm9zIvhRFcGeOaBMuMuuST8zYEWSlkUnf3yMeS1GKUhMmVkezn0/ZLJ7hX2Ud3Ixdd8diP+tRNydo1X1XkzwrFp0Nj6myi+fIOet1cpTencnbna29Pi6OZtOlJe/kF3b/V/YapLd2MGgb2qzPOwBZcJlfNdvJaOiuquV41kCZXcCGVWnF95Zmoop5/zmLnSYGEJ5Fdza1o+fe26j3EAQSo5jXPcTDLfepTDJjW1RynLKZVFM+aEuFkeUwskL9+PmkfksovyhcUT5f+njrcZfEFIx/fVDxjhcV7S5Mm6xudsHTHCpSrhlZbfwGfsLY+2z1e1TFPZx5iY61zEk+r54J3Bl91iad3PimgaUgnCRdZ3rseRAeb6F7HRdyKQGHXC5UDEIFuRpHAgwofvHBhwsV5k/FJEXy1wisRfD7SWnqkpiZWXXsO/zFQNWnVMQQMH1E5h0/IqRlkfIeaChwchJ372UGUt2knI3n2snt7LQ0JzoXGUcsTPaudSQ1b5J3M2/Sfy2RRiZhXFLdMnJbnJ880QaNTHkkHpUJpB7cAHNfh6J7/m75N88zeoetZjsdJKkxOPstZ7A8Bm7FSO8RzmHWdz+K0ZZxnL8+DFiw9yZNXAhccVF5JxZR+dvB+Nx4izxB92Zb2CI73llyykTrhBoOomF9rFcLSgg89gOFnTrh3O62OCKCTWqRePhu6sZ/ecRYGFFoqpjLauOxAoDGPLx1ywJLkYoPsuphPJeuDKJQVH6CZLuaWL5fGpQlGVN6zfb4na5ch6lHLFcR7i6o6hKYoVJpjR4syc+V9M4eeKWquOqTGJyck6drFYokcR8A/NJ9RjAzw1NOavo+SuTWB4Xj0cQGn6MbI1xSuGNRCIPHOBw0k0t8hU7yUs7hvHjD4a4bFnPruRy7KoV4RkD5aRsak/tjk7crgRTcY41rd/qireqR65CYvIEFjX8gHHONyi8cJyEm8oMKpNYmXCN0ydynlGePy5aZRKTF0ZhWKsGi4OVjetJJKbQJRGL69oglZVlYdHuQ0bb5yIXMlXXN7SITqy3VBXGSogK2eXpzaGVLemzNFE9gCxIDCEi2ZWeEon9cUrw+uasJLHaXVayZ/9+ggLcMZ00kOkWh7ilHkgV4W1Qn2Vh2v6v4qsu9Gkwm6MVg3xuBI2n/dCd3BUEMlz70XX6IfXotowH7BzfhFk+txVw5x3yYv2S/szequxIy4QcQvasZ3zHpSQqzKciAia1YmW0skMrLfRn6JdD2a94X4VMZUIumZkF3A0OIOoRyGV7GNNgJkcU4pZywrwZHWYepgSBDPf+dBx3gIp+VU664zLc0+WIo8qVrd6ht8l5dePTpRdKEvscQ/dMxZzXpdR4ti4Zwsj5Qag4XCNpVRLTePhCl/d3G/D5O6M1yEpXNiKJfcBI6wyuXMnh4rlwrCb1ZYZDonrUrkxZmcR05QdKEiukTMhmy8Dv6bHoKIVUkFhZ2XX8jEdjFX2P4jvBzB1mzpkiuBm9BuNVEdyUFXMp2Iw5FnFagwVx8OQ25Gvq9fNGMfjXLcIzPinCx+BTmhjs0/AyKJPKi3Yx8L0vWbhPqdMiCdTqaMOFK1fIzkwi2G4SgwyU7jjNl1UmMc1nL/NalP+7FosVbXjPLgfmDR/Ikq3JGm1PtyX2YI8BX3wwjkj1wKe8JIVsG/ERv44NplAWyJAP/8v8PVUicdd/BJ9/PFHlHhdJbAe3szbTue5cTingLeRIcBQ3HrpLJFYOrfT7eyJQ1RIreXASi4FtmemepurMKwhD883Xtw/gl97btTqi4hv2dPzYgLCiB2zp/wlTPNVmlsIlccmhC02G71Zkk3fIF/8ET0b3syFHDo/TQgg7v48pWiTWmDGWewkK3MrKKSNZui1V5ZITZarFEDMfPK0nsNAuRz1CFEnM4OehuMbEEbnPjcVT5rInXYZIolv6fcIkt4eaxaD0wU1uF4lzXdmsbf8OPZYmaJBYPhlHI4iIjCQyMoJDJ7IV7xdJbFXr9xlgGkFMTCxxRxPJvqfLevj9SezB/vF89eYIQtV9isD15GgiIpRyRh25pOrARBJ7n65z9hItynn8PNfz1KMTDRyen8TExMXXvOn/XUMsDt9SuxPzji+kWXMLLhSXUlpawP5Z7VkcepzFzXqwXW2lX2JNp/Y4plTIIsgz2Go+gXZf1cEsXLuONAR9jssidoz9Lw2GBlQhsdLH3vR5+yuF1SxmKJLA1w3nsS86htjDx0m9llft4qYnkVhx7jGCdu/nQMhpxQIgKCX7eBB79u8j4tQtSu4nstN8AuMWOeG7w5dtTpbY+6dVGkw8R/E0ola2xAqv7GZyh25sPqzEUZclJiuW8SBoLF+8OVJDl8ozLsBt4Ac0nxDGY9kehn/yIUZ+aoUrj8Qdn0H895MpxKoISySxR2XXcej3C4sPFFDyMIaD0Q+RPfLQIrEyeQGXj2zFO1xp3QlCBt5rzLC2tcPOzg7n7XEKr40mrncEgVtJgZiMH4mJoy87fL1wtLTiQGq5I1gt1gtdSO7EF4LtZSeqSmKiRA9CJvHte+Uj/cokJuda9jVu+A6mTuct3NMowuPLG2jz6XgiivJwH/QpYx2VVpcyipzzG9vRbPQ+xa1IYkFZD/A37I31mXxOBodzrSi4EolVWGJ5Fz0Y02k6oYqOsEKm4pte2LldQS5c48pVucoSm0zo9VtkxK7FYKg9GQrLLh/PIZ8xzlEpsWgxHAvaip3lBtz2pSJ2OkdXNKZen+0alkAp965d5JjzQL75fDS7Mx8qOjcliX3I1O3P0nh+fxIrvuVOjw+aYHOuYsYv/3YWZ/dOo9a/O+OcfF9FxEpLbJRNZTeQRqUpLqsjsVJOOo6jR9eudO3ajTGrDitilltiyhwEMncOp3bdGSxz2qXokO/uHML3vy7mgIL4I4mMiiPlqjf93+1LoMoELuM2Nj2+Zt7u8k6xlCSvDey+IOOC9xB+rGXEkfJplsqiPvO9wKUtPfi+wUoyKrhSkbrgzDLqvdOfANG3rSKxWm0dqrgdK79KF4nJbu9lqZEXV+WFhK1ZTUy+wJV9SzDZlo3scRRrV4Yqsrq7azSj1irnSkV3u0WnTrhcrCRc5Zc+w31lEhN1+eTKptRq74Do5quOxMT3B/gepvi6M53faYrteW05BPkp5tf7mMnut5GXXWNz94/oY5pcidxLiVvegF96b1URt9ISE6v5ur8BrYbsICUiWOFyLq1EYo8yjxHuOZlxK5SeD/njfWxY7Mx2Ly/c189gsUs6hVVwFcEoZJtBDzVuRdlWdG+7kWxt8Z8BtapRJBKriskrEFIdiclJtevMt7WWoJzWqSAMsUCCPBmPLVHk3d7B0DqDCFD7z+SkOPSgw4Rg8sTFC34jadVvm2I0JaYrEzKx6l6fxfuVo8O8Qz4EZcl5dHQR/Sas40D0feQluklMXLW2ofM3LNgrdnzaMon5FyZ64nNYXFRR4U4U5OdY0rQxG06Knb1AbtAE2vXfpl45JY7+zNu2wSFV2QJkt/cxvn5rrI5r+EiBhwenUKvmfMXqRGVZREvs5ZGY2JAPmTWn46QQNKcpC5PNafzW0EoLOz7gxUhMLGnVj+yOL94BFeQtDga2jfqeuv28FCRWlGlDjxYmJKv4VbRAEjKSsejYEXuV5SWUnGB+y954XVbiXpDiidW2i8oBQlkOWwZ+Q6dZ0VpWflVJnh4izz/EzPpNWHO4Ql7II3h2Q3ouPqK2gkQSeHESk3Peqgdj1kQQGrKbkKM3KBUuYtFtANYRoQTv3sfp68py3t1lwKBlR8jOyeRUyEZmTHEkpZzHn14cnTEqk5jodfCb+C3NDIIUrvPqSKwoy4kVG8XBWx4h8xrS3ThejYf4ojvRs2jaYBmnVdDdiZlLk/oLOKUBpTw/CsP6TbGMKw8sxM/DV/FO+eNQJtRpzpztCYr5z8okJr4j7/A8JporSUy0zAoKQShOwHtLFHeEqrgqUSxk26hObIjPJjszgb1W05lnn1jF2tYJ1hMeSCT2BHD085GctINOLOz9DV82noKNkxNOjjasM53CwP6z2ZUkduQCd84HYtjsU/ou8sFvpyfrp7dn2PIkSkVSiN+M0Yx1+Mcc5aCXObOMt5CoGkGLS9GjNhsxd+1OYo5G4bFyOsucTikUXCg4j+eUrowyjeSKLIV1oxcSX/CQxANL6PBjX6wjMrmRGsSs1t8wcLkv/rt8cVk/jdGTPcko1pbJ398f322WjGs5mG2590ncv4A2X3VlXWgGhcJ9Agzr0m95EJ7uoZTxkKNui5hr5knkqUTigl1ZPmoK2zVGw4WXA1k4YihzNwZwJCmVs7E72bzWjGnj1ykWdohWWKirKb1/eJfWo60IiFeuAKuujouvHsbb1ZbpHf7Ld82nYeuyjfhMZVOsLv7zhImLCvauMGDEVEuCjqWQfOoAzmvMmT1ioWKuTFzEEuVlybBf3qHJoNV4RV6uNIpWvq046xBbXTYxsdXH1GxrhL2rD8dE/241n+KscDbM6ECLbgvxjquw7mS3ApllrLTEQEaSzwJmm2wlLDqUXZ7BZBRBfup2FhmvZ1dYCNs3LGSt70Vkj8+x03IuvX6uxah18Yq/J4j47prTmo/e/pERyxyISNXlpq1GwGqC8i/twnjYeKx2J5CZeRrfVeOYsGgv1xQkK+fy4e2sMfiFT2sOYb1bFFeqLbqcC9HbcN48lV8/+oyes23x8D5MriKujPB5DRi2QbSw8tg1cxQuKdHMrNcBt4tySgv2MnO4rUIykcT6z48gOSWZM0cCWD3HnEOVFlRUU4QnBMnJjPVgxdBafFp7NFZiG3bYxMr5Ixk0agPHbgmUCVkcdFtBnxpv0HToWhydHLC2mEPP2hWudUF2Ea/5w5m2eg9JlzM5FrCKcSOWEJZVYemLC59Sds1n5DgLDpy9zIUz/piNHcnq3dkKq1/Ut3DnuXRu2IlF9tHkymXEmE/C+VwpxVdicF8/ijrvNmHmJj/OqhbKaJKYspBy0l3N2JomAlsVV0/FikeRxDqwKjyZlOQEYgItWGAW8lQr+gkgqh9JJKaG4nW7kHE3+xLXHmoqfAUGZbL7ZF+6wqPqH1dE/MOuirl9OVO9jF58TZlQwLX0lCfMY0HR/YuciD3Mucz7lVbS/WGCvlDGZbK7pJ04zPHzuRRqLzB7ofx+r0Rl8jzu3C1Uz1Uq85Xz6NZtjT9c/15ve1o+ch5dTeTIkXPcqPo/kKclfspzgTTbfhh5i4M+GYcWdWVJcDob+hgoFiEJJUeZ12m6Ig9Nd6Lo8os3+ZWJTnefkv+f97ik4AoJ8UdIuVZQqd4qZCiT55OdGM+xpN+ub5VJTPTWWA6dToxifq0qrsuDxQfa7kRBfpqFTfqw406FjC96JZHYiyInpZMQkBB4pRGQ3Qtm1SIXDp/ez8rZGzhTIJAbshqzLYc5dmAVi9Yfo+TBOXYYtaDlSFv8/Hfh5baaaePNOayySl5pAF5A+MLseLxX9qPDMCv2H89V5CAvCWVKu/mcVBnf1eF6+/xuprX4hSk2fvjv8sF5zVSmm0Wo5uReQBCNJBKJaYAhXUoISAi8XgiUFd8m81Ku1h/+H9/O5HJuxZ/8Xy9EXqS0xdy9eV/L7V0dri+S87OkkUjsWVCS4kgISAhICEgI6CUCEonpZbVIQkkISAhICEgIPAsCEok9C0pSHAkBCQEJAQkBvURAi8R8fHzo3r279JUwkHRA0gFJByQdeOk64Ovr+1Ti1CKxS5cuERISIn0lDCQdkHRA0gFJB166Doic9LSPFok9LbL0XEJAQkBCQEJAQkCfEJBITJ9qQ5JFQkBCQEJAQuC5EJBI7LngkiJLCEgISAhICOgTAhKJ6VNtSLJICEgISAhICDwXAi9EYmWy2yTHB+Hl4cmew9kUCg9JTcl+rhc/OXIB1zMvkpGRUel7kcs3NM+6enIumk/zrycQGRRA4L5oUq/r3oJadj2cvVG3de5BppmndC0hICGgPwiUyYqR6dE+lPqDzP+2JM9NYvcTnBjXbwJ2oZk8ForJPeWHxdwedBwV+LshJe6knhi1lbF136TjjJ2ER0YSHhqEp+VY2g9wUezSnuMznMbdnFCdVK7z3WXCfWKsxjNx+V5yikAoTGP7guHMtj1dzZERcpKt21Ons7PiPB+dmUoP1AiIdbVnYW/M95UPDASuxDqwfIkpJosmMnTEQgISnz7wkN0JYW7PuUSrsikrzuN61in8N81kUH8L1fEy6te+phcCuScC8fLzx8fFBMOJ1pxSHaecl+zDRht/9u/xYMP6ALQ2MlehpSuO2Ebit1ri4O2Pt/1afI+XH9GhnzCX3IrHc7MttptXMnX0NLxOKQ88u+I+hJ8atqez4iy1rnTr1p91+zVPzhPLU8S5CB92+gewc6sdNttPqfoBXeH6iUFlqcrKbnEkcDt+/jtwMJ2M0cYjPFJFKnlwAndLO3z8fbG38CCxEK3jWxTHHVWrVwK302PY5TSHnp3NFKdBVH6vPtw/F4nJ7gcx9qeW2JzVPmbh/tEFdB/5+5GYCEx1Z+mIOyGHO4okBkLBZZIvPHiKxSRwZfdYmlciO0GexqoOv7DkgPYJfuJR9y4rhtDqgy5sy5aGdE9X0FKS3Prx6d//zdStyo5PXrib4Z99yCCLREqEVEyb/Zsv6yxH4xzIKtkK8nTs+n3JO38dwF5V/1mY5MPK1ZtY0v8rPv7EkDjFDtlVkr5WAYKQjnnHWiwNEndez8N1wEcMWXeZUiGddX0N2KPYEVzgsttIJttna7UNMW31ceSctenHOMs05MiIW9OdaU7ZWvvg6RfIxYSZTccrTakQdw8aUvenRZyVyTjsvArPsENEx8QQHePPCiNrkirxcfEVFyydyk8UL+X4GmO2ZQnoCtevsuuW5vGFdXT8fhpRBVBa4EWft9vgmilHEFKx7Nsf11Q5QslJTLsNxzdLzjWNo2t06ZUcgftXLpKT40jPOhXn8umW4uU8eQ4Sk5OyqT01W1pxtVL/LpQcZ+OaA4oSlMmuczbqACFR57it4DqBu5dPcjjqJDmF90mNCyc24YbiuPiiu9lcvJTJxYwrPBIg/9ZlLl26yNV7pVVI7P6xOJJlcD/hFJBPVkIcMUfF49x15y8eEWDR7mPGO2ueVCyKKZC6uQO1OzppWVxFGR647cvAtud/GWGt3Qm8nOrR77fePrqe2SbTaPnP/6hJTDzQ0nHKEJZ6X6ZEFseMWm9Qq4M9VzUajWapxLPCDq2fzdLpbXlPg8SUcWRELagtkZgaMDk3U86SnQ/igGtlmxos3J9P8XU7utVfQpLq2JxHsbNp0cNV6/RuXXFuyw5hWKsH3teLePhA91EeahFe+kUeAbNbM22L8py1oqyNtPpPf3bnycnOVJ6RBXIu7rMn8Lz2YFsUvSjLmu4NJ+CXlEcZ9whYZkrkfd3hL724zyhAmXCblLOZCqvyceZ62nw9kUP58ChmJs26OHGr6CEPdR5nU71elb+65I7z/wqJiafyfkKjEXt0nsYpfxDFymlrOJwro+DqAUwM13EyTyD3nB+TGzdmhkMQ527d4oBRRxYEPqQgJ54Ng2vQYVIwN+QCN0+aMrj/WmIzi1Qk9h7d5+0m5IAPy4bOUxwaKAIruj8S982mdXMLMuW68w++FciQj75jeXjVYfyDoLF89fFEotWPSklwc1QcUZ7l0Zfara3RccZged2+1r/FVwNYMteHizcc6aBBYkpQSkkOsWPZ6Cb8/OvcSof0acImcDFgMWZemVxy7iaRmCY0Oq8Frpz0Z/P8UczceJSHAhScWUbzJivJUA0U8k4uomnjlWRonAWnK05C6lpafNoZE3c/QiO9MZlqRtTTfPQ6ZfuzHwhkbx9Ew072Wsfci4d9bnJMqvY8OfH05Mi1Xfji3Rp0HzmXbSeUJ5brCv+zS/Rb3iee1n0kwB7jUeNxPnIfATmJ61tTq+MiPP3CCPUxxcgkWDFwr2SHKAb2lfWqXJb/KRLzG/8FDYYG6CCxUo6aNWfM5vITc+UkW3Vh8OpU5DzAsV8dzKKUjHHNrT+DVojhUHjOjE5d7BRzW4+O7GCf4nTQcnfi+wxdd45L6SdwmWVCRPm0C1D6eDtDWokkJsatPn+rxACGf/Ityw6qmaq8XniwdwxffzJZdZAbiBaEw1pvLly5QuY5Kzq815otihNJ1UmkCxUC4lHkm2at5Mg9geLbTtWQGJQW5XE9bScGtd6n5chtqE6018LwUZId81cc4qEgkCWRmBY2T7spyA1mVtdeOBzJJ+/EQi3SUpBYfRNSNEhMV5wTZ0yo988OeOYou7Uct760nRhGJS/c08R5Kc9lt/czq+80grM1zXwZsabjcdDhvxZP1g51tWWblxUGjT+m5q+mHL8vDoyrD38pBftNLy3mcuhcenVfz5n8Uo6ZNaJmGxtyBbGfvI1dn9osCSkgVvRIV/PR1Kvyx/9DJCYgWig/NFhF5b69rOwG+wLD8Bz8KZPdKibxc1x60mxUEEUiyfRvjvVZZavKde9Pv2XnVMdjZ2LZrT2bk+5y0Hev2r1XeU7s0flELmk0yiokVk3+GxMus6nbJ4yyuaY1PyC6GxLXtaJBXy/1oWyFSU6s3BjAnj172LNnF0u6/JcBq8V5AumjjYCMaJNO9JpqwYYNG1i/cgg1//EPWo5az864WzzODmb98o1EKzqWYvZO/YZ//O179QCmPC+h5BgLO3RilsUGRT7Lh/7EG3+rx+R12zipPnBQcieW4yX+KhY8xSVxR6GUpRw1bUTtzlvITl9Lu3pL1e7EvDgjmrSx1nL7P75QfZyL12zp8MFIQlUDxAd7RlOr0Rou6bniC4WJuJluJDZXQHNVorwkhhkNDdTl0cRPnEbI9p2N+R6l9SXIMvGa2IQJDrlkVRt+o1K/oZ2bPt2V3EniaOItRX8lyM+ysMH7jHHMJcO+M43UhkcRPqO/oJ95GvapFRWsS69uq8y1/yESA/njIyxs1ojlB7UXRBSk78D74B0SrDszxFxpYYlHfscua804+yylJVYNySg5SSDHayjtxpriG6xaaqVzYUeF2jwLiW1KLOVWtDHNf11BctE1Ym0ew84AACAASURBVP13smfPEbILjjO/ZSusjpabdjKOOtmjvgWu+w3np0arqxB2hQSv79XDa+mkpqSQkpLC6YApfP9//2L4ugSybj8my6UHb/z1HQw9xbosxHfMZ/zzbw2wPlNK4TkHBrXpxrowccVYHlfTUxV5pKScw2vKj7z11y44ns3krtpwlkhMU8sep62m6bsdcVeMImVEL65LnS4u3CiKZkaTsYSpRtc3dwynu1E8MmRcS0nllgzksurjFMvPY9a2N17iUB3I3TaAZkP9Ua7303y7/lyXybMIdd1BoqK7ELjo7aFe1SqSdetvZ3BErUNQVnKd1JQbiIPXBMuJbDhaMVeWF7+MRU5XOFNteO4rQmJyzqxpzk9tN5MlB3GAOKfOB4x1ukF+yhq6dHfglsISu49z/++ZvfMhwQ8rdEOXXv1PkpioxsW5wSwbNZT51kGcSk4kOtAFe8847gggyFJwXzQX6x1h7Pe1ZMnynWSVysmI3MSwet8xZHEgZ8+HsW5UPer1MCHkvGpFW8F+JjSfpjZxy4Qswj3XMKj2OzQdsgZXn3i1hSbKID4PtRtDwxr9sAq9qDP/Ycv2kF5YTIrfQibOmoGFSwrZJ6yZ2H8ilvtyKJXJKBYuErhuIi1qd8TMP105knl8Hj/zgdR4qyYjTT04quWu0J/G/HIlkZO+ZxXTR7Xgszf+Q8NeM9m09wpF92NZZTCIqcs2YLV6DA2/rMPY9crlvo/iTWj8xY8Y+4odivJTJlzGb9Ushrb8nDffqM3AGRaE58gpygzEdPZMhrb4L++835jhM2ez2iulPNlr+Svq/R47W3YfTiEp1p5R7fvhekZsQ3LSdxgz1zqezMxDWEyZR/BVAXlJLEYNGmMRK3ba1ccRrZPrkebMXrGPxPP7WW4wBb90DZeH3iFdwMEljfng3Q/44APx+x41fl3DRZVhkRdvzC8/zOVUBU+RF7eApvUWKkoiu7Ef8znr2Hs8nbTTB9iywYukAtAVrnfF1yFQcfY+7G12cSQlhXCHMXTrvRmxey0T7nLAfCobgpI4c2AF4yd5klkKMg3d0K1XAleP7cR+1WBq/7cDC+3ciUrTAFaHLH928HOsTtQW7fGddI7HneDS3aqFKs27xZ38CnNVO2V1dwKFBX+sF75Mnk92Yhyxx4NYMHw+8ffyOG1nR8Qf+9rqCvtahJUW3CIn+wb5+twfvpI1IeN62hFi4pK4UWleo/DWeY4dTeGOhhVSuYi64hTePMeR+CRuvOLtoawkl/PnriumKiqXvfy+TH6PjOMxxJ/JQXPBnq7w8nT6/lsmu0XykWiOJuZWmtMs4tq5eI4l5lb6f5hmiXTrlWYsfbx+YRLTx8I8m0zFRC6sy7/+8U/q9nBTTHg+WzoploSAhICEgISAviHwGpIYCMU5HAkOJ/Vu1YWm+lZBkjwSAhICEgISAroReC1JTDcc0hMJAQkBCQEJgVcJAYnEXqXakmSVEJAQkBCQENBC4DlI7MV2lhcKczkTFURAwD4On7uh44/S4n9gcjm4K1r9vy0tKSvdCI8ziQ7wxntnCKn38slIq7zXm0B2hD9HxXWl0ucPQKCUgrzCP/Q/dGWlBeQXPs/ioD+gmFKWEgISAnqPwDOTmPiHuBXW62jx3RvU77eJ+VZbmW/pxPip/anbahUe0ccY3fEbOhlF4xFzga0x6Vg7LqJNxxks9DmPZ0wiK8wm0qq7KWtCxOfaX6cdi6j5nzbM9E+v8kwzrtvBXQzub4Tp7jQ8wmOYM7s39TtuwlUjP49DkfSv/z6djOJUslS8S+9r5BUQUF4SQesvP+aLnxpRq2FL6jZpRd0mrekwcQdusWeYMrw9rfpNpu+46XTt1Z0eM/fhrlE/5fXpGXuGJYsm0GnwbPqNHE63MU5YRyjrytZ6BJ+8/z3f1W1K7cZi/uK3KxNskl8BhP4oEeWYTG/HV7Xa0KxrH1p2m8PyAGV7cfDcyPBZthitXsvwKXZYRVbovBLvNJ1pbT3W0m/MciYtXszASU5sjqqcVr/unbYvpM7X9ajfvh/N2w9kjOURRTt33R/KglUr6Ni0NWMdk3X2Iy4H/BlvaIKhuRUGk9eyJuwCnjHnMV2+gLErtjBj0QLGrIioVmfLdVffft0PhdC/eQ1qNu1Bi8496TrOE7tDFxT98tKVlkwzt2bUmKH0mLoTpyptMR2b7d5MNR5Lg0YzWaNqg2IfvtF+PSNnmTBswgja9lml7rv/KA1/kXyfmcTEzD1jjzG06Vt0nXtarSCeMUnMm7NKcW+/cz8b9ysb1ZYgO379pj0zdlSQkmdMKiZTf6V+D9dKQKZhvmgmDeu9T4eZMVWIR1NhbCwG03rUXrWCuUVspc8wWy0Sc/JdS892jfm2gSk2h7Qb4IuAJKXRRqDkrgt13v2Uz7//ia9+qM2X33zKe+93Zub2ZDxjjzPs1zf461/+zr/fq0HzQVasD9WuA2V9pmO5qh+fvTuAxSEX8DgUQd+6b9Fs1G6FLq2b3553PvyeL2vUVrzj04/e5Ifmq9kYcUFbmNfqrhSTOVMZYGzOJFNvrFSDQY9DYQxs2Ze5e0Wc01m7oBdd1YNJJfaKtldNWrcIL9p/15YZu9IR4ywZ344+JifU7Vuz7enLteM2M/oamDN2wWZMvBPUsroGx2Lpf4hhzWsx0rZ6EhM7+0GtOjFlaxrukQH0btKLWTvScPJeTPMOG3CMETv+eEZ27KrVd+lL2XXJ4R4VzEiD6YwxXsPsTZFsURGVk/c86nw2gmWhF3ANs6T+v5tg6JumxkyZXzr2uyKw9FtBva8nskpFYm6R2+nbx5wN4SLJJzKt7zc0HroLtxj9aoO/icRsHXxZG3EBO9dA3ENjWGG7nRXeiWyNSWPFjGZ812Q1DpVY38lvCTXfaKVlcbkfCmXinC2YzW/HN78sY3Ml4tGsODvbMXz9zXAW+CYpKkIcQa1yCVGTmuLdC1dg7LOSOu80Z5oGiYr5SJ/fjkDBmVX0mxapGGx4RB9mUv+eGGxWDmxEEhvVfThLQlKfOBgRrbCx7d7hvz/OZ2OU2EiSmdbrUz76fJKiM104YRTTvJWNzTnQlo7tZrBiv7JD/u0leFVzKMVs4SpMw7UHBc6By/nle0PWqKwvW5vR/NRMu+2JBFVdWgePGXz/3iCWHVTmab28Kz+0tFJ3gpptT1+uHbdbMG1D9SQlDrSfRGK2m0dRq7E5tgfP4BhaPsBOZ7VRK5oOC1D0I6Iuzh5Uk67Gxyt19tq46wseohzuUSFMmrtVQTCacnkcOs4aFyWpOfnOp9bHg1lajSdMTOMSZK5FYq5hW2j/cy9mKUgvjRXTm/B98/UK3dCnFvRCJNZ4iD3G66zo1X4CS1TK73oghCmD6tF6fBgeMeeZ2OUDfulVFVS3CAeavPspA9dUKKHj9jUYrk3Aec8a6r7TDEOfyiOFCuXxOBTFuB4/8K+/v8nH37egg8FmrZG+WJkTjNxwjj7CiJYf0tZQ2dmWV6w+gf8qy6LEM40V87rSceIB9SBCJLER7X6h9chZ9B8/jpbtRjPd+XQVQhOthz4//4MvflmmsJYVHcfAr3jrnz21Og7RQhvethOT3FPV4a8ybr9N9lJMZk9hmJkzRmvWMmCUucK94+AylR9/NMJS5Qa0d57EDzWNsNRwKSossWrSOu9ewc/v92FRmNjG0lk7rzWf15ynGFiUtxl9+3Xctpx+Y20wWuvEuPFTmep0Vq0bTyaxNEwMG/FVw8lMMnXHePkMeoxxwfZQKgtG16HFmGCFnoq6OH/kT4r76tzg+oaHKI9oiQ0fMZsZa7YwY/FsBs8JVA9ERMtyibkpXTsPZJLdqSptsbw8lUmsPFz89Yg+ikHbr+gwS9mf/jY9/n1TvxCJtZmyn3Ve/owZMF1NYmJBrZd2U5PY5G4fUrenZ5WRgVuEvYLEBq1VkpjSzTGf2V6xbNwVRP8m79FmUoROoMX3iEpmvXUrk2YZ0qjm+9Tp6qx2Tzp6mmGw9CBWu2JYMr0VX9ZegJWGj//3he/1zU2shy37bGj6SStmaMxjivW53jkQO4V1lcjk7h/y3ntVR3+iW6fnT9okZjToa978ezd1hyR2quvNe1OjgRm2Gtb564s6uIacUbnO01g2qSktxuzD1kmbtEQSq/H9dNZpkJhYX9WldYs9i9GYzvRbHovrwTAMhzTgyx/1m8Q8I87ipCDdCzhuN+LnWkbqsj6JxETdXDy2Nl+VD5xijzO85fcMXHeKeSNqqUmrnMSajayYthDx0+evKLPTAaV3Smxbveo1ZLxbxcBPfL52/TgaNl3Aqme0xCrKm876dWNo2dsOG1Vfqk9t8IVIrHxOzMEjiI0aDaWCxNJZNbc139SrOifl5D2XGu90xXi30pR3j9qLwdjFzFzlwKxVDkwa15KvntCIHJ18WKXhTtmyz4qm3wxhmcJvm8oy45kYmCvzmmE+g1rvNmTS1grLTp/Af5VlEQnGYn47Pv5wJGYa9eHsa0mHduOY55umGGwYD/uO//trTQwcKxqU2Dg8oo8wpPG/+G+tBVir3InT+37Ge+8MU3cW4ghyWPO3+aX3Nq3B0KuM22+RXZCdZ/KUDaxVeT/Wz2/Lt40tsPaeS+3vpqrdiXZ2Y6j5yxI2aRC/e8SBatOKk/yeMQlYOLhjvHYXy+d34KcOdrjoaactdsbiAozZrucVeuKyfyU//7M9c1Su5ieRmKizq41a8GN7O4U+iVMRE7t8RMtxwSyb0phmwwPV7kSjwTVoZ3joiYPpik7+5ROcg6cVBkuClOWKPcGwX99WrF1wCdqHidsxRTnEvrbrD2/TyUi5EKay/LosMXHhzyBD5UIR1/BkRV6/RY9/77S/icQqg1BBYhdwC95Gh5qNGOt6nvX2tkxb6ciKHQnMN2hE81HKyUExvYOLOVM2KhVSvHcNtqbh+/WZ4FlBPJrv2Ww6mZFW4rybUnHconzo2sKQ1REXcI8MZNzs7eoOzzP2FOM6fkLLsSFqZfy9AXxd8xM7gCk9P+KTr6ezVmMgY2tjwH8/bMtU91TERT+iJfaff3dl7t50HD1MaVyvNSPXn1TMey2dVJ+PPx3HyogLeMaeYFSrt6nZZlNF3UY48+sH/6TpCO0R8euKeWneTjq3WsxqxUKZNEwmN6bRoB1sifSiw48DWKxaQGNl0pMmg3bgpvBYhGAXIVph1tWmdY2OZ9a0+SzyS8Mz9jSTezVgoEWFe668nenLrziXOmFgVyY5KvsAJy9jfvpqgkKHRBmrIzGPyHgsth5R6JXD1jnUb2qmWLkn9g+jWn9JH9Mz2NqMoUEnewV5K+ZrO9ZlpE3FlIe+lF+XHFZmo+lo6K+QX1xj0LtuTUbYnGPZpPp8/csyhTfKPXIXnb59h85zRBITvVlK3SjPszoSc97pyeTlAQpPl0d0DIYLXRT9qz61wWcmMXGn47mLjGn09ZvUbGfMxGU7tFw8W3ZuZUi32tRoYcRS1eoXB99NdOsxmm5j17AmcBeDenWhy5gtbI5Mxi0ijVVr59H85xo0G2ynmtxPwnT1LOp98SY1Ws1k2qaqKxU3mwyiQY9ZGCzZxsptgUybOoaR5nE4+TjTv1dDvq8/gfnblARo72lPz7Zf897nXRi6wFthCusT+K+yLAqC6vYBX9VdrrUQx+NQNJNH9qfz6EUMHdePGt+1YOgqZT3a2k3n64++pc9yZYfiFurPwLYNadp/KQZTB1K77nDmasyHuoaL86f/of308r9tKAcurzJuv032ApatWMFsmxBWbDalQ4fpmASJHo00VpmOpdf0Haz33c7A3hNYsCsdtyhvOv5Qh5GbkxWry6pL6xEdi0H37oyzD2PREkN6G3rjqGHBlXdw+vRr52LDBFMfVm31Yljf3ozacEIxSHXZ7ceUeUY0+PpTGg8yYeq6MAVx2dlNpMZ3kxTXntEnmDVuOCNX78N03Uza9Vyr8CZ5HDrEhMEjmGgbxerNC+gy2K7KymZ9wqCyLG5hu5k2ZzPLtgZjbDycjqM8FfXovNOZ0bNsWL41hLnG/ajffBkWYRe0dEO0UDc4bGLUxG58/sGv9JptweJtybiFedP5x/f4z9vvKr9vvUWrcUqD4Lfp8e+b+plJTHxtZeCe7T4NB/8gltrsYurI3gy3OomNpwmTVyr9t8+WR4W57rbvuKJynAL3scR6B+t2V1hxz5LX7wvf65ubiLVH2EmNFV4VdSQ+cws9hk1QktoC1l036WzZH4f17gT14hDNuM77TuJSqVN9fVFH4aLd7OXHMqconDTmekXMnPccYLl9CHYa7l1NLEVXXHVpPSKPYG7jwxr/xGeoL+161sz/z7zesi+MZTYBWO+v3mPzJFlEL4KVxw5M3OO0/pojhlt7+mG69Zjam/OkfPTtmUfkCVbb+7Byh/ZCKo/IY6xy8FKU9/dyE+tTG/wTSKxC6W0dJvLJP/6PN9/swOyd5ctbK57/GUqhT+BLskgISAhICEgI/DYE/lQSE0c66x1dWOJ28qWN+H4bXFJqCQEJAQkBCQF9QuC5SEyfBJdkkRCQEJAQkBCQEJBITNIBCQEJAQkBCYFXFgGJxF7ZqpMElxCQEJAQkBB4DhJ78lEspfnZnDt7mtOnVd+zSVy8nq8+rqPq8wRSL99FJp2WImlhNQhIR7FUA0qloNLS0kohyltd4dVGrhT4W9JWyurPuS0tpXoUnvf1MoqLpc7oeVHTh/jPTGLiUSyJUVsZW/dNOs7YSXhkJOGhQXhajqX9ABdKH1zk8G5jGv6rGSv2xRF/+BB73Jcxsv9s9l6QKZ/vMabhGy1YFRxHfHwsB7xNGdRpFNsSCvUBC0mGZ0SgjIfsnN+H0TMWYWq2HONJQ5hlk6TqTIp5dPMyx/ZaM63/QOyOynTmKuZz1GM+0+eYY77IkFmmQeSooj+Mms4Pn/1Es1bt6NC5K127dqVbt0HYH9adn84X/Q8+kN3cxfhBllyudOSarnARgjLhPvFbLXHw9sfbfi2+x7Xb3ZPS6iOEZWXX2T5uKM7pShCE4lTCfHbiH+CHh91G/E/mVSt2yYMTuFva4ePvi72FB4mFUHLXjf41G6l1rWvXboxaGsyDV4TXBCGVNT1/pkX3oYweNYzpK8O5rZC9iLM+G3D0P0Cg53psdl2shvQFrh7ZxmZbO6xXTWO8oQtJCujknLXqxS9NuzNs9ChGjltN7HX9A+SZSUzRCLjF5m4fMMHlgYZyFBLu6KK4Ly3YSs9/92V3fvljGTFL6lGvrxf3AfF5r7cGsFfdduSctWhO7U7OSOdXlmOm/79l3Ma2x9v87a//xzuf1GGgkQ/pBUq5BXki28xXs9GsH1/835csDSvWUSCBa7vH8P0nYwl/BIJwkZWt3qfnstOK+Dmuvfn0i5/4pX4DGjRoQK0v3qFBb1cyJQ6jrOwWIVb9aNJ8rRaJ6QpXVoCcszb9GGeZhhwZcWu6M82p4jDZJ6fVUYUvNVjgapQVvWq3xlFBYgIZLqvxzVZ2soI8gVVG9lyr1OeKnb1l3/64psoRSk5i2m04vlly8o87s84ljEPRMcTEROO7cg5bTqg7qpda0md5uSBPxnbpcpyc3AiMvoSqOfL4gjVDRij7X5H07YcPxjtLGxS57BCmhs5cUjTVAvZMrUWXeccooZQzjsswc3LCc2cMWep+/Vkk+vPi/CYSu38sjmQZ3E84pZC4KomVcsysMXW6uipGBVVJrIDg2bVpNSFUDfqfV3TpTS+KgEhijuOmEpFfinZzqMgx79h8fngCiYlWmOfgD/m2yTpVR1zM7slf8/kPC8XhDvGrZuJxUTnCluUGMH2oKSfEkdBr/xHIjfEiMGYj3VtpkpiucCVgYkdlWKsH3teLePigoFK9PTmtPkJecican4BoLDq3V5GYnJRNPekwditpeVD6IAjT5ft5VEn4RzEzadbFiVtFD3lYUKG9suxMrqn8ksVXgnD2SqakUlp9vhXkKXhsiUR7jCdw0b4bPRacVHtJQo1+YYLzba2iyAv2M73lcHZkiu1NTrJVW+r23kY+pSS6u3BE1zhUK5eXd/MCJPYe3eftJuSAD8uGziO8qEJ4BYn9pytuGVe4kpPJmQhbxvcZo3YXKp6/1ZoVu/ezL8gPm0XDGTFzOxnlw4aKrKQrPUZAJDH7wc0Ys2QF5ubGjB48C59TD7Q6xqeRmCCkY97i39RoY0Ouoi8pJnT2D7z3xjCtkosWms2g/jid+31mPrQyfwVvSu7FsiMwjfyrm7VITFd4eREfX1hHi087Y+LuR2ikNyZTzYhSmSlPS1ueh778lgl3OeS9mwuyHNarSQzkD+Mw6/IVn3zfiXHGziRqOowUwstJXN+aWh0X4ekXRqiPKUYmwdys4DLKym7gZ+lMqjYb6EvRdcohWmKbF5vjv+8Au7eaY+5wSkFCccsb0880WbU2QUbM4gb0NU2uxqWozLqs7CZbBtbAwOYSctESs1vC2l372L/Pk5VLnFRuRp1ivJQHL0Bi7zN03TkupZ/AZZYJEZVIrMd/mmCyJ5qY2MOcPHeNfA2ffWVLTCjJYce0jgwzj6d67/VLwUR66VMRKOXSqVPcUtRtATvHfc7Hn0wkUmPY+3QSS8GkqSaJyQgzqsl7/zdY4+0CVwJG0bCjg1ZHoxHhtbosE+4R4x3IRRkUa5CYrnBNcAqTTKn3zw545ih77By3vrSdGEaBjjw10+rXtcDNeB8OpMgoK9MmseLcCFxtt+NpPY4GH31C32WxlSwx0TPUiJqqgZM4GLPrU5slIRWj6LyTpsxYdV5nJ69fWGhKIyPvodL9KQgpmLRphU1iIdGLNElLSWK9liToKJ/AleA5DJ8SwDVVv12c95DHitfIObOmHf3Nyue+Nd/9cq9fgMQq5sQenU/kksYAWWmJdcJLx+RfZRITi16YbE6jNzrjVdl5/XJxkd7+BASKLnkxbbAx+xSrCmREzq/Nv/5el/XHK5ThaSQmjnitO7/Fd80syVY0mGL2Tv2Gjz+eon6z6MO37fkhraZEVXKTqKO8VhePjqzDyMQFT09PXDePpv6Pw9nsFUtKbPXh5R2RCFLxdTs6fDCSUNWg88Ge0dRqtIYzsWurzVMzrT6BLJfFsWa6Ka6ennh4bGLEL7UZb+nNkcu5eM6aR4TK+iq47M/Yhn3x0eqLRPdaZxoNDUDpISvCZ/QX9DMX5wnFj4ww46bM8dMYmetT4Z8gS36yL5u2JSrKVcZdbHuI/fRdEte1ocfCUyrSknFwzs8M35Cl5TUpz/ZB0lZWWoYr1ifIiosplZ3Ha50XKSo4rrr2pE4390oDg/LUL+/3N5FYZbFfhMRu7J9AjU/HqZWvcp7Svf4h8Ch2Fj990QNPhYuvUGGJvfv2IPZorM6pjsQKzzkwqE031oXdU8x7nbJozhffGXNSBmLDc+77AQ0G7VQXWC7bh8Fnb9B98VkdI0d11NfjQi6jqLCQwsJC7p5YQotfV5FWIEOuKxwZ11JSuSUDQX4es7a98VL6bsndNoBmQ/15pDOtvkIqR1akxCA//xjzGrXGNqmA4pI0LMYvJ0HtBpQRsWwmO64JlJVcJzXlhqJAhSlr6NLdQdFRl3Ef5/7fM3vnQ8Uz0cW9otWPmETo+SRQNVVz02824zecQLTFBCENs9a/sPqwjEexRnQetVsRLs5DewxrqggXCbtcN8TsirLD8fA9qSAo0cL1dg+mIG8HswZYkaAwVOUkrG1DZ6N4vZsrfGYSE49iCfdcw6Da79B0yBpcfeK1XDzFV2Px2GBAnbdqMWaVO3GKScIKtIuvxOBiMZSa/6zL5I1OODk5YGUxjxF9R+MQfbvakUFFaulKnxAoE7LZsWQs05dvZMOK0bT8pRPmu3MUo9ky4RI7zGczbVRzPv33h7QcPJO5S3y4UAqP4k1o/MWPGPsqOxR5/gnWDmrFgJmbcLQcR4fWUwm6pPJjiFPMRXsY/vHbjNiULemHhgIUZ4Zjt7Ar333Xl9XuMRWun0rhOUWxGDVojEWs2LMLXI80Z/aKfSSe389ygyn4pVdYzrry1HitXl2KehbivICOX9VguIkHR7JKyD6wioVrAziVnsHxYBdstp9SdN55cQtoWk9cMCT+zeAuB8ynsiEoiTMHVjB+kieZKhjkJYeZUasOq17Bv3HIC0/j5+BHfEoy4Q7TmLg0gvuCWN4sts2Zxpa4y6RFWzLDOIAbAshLKnRDXhiLcaOPePeDD/hA/L73LgPNU5FTwHF/RwJjU0iMsWfqcBMO39GYQNQTjXhmEtMTeSUx9AiB0oJb3Lhb+BsJRqDoQS5Xb4proap+Cu7do1D/2k1VQV+RkMKb5zgSn8SNV2f1+HMhW3L/IsdiDpOUnf8EvSzi2rl4jiXmquZ7yl8h4+q589yqThHLo+jxb5nsLqlHD3Mm84F6kwmluEXknj/KieRbL+CWl3Er4yjxJy7zqGJ8qVcoSCSmV9UhCSMhICEgISAh8DwISCT2PGhJcSUEJAQkBCQE9AoBicT0qjokYSQEJAQkBCQEngcBicSeBy0proSAhICEgISAXiHwzCRWJjwkJy2NjIwMMjLSuHAtj5IHOaSmifcZZKRlcOOF99aScyP9CFFhx7kiE7eMucSx6HCOX3jhDPUKZEkYCYHfCwHx/zuv+zqXMlnx73T6RSk6DgL4varrT8zn9d2F/zlI7B4p8TuZXP9NOs3w58SF+5TcSyPSazI//bMppnuOkfXCWz7LuZrggUGdHvjchJJ7KfjNac6glenIEcjxGU7jbk5VNvP8EzVEelUlBO4k+bDWfA2rVixh9rQF7DhTvudKAQl+5syZNZ2xA3sycJw5B1KqH4wIhcl4m89h5vTxDOjZh6lmgZSPW560w30lUV6bW+V2X7Vo0r6zYld/cWf/Xn2XE3W3lNwTgXj5+ePjYoLhRGtOVdlnUtdO5brC9RfWK+5Dr/nzEgAAIABJREFU+Klhezp3LT/doD/r9t9Dnp+Iq4kp9ts92Wi6loNZT1pOJ5DlP45JazNUfw25R3LsbmyMuzNs+Um9+y/U02rjSbvwV7drv3Z+unarF7idHsMupzn07GxGovo/eNqpX/bdM5OYKGhZNbvYF1+zpf2/NXemf7EilQmZrOnYR0FiYg53vIYwWEFiIBRcJvmC9t58L/YWKdXvgYBQcoS57SYRqVqmXZi2mrb1lyP+9znv6AJqvduQ9bFFCMXnMG/7Lh99NoHwKp2qjMiFP/PdL0s5WQSPktfR8u1/8OuYIMV/mp60w/3vUYZXMQ+h5BiOK105eCiamJgYIgPMWbzxFAXiPpQda7E0SPxXah6uAz5iyLrLWsusde1UXqRzB3N9RUjGYedVeIYdIjomhugYf1YYWZNYWESocQPG2F5TWKqF580ZOHKX4vSM6koiux2JRb+fGbSqnMQecuXiNdKce9FlztFXjsR07cKva9d+bUx07VYvcP/KRXJyHOlZZz5nXh8Sy+Pi8QhCw4+RXT44R8bVhHCCgsJJuaH9JwyhIJezh2NJzE7WQWL5ZCXEEXP0EuI/ku5ePsnhqJPkFN4nNS6c2IQbWv99eJB1nIiQcE5dUr9cu76ku9+MQGmBF33efptG/cwJTcvnUaIp7dtYIv6/vTDZjn5NO2EVLTKcuFdbXd74WwOsz2jXu7hT/Sn7gbRqb4Z4rJVQcpw5df7FVz+b8uQd7n+z+K9sBuKfzLOylDiWCTnssfMhQ9GxyLmZcpbsfNWRNm1qsHC/tvWra6fyhzp2MK/YTVDf4JKTnZmt+k+hnIv77Ak8L0M8Amhxk69ZGKTcI6n0sTd9PxxEkMZ+nuUlEc9GjPQK5KB1F4aoSKz8WfaWV5PEdO3Cr2vX/vLyKn+fvFt9yR3n/zUSe5fWYzfh5CTuuuGEneUIav9TaYmJe935GY/GKvoexXeCmTvMnDNFAlcDp2O4MZnH90+ycshgPJOVDfFRkiMzZzhy/No9Lh62Z0AtpTtRBFZtiQn3Sdw3m9bNLciUC+Se82Ny48bMcAji3K1bHDDqyIJAcdsYgawgY6aYxnJffps980ewKf7V2wNNW7n0807c9SBoXiPe+ttf+Psbn/HdD91xPV91qx5xB3qL9u/yVZ0lnH7Kn2sfZ1rT6s3/0HXhEZ51h3v9ROfPkEogO3AD27X8OwJXTvqzef4oZm48ysMnTJxp71ReIa+u8IoY+nUluxXIJsckhdVUVpbLpu5fM2eXUtGK7zjT6d/NVce0aMotcOOwN8GpxaTbdP2fIbHyEmrvwv/0XfuV6Z68W/3/IIlVbAAsAqDpTsw7vpBmzS24UCxOmBawf1Z7TA4Wc+e4B877cygpvkv4omZMcLiL6D7c0LU5lqpNY8Wdl01b9arWnVj6eDtDWokkJro0H+DYrw5mUcpO85pbfwatSKWk5Chz6rXBIaUY8Yj1e1Gz6GMYUV630u/viIAgz8TbqAd9xkymZ+13+dtf/8b3jc04ozV8L+KUbU/q/zqXsOzKVpi2MKLb0bJHbXoa7VWc6aTYhfupO9xr5/E63YlW6/KxFmjsGqUufkFuMLO69sLhiLYlpo5A1Z3Klc90hVek1K8rGbGm43HQOKLnTuxyhk/aSa68kHP7jWn2bosqJFZyP44dAamUIPxPkpj2LvxP37W/vE6ftFv9a0Vid3cO4ftfF3MgMpJI8RsVx4U7Ag9TfFg2fzWeuyPwNW7GGJsblJSEMeG7DripThlVkFjrZySx/s2xPqvsGHPd+9Nv2TmKi3bQ792WmO9XvTsyiqMpd8vrSPr9HRG4HTCKn1tbc0UOgiyHA2v78s0b32psnCojadtkRszcSWaRnOx9rhxQ7HhfVQihJJ0tkwey1DeDIuE6e1z8FWc6PW2H+6o5vT4heYfn0HF4+U7sILrHEuOSuKNYx1DKUdNG1O68RXU8vTYulXcqLzfYdIVrp9afO3lJDDMaGqh35S+X7OHlo4TtD+ZoyhZ6fWZAmNbAqpiodTNZ6eKJp6cHlmPq0WzoBvxiKk64flXdicryV96F/2m79itTCU/Zrf61IrGiTBt6tDBB5S2k5H4iCRkXWd+pEWviROd9KfEmTTHYnMLh6EicBrZQ7ags7rJ9jqXNe+KjOqFO7U4UU1W2xKohsRLhIpbd2mB1unzO4B6JZy+V67b0+zsicMWzL3U6VJzxpbCcWjZTzXuVkuJjQKOGAzBavJjFi+bQr9Eg/O6A9i72Yp1fws2gIS36z2bx4sUsMB5Au35uz7TD/e9YnFcsKzmJ61rT1rDieJrHaatp+m5H3C+ILCYjenFd6nRx4bbw9J3KRX9GdTuYV3UO6xdMjy+spfW3MzROHRbIDrPCZu8VxYKWG/sm0n1iiOLEeM1d7OWyIsUpAIWFeUQvbcKAFecolFWsYnyVSay6Xfh179pfoRuleTufuFv9/wyJae5i/+uwdeyIzqHwciTOKwfxw5v1mLzBi+NXH5Pks4DZJlsJiw5ll2cw6UX32b+4F5PXh3M4fBfulqPpM2o57vtyeJy5iyWz17LncDwHtpvR/bsajFoZQnF2JBvHNKRBb1OCk9IJtRtDwxr9sAq9SEbkJobV+44hiwM5ez6MdaPqUa+HCSHnCylI92H+NFO8QqMJ3rWViPPSnNgf0fXIC06wYWRH+k5YhuXG1SwwHMkMi1jEf1jkHV3MT2/8lb/85S/q7ydfzuBoceVd7GVELqnHv/5aEe8vf/k7rQ0PKUR+2g73f0S5Xo08ZYTPrUPH2RVHYohtc4+dLbsPp5AUa8+o9v1wPVP4TDuVy3TuYK7faPx/e+cBFtWx9vHv5t7kJvfm3pie3PRqYmJiEjX2qLGbaOy9947GLmJv2AURKaJUEaxYsSCgYFeUqtIUaSIgdZfds7/vOWcLC+wiWCIkZ59nnz17zpw57/znnfnPvDPnfXNOTab2p1M4b9gxJ3B96xCGLj9NxJltzBq/klCdx3VjL/baUqmJOroJizbvS32Me0ACKk0KQd72zO3xJTV/msxGp0PcNORdtbHQS2fKC785r/3GXuwx661e4HbodmwXdefLt1sw3caZ41FVD5RKbbHXg/WgX406h7ulvJurCzNIvacd3ynzC422/6rISk3hvlJJZmoKmXnlr5886Nmg5n56BgV6O8mDb5BTPCQCqvwMbsUnkf3Ehu3le7h/SLGr/W0FyVeJvFO6nShJjjrNyeAwUkqY0Kp9cU0WQFN0h2tXk0tFPlCSFB7E6UtJf9HIB+a88Jvz2m8MbdX3Vm8srfHxEyEx4wfIxzICMgIyAjICMgJPCgGZxJ4UsnK+MgIyAjICMgJPHAGZxJ44xPIDZARkBGQEZASeFAIyiT0pZOV8ZQRkBGQEZASeOAIVJjGNkEVs2CUuXLpMvPaFFEm4+0lRREZFExURRVJqCrE3dF7tJW/3+uMb3L6nRCPkEHf1EhcuXJC+ly5dIy41z2iTxxMvr/wAGQEZgT8pApX3bv9n8mL/J63UChSrEiR2j4iQXVi0epsv6y8mXNqRJnA3+ihr+rdjlM1RIq6HcXjbCGo99x0zvPylF56P7N/B+qmt6TQxCKWQSXToTsbV/Q/tpuwjMPA4u7YuYGi3YTiHZlVAXDlJVUPAtIfsQi55rMTOZz87XVawfseNUrvItKXIjfLHY7sPPt5bsVnpRpjk7rL6eVX/Y+pEzZ2zTkwbNZZplnOYOdfLsAX87sUtTBk9mQWLZjB65HwOm9kbrhGSOWYziZHj5zDHYhgWS/xJ0b0iJfqrPO+5ggVLFjNv1kQm/L6Fa1XU/WhB9Cra1qpPh54D6NdrCGsPp0hOf815tzddPyW92IuOk8+5WLFgvSvOq61Yt7v4BWjT91ets6Kj3yUdvqJhu54M6NeLsQv9pZfdzZ0vKb05L/agETI5tdWaje4+uNsuw1N0dFrFPhUmMVFuMRSEp7MNq379iE5zz6PfWZ3p68ZBXdnEF+NavdCRHUZeyzWaeBw3+EovHorKYt/pVUY4F+8DTtramQ8+m86lqvcKQhWrrqoljjkP2QXX19Cjj5vkQVz0p2nbuzvuOs8s+hJoNAnYL3Tgju5ViPywJcxen0BRtfOqri/Rk/wVSNw3nC/e6YpPvJr8yKXU/ZfWQ4oycye933uXKTtFxlFzdWUTPqxtxdUybUnFBeumvPZCZ3zTRIfLoVh8/Qo9raMkwXNCZ/DL4H0USP9UXFzShA4zLzzJQj103vmRdljO2YS9kzenb+jda5n2bh9mps8t7cU+O9CChq03kiKIL+GHYdluIPvvPrSIf/iNgjqcDbMt2bTJiZ0BN3V9rVgW0+dLCmjOi72aS+s7M9g6CjVKgpe0Y8ymqkfulSYxr617yEpw5pcP6rE6RKsh5kmskBN+/uSi4vqFyzqnpKVJTCDB6Rc+/mYeEaVffSmJtPyviiFg2kO26OqmLe2nndPNvhQcmlSbofbpJaQXfWcua1+XSVuuSg0ueZ8l1nuzMOdtvXjIUyKbv8QfyZtN3X/xbbcdiC1OUERzwG2/FHstdXsPXnmmIZuitVOq7MPD+d/fv2RZSEkWEx3krvr537zx7gRCFeKANJONHV/kgzqLJQzT3Lvx339/Tv95e7iZW0jIvCb0XBZdJfEtiNyCq79+CK0X0bR3e/1V49+yXuxVnJlfjx8H7tcNzPNw6vo/xrhW0amocWF0x4I6gi2bj5WI6CFeMne+ZBamvdiL4XtG1WyPe3Ih2Vl5VTYY60ORWAFqol27Uevb2ZLT19Ik9vMLdRhr48CGZSNo388Z0cd88UcksZfpNOcwJwOOscd9HdPGz2JPhJkhU/GN8lGVQsCch2wVwZY/0NkqXLfWKYZjqUMnq/AyJsW000tp8c7L1GrZj9l25yjdZVQ3r+pPqnpUWU60eeEffPbTaKzmL2LW0A70tPAhQSXWQSNe/FsLXBK1U9r7gRP44B81GO9Vuj3l4NrnDV5/cxTBCjFkSwwLGr3Aq68Ok8RW3jvM+O9e4pn/+xsvvvUeP7RaS0xpnnhSBaxkvvmRNsyZv4N9+/fitGA2286V1Bxj7/ZlszblxV4g1r499XrtlGaiGk0a69rW4LdSYVrK5lV1zogzrnUzF+Czb7+0RLNg43lpcGjufEnJTXuxL7i+nIZvtmKuszeHjrkzd/Q8jidVPS8SD0liotPRWzj1+IS2U4O5vcO0OVEoCGPlQjeJxDQqBVoXZdqZ2GC7FLKzUrnkO4Oe3a25UFIPS2Is/6uCCJjzkJ1JwAxj0tKSWMdZl0uRmJr4Y07YurqyfMgPvPlaY1YGGK+LVjev6k+uihTJNjT95zN81sxWMncpbq+j8T9r0G/DDS4uqV+CxHKCJvLhP15krHtpEoO8KHs6ffotk1yDOeq1jF9rPstrrw+XBC+I82Fcuw4MHNmJz//7DM88U4NfLEOeXKEeIWeNMof7uuKJfiNb1F9IlMGKU9a7vfGjzHmxV+cGMLPzKPYlqrkfs5dxDV+lSzUiMdFnZk62FhTJl2nTxqy/LIJi7rwxKmDKi/39MCu+ea54gJTo1Ilmww5L1oCSdz/dfw9NYqLYimQvun38LROn2nNAp1Ql18TU3IpLkOL9ZB705FCayOIlzYnietmy5q8zwvne00VCfnolETDjIXthOBeXN6X99PM60lJyxOIreq+ML2GOUKZ7MNlir272pSDSdxhNO9gbPK9XN6/qlQSvUsnVhTvoUuM5Ggw9LJmLVDkutHv+79QbuJe4Ta158W+N2XxDZ070H8U7/3iHWQdMTaOUZCbf4GLgMU5HXWN5i3/zUX1rqU269X2PXiu10aDzbvkzv9NHvPW/sZWS849JrOCSpzU+l3XBLzMdaGO0Bm/Ou71WtvK92At5sZw67Mfh0HA2dBJjk1UfI3ZuuCdrt12RzKEaMtjQXhsyy9x547oy58U+PdmGFq/0NUQKyNo9gJrfL+Fmsb9k42ye2nHlSEyThofzbt3iryizQPyOfnz8fDf2mCQxbblE+/uuhdack8z0pUiMbDb9WoPmE4JRCjlkZpa05T81ZOQHPxABcx6y7wdOolW/XdKITdz1tqVXPV20gmLP2QUx1oybdcYQBl50XjpnjANiEIPq6FX9gWA9QgKNJgnbX16j9m+ekolIkbSeps+9QNelUYh1UPdfn7MwUNtubm/rzJsv98IvQ1wPucKans3oMdMfcY57x60rb/yzC7vuger+Tnq+/TqDNyVIG7bsfn2T/hvuGAYa4gyn2XdWjyD1k7lV1KdtE7tjH6olmIKYpTT74neDI+Cy3u2hIl7slSmHWLNsH3fUIjZ7GPzjKAJMRIV+MqV69FxTvScyZOVZ7ZqpEMW8JrWlNmfuvDhDS4qIJE0J5rzYK9TXmNfsF9x0u6/ubOtC/Z4+Zcz+jy79o+VQYRLTCHEcWD+O5t+1x8rplGHELEYS9RzzuzQTUyQcZ9OCrnz0bE0GL7WTIj9v3LCC6YMa8m1rO1JVtzjuZk2v2i/RqO9KtgeKYRPUXNrYgS/rz+fMVU92HNfvNnq0gsl3P3kEzHnIFr2qb7MYw+bgOKICrBk32Vcygxl7zhZ3Le5aNJk1PmeJjjnPXodV+JzLQ11Nvao/abTzrrsxuFkLhs9byew+jWjey5YwqankELS2G83aTWDd5hUM+KkZ0z1jpfVItTKI6fXfo9GA7VJ7zY/aRNemPZi7ahFjOrdhxPKT6By9c+/8enq1aM+o2dasXDyDkX2HYhNQNa0jWRd9cNgeRHhEAGvH9MH6SIaBfMt6t4eKeLHPT3JhSDdrQqNDcZ41gY0BxXk+6bp9HPmr8y/gvdGbUxHh+G8cw7DZR8kUwOz5okAm1fmBpdLgJ48zPnbsDIzgyklbRveeS5CkGALJxxYwcf4+rlzzw7L/SLxNRWJ9HAV4hDwqTGKP8IwK3CqQERtK8NmEKmdvrYDwf/Ek5jxkF3LnWghnw9PK7JgqBkxN+s0zBAZdIim36i0YF8tZdY4K7qWQkV/WnqNWZHHnVioPCgKhUeWSlpxuJsqDmryMWyQkZZZTZ1UDi8J70YQEnSMxqyQWpr3bV0zmgtRrBAddJjmveuqiRplBZEgQF2OzSjiQMHe+JCrmvdjnp17l9KkwUnTWtpL3Pf1/VYTEnj4QsgQyAjICMgIyAtUPAZnEql+dyRLLCMgIyAjICOgQkElMVgUZARkBGQEZgWqLgExi1bbqZMFlBGQEZARkBGQSk3VARkBGQEZARqDaIlCCxMQQKXZ2dvJXxkDWAVkHZB2QdeCp64DISQ/6lCCxAwcOMHr0aPkrYyDrgKwDsg7IOvDUdeDgwYMP4jBKkNgDU8sJZARkBGQEZARkBKoQAjKJVaHKkEWREZARkBGQEagcAjKJVQ4vObWMgIyAjICMQBVCQCaxKlQZsigyAjICMgIyApVD4KFJTJnsz57j6QbHm+JjVVk3CQ3w58z1XDRCJtfPBHAs+IYhVLaxaOr8DJJu3eKWiW/SnXtGnvKN7yp7XHQvmlPHD3M+VhuaoWyKypxREnXQgT2XH1dwM4GUEx74x5f071YZieS0MgIyAjICjwcBFSpD3LXHk2NVyOUhSUxN+Jrm1GplL4XO0Bek6F4E3hYN6LYwGpWQwZV9FjRpsJTYMn24wM2NPWg7Yg2u3r64zmvD22/+ygpvXzy3WjOwQR98M/W5lv9blHYFp+F16LcmqQShln+XqasC8fuWsm5f8iPmUzJvdeFhVi4PrvIOVUtK/eB/GiGdYKeZjB47Fcs501jmEa0Lq5LHZe8FWEwYy6CuHeg6eAH7I0xHJlDl7Mbil75MmGGFleXvDOsxBBcpkB9oFDkkx5/HZ+14uv22lMtyhB4QIz6s7kjteu3oNaAffQcvJjBZdFYrkB59kh2bLOjQah5XzGB1L8wFq7kb2LZ1NQuW7CVJ1y5zwj1Ytd4Hv91bWLnCl/gq3tEVRK+iba36dOg5gH69hrD2cIrUZovSz+Dr6s0Or83MGTEC+2DjIKt6nS7kksdK7Hz2s9NlBet33JDi3mmEe4QH7mL95Hb0sjxnCBGkv6uq/wpCJEs6fEXDdj0Z0K8XYxf6GyKNaGUXiPcZzPBlMSWcA+uv3Tm7EzdvHzwc5jJq2BrOS/2vOX2rWmg8FIkJwg0c5veg8Sut2ZZQ0uPzXbcedF8YLQGlKnClR2NTJKbivONmQnWTp+yA8Xz9+TQu6Rpfxv7N7Khw5DWBGJt2DHhEEitMdGWG5SEp7tLjrCIxAvaGqcuJqeIdQ2XKrNGk4j38C77vtJXbahUXljXkf/8bx2kF5IRMo+ZL37EisBBBcZUFzV7itbeG4m9iUFKU4UCrF/7OM//4F+9+2Q5Lt2uGKAb5YR4sXLyWWb+9x+tvjCLYVIzHygj9p0ir4qLdHOZt2oTL9pPEG8YGApm3bpCYaEeHWlO5aILE1IoAxtVuj+ttsb2qOLuwIxZeGQhCNMs79Wf3XREggTinvoywTXisA7nHDX1+pB2WczZh7+TN6Rt6ENRcWfEzTUful3Qozb0rnzVYxa1SA+iC62vo0ccNUR3FcEC2vbvjHi+gEbK5dSOJKPuOtLYIqX4kpg5nw2xLNm1yYmfAzTLWL2X6MZZ2/opuJqJVizqwoGVNZu8VY7Tl4NjlNXosFwOkmtO3x12jj5bfQ5FYYcwWnPbFsKHD2/RZU1LhK0piUWfOkaHjv9IkprxznksJBdy+7M/evf5EpBQzgJAfS4j/YY4HX+ZiRKLU8PQkphayuB1zk7i4RDIKoWxac2DlsH/mGDziSmp80f2bnAuN4b5a0DVqNelRF4i5K6ZTkx4dRbpBNDUZsWe5cD0btVBM7GK8NduRUwn9E3XC+eELqPNcDcZ7aQMT5sYcxMsvUmo4+eE2dK73M6sDxLgNSk7O/Jrnn6nDmosGoAyVUJThxMgR+8hTFeNluCgdKDk+7UuZxAygqLji7CANFgynjA7EqOrmSCz/6jzqvTmUE7qB412PbtTp7EFGsg1tv51FmK567gdOpGF7R6pmJDFtYQsit+DqX7ZBKe9GEHZTJDU1V62b0WjQgVKduRiNvC3tp53TRR1XcGhSbYbapxtQTNhcXUksgi2bj5m0+Gg06Rxz28mRNa3pYYLERLxSIy6RkAviBGVh00+Y7ifiWL6+GUB7ygcPQWIqLjvZcTJXIH5LJ75ssoZEo76/YiRWstSlSUwcEd7eOZZRq8IpyDzHwh7dcQlXSaNGmzm23CgE5b3DTJ3mUYLEVOpLzG7bkqk2h7ieHU3ZtCWfq/+nSHFixHBfigO5CqScWs74ad6Ex/gy8vtfcI4TyDjthlugM8N7OpAmJLGhU32WBCsRleTw4tEs3hFJmPcoGndYizTgFaVTX8aqn5Whk9A/szr/Jjn/wgvPvE27UXOZv8iSQR1+wcr7pq5jKC6Z2CCWNn+J92rN4oKJWERFGY50rduH2fMXMm/KAPqNcyJMjORn+MgkZoBCOlBx0WYWy3bsw2+fCwtnbSLMaPm2PBJTpGyizVv9OCzVg0DC5nZ8/MMyws7NoUHdhcTo2nDOuRnU+2FhlbYc5EfaMGf+Dvbt34vTgtlsO1cMQlHKWbZv/J2Bg9Zw4Z6xLokAqgi2/IHOVuE6k5o4yKpDJ6twg+5WXxILZ93MBfjs28+urQtYsPG8jsAFUoLcORCpIHp9GzMkJmIjcOucD+um9mP8qhCyJejK17eSuvn0/lWaxAT1VTYuc+f6rVvEXl1NixpN2Hy9mMUeD4nB3TNbsPdLpEiRgf+M+gzdmIGgPs+sls2ZuMqL4MhUbtyMN5BYn9Xn2bthLUdjtbYU02lNAS0Q69ifaduLG4I6z59RdXuz9y6olQcZ8dNUziuVXDwWwHX/8XQeewKlaHpxWIxbgkD6oVG06OUtkWDWodF0mXzaYI5Q5XgytK+rEUGakqE6nRNHsz/z3N9eoO96cR1SIHp9C176VxtcSyx+FnJ+Qwe+/XEKhxPKzsLEEmvUcVw4lyx1KOqCPfR585/8OMjPCAyZxIzAkA4VOdm6TU9qLi75id/mhRk64PJITDQTHbbswnTPWxQVRLNjckM+rbuMyyHTS5CWRGLfziXCdJWVFuep/Ncoc7ivGxQVRC2mRf2FRBnJq1EmsWtKW/otDS01E1MSMMOYtLQk1nHWZQOG1ZXERKtHTrYWFEGIYG7Txqy/rKIoMxgv30iKpHZaHolpqzLvzgEmtOnIxtNaM215+vZUKt/EQytNYvlhm1i4ypfdu3eze/cOZrV+my6LowyLhY+LxLIjPJgzdTEuu47iObk+A9drF29zYw+wcsoA2v7wOW3GiR2euCbWhsZ9VjKjS3MsD94z2PPLpjWBAPl4Du+Lh7RArr2edWA49X/dJq2P5V2Ywy/9d+nWahT4TWjB/GOiKUPBMXsHwlWF7Bhak1EuopVdRfDcVkzz1ZrZxNyy/CcxyvqGAR9TElS3cxk+vXnp7+8w64DWpJOytRPP/+0tpu3R7xBVErZtBH3Gbye2UE3CPkf2lzLViiaMcLdxDJjkzW01CEWnGF/zn7xTc5YRHDKJGYGBoLyG23I3InQw33bsQK22zoYBUvkkJuaUy/WQw+zfH8pFx07U672TjOvL+Omb2QZLQU7wJOo2XWOwJBg/v2ocK7jkaY3PZS0IqkwH2rzQkR2ZAilhwUSkagfUeVfm8u2/2uFp1K5FnbuyvCntp5/XkZaSIxZf0XtlvKHPqK4klhvuydptVxBbpIYMNrR/hSEOqRxfPp6FDi64uGzBeuA31O+5Eu+TCSX6I9GSdCU4DGmVBBUhVt/zZavNpBSWr29VQx+orNspJSGbbAnR91VAsndvvvh+MfrJ2OMgMY0Qx4qfv5dMdSIxnJpbj/7rIgg8asv6NZckBdRoElnbe5SOxNoz0jEbZaoPA5oO43CKgFq2V2+oAAAgAElEQVS530TasrCLo5bF/WZwzmgxPM29J51mic9Rc3nFzwzeeI29PgHkCzEsatMXvxxQ3z/Olm2iGSIf1/5NWHlWhaAOZ16L3/BK0Zsx8tk/ZQCO0cUz1bISVL8zimQn2tZ4mbFu4uxVIGbDz7z4j0ZsihTLqSLCoz/ff9eFSTNnMnOGBZ2/74b3Xci/upFuTduy/LC44qLg0MQv+aa1DdEqUBeKM7Fn+aazuxEgMokZgYEqZzsTuqzmsjRGUnN5WVNaTTplmPWXJTElSRGRpCm1mxj8Vi3HP1GNhiy8hzVi1v4c1MoAxtUdxGHduCvVqzftjPI0fn5VONaQzbaJ3bEP1QpcELOUZl/8ztnCCOb++Bq9V2sHjDlnZ1Dr3+3xShbQFCUTGZEiiX8/cBKt+mkHpWJeW3rVY3FQceOvriSW6j2RISvPSoNtQYhiXpPaUrnUykLy8/PJz88hYHZdusy/Sr5SbKfFuiHOZuu91BJnqRNXEjDza2q1duBOtle5+lYV9EGUocIzMXF9Y+fyYTT8siXzfLS7D4WCa3gv6MonL35GX6stHD91hFUDv6POL1YcCIvmkM1Avvu4E8t2h5ssr0hWR7c5YD2mMW+90ZypGxzZEXgLNZn4zezIiBX+BPnvwNl6AL/2s2TzLkdGd7DA88Q5LgRtZ7XNYQqu72de91o07rOWgJux2Hf9gM8aj8P1lHuZtKaEEIrOYjliDQlGPKNI8sJi3CaOH3Zn9biujF60Bod9aQjkcnD2EJbuPYzXRm+u6CyQqceXMHOFD9vtBlLvxyXoN1Yq725n4iiPUltdTUlR3c4pueo+nObNBzF/pRU9GtZj1AatDT4nZCZfPP83/u///s/wfePdcYQo4P6pufzwzudM9tR2KIWJu5g+cCTzVq1hzsDG1GsxjcO6iiiM3YnVxPH0bPg2/335B3qPn8hit4jqBtRjljePMz527AyM4MpJW0b3nkvQXXHAJHA7dDu2i7rz5dstmG7jzPEoJeqiQCbV+YGlgeK67S3sh/TE9lQ0Qa6zmbYiEO3yo5por8lMWXOK2NgTLB35Owf0C7qPWfrHlV3WRR8ctgcRHhHA2jF9sD6SgYCa63622HkHEx55krUDWzBo9SXJ9JoTPI1630yXHq8R4tlmMYbNwXFEBVgzbrIv4phT3IAV5G3P3B5fUvOnyWx0OsTNYm57XKI/sXzU+Rfw3ujNqYhw/DeOYdjso7r6FR+pJuroJizavC/1ze4BCRQZ64YQz26bDewKiiAs0JZ+zTvjeFE0TZrTtydWjIfKuMIk9lC5P+JN6sIMUu9pTVbK/ELUKChUCBRmJpF8T2EwAZh+TMXSikS6duoqA/Ho89IUZpKZJ3YQanKz84yepSQzNYMC/WRLfwNw78BIfrPQj4zzOLlsEi7XjIz1Rmn/HIeFZKSkU2A0AKh8udTkpiVzL98EoJXP7C9wh5K0mBBOnY3jfiVx1xSlEBYUxNXbxvqshSw/7RqhIRHc1Ta3Ko9j4b1oQoLOkZhVEoTC9EhOBwQTnlRs0i9bmELuXAvhbHiayd18ZdNXjzMaZQaRIUFcjM0qYS6smPRKkqNOczI4jJQS0D28vlXsuY+eqkqT2KMXryI5qDi9ZjlHircmVuQmQ5r7AbMZuuAshXlhrBrQH9cIsVEJpASuZYXHzYdQJkPW8oGMgIyAjICMwAMQkElMtA6nHGC73x2j2dYDUDO6XJR5hQM79+K3ez8Xk7T2h5yoPWzxLX5x1yi5fCgjICMgIyAj8BgRkEnsMYIpZyUjICMgIyAj8MciIJPYH4u3/DQZARkBGQEZgceIwEOT2J/di71Gkcr1axFERV4j/OY9VOp7xIr/o8KJuJEmbWvOvhVJeGQkUbHpqMglIfwSFy5c4MKFS1y+eoP0vJKLzhohg6Me+wyOVx9jPcpZyQhUCgGl4kEboyqVnZy4miOgMnJvX9104yFJ7M/vxV7IT+DInEa8//0UDoaloSpKJmhFW97/cgQ+5+5IHjtSLq3ktyaDcA68hVLIJPyEJY1eacmKgyfx3+fB0uG/MsE+wvAej6jnmUdWYBdYTbaAVbBhiu/bhGyZyliLBSyYMYoJVntJNLM9WZESyJqJw5k4Zy6Tho1m/eEk3eYXgVuBG7GcZcXcGcPo2Wc6vlf0zl0rKMhfLNmDvc8L3D69jXUbbFizaAxDRjlIbqo0pGPbvSZ1m7eiTZs20rdjJ0uO652ZViMcBcU1XK034Lt/N04rrNl/vexuYI0mjdM7XfH28WKj1QgmrTotvSBu7nw1Kr4ZUU176jeTGGXqDoZ0s0b0R1AddeOhSOyv4sU+w7cPtdtvMfggi3fpTM0fVxp8RWYEOeB5pvjNb9F5at2f1nNHt1s8J9iC2p+V9CquSLLDckmxrzZzilV9zgsk7RrIx28Mwv++zoFo45fpMOdCmSKIL4MvbFKDrzpulRzMii+kfvWf5jhGqlHn76L3W6/SbekVioRIrOq/wLu1LLlatk8qk+9f8URFvM+rlSewGmXPTWnMlMfu0TVp/XsohUWh2C105MiJAE6ePMkx3wXMXHW+wjH8qg7eaq6s/Y2JWzMkkRSpjvT7bRNJpd7WKLi+nJYfj+F4Hqjy3Pj1P01xjFVj7nzVKd/DSWLOU7+p3EQiP7i6M3UbLJNITKiGuvFQJPZX8WKfdXA43zS3IU0AdcEZbMa35ps68yQ/bYI6Eje7gyVeKLywuBHtZ1wwuLQ5u6ghTQbtKxHeRZG2mQmTA/4076eIszCX7q/yYd3lUiMQPXHsGvE+//tU+3KpccNRpNjS7J//oOkYbflV2c60ef45Os2PQPTJaTeyB7Pd4yhSBjOu5vPUbGEruaQyzkM+1iKgqID3eXWeH2Mb9cZL8mmpJnx1M77+ZRs5QgLxuqBhGiGR3TYexJiZOVdlvDWaJFa1qcuKM9qRjuh9ZEytX9meVlJqMfZdxKVYaTBaELuCpu8P40QumDtf8u7q9u/BnvqLSyRw56QbO0+uol1jLYlpqqFuPASJ/RW82GurWZxZfd9gObFqNVHe9uw6YUXDL8SZlcAtP3t2631tSbGJUljf/l16Lz3KiWP7cHdYzvwlOykdcDo/bAGj5uh9txWrU3U9kmIRNXyBT5rqZ6CiO6lPqfF8rzJFUuXtoOurz9FwmL9E4gU3VlD/2b9Tf9B+XVoV4QdtmDOgLl+JjoOrenTGMiX8407kXayc93kxBtzmrp/Qf73xu4sCCTtX4mouiuYfV5yHepLo5Hvadw3ZcE279iwUnWHyNw3YYMLBgBg77LSvLZP7DcH+dKbhdRpz5x9KoCpx04M99evFLLoXiNfOKHJvrzOQmP6a+K5rddGNSpPYX8GLvb4i8y7Mpv63c7mc7o/ztghy41fR/MPRnMwKwXlzSAnziyp/J70/6oL37WzuJp5mae/O2Bk7ZNRlmuo1mEkupiLO6p9avX4lj9n1jElMyeFJn1HjH91NFETJpc1dqfX1MLyCA3Bd3pVP//F3fhx80JBWVZhDctR2+td8mUZ9t+lmd4bL8oEOgZyzlfE+L3DrgAW9R/qW2FQkdvqWg5ZKviurI7Ci6cuidjFpaUmsLqtNxK7Tlk9B3KEpdGy3goslllvNna+OqDzYU79YKjGS9Un3ndxQgsIEiVUn3ag0if0VvNjrVVd0jNno8/HYOzly5j4o7trT5s1+2GzdhH8J79ggrn/Va7ZOZ49X4DfuC/quFkOVGH8K2TupD45GMzjjq9XxWPQ5t6bVi3xU31rnf1LBntEf8PrrI00WR6PMIunGZQKPBRMesZYmz/2Tbktvokg4wArLVQRIvhO1eTz7zMfMO/7n2gRjEpSHOFlQCe/zWWFbWWjtL5nFjXee5QRZ0LK3r+T5/CFEeOq3iFaA+U1+wPqszpxYFMiYmi1wiivZ6oruhhFyJU3aQCSoLzG9zssMsktBYeZ8ybufejErKcCDPfWLGd4/vZxJcx1wcXHBcd0Avv28N+vcAg2DnOqkG5Uksb+GF3u91ihuraXpq18xbbuWjFQFbvzy0scMd4gwxB/SplVzaWkTOujWw6TZSf1XGLU1F2VaimHdTF1wlMl9V//JZhcqzi9twDsfTZYiAYhhIOw7vUKdbtslj/Zn7XrSsvUcgjLEnU/3cOjyKl//6ko2kLJrAO/W6IhnfBHxDu15/m//1YW0ycdz4Fs8ZyYitL5+/sq/5XmfL0iJICZZu8hVmODPFs9zut14ibg7H9CRltjZNaHZqOPVeH1WjMzcgCk+Wmd/Yuy+nt/9znnRa7/Bc70Yd60BXzRbR7wU8icUi1qvMGhTEudNnteGfKrOumXeU3+x53rUSgol7/b5ZJydRcMfFxGVp9TtFK5eulFhEvsrebHXK7Aq05Eu9ecRrlv0Vit96ffdGAKN/CxK3q89V9L/h9f5eYQTobfViJ6y13f4kKGbz+K2wYdbksleINF7PDNcq38j0eOj/1XnnmVZt8Z0Gb8WO+vBtGgymr2SK38FR61+5ONPBrNPmrmquGTfnVbd52C9eDydW/fFNiBVmq0WZQayqH83Rs9ZyerFA/nu3VoMWqHdCq1/jvxrjIA57/NKjkz/nra/B6POD2Ty96/x0iuv8Ir4rfESXRdE6joqJf5TatFyot5htXHe1ee4MMGLyaPXERp3k8PWo5i3QzvgNPZcr0jYh+36HZyOiMB/40Da/rKOa/lg7nz1Kb1pSc156jeOaqC/UxHrj830Nnz0UScWO5/UzcSql25UmMT0hf4jf5+mF3uxnOJieGJCsUtnjZDJ7aTiCNDlYSEUJHI+6AJ6Z9rq/BCWTLCp0mHfyyvPg68JFGbd4XZqbqlZatk7i3LTSEnPL2Vq1aZT5aWRmJBCrry1vixwJs48ivf5guSrRN6p/kBrClO5GhJCdKp507NGmUb46QBCrtzRBbjVgmnuvAmoq9mpR/PUX510o0qT2B+jNY/mxb4iMoqLqEfXzmfPjZIePCpyr5xGRkBGQEZARsA8AjKJPaIXe/PQ6q8UErZ3M/uvFs/o9FfkXxkBGQEZARmBR0NAJrFHw0++W0ZARkBGQEbgKSIgk9hTBF9+tIyAjICMgIzAoyEgk9ij4SffLSMgIyAjICPwFBGQSewpgi8/WkZARkBGQEbg0RAoQWJLlizhueeek78yBrIOyDog64CsA09dB5YuXfpAhitBYgUFBWRkZMhfGQNZB2QdkHVA1oGnrgMiJz3oU4LEHpRYvi4jICMgIyAjICNQlRCQSawq1YYsi4yAjICMgIxApRCQSaxScMmJZQRkBGQEZASqEgIyiVWl2pBlkRGQEZARkBGoFAIyiVUKLjmxjICMgIyAjEBVQkAmsapUG7IsMgIyAjICMgKVQkAmsUrBJSeWEZARkBGQEahKCMgkVpVqQ5ZFRkBGQEZARqBSCMgkVim45MQyAjICMgIyAlUJAZnEqlJtyLLICMgIyAjICFQKAZnEKgWXnFhGQEZARkBGoCohIJNYVaoNWRYZARkBGQEZgUohIJNYpeCSE8sIyAjICMgIVCUEZBJ7krUhKMnNzkNl9hkq8rJyKTJ7/a97QaPKIztXWeUB0BSmEnE2mDNhyTzY33bVK05RXhZ5lYFZ1GlZZwEBRW42+eYbd9Wr7D+pRDKJmaxYJUetfuLHnwcwefY85s+fzG/1a/Ldz8OYu2A+ltNG0qnht0zammbybvFkYcxOZnT9ig9rzuSKCUVX3D7Iwv51eff14ZxUmM2m3AsFd4LwdHLGcbMttk7HuFVYbvJKXBRIDLDHcmJ7Pnv5dRr3ms2CBfOZbzWb8cNGscI7grxK5Fa5pGpuHlpOr7qv8ePgg1Smf63ccx41tZIw90kMs7Dn2LVYLnmMoe+0408Ql4rIKxDnZ8nwSV4kqMtPrxFusWd5f7579R1m+FVMAQvj9jCn57e8/5EF5/7wiql42cov+aNfFYRoPOd25cuXvmZFqInG/eiPkHOoBAIyiZkEK49tY4fjlyZIVzWaBJY3f5FGI44aOtWciwuYsPCqybv1JwsiF9Hka9MkJqZRJNvw8zsPR2LKe4dZvWQ/6brO6t6llYyy8CNTK7JehEf6VRW40vGFN/l9VzE7Ku/uof9Hb9Bz5bVyZpiP9FhplHvDtjUNjUhMrQzCcdP5KjNrVWW60uWr0QTmi2UVuOE+mDb9XbnzGPGvPIpqojxH81u/zVyvQN+q0SSxstXHFSYxUZ6CuJU0/+xpkFjlymYOu8elR4IQjmXD72QSMwf0H3heJjETYGs0iWxe7cF93TXx//IWL9J4xDEDiQlFIdiuPWni7uJTBVGLaVoeiaXa0uq9hyOxezuHMmxtIvo+UxwdLu0zhdCKDaqLhSznSFXoJpHYVCMSgzycu9bgnZqzCKtAR1lO9uVcEojd2IZGRiSWH7GMUTNCqgyJiXX7U/1FXH/AjKecQj71SxpNMqtaf1IpEiuMX0WLz58GiT0euB6XHglCBHMbyST2eGrl0XKRScwEfhohg6tXEtD3T6ZITCNkcfVKLOr7kRz2dsfZfhO7z6QZ7hGz1ZNYSMIZ9nl74r33LHeKJzUoypCYmuQrfmz3dGfLlr1EZekpqqyQOedm8tVrPzDF6TL3BciN2sCYKQfI1iUtSI0kKsnoYWWzeOAZUySmEW6ysPGL1Ouzkwwj8ZR3L7Nvuyduzi4cupqlI9d8rgf6st3Hl517/Nm3O4DbRfe4fGg7Wx2dOXlTjYZsrh3zZqujI8ei9axoTGJq7oR5M6nx69TpspY9fvsJuJwq5S8UxHHCdzs+vr7s9d+H34mEcsqUS0SAN+7u7nj6nCJJR/ZFmVc54LWVLe7BpOTFEbDTDReXvYTfNSpcqVyL0i7j5ziEb2oOxmmvH357ffFwcWCzSwApaqhInlK5j3rjtsUex+1nuatTNo1wj8uHvdjq6MGZ5FyuB+/EbctWDl7OMAxYRHGE/Jsc83LGydkZb/8bkhlTyIvmmPc2nF2Oc1ufn4ivieeIeVSUxFRZ0RzfswPfPYFEXLMuQ2JCXgzHvN0lbPcEJ0oDvbLl2IWHhw+BkaJuKIgN3YO7y1b8zpdsM+baU+myVQTjktVmXo/EdBlR/mx338JmO28upepbvnilrA4nqcGYxMSyhgcf50TASQLPxUtroznxJ/Hd7oOv7x6O7NvN6XjjPEtKJv97NARkEqsAfqZITLxNUIdj3aMXrhFqhIIbbO73IyM23zQQmUhi9d5qwESbIO6q1aRftGVAx4kcStR2kCVJTMlFh150HbtPMhHmRq+na6vFhJtZexCJ9tD8Frz57PN83rwnY2d6c1PHWRoy2PRrDT5rakOK+b74gSXXkthrDLW7QkTENS6G7GfT3AH0H7uF8Kzi2/MiN9Oz1QT8k9UIykisu7RjwwUlyX7TmeuaLOEhFFxmfu/phCpzuBHqSO9P32KyryhwHrHnPRla+1XGeUi2Ock8VzwTU5ESHYrziFr82M+F02fOEnZT7Ajv4TNtMrvvaDuHrLDFDP3d9MxYIyTgPqEPi/fdkUygudHbGNljDoFpAkJuHCddBlHr0y4s3OjHjTyBVP8xNGy2hjgz/Y4qK5bTO8fx3SdD8Dp9hjNnQjm2ZSBff6ydoTw4TxUX1/dklE0kavIJ39aPFp2ddOtYOVwPdaZvzZr0sbTjSGQeRdmHGf5tS5yitQIpU/YxrsVv2JzNQsM9doxqwEiHRARFEqfdh1P7bf3svrznVIzE0k6voE+vhZxKViEUxLBjXjtqGq2JFSb6MK7XDI4liQMQBVdcRzF4pj8ZgulyjPjuZ2bY2OEXlk1R/ikm1W2CrW7RuLz2VLpsD8a4WD+1R+b0CPIj19Gnux031JAdu5Xe9brhJf4Bkzp8RlmKxDTpeAz6hmb9l7Mz9Bb5WXuZMdGDNCmLfEIX92XpCTMNubSY8v9KIyCTWAUgM09il7Du2Zk1J7XscdenN7Va2qEfxIsk1ujDcZw2mPjUXFjcgPr9dpMjNnmjmZjyrhud3miGY6y28WhIx/bXL5hz2HBzKUkVxByywcpqCr999RLP/+cbJrvGGMxt+XeuEp6gJ4VSt1bwr5bEXmXA6mDOnAnh2I5lDPy1F6sOJBmIWqNJw7HrO/RYHmc4l+jciabDD3F5WQtajvDkijT9VHPzZACxKrHzTGBZi3d1JAYaMrHt+KYZEhOFVRFiVZdWFsXmREGIYl7zukz1uEBqIWiEeAJPxJgsWcrugXzz0wYjQlcROr8eLScESbMGxe11NPv3z2y7rWV8VaYD7d4YgH85E1n9LFu/aae0ma38PFWErutFP6sgxNpVFeyg2yttcNeNOLT69gp91yVJsy9xULKhw3s6vBQcmvQFTUcek+4VZwEHFg1hsW+SVHZjnRJxK/855ZsTRZO5Re2aWB0rBiL/6jzqf6Ilaw1ZeAz4iL5rtHKKAgjqy8ysW4uFJxWYLEe7GnRbFqvTlXy2dHuL0a5aPRXU5benkmWD8jE2pQpl9UhMlX9lHb1/WcBZqZh5bOv1NgM2iLN9tVkd1s/Elofmc/2gDba+kYbdqQUxS2lRZzS+55OlOlIknOSUwcpgSi753KMgIJNYBdAzR2LirUXZV/G1W83aTS44zmpJzUar0fWFkjmx0cfjS6xTZXh1590PJnJWWZLEso+P4YMXmjDHzQsvL/HrweoZw9gYUNyBFIsqkLhnLCMWXZYaiTgL3D79J/73Yiu26kiwOO3DH5kyJ97dM5B3X+7B3rvafNVFRxn2wUt0mOKmk9sL1/XTGb88EGXaYWa2+Yh/P/siH33biQU7bmhNTeIaY8tHIzFxM8UtfytafvQi//z3OzT4ZTr7r5sifAUHJnxKnR4+ElZaqQUSNrfjw9rziVJpO8Pmr/XniA5qVbYT7V7qwf5yxgAVIbHy8hTN0Zf22LFmzSa2bJvJT680wylOS6KSvrV8h8k+WoGMSV5QX2NO3f/Sd+2dEuZFfS2X7ujLf075JCZuTPrhuRZs1Su02OGHz+dHHYmplccZ+fHrTNxerKPioGZd25foaHlVS2JlyvEGY931wOaztcebDHUontaX155Kl00ksfIw1mNS/GuaxERdunttNxtXr8HeZSu/t3yDniviJXzN6bBEYg1r0nPGRFrU/BlHI5OJRpPKXqs2vP/vZ6nxvzr0nupLvCnVLBZMPnoEBGQSqwB45khMmbKbQbXrM/9ImqTwWX5D+KrxahILc6V3b6SZWCkSS3P9jQ++mC1tijBulDnnZ/H1P39jt343STlyiZ3a5u6/lOhcxDWWnSN/YIqu4yvn9gpfMkViOWem8/mz72N5RNsqhaKzTPm6BiNdxLml8UfgTtgVaY0oPz2SY+6zaPFxCxyvq6XObUUJEktnfbuKzsTURAcGkliUwtXL4oywkOSok2yZ1Yo6TVcbC6A7VnJi+lfU7uyJvuuUdhPa/swn9VYgLlWInWGL14tnXo+LxMzlKa5FeQ3/lnYTj0rrimrlXvr97yccYwvJzVXqOn/TJK8R4ljS7CW6LS02WxsX2linHvyc8klMnF02er6YXMXnGJOYOFOb+OUrjHErfuFC2vHYsgZdl9wotxxamUuS2IPak3HZxPsrX2/GJKbVo9tqgfg9w/nhh8kEpYqDiEK8h7xPzxVxFObeN6vDWhL7mnnHs4nzHUjdRkuI0HF5UWoY4bfVaArTuBboxu+tPqWX9Q3japKPHyMCMolVAEyJxKQt9sW7E8Xbkrd24uM6+h1qAolbfuXzBquIDHHB+4JKmok1eGswRw1tPJc9Y7+m8/zL0tqMIsWWVrot9kLRJWY3+ILpfvqtGZB31ZXtQaaGcIXsGt2G5aeLR8DiesTJecNxiBDNkWquH9rAxt03DObFChSzTBI9iRlvsRdNJXWffY7ukvkwhysXwgha2JCmgw9IJlIxE6EoDFeXAALmDGLjNa15FJScmNWdVaEqRPOYbcf3sNihlV/c9jyu5itGI3SBmxvbGG2xV3NpaWN+GhOAEgVHnbcSUxjCrAHLiNFlL3ao07talimDeOL++bn8WGsyZ3QsJprBvAZ/Qe9V0ZJZq/KdoXbTjvT6hG6pQzInGm09Ly9P1X0X2r9YD5twrfDKu860+29T7GJOs23LpQd0/mqu2bahdgsbEvVlV1/DfespqezGOvXg59xhVWvzW+wFIYYVP9dk0vZMA66ZgRP5+qNJuvfEFARa1aPVhECDKU11fzf9PmuJU5R2sFL+jDsfl+5vGGZiD2pPxmUTBSoPY4PAJQ7K6tENdRabfn2FTvMiJF0QZ5Ibf3md7itiCHRxMqvDWnNiY6kONZp0fIbXpuOMU9IGm5wzcxi7ONxgXs85M4tBs85KkigSDrJh9V7i5SWyEjXzKH9kEisHPXFUecTBmiULRtHs/f/yTu2ezFmynPU7tO9IFcZ7MaRZW+a4HuOg1yZs3dcw+Md2/D7fidAssaNbweDhy9mw2hm/gEB8N0xhouVebqlAeec466e05+OXv2eIpSfX8iE/bhfT+g9hocMe9u92ZK3NQcSdUKY+hYm7mDpoAnZ7zhIRcZbdDvNZ4nhBakRiJ+057HPqdXcnXWuhMpVFOecEEgOdWTyzE5/9pwYNuluy1u2sRFKCEMn6rh/zdWtbrkV6sdE1BlVRLNtnDWCUpSN7Du7CYZUtx+MLODGrDd1nbGb38dOc3O/A/Pn6l3AFrnsN4dfB9gSEnGSXhw9zOr7Mlz9bsDUonfgTdkxo/wnv1enDUo9LUgdZEGNPl2aDsHG1YdW2MJRFIUxt1REr+52cOB3MHkcrVnjcNFMmBdd8ZzJ8zHI89uxk86JxTJp/EHFPiOL2UdZatOXD/37DAEtXzsYGYDvtVz596Sv6znbmnH6B0yjnwpt+LBvdnPffbMTYxTu5dvMgKye04sPX6jNyvjeX48vP80zaTbaO+ImukzBC/kYAAAJVSURBVN04etiLTTZurBjegM4TF7DtWDgHbS34+aOXadBtLttDb+K/eRrtPn+Zer/NwT04A/FF5d1WfRkw1Z59/ntxWbuBIzfVJXRq+JJ9xBXFmX2O6/FIDttNofUnr9G4txU7LxbPU42KiuKWH7MGjmCV11H8dzuwaEpbPvpXLfrN9JRMsYIyBs+ZQ7FY5s7unU7MHzeaVftvoRJumS1H3c5zcAuK4cSWOfxa62XqdJyOy4lkymtPJ6OK24tYtqiE8jE2VW9iuUrrkQo1UdtH0LL173geO4zHJhu2rh1GkzaTWOp41KQOxxXdYNfqMTR99z06jFnNkeh7HLVsQI1/v0f74Rs56j+Ddu1n4uR7nFPBfmyyWsAe3UaRlN3DqPl+b3YnP1TDNK4a+ViHgExij0EV8tPjSUzXzio0ihxyS02eNEI+KfEJZJY6b+7R+elxxKXkmVzzML5HI2Rz46w/fgcCiUo2npUZp3oSx/nEnvFj/7EIjN8C0BSmE3czhXxd+1Tk5aFEyd34SK4nZRtGpnqJirKTuBmfgRKBrDux3ErLRllO2xby04hPvKebXSrJy1OiUd4jLjKa5GwzbK9/mPRbSFrCLbKryChYxCs+Pl23VqcgJ6eCCqIrk0aRTvzNZPLKwUxM+qjPEc2v+WnxJKQVolakkxB3h3u5Yq0Vf8RnJN7KfKSZvz63B7UnfbqH/S2pR/pcCkmLjydD14wKc3K0m34eoMP6u41/Nco88pVQeC+eqOgkciqimsYZyMeVQkAmsUrBJSeWEZARkBGQEahKCPw/Hsut3bXNjrUAAAAASUVORK5CYII=)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "7aO6BJTKzDPz" - }, - "source": [ - "### **SetFitABSA**" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "LjzpXKMZP3Lv" - }, - "source": [ - "[SetFitABSA](https://huggingface.co/blog/setfit-absa) is a framework for **few-shot** training of domain-specific ABSA models. It provides an efficient and accurate technique to detect sentiment towards specific aspects within the text. The following graph shows evaluation results of a SetFitABSA model that was trained with just few k finance related sentences and tested on the same 235 FiQA_SA test sentences that were used used to evaluate BloombergGPT. The size of the model is based on its underlying sentence transformer and is usually considerably smaller compared to SOTA LLMs.\n", - "\n", - "Specifically, in this work, we used the **paraphrase-mpnet-base-v2** sentence transformer which contains only **110M** params compared to **50B** of BloombergGPT.
\n", - "\n", - "We separated from each prompt in [FiQA_SA](https://huggingface.co/datasets/AdaptLLM/finance-tasks/viewer/FiQA_SA) the tagged and non-tagged sentences, to create [train and test sets](https://huggingface.co/datasets/ronenlap/SetFitAbsa_FiQA) correspondingly. The train set contains 646 unique sentences overall.
This graph summarizes the evaluation results. It presents the weighted F1-score achieved by SetFitABSA as a function of k, the number of sampled training sentences. The result for each k is averaged over 5 seeds. We can see that with only 50 tranining sentences SetFitABSA exceeds the BloombergGPT score. BTW, if we use the full training set for training the SetFitABSA model we get a weighted F1-score of over 86." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "RP52Iu84mpni" - }, - "source": [ - "![SetFitABSA_vs_BloombergGPT.png](data:image/png;base64,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)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "lIYXB0_WsS9v" - }, - "source": [ - "# **Demo Code**\n", - "The following code demonstrates the training of a SetFitABSA model and its evaluation over the **FiQA_SA** financial dataset.\n", - "\n", - "Specifically:\n", - "\n", - "* We sample **k=24** sentences from the training set using an arbitrary seed\n", - "* We trained the SetFitABSA model using only these **k** samples\n", - "* We evaluate the trained model over the entire test set which is exactly the same set that was used to evalute BloombergGPT model. The evaluated task is **SB2** - aspects are given and the model needs to predict their corresponding polarity\n", - "\n", - "With just 24 training sentences we get a weighted F1-score which is on par or better than the 75.07 score achieved by BloombergGPT (try it yourself!)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "CKhJf7WGmbE7" - }, - "source": [ - "### Install setfit with **SetFitABSA option**\n", - "We have to install SetFit as well as download a spaCy model for doing the initial aspect span candidate selection. We will download `en_core_web_lg`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "vMlfBkqRPBh5", - "outputId": "7fc3451c-104c-481d-ab45-15261e75c181" - }, - "outputs": [], - "source": [ - "!pip install -U \"setfit[absa]\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "P44W5eNik2Hi", - "outputId": "64420ddd-8f1c-43d5-af4d-3347832b4a51" - }, - "outputs": [], - "source": [ - "!python -m spacy download en_core_web_lg" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "lTpFKfgWUc0T" - }, - "source": [ - "### Import required packages" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "id": "G8McUhomj__u" - }, - "outputs": [], - "source": [ - "from setfit import AbsaTrainer, TrainingArguments, AbsaModel\n", - "from datasets import load_dataset\n", - "from sklearn.metrics import f1_score" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "7ECeJLDYi1E9" - }, - "source": [ - "### Load the **FiQA_SA** dataset" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 336, - "referenced_widgets": [ - "e06d2b01b0484fe88f52a6b71a33e043", - "b26e47b7e9824878a9d543a6a6732b1d", - "05f600763aa64cfe94dc86b4f9478b45", - "0574d93146fe4208a9ee037d214b881d", - "d5d1263d48cc4baf8d049deda2d744eb", - "1238997607b6443098ddd323cbbd8c2d", - "d815dff5b89743ee8d6b59de3c60001e", - "4cf0b7e5acda497c90da291e47fe4a08", - "40805b9b94aa4a60909e7230903b029e", - "f709740caf7b427b8000267e0bf05e00", - "ced143fd52be43b88c66ea316ee2b7f2", - "229b86a7fac84888ad42784d8d8ea292", - "2161e920c2744a60bb0b3951f0b89d07", - "26bf755cb4ce4ea8a32fb30a143fc391", - "82a70a7c935d4e339911fb040ba0b224", - "2a281758d1e148c2846a07a3f902ebc3", - "e80dbe893a7241d68732447d6e049dcb", - "046cf109dbfc4b82b9a0b5512d128e90", - "1991da20f6874507889b32edd9f99512", - "a2ff4c8e822f4b47a24831f76d8f5849", - "7aca9a8939874d1088435ad1dd22827b", - "69ec4a7c75a7465ebaf1def98586d3f3", - "431fc6fb184d4179b9a2536e52f371c0", - "4e4e78540aa442a1a5db003710ce9c13", - "953c19ec34f147f88a4b07c552a0a78a", - "89181779727e4e459b6fb95381b35136", - "16e00ff5ad9b49f1941eb90f289d24ea", - "fe8231dd634a43c2a11dfcd49c0d7c8a", - "ee2ded5805ad4523aa01ae4efe78ae78", - "2e18eaa2a1c64d14a221d61b5fe6cd46", - "eb766419d30e47109c6662002a7e8f28", - "bb2dd2c5ec4f4d2a9d1e90509a73d173", - "22370e269d9d4a4a9e2bb061961437d9", - "1cb69338bc9e42fe887f5db1e278ce59", - "da35c90fbc9b48e3b151a3cfca3507a4", - "56393249cbee4e88872bf1b4b3c20594", - "6d96dc234dc94121b92ebf7770e54d8d", - "c541675c7a094375a3a8bd8a03bcfce1", - "f301edca5dcd4093b98d6b064cdebb40", - "b1b50ae1c7c54e628ec9a704bf77f2be", - "0be4949f369b4c559d69a12e89137eea", - "1eef4ca2922b429e9560dd579b029974", - "69950e4a7ccf44a88667f5d7d94dbe79", - "cf2dce75a7d642cda1f73799a3ce96aa", - "68473913e161408498d19069466e3dfa", - "3aab9d78b7cf4f74b04c5bb8e41c848b", - "5642010c2a2d4222b447aa3d8fa36961", - "0d80fb25a9a4488e8692b51ef3774d95", - "205f42ecc339407f9de72887783335ad", - "c8a4df684262491bbf25410fe855d937", - "0cd1b235793f4cb9975bf2896cffe09e", - "34970b2436ae4938ab5ffe193d109d5f", - "8638bc2a87044d3c930e9430ab084076", - "3573db95c664419287df88488b6a3a5f", - "8900636f06b242f6a7fcee844b229350" - ] - }, - "id": "cDDyzGgUPmyS", - "outputId": "86adc620-2ce1-43a0-d558-e90ef357f407" - }, - "outputs": [], - "source": [ - "dataset = load_dataset(\"ronenlap/SetFitAbsa_FiQA\")\n", - "train_ds = dataset[\"train\"]\n", - "test_ds = dataset[\"test\"]" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "id": "_eqmbNT-W_EY" - }, - "outputs": [ - { - "data": { - "text/plain": [ - "Dataset({\n", - " features: ['sentence', 'aspect', 'polarity', 'ordinal'],\n", - " num_rows: 670\n", - "})" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "train_ds" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'sentence': '#Apple breaks major support, here are some levels to watch - http://stks.co/jRmW $AAPL $QQQ',\n", - " 'aspect': 'QQQ',\n", - " 'polarity': 'Negative',\n", - " 'ordinal': 0}" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "train_ds[0]" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "_pGtvlgeoETT" - }, - "source": [ - "### Simulate the few-shot regime by sampling **k** text reviews for training" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "id": "2QioIQtf6R49" - }, - "outputs": [], - "source": [ - "k = 24\n", - "seed = 35" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "id": "WiWN392BhTNb" - }, - "outputs": [], - "source": [ - "experiment_ds = train_ds.shuffle(seed=seed).select(range(k))" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Dataset({\n", - " features: ['sentence', 'aspect', 'polarity', 'ordinal'],\n", - " num_rows: 24\n", - "})" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "experiment_ds" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "yo-AQvyEjDSp" - }, - "source": [ - "### **Training a SetFitABSA Model**" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "iuQd_TN-ko0H" - }, - "source": [ - "#### Initialize an ABSA model\n", - "We'll initialize an AbsaModel using the strong [paraphrase-mpnet-base-v2](https://huggingface.co/sentence-transformers/paraphrase-mpnet-base-v2) base model." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 488, - "referenced_widgets": [ - "6beeb4dea66540d89c373fc0747ce101", - "6c9a74069792497fbe8a61e3962bcab9", - "067e3d276820408e84e332ff0256f813", - "b3ae485a63924725a245c1b351d2db27", - "1e8a700fcd3349e3a766696d30d8e038", - "b86fd8847d15409b9f6bba38accab4a7", - "2e96b451a3ae4dada64d95b3576dea84", - "173a69869a6d48ed88766a7286b49ada", - "dfce7f1fceb84c1c90fc3edca3f0acff", - "5df5bfcd6e224b39bbd8bd34166cbe48", - "12c4cdad1d7c44b99059b2f9e6870664", - "d4b2aab89e4f4fa0b3da3a5f1a810a86", - "16e18a98500040d28d071165e5b8fbca", - "ff681c613bb04ba8bef375a5c22aaed2", - "fc591a522aa445dbb75eb2bd46f0d593", - "eed5ccd492174f6893ee6776c20481b1", - "ce87579292504461bba22e0df6b03393", - "aacc124dd9b341e0ab23186ad2ad8af9", - "0fef9f08dbbe42af819db95e5706ead8", - "21abe380f82b41798615fad2d18d33a6", - "728117ebedee42108d769b743468734f", - "583120a0c1f549cc8d2c5cb65218807f", - "096b4667b25244b9a66c4a3bf3ed6b02", - "9bfb9a45099441d291244a38823bcb88", - "5a846985a7a648d68d0f01f06f825525", - "6470d777e36a4797ae4d576bc7635bb0", - "01a88629fa804914ab7ff538a8f0070c", - "195af3441f1146108a9b3cc8b98a16ef", - "9e43bb8954a643e998c71b0d03c63883", - "4aa95644aed644c3b363122959a0c222", - "6ca7ffafebea4cd286c813c7dea0de36", - "150d2ef14364464681a1e9c7c77b945d", - "bae38ef84f7c450bb68acab6c6c4e667", - "1baee8aec0794a8182105e78ba8145eb", - "d6cf5de1370c465bbfedfdcbd3d86965", - "5d7f6ea449cd4004936f5559279884e0", - "06eebf7d8b2743609563678a9e362d73", - "d68bbca7f5974981ad779bb9a209e009", - "efa03da0d9c24df4b6fcde8dc51bee0a", - "1ba311e4a70445c081e7857068302242", - "7b6edfad302043879fa8c7d9a6852b4a", - "ed8df7e8b1624750853fcfcbf9899e0e", - "f318b800d8664b9b91bf172bdfe724a5", - "62d617f9f31d491796d6fbef2ae07169", - "394192bf9de14658bfe45f3f1aae9eb9", - "35bb11297c2a4b818f18ddb8267cb3f9", - "98c7ec6c7db94bcdbcaa7eab2e54a68c", - "2dabc3460bd6498fa99d27fa99326cba", - "56fd21251ecf48c88115326466999461", - "a501836413ce4c02a2eef16338869022", - "505c59bd2f4d4f9aa9ee3113b5a63187", - "cad655a8725b4f819bd3e6d926fe8470", - "41ce5ba9a80e4867aca7c5284aa45c9a", - "ccc0a8f6ccaa48d1bf7d5546b8336a99", - "b4cb83afe22f447abc51809619a8c13c", - "028d1774505b46b3be082a38d2b3cbda", - "0219b6fc5bf348359eef7eba8872c722", - "178b7a5f4eec49a5aa473be62f017909", - "a51aff4843be4504937e42c5704fc13f", - "4b807ba2b8f340368d33f845078af81e", - "2a7fef61214646268247cbfd5dbe628f", - "d74048f7a81d4c2da25faf6eb45279ca", - "b32a7331c1e44f22b18ef6c79f7526ba", - "e1958171f79d4fbb82f84ccad8cc9f34", - "a7e9a2a1b2d0478ba31ad0011d86a61c", - "ad83457ccde6455bb80b3afa2d87ae1a", - "c5279fa012564b619f437b547f5de377", - "ca948c9d54ed4acc8b01a1e7a34240bd", - "b517ce2622a74ab18476d00bd64b630c", - "e2959cf95d524403a9e89f15768370f6", - "a37f21c5486a46059015d794a5cc8baa", - "1b0aa39be3d54007b323cfcc589f7de2", - "d67a5fd55a8b4d2c9cc75ef1a2b48391", - "4d5f8e9ebe8849699d9f4fe5ff73839f", - "5f24f32ce88b4a768831211a669d7738", - "bec1a9fb6c2f464ba13a9b225c227688", - "10cd1ddf9c0a4ec4a59c6fefecf0d71b", - "1661425eb22046fbb233eb04e17b5619", - "f5efad47c5f24f999d9f6bf2684768f0", - "aa9b023ae06d485285f7c48cc6edaef4", - "761d7c7c432b443fa328d668fdf2ff2b", - "50a490b33f9e419a9dc98c502ca3ee9b", - "558cf3fcce024deb9b7b0e4f91918bff", - "8b022131248e4eb68472a8fbd148020b", - "84e5c52b7ac44ae489853f7775d22619", - "067171111a0d43a4a431cd0adbf54833", - "5d4e027cec454a98a75ca3b6e0a395e1", - "73a151d8a53a450cbaaddc5ee819b58e", - "edbfff693e084865ae6ea37dccc28914", - "032d0f7861ec488d8fa45f921eb034d6", - "dbd7c5b4e0234983a313b74824c323f3", - "b82824d0fa684ee8b810db504b30a7a0", - "b18ab3e180fa4de9be45116751cc9885", - "9da1131a66ac45c794ddb15bd3588290", - "492c63ad811c4ceda473a33ee8e4c524", - "6daf5a2f354149029e1b6a54bb80000a", - "635a2ca574304108bd87f6998360ceb8", - "c93d5f8c442343af9fb1ae4d0c3ce4d1", - "3f4b8e5d582e45bea4115eb297999e55", - "530edcbd07e94b05a0dea890f3331b32", - "589fddee60b4449084ed8c535ad88743", - "d60441fce1474c7d9857b09bb24111e1", - "1a7a275109e54f478dd9b83e9d9be244", - "96172677cc984cc6a7acbf1af3948a23", - "77c5c6e4bc1247839c23ac73920eafd0", - "f179eddbaac64034807f8ff1a6e0a454", - "179d3f742e4a409dabb004bb32c94285", - "433741a02d6a46a4b2be99d228b14d05", - "2b42fb695c09455cb7a2697a86f6375e", - "2680126c99c74c2eb340c0ecc74325ed", - "f53d7890b2294c02886aae4ea7a19e71", - "c8013e709d9e4936a2883bd9deb2a8d5", - "d768c7e1208640d1b83d2f52b8c91bd6", - "713edfe937a64764965f497fba95d3fc", - "d757c1812bca4ec983868b997267a731", - "597dafae4a324db4b1caa4c947b46943", - "3996be2ed71e4f21a3e43a3ad970f68a", - "ca307a3ae03c41d38d1f5a12fc79bc94", - "3c00d654dcbf4462ad9464f8c76afe0d", - "c9d8aae7ac934780a0d40e51036ef254", - "de0d4553d0dd41b9bcfe0a5d49249ed4", - "8b81d14da76c4adca2c2ef6efdc1e2bf", - "42ae72b7d88d4ee29c0f490f602d588b", - "e8e538101fb04a60b9559449fd3e4580", - "8fa7ef16843c43b79ba6b29a5152ac78", - "187d461494e84fafb9679e8a8ddcdbf0", - "740d00a1b0544e088648e05ef10924fa", - "160b449400e142ed9a71d645bf9cdd3e", - "d27edcefdacc4961992037ea8d550f95", - "14f5853f191745f58d1f6d3e14c1c7eb", - "77f31477fda7478f91a5208b2e64c232", - "bc64f84e245440acac4ebad08c21a7ad", - "69072b3d3acb4ed8b65877c7a016073e", - "4075c90e43164f43866d808a40ebeb34", - "beabae6791d34225a4e0d3c22438fad9", - "f90b21aba3db4cbe86469dfafe7b8b79", - "fe15ca0935ff4ad6814b760af6a89b87", - "da06d027bc9548929bfe887bdfd71095", - "d6ad45885d114fe78bc029649ab1b5bf", - "522a28d0e47d426997b5b72b519f986d", - "f34bed17541f42579ddbd4c0eea023b8", - "b61613f4a3d64a928974cc4b263c6f51", - "6f1b08e65c8644b481f38f07084504ee" - ] - }, - "id": "_IGAjO9mjIZa", - "outputId": "b2cb3a00-6f3f-44fa-d31e-e9deb0e65384" - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "model_head.pkl not found on HuggingFace Hub, initialising classification head with random weights. You should TRAIN this model on a downstream task to use it for predictions and inference.\n", - "model_head.pkl not found on HuggingFace Hub, initialising classification head with random weights. You should TRAIN this model on a downstream task to use it for predictions and inference.\n" - ] - } - ], - "source": [ - "model = AbsaModel.from_pretrained(\n", - " \"sentence-transformers/paraphrase-mpnet-base-v2\",\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "RhLd40H1ltYv" - }, - "source": [ - "#### Setting the training arguments" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "ZXrk2_m3j32r", - "outputId": "1e9f5b92-a6d9-49ef-b7ce-6edefa76601c" - }, - "outputs": [], - "source": [ - "args = TrainingArguments(\n", - " num_epochs=1,\n", - " batch_size=4,\n", - " num_iterations=20,\n", - " save_strategy=\"no\",\n", - " report_to=\"none\"\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "mrEK4zCmmVzo" - }, - "source": [ - "#### Creating a trainer object and executing the SetFitABSA model \n", - "We need to apply a column mapping as the AbsaTrainer expects `\"text\"`, `\"span\"`, `\"label\"` and `\"ordinal\"` columns. The text refers to the sentence, whereas span is an aspect span in the sentence. The label is the corresponding label (e.g. \"positive\"), while ordinal is used to distinguish spans if they occur multiple times in the sentence. For example, if `\"stock\"` is the current aspect span, and it occurs 3 times in the sentence, an ordinal of `0` indicates that the sample is referring to the first occurrence." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 371, - "referenced_widgets": [ - "63df5c5a27ac4ade82081d386057b5b6", - "fdf8fefd47844507a6fea04767e033b2", - "82cbc04da8cb48ff988df380a2d05374", - "409b8df79b9e46e88a49a54fa0972930", - "0c16a01e4b5b440dbbd021c9acda7c07", - "f2238f666c1440c6a090ca432acc5233", - "07d7ce843d734ba9aeb98430e96a777d", - "2b3b23a9160e4d7a8062a95443dd51b0", - "ede2ed3ce3f448be8fb24bf268755956", - "9aa53159685a4785bb10afd8482674c5", - "bfcbcf192e9b4d1fa769864c098021c9", - "1ab27053faff4b1996837a239ba1717b", - "d2ae5f3116da4fc0b44db3a17269de6b", - "4c119e2bf4f6487cb7c3cbc924e9fc24", - "ac6601bf14c846628c0181134224ff3d", - "4b313af921ec49df86dffa10bcb1fb4c", - "d19e9d65c1764035af9f683b167b9654", - "424148fb51444210b1afd9a1047a31cb", - "ab3fc011fe7b4c55b99f92f10a2e01d5", - "c7f12a38716f448eb88cf8278672005d", - "80ff98269952495cb5b46c1d1181d046", - "d10b93bea07e45b2a4fdb761d2f6bc47" - ] - }, - "id": "UQhELf4kmgdi", - "outputId": "a3828fd6-2767-40cc-c4ec-61c7f63376f2" - }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "2854777a708e48b7a010838e0bdeda32", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Map: 0%| | 0/97 [00:00=68", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "setfit" +version = "1.2.0.dev0" +description = "Efficient few-shot learning with Sentence Transformers" +readme = "README.md" +license = { text = "Apache 2.0" } +requires-python = ">=3.9" +maintainers = [ + { name = "Lewis Tunstall", email = "lewis@huggingface.co" }, + { name = "Tom Aarsen" }, +] +keywords = ["nlp", "machine learning", "fewshot learning", "transformers"] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Intended Audience :: Education", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Scientific/Engineering :: Artificial Intelligence", +] +dependencies = [ + "datasets>=2.15.0", + "sentence-transformers[train]>=3", + "transformers>=4.41.0", + "evaluate>=0.3.0", + "huggingface_hub>=0.24.0", + "scikit-learn", + "packaging", +] + +[project.optional-dependencies] +optuna = ["optuna"] +quality = ["black", "flake8", "flake8-pyproject", "isort", "tabulate"] +onnx = ["onnxruntime", "onnx>=1.17.0", "skl2onnx"] +openvino = ["onnxruntime", "onnx>=1.17.0", "skl2onnx", "hummingbird-ml", "openvino"] +docs = ["hf-doc-builder>=0.3.0"] +codecarbon = ["codecarbon<2.6.0"] +tests = [ + "pytest", "pytest-cov", + "onnxruntime", "onnx>=1.17.0", "skl2onnx", + "hummingbird-ml", "openvino", +] +dev = [ + "optuna", + "black", "flake8", "flake8-pyproject", "isort", "tabulate", + "pytest", "pytest-cov", + "onnxruntime", "onnx>=1.17.0", "skl2onnx", + "hummingbird-ml", "openvino", + "hf-doc-builder>=0.3.0", + "codecarbon<2.6.0", +] + +[project.urls] +Homepage = "https://github.com/huggingface/setfit" +Download = "https://github.com/huggingface/setfit/tags" + +[tool.setuptools.packages.find] +where = ["src"] + +[tool.setuptools] +include-package-data = true +zip-safe = false + +[tool.isort] +multi_line_output = 3 +include_trailing_comma = true +force_grid_wrap = 0 +use_parentheses = true +ensure_newline_before_comments = true +line_length = 119 +lines_after_imports = 2 + +[tool.flake8] +ignore = ["E203", "E501", "W503"] +max-line-length = 119 +per-file-ignores = [ + "__init__.py: F401", +] +exclude = [ + "results", + "scripts/adapet", + "scripts/tfew", +] + +[tool.pytest.ini_options] +testpaths = ["tests"] +addopts = "--cov=setfit --durations=10" diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 36bd263e..00000000 --- a/setup.cfg +++ /dev/null @@ -1,23 +0,0 @@ -[isort] -multi_line_output = 3 -include_trailing_comma = True -force_grid_wrap = 0 -use_parentheses = True -ensure_newline_before_comments = True -line_length = 119 -lines_after_imports = 2 - -[flake8] -ignore = E203, E501, W503 -max-line-length = 119 -per-file-ignores = - # imported but unused - __init__.py: F401 -exclude = - results - scripts/adapet - scripts/tfew - -[tool:pytest] -testpaths = tests -addopts = --cov=setfit --durations=10 \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index e5aa1693..00000000 --- a/setup.py +++ /dev/null @@ -1,90 +0,0 @@ -# Lint as: python3 -from pathlib import Path - -from setuptools import find_packages, setup - - -README_TEXT = (Path(__file__).parent / "README.md").read_text(encoding="utf-8") - -MAINTAINER = "Lewis Tunstall, Tom Aarsen" -MAINTAINER_EMAIL = "lewis@huggingface.co" - -INTEGRATIONS_REQUIRE = ["optuna"] -REQUIRED_PKGS = [ - "datasets>=2.15.0", - "sentence-transformers[train]>=3", - "transformers>=4.41.0", - "evaluate>=0.3.0", - "huggingface_hub>=0.24.0", - "scikit-learn", - "packaging", -] -ABSA_REQUIRE = ["spacy<3.7.6"] -QUALITY_REQUIRE = ["black", "flake8", "isort", "tabulate"] -ONNX_REQUIRE = ["onnxruntime", "onnx!=1.16.2", "skl2onnx"] -OPENVINO_REQUIRE = ["hummingbird-ml", "openvino"] -TESTS_REQUIRE = ["pytest", "pytest-cov"] + ONNX_REQUIRE + OPENVINO_REQUIRE + ABSA_REQUIRE -DOCS_REQUIRE = ["hf-doc-builder>=0.3.0"] -CODECARBON_REQUIRE = ["codecarbon<2.6.0"] -# 2.7.* fails with AttributeError: 'EmissionsTracker' object has no attribute '_cloud' -# 2.6.* has an accidental print statement spamming the terminal -EXTRAS_REQUIRE = { - "optuna": INTEGRATIONS_REQUIRE, - "quality": QUALITY_REQUIRE, - "tests": TESTS_REQUIRE, - "onnx": ONNX_REQUIRE, - "openvino": ONNX_REQUIRE + OPENVINO_REQUIRE, - "docs": DOCS_REQUIRE, - "absa": ABSA_REQUIRE, - "codecarbon": CODECARBON_REQUIRE, -} - - -def combine_requirements(base_keys): - return list(set(k for v in base_keys for k in EXTRAS_REQUIRE[v])) - - -EXTRAS_REQUIRE["dev"] = combine_requirements([k for k in EXTRAS_REQUIRE]) -# For the combatibility tests we add pandas<2, as pandas 2.0.0 onwards is incompatible with old datasets versions, -# and we assume few to no users would use old datasets versions with new pandas versions. -# The only alternative is incrementing the minimum version for datasets, which seems unnecessary. -# Beyond that, fsspec is set to <2023.12.0 as that version is incompatible with datasets<=2.15.0 -EXTRAS_REQUIRE["compat_tests"] = ( - [requirement.replace(">=", "==") for requirement in REQUIRED_PKGS] - + TESTS_REQUIRE - + ["pandas<2", "fsspec<2023.12.0"] -) - -setup( - name="setfit", - version="1.2.0.dev0", - description="Efficient few-shot learning with Sentence Transformers", - long_description=README_TEXT, - long_description_content_type="text/markdown", - maintainer=MAINTAINER, - maintainer_email=MAINTAINER_EMAIL, - url="https://github.com/huggingface/setfit", - download_url="https://github.com/huggingface/setfit/tags", - license="Apache 2.0", - package_dir={"": "src"}, - packages=find_packages("src"), - include_package_data=True, - install_requires=REQUIRED_PKGS, - extras_require=EXTRAS_REQUIRE, - classifiers=[ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "Intended Audience :: Education", - "Intended Audience :: Science/Research", - "License :: OSI Approved :: Apache Software License", - "Operating System :: OS Independent", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Topic :: Scientific/Engineering :: Artificial Intelligence", - ], - keywords="nlp, machine learning, fewshot learning, transformers", - zip_safe=False, # Required for mypy to find the py.typed file -) diff --git a/src/setfit/__init__.py b/src/setfit/__init__.py index b2609537..76bc95c6 100644 --- a/src/setfit/__init__.py +++ b/src/setfit/__init__.py @@ -7,7 +7,6 @@ from .data import get_templated_dataset, sample_dataset from .model_card import SetFitModelCardData from .modeling import SetFitHead, SetFitModel -from .span import AbsaModel, AbsaTrainer, AspectExtractor, AspectModel, PolarityModel from .trainer import SetFitTrainer, Trainer from .trainer_distillation import DistillationSetFitTrainer, DistillationTrainer from .training_args import TrainingArguments diff --git a/src/setfit/model_card.py b/src/setfit/model_card.py index 30706bcc..0c16f012 100644 --- a/src/setfit/model_card.py +++ b/src/setfit/model_card.py @@ -25,7 +25,6 @@ from . import logging - logger = logging.get_logger(__name__) if TYPE_CHECKING: @@ -39,7 +38,12 @@ def __init__(self, trainer: "Trainer") -> None: self.trainer = trainer def on_init_end( - self, args: TrainingArguments, state: TrainerState, control: TrainerControl, model: "SetFitModel", **kwargs + self, + args: TrainingArguments, + state: TrainerState, + control: TrainerControl, + model: "SetFitModel", + **kwargs, ): if not model.model_card_data.dataset_id: # Inferring is hacky - it may break in the future, so let's be safe @@ -57,13 +61,20 @@ def on_init_end( model.model_card_data.set_train_set_metrics(self.trainer.train_dataset) # Does not work for multilabel try: - model.model_card_data.num_classes = len(set(self.trainer.train_dataset["label"])) + model.model_card_data.num_classes = len( + set(self.trainer.train_dataset["label"]) + ) model.model_card_data.set_label_examples(self.trainer.train_dataset) except Exception: pass def on_train_begin( - self, args: TrainingArguments, state: TrainerState, control: TrainerControl, model: "SetFitModel", **kwargs + self, + args: TrainingArguments, + state: TrainerState, + control: TrainerControl, + model: "SetFitModel", + **kwargs, ) -> None: # model.model_card_data.hyperparameters = extract_hyperparameters_from_trainer(self.trainer) ignore_keys = { @@ -101,14 +112,16 @@ def on_evaluate( metrics: Dict[str, float], **kwargs, ) -> None: - keys = {"eval_embedding_loss", "eval_polarity_embedding_loss", "eval_aspect_embedding_loss"} & set(metrics) + keys = {"eval_embedding_loss"} & set(metrics) if not keys: return if ( model.model_card_data.eval_lines_list and model.model_card_data.eval_lines_list[-1]["Step"] == state.global_step ): - model.model_card_data.eval_lines_list[-1]["Validation Loss"] = metrics[keys.pop()] + model.model_card_data.eval_lines_list[-1]["Validation Loss"] = metrics[ + keys.pop() + ] else: model.model_card_data.eval_lines_list.append( { @@ -129,13 +142,18 @@ def on_log( logs: Dict[str, float], **kwargs, ): - keys = {"embedding_loss", "polarity_embedding_loss", "aspect_embedding_loss"} & set(logs) + keys = { + "embedding_loss", + } & set(logs) if keys: if ( model.model_card_data.eval_lines_list - and model.model_card_data.eval_lines_list[-1]["Step"] == state.global_step + and model.model_card_data.eval_lines_list[-1]["Step"] + == state.global_step ): - model.model_card_data.eval_lines_list[-1]["Training Loss"] = logs[keys.pop()] + model.model_card_data.eval_lines_list[-1]["Training Loss"] = logs[ + keys.pop() + ] else: model.model_card_data.eval_lines_list.append( { @@ -226,15 +244,21 @@ class SetFitModelCardData(CardData): # Automatically filled by `ModelCardCallback` and the Trainer directly hyperparameters: Dict[str, Any] = field(default_factory=dict, init=False) - eval_results_dict: Optional[Dict[str, Any]] = field(default_factory=dict, init=False) + eval_results_dict: Optional[Dict[str, Any]] = field( + default_factory=dict, init=False + ) eval_lines_list: List[Dict[str, float]] = field(default_factory=list, init=False) metric_lines: List[Dict[str, float]] = field(default_factory=list, init=False) widget: List[Dict[str, str]] = field(default_factory=list, init=False) predict_example: Optional[str] = field(default=None, init=False) label_example_list: List[Dict[str, str]] = field(default_factory=list, init=False) tokenizer_warning: bool = field(default=False, init=False) - train_set_metrics_list: List[Dict[str, str]] = field(default_factory=list, init=False) - train_set_sentences_per_label_list: List[Dict[str, str]] = field(default_factory=list, init=False) + train_set_metrics_list: List[Dict[str, str]] = field( + default_factory=list, init=False + ) + train_set_sentences_per_label_list: List[Dict[str, str]] = field( + default_factory=list, init=False + ) code_carbon_callback: Optional[CodeCarbonCallback] = field(default=None, init=False) num_classes: Optional[int] = field(default=None, init=False) best_model_step: Optional[int] = field(default=None, init=False) @@ -256,9 +280,6 @@ class SetFitModelCardData(CardData): init=False, ) - # ABSA-related arguments - absa: Dict[str, Any] = field(default=None, init=False, repr=False) - # Passed via `register_model` only model: Optional["SetFitModel"] = field(default=None, init=False, repr=False) head_class: Optional[str] = field(default=None, init=False, repr=False) @@ -295,7 +316,9 @@ def set_best_model_step(self, step: int) -> None: self.best_model_step = step def set_widget_examples(self, dataset: Dataset) -> None: - samples = dataset.select(random.sample(range(len(dataset)), k=min(len(dataset), 5)))["text"] + samples = dataset.select( + random.sample(range(len(dataset)), k=min(len(dataset), 5)) + )["text"] self.widget = [{"text": sample} for sample in samples] samples = sorted(list(samples), key=len) @@ -321,7 +344,9 @@ def add_naive_word_count(sample: Dict[str, Any]) -> Dict[str, Any]: return sample_label = dataset[0]["label"] - if isinstance(sample_label, collections.abc.Sequence) and not isinstance(sample_label, str): + if isinstance(sample_label, collections.abc.Sequence) and not isinstance( + sample_label, str + ): return try: counter = Counter(dataset["label"]) @@ -330,7 +355,9 @@ def add_naive_word_count(sample: Dict[str, Any]) -> Dict[str, Any]: { "Label": str_label, "Training Sample Count": counter[ - str_label if isinstance(sample_label, str) else self.model.label2id[str_label] + str_label + if isinstance(sample_label, str) + else self.model.label2id[str_label] ], } for str_label in self.model.labels @@ -339,7 +366,9 @@ def add_naive_word_count(sample: Dict[str, Any]) -> Dict[str, Any]: self.train_set_sentences_per_label_list = [ { "Label": ( - self.model.labels[label] if self.model.labels and isinstance(label, int) else str(label) + self.model.labels[label] + if self.model.labels and isinstance(label, int) + else str(label) ), "Training Sample Count": count, } @@ -365,7 +394,9 @@ def set_label_examples(self, dataset: Dataset) -> None: break self.label_example_list = [ { - "Label": self.model.labels[label] if self.model.labels and isinstance(label, int) else label, + "Label": self.model.labels[label] + if self.model.labels and isinstance(label, int) + else label, "Examples": "
    " + "".join(example_set) + "
", } for label, example_set in examples.items() @@ -444,7 +475,10 @@ def infer_st_id(self, setfit_model_id: str) -> None: # In that case, we take the last part, split on _, and try all combinations # e.g. "a_b_c_d" -> ['a/b_c_d', 'a_b/c_d', 'a_b_c/d'] splits = st_id_path.name.split("_") - candidate_model_ids += ["_".join(splits[:idx]) + "/" + "_".join(splits[idx:]) for idx in range(1, len(splits))] + candidate_model_ids += [ + "_".join(splits[:idx]) + "/" + "_".join(splits[idx:]) + for idx in range(1, len(splits)) + ] for model_id in candidate_model_ids: if is_on_huggingface(model_id): self.st_id = model_id @@ -464,15 +498,22 @@ def try_to_pure_python(value: Any) -> Any: pass return value - pure_python_results = {key: try_to_pure_python(value) for key, value in results.items()} + pure_python_results = { + key: try_to_pure_python(value) for key, value in results.items() + } results_without_split = { - key.split("_", maxsplit=1)[1].title(): value for key, value in pure_python_results.items() + key.split("_", maxsplit=1)[1].title(): value + for key, value in pure_python_results.items() } self.eval_results_dict = pure_python_results self.metric_lines = [{"Label": "**all**", **results_without_split}] def _maybe_round(self, v, decimals=4): - if isinstance(v, float) and len(str(v).split(".")) > 1 and len(str(v).split(".")[1]) > decimals: + if ( + isinstance(v, float) + and len(str(v).split(".")) > 1 + and len(str(v).split(".")[1]) > decimals + ): return f"{v:.{decimals}f}" return str(v) @@ -498,11 +539,18 @@ def to_dict(self) -> Dict[str, Any]: ) for metric_key, metric_value in self.eval_results_dict.items() ] - super_dict["metrics"] = [metric_key.split("_", maxsplit=1)[1] for metric_key in self.eval_results_dict] - super_dict["model-index"] = eval_results_to_model_index(self.model_name, eval_results) + super_dict["metrics"] = [ + metric_key.split("_", maxsplit=1)[1] + for metric_key in self.eval_results_dict + ] + super_dict["model-index"] = eval_results_to_model_index( + self.model_name, eval_results + ) eval_lines_list = [ { - key: f"**{self._maybe_round(value)}**" if line["Step"] == self.best_model_step else value + key: f"**{self._maybe_round(value)}**" + if line["Step"] == self.best_model_step + else value for key, value in line.items() } for line in self.eval_lines_list @@ -510,12 +558,18 @@ def to_dict(self) -> Dict[str, Any]: super_dict["eval_lines"] = make_markdown_table(eval_lines_list) super_dict["explain_bold_in_eval"] = "**" in super_dict["eval_lines"] # Replace |:---:| with |:---| for left alignment - super_dict["label_examples"] = make_markdown_table(self.label_example_list).replace("-:|", "--|") - super_dict["train_set_metrics"] = make_markdown_table(self.train_set_metrics_list).replace("-:|", "--|") + super_dict["label_examples"] = make_markdown_table( + self.label_example_list + ).replace("-:|", "--|") + super_dict["train_set_metrics"] = make_markdown_table( + self.train_set_metrics_list + ).replace("-:|", "--|") super_dict["train_set_sentences_per_label_list"] = make_markdown_table( self.train_set_sentences_per_label_list ).replace("-:|", "--|") - super_dict["metrics_table"] = make_markdown_table(self.metric_lines).replace("-:|", "--|") + super_dict["metrics_table"] = make_markdown_table(self.metric_lines).replace( + "-:|", "--|" + ) if self.code_carbon_callback and self.code_carbon_callback.tracker: emissions_data = self.code_carbon_callback.tracker._prepare_emissions_data() super_dict["co2_eq_emissions"] = { @@ -529,7 +583,9 @@ def to_dict(self) -> Dict[str, Any]: "hours_used": round(emissions_data.duration / 3600, 3), } if emissions_data.gpu_model: - super_dict["co2_eq_emissions"]["hardware_used"] = emissions_data.gpu_model + super_dict["co2_eq_emissions"]["hardware_used"] = ( + emissions_data.gpu_model + ) if self.dataset_id: super_dict["datasets"] = [self.dataset_id] if self.st_id: @@ -538,16 +594,17 @@ def to_dict(self) -> Dict[str, Any]: if super_dict["num_classes"] is None: if self.model.labels: super_dict["num_classes"] = len(self.model.labels) - if super_dict["absa"]: - super_dict.update(super_dict.pop("absa")) - for key in IGNORED_FIELDS: super_dict.pop(key, None) return super_dict def to_yaml(self, line_break=None) -> str: return yaml_dump( - {key: value for key, value in self.to_dict().items() if key in YAML_FIELDS and value is not None}, + { + key: value + for key, value in self.to_dict().items() + if key in YAML_FIELDS and value is not None + }, sort_keys=False, line_break=line_break, ).strip() @@ -571,5 +628,7 @@ def is_on_huggingface(repo_id: str, is_model: bool = True) -> bool: def generate_model_card(model: "SetFitModel") -> str: template_path = Path(__file__).parent / "model_card_template.md" - model_card = ModelCard.from_template(card_data=model.model_card_data, template_path=template_path, hf_emoji="🤗") + model_card = ModelCard.from_template( + card_data=model.model_card_data, template_path=template_path, hf_emoji="🤗" + ) return model_card.content diff --git a/src/setfit/model_card_template.md b/src/setfit/model_card_template.md index 41c73cba..05292f8e 100644 --- a/src/setfit/model_card_template.md +++ b/src/setfit/model_card_template.md @@ -4,21 +4,15 @@ {{ card_data }} --- -# {{ model_name if model_name else ( "SetFit Aspect Model for Aspect Based Sentiment Analysis" if is_aspect else ( "SetFit Polarity Model for Aspect Based Sentiment Analysis" if is_aspect is False else "SetFit Model for Text Classification"))}} +# {{ model_name if model_name else "SetFit Model for Text Classification" }} -This is a [SetFit](https://github.com/huggingface/setfit) model{% if dataset_id %} trained on the [{{ dataset_name if dataset_name else dataset_id }}](https://huggingface.co/datasets/{{ dataset_id }}) dataset{% endif %} that can be used for {{ task_name | default("Text Classification", true) }}.{% if st_id %} This SetFit model uses [{{ st_id }}](https://huggingface.co/{{ st_id }}) as the Sentence Transformer embedding model.{% endif %} A {{ head_class }} instance is used for classification.{% if is_absa %} In particular, this model is in charge of {{ "filtering aspect span candidates" if is_aspect else "classifying aspect polarities"}}.{% endif %} +This is a [SetFit](https://github.com/huggingface/setfit) model{% if dataset_id %} trained on the [{{ dataset_name if dataset_name else dataset_id }}](https://huggingface.co/datasets/{{ dataset_id }}) dataset{% endif %} that can be used for {{ task_name | default("Text Classification", true) }}.{% if st_id %} This SetFit model uses [{{ st_id }}](https://huggingface.co/{{ st_id }}) as the Sentence Transformer embedding model.{% endif %} A {{ head_class }} instance is used for classification. The model has been trained using an efficient few-shot learning technique that involves: 1. Fine-tuning a [Sentence Transformer](https://www.sbert.net) with contrastive learning. 2. Training a classification head with features from the fine-tuned Sentence Transformer. -{% if is_absa %} -This model was trained within the context of a larger system for ABSA, which looks like so: -1. Use a spaCy model to select possible aspect span candidates. -2. {{ "**" if is_aspect else "" }}Use {{ "this" if is_aspect else "a" }} SetFit model to filter these possible aspect span candidates.{{ "**" if is_aspect else "" }} -3. {{ "**" if not is_aspect else "" }}Use {{ "this" if not is_aspect else "a" }} SetFit model to classify the filtered aspect span candidates.{{ "**" if not is_aspect else "" }} -{% endif %} ## Model Details ### Model Description @@ -33,15 +27,6 @@ This model was trained within the context of a larger system for ABSA, which loo {%- else -%} {%- endif %} -{%- if spacy_model %} -- **spaCy Model:** {{ spacy_model }} -{%- endif %} -{%- if aspect_model %} -- **SetFitABSA Aspect Model:** [{{ aspect_model }}](https://huggingface.co/{{ aspect_model }}) -{%- endif %} -{%- if polarity_model %} -- **SetFitABSA Polarity Model:** [{{ polarity_model }}](https://huggingface.co/{{ polarity_model }}) -{%- endif %} - **Maximum Sequence Length:** {{ model_max_length }} tokens {% if num_classes -%} - **Number of Classes:** {{ num_classes }} classes @@ -93,19 +78,6 @@ pip install setfit ``` Then you can load this model and run inference. -{% if is_absa %} -```python -from setfit import AbsaModel - -# Download from the {{ hf_emoji }} Hub -model = AbsaModel.from_pretrained( - "{{ aspect_model }}", - "{{ polarity_model }}", -) -# Run inference -preds = model("The food was great, but the venue is just way too busy.") -``` -{%- else %} ```python from setfit import SetFitModel @@ -114,7 +86,6 @@ model = SetFitModel.from_pretrained("{{ model_id | default('setfit_model_id', tr # Run inference preds = model("{{ predict_example | default("I loved the spiderman movie!", true) | replace('"', '\\"') }}") ``` -{%- endif %} -- \*\*Language:\*\* en -- \*\*License:\*\* apache-2.0 - -### Model Sources - -- \*\*Repository:\*\* \[SetFit on GitHub\]\(https://github.com/huggingface/setfit\) -- \*\*Paper:\*\* \[Efficient Few-Shot Learning Without Prompts\]\(https://arxiv.org/abs/2209.11055\) -- \*\*Blogpost:\*\* \[SetFit: Efficient Few-Shot Learning Without Prompts\]\(https://huggingface.co/blog/setfit\) - -### Model Labels -\| Label\s+\| Examples\s+\| -\|:-+\|:-+\| -\| aspect\s+\| [^\|]+ \| -\| no aspect\s+\| [^\|]+ \| - -## Evaluation - -### Metrics -\| Label \| Accuracy \| -\|:--------\|:---------\| -\| \*\*all\*\* \| [\d\.]+\s+\| - -## Uses - -### Direct Use for Inference - -First install the SetFit library: - -```bash -pip install setfit -``` - -Then you can load this model and run inference. - -```python -from setfit import AbsaModel - -# Download from the [^H]+ Hub -model = AbsaModel.from_pretrained\( - "[^\"]+", - "[^\"]+", -\) -# Run inference -preds = model\(".+"\) -``` - - - - - - - - - -## Training Details - -### Training Set Metrics -\| Training set \| Min \| Median \| Max \| -\|:-------------\|:----\|:-------\|:----\| -\| Word count \| 5 \| 14.5 \| 23 \| - -\| Label \| Training Sample Count \| -\|:----------\|:----------------------\| -\| no aspect \| 1 \| -\| aspect \| 5 \| - -### Training Hyperparameters -- batch_size: \(1, 1\) -- num_epochs: \(1, 16\) -- max_steps: 2 -- sampling_strategy: oversampling -- body_learning_rate: \(2e-05, 1e-05\) -- head_learning_rate: 0.01 -- loss: CosineSimilarityLoss -- distance_metric: cosine_distance -- margin: 0.25 -- end_to_end: False -- use_amp: False -- warmup_proportion: 0.1 -- l2_weight: 0.01 -- seed: 42 -- eval_max_steps: -1 -- load_best_model_at_end: False - -### Training Results -\| Epoch \| Step \| Training Loss \| Validation Loss \| -\|:-----:\|:----:\|:-------------:\|:---------------:\| -(\| [\d\.]+ +\| [\d\.]+ +\| [\d\.]+ +\| [\d\.]+ +\|\n)+ -### Environmental Impact -Carbon emissions were measured using \[CodeCarbon\]\(https://github.com/mlco2/codecarbon\)\. -- \*\*Carbon Emitted\*\*: [\d\.]+ kg of CO2 -- \*\*Hours Used\*\*: [\d\.]+ hours - -### Training Hardware -- \*\*On Cloud\*\*: (Yes|No) -- \*\*GPU Model\*\*: [^\n]+ -- \*\*CPU Model\*\*: [^\n]+ -- \*\*RAM Size\*\*: [\d\.]+ GB - -### Framework Versions -- Python: [^\n]+ -- SetFit: [^\n]+ -- Sentence Transformers: [^\n]+ -- spaCy: [^\n]+ -- Transformers: [^\n]+ -- PyTorch: [^\n]+ -- Datasets: [^\n]+ -- Tokenizers: [^\n]+ - -## Citation - -### BibTeX -```bibtex -@article{https://doi.org/10.48550/arxiv.2209.11055, - doi = {10.48550/ARXIV.2209.11055}, - url = {https://arxiv.org/abs/2209.11055}, - author = {Tunstall, Lewis and Reimers, Nils and Jo, Unso Eun Seo and Bates, Luke and Korat, Daniel and Wasserblat, Moshe and Pereg, Oren}, - keywords = {Computation and Language \(cs.CL\), FOS: Computer and information sciences, FOS: Computer and information sciences}, - title = {Efficient Few-Shot Learning Without Prompts}, - publisher = {arXiv}, - year = \{2022\}, - copyright = {Creative Commons Attribution 4.0 International} -} -``` - - - - - -\ -""", - flags=re.DOTALL, -) diff --git a/tests/span/polarity_model_card_pattern.py b/tests/span/polarity_model_card_pattern.py deleted file mode 100644 index e9eabf67..00000000 --- a/tests/span/polarity_model_card_pattern.py +++ /dev/null @@ -1,199 +0,0 @@ -# flake8: noqa - -import re - - -POLARITY_MODEL_CARD_PATTERN = re.compile( - """\ ---- -.* ---- - -\# SetFit Polarity Model with sentence\-transformers/paraphrase\-albert\-small\-v2 - -This is a \[SetFit\]\(https://github\.com/huggingface/setfit\) model that can be used for Aspect Based Sentiment Analysis \(ABSA\)\. This SetFit model uses \[sentence\-transformers/paraphrase\-albert\-small\-v2\]\(https://huggingface\.co/sentence\-transformers/paraphrase\-albert\-small\-v2\) as the Sentence Transformer embedding model\. A \[LogisticRegression\]\(https://scikit\-learn\.org/stable/modules/generated/sklearn\.linear_model\.LogisticRegression\.html\) instance is used for classification\. In particular, this model is in charge of (filtering aspect span candidates|classifying aspect polarities)\. - -The model has been trained using an efficient few\-shot learning technique that involves: - -1\. Fine\-tuning a \[Sentence Transformer\]\(https://www\.sbert\.net\) with contrastive learning\. -2\. Training a classification head with features from the fine\-tuned Sentence Transformer\. - -This model was trained within the context of a larger system for ABSA, which looks like so\: - -1\. Use a spaCy model to select possible aspect span candidates\. -2\. Use a SetFit model to filter these possible aspect span candidates\. -3\. \*\*Use this SetFit model to classify the filtered aspect span candidates\.\*\* - -## Model Details - -### Model Description -- \*\*Model Type:\*\* SetFit -- \*\*Sentence Transformer body:\*\* \[sentence-transformers/paraphrase-albert-small-v2\]\(https://huggingface.co/sentence-transformers/paraphrase-albert-small-v2\) -- \*\*Classification head:\*\* a \[LogisticRegression\]\(https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html\) instance -- \*\*spaCy Model:\*\* en_core_web_lg -- \*\*SetFitABSA Aspect Model:\*\* \[\S+\]\(https:\/\/huggingface\.co/\S+\) -- \*\*SetFitABSA Polarity Model:\*\* \[\S+\]\(https:\/\/huggingface\.co/\S+\) -- \*\*Maximum Sequence Length:\*\* 100 tokens -- \*\*Number of Classes:\*\* 2 classes - -- \*\*Language:\*\* en -- \*\*License:\*\* apache-2.0 - -### Model Sources - -- \*\*Repository:\*\* \[SetFit on GitHub\]\(https://github.com/huggingface/setfit\) -- \*\*Paper:\*\* \[Efficient Few-Shot Learning Without Prompts\]\(https://arxiv.org/abs/2209.11055\) -- \*\*Blogpost:\*\* \[SetFit: Efficient Few-Shot Learning Without Prompts\]\(https://huggingface.co/blog/setfit\) - -### Model Labels -\| Label\s+\| Examples\s+\| -\|:-+\|:-+\| -\| negative\s+\| [^\|]+ \| -\| positive\s+\| [^\|]+ \| - -## Evaluation - -### Metrics -\| Label \| Accuracy \| -\|:--------\|:---------\| -\| \*\*all\*\* \| [\d\.]+\s+\| - -## Uses - -### Direct Use for Inference - -First install the SetFit library: - -```bash -pip install setfit -``` - -Then you can load this model and run inference. - -```python -from setfit import AbsaModel - -# Download from the [^H]+ Hub -model = AbsaModel.from_pretrained\( - "[^\"]+", - "[^\"]+", -\) -# Run inference -preds = model\(".+"\) -``` - - - - - - - - - -## Training Details - -### Training Set Metrics -\| Training set \| Min \| Median \| Max \| -\|:-------------\|:----\|:-------\|:----\| -\| Word count \| 8 \| 16.8 \| 28 \| - -\| Label \| Training Sample Count \| -\|:---------\|:----------------------\| -\| negative \| 2 \| -\| positive \| 3 \| - -### Training Hyperparameters -- batch_size: \(1, 1\) -- num_epochs: \(1, 16\) -- max_steps: 2 -- sampling_strategy: oversampling -- body_learning_rate: \(2e-05, 1e-05\) -- head_learning_rate: 0.01 -- loss: CosineSimilarityLoss -- distance_metric: cosine_distance -- margin: 0.25 -- end_to_end: False -- use_amp: False -- warmup_proportion: 0.1 -- l2_weight: 0.01 -- seed: 42 -- eval_max_steps: -1 -- load_best_model_at_end: False - -### Training Results -\| Epoch \| Step \| Training Loss \| Validation Loss \| -\|:-----:\|:----:\|:-------------:\|:---------------:\| -(\| [\d\.]+ +\| [\d\.]+ +\| [\d\.]+ +\| [\d\.]+ +\|\n)+ -### Environmental Impact -Carbon emissions were measured using \[CodeCarbon\]\(https://github.com/mlco2/codecarbon\)\. -- \*\*Carbon Emitted\*\*: [\d\.]+ kg of CO2 -- \*\*Hours Used\*\*: [\d\.]+ hours - -### Training Hardware -- \*\*On Cloud\*\*: (Yes|No) -- \*\*GPU Model\*\*: [^\n]+ -- \*\*CPU Model\*\*: [^\n]+ -- \*\*RAM Size\*\*: [\d\.]+ GB - -### Framework Versions -- Python: [^\n]+ -- SetFit: [^\n]+ -- Sentence Transformers: [^\n]+ -- spaCy: [^\n]+ -- Transformers: [^\n]+ -- PyTorch: [^\n]+ -- Datasets: [^\n]+ -- Tokenizers: [^\n]+ - -## Citation - -### BibTeX -```bibtex -@article{https://doi.org/10.48550/arxiv.2209.11055, - doi = {10.48550/ARXIV.2209.11055}, - url = {https://arxiv.org/abs/2209.11055}, - author = {Tunstall, Lewis and Reimers, Nils and Jo, Unso Eun Seo and Bates, Luke and Korat, Daniel and Wasserblat, Moshe and Pereg, Oren}, - keywords = {Computation and Language \(cs.CL\), FOS: Computer and information sciences, FOS: Computer and information sciences}, - title = {Efficient Few-Shot Learning Without Prompts}, - publisher = {arXiv}, - year = \{2022\}, - copyright = {Creative Commons Attribution 4.0 International} -} -``` - - - - - -\ -""", - flags=re.DOTALL, -) diff --git a/tests/span/test_model_card.py b/tests/span/test_model_card.py deleted file mode 100644 index 6e4585f8..00000000 --- a/tests/span/test_model_card.py +++ /dev/null @@ -1,50 +0,0 @@ -from pathlib import Path - -from datasets import Dataset - -from setfit import AbsaModel, AbsaTrainer, SetFitModelCardData, TrainingArguments - -from .aspect_model_card_pattern import ASPECT_MODEL_CARD_PATTERN -from .polarity_model_card_pattern import POLARITY_MODEL_CARD_PATTERN - - -def test_model_card(absa_dataset: Dataset, tmp_path: Path) -> None: - model = AbsaModel.from_pretrained( - "sentence-transformers/paraphrase-albert-small-v2", - model_card_data=SetFitModelCardData( - model_id="tomaarsen/setfit-absa-paraphrase-albert-small-v2-laptops", - language=["en"], - license="apache-2.0", - ), - ) - - args = TrainingArguments( - str(tmp_path), - report_to="codecarbon", - batch_size=1, - eval_steps=1, - logging_steps=1, - max_steps=2, - eval_strategy="steps", - save_strategy="no", - ) - trainer = AbsaTrainer( - model=model, - args=args, - train_dataset=absa_dataset, - eval_dataset=absa_dataset, - ) - trainer.train() - trainer.evaluate() - - path = tmp_path / "aspect" - model.aspect_model.create_model_card(path, model_name=str(path)) - with open(path / "README.md", "r", encoding="utf8") as f: - model_card = f.read() - assert ASPECT_MODEL_CARD_PATTERN.fullmatch(model_card) - - path = tmp_path / "polarity" - model.polarity_model.create_model_card(path, model_name=str(path)) - with open(path / "README.md", "r", encoding="utf8") as f: - model_card = f.read() - assert POLARITY_MODEL_CARD_PATTERN.fullmatch(model_card) diff --git a/tests/span/test_modeling.py b/tests/span/test_modeling.py deleted file mode 100644 index 28fdfcec..00000000 --- a/tests/span/test_modeling.py +++ /dev/null @@ -1,227 +0,0 @@ -import json -import re -from pathlib import Path - -import pytest -import torch -from datasets import Dataset -from pytest import LogCaptureFixture - -from setfit import AbsaModel -from setfit.logging import get_logger -from setfit.span.aspect_extractor import AspectExtractor -from setfit.span.modeling import AspectModel, PolarityModel -from tests.test_modeling import torch_cuda_available -from tests.utils import SafeTemporaryDirectory - - -def test_loading(): - model = AbsaModel.from_pretrained("sentence-transformers/paraphrase-albert-small-v2", spacy_model="en_core_web_sm") - assert isinstance(model, AbsaModel) - assert isinstance(model.aspect_extractor, AspectExtractor) - assert isinstance(model.aspect_model, AspectModel) - assert isinstance(model.polarity_model, PolarityModel) - - model = AbsaModel.from_pretrained( - "sentence-transformers/paraphrase-albert-small-v2@6c91e73a51599e35bd1145dfdcd3289215225009", - "sentence-transformers/paraphrase-albert-small-v2", - spacy_model="en_core_web_sm", - ) - assert isinstance(model, AbsaModel) - - model = AbsaModel.from_pretrained( - "sentence-transformers/paraphrase-albert-small-v2", - "sentence-transformers/paraphrase-albert-small-v2@6c91e73a51599e35bd1145dfdcd3289215225009", - spacy_model="en_core_web_sm", - ) - assert isinstance(model, AbsaModel) - - with pytest.raises(OSError): - model = AbsaModel.from_pretrained( - "sentence-transformers/paraphrase-albert-small-v2", spacy_model="not_a_spacy_model" - ) - - model = AbsaModel.from_pretrained( - "sentence-transformers/paraphrase-albert-small-v2", spacy_model="en_core_web_sm", normalize_embeddings=True - ) - assert model.aspect_model.normalize_embeddings - assert model.polarity_model.normalize_embeddings - - aspect_model = AspectModel.from_pretrained("sentence-transformers/paraphrase-albert-small-v2", span_context=12) - assert aspect_model.span_context == 12 - polarity_model = PolarityModel.from_pretrained("sentence-transformers/paraphrase-albert-small-v2", span_context=12) - assert polarity_model.span_context == 12 - - model = AbsaModel.from_pretrained( - "sentence-transformers/paraphrase-albert-small-v2", spacy_model="en_core_web_sm", span_contexts=(12, 4) - ) - assert model.aspect_model.span_context == 12 - assert model.polarity_model.span_context == 4 - - -def test_save_load(absa_model: AbsaModel, caplog: LogCaptureFixture) -> None: - logger = get_logger("setfit") - logger.propagate = True - - absa_model.polarity_model.span_context = 5 - - with SafeTemporaryDirectory() as tmp_dir: - tmp_dir = str(Path(tmp_dir) / "model") - absa_model.save_pretrained(tmp_dir) - assert (Path(tmp_dir + "-aspect") / "config_setfit.json").exists() - assert (Path(tmp_dir + "-polarity") / "config_setfit.json").exists() - - fresh_model = AbsaModel.from_pretrained( - tmp_dir + "-aspect", tmp_dir + "-polarity", spacy_model="en_core_web_sm" - ) - assert fresh_model.polarity_model.span_context == 5 - - # We expect a warning if we override the configured data: - AbsaModel.from_pretrained(tmp_dir + "-aspect", tmp_dir + "-polarity", span_contexts=[4, 4]) - log_texts = [record[2] for record in caplog.record_tuples] - assert "Overriding span_context in model configuration from 0 to 4." in log_texts - assert "Overriding span_context in model configuration from 5 to 4." in log_texts - assert len(caplog.record_tuples) == 2 - caplog.clear() - - # Error because en_core_web_bla doesn't exist - with pytest.raises(OSError): - AbsaModel.from_pretrained(tmp_dir + "-aspect", tmp_dir + "-polarity", spacy_model="en_core_web_bla") - log_texts = [record[2] for record in caplog.record_tuples] - assert "Overriding spacy_model in model configuration from en_core_web_sm to en_core_web_bla." in log_texts - assert "Overriding spacy_model in model configuration from en_core_web_sm to en_core_web_bla." in log_texts - assert len(caplog.record_tuples) == 2 - caplog.clear() - - with SafeTemporaryDirectory() as aspect_tmp_dir: - with SafeTemporaryDirectory() as polarity_tmp_dir: - absa_model.save_pretrained(aspect_tmp_dir, polarity_tmp_dir) - assert (Path(aspect_tmp_dir) / "config_setfit.json").exists() - assert (Path(polarity_tmp_dir) / "config_setfit.json").exists() - - fresh_model = AbsaModel.from_pretrained(aspect_tmp_dir, polarity_tmp_dir) - assert fresh_model.polarity_model.span_context == 5 - assert fresh_model.aspect_model.spacy_model == "en_core_web_sm" - assert fresh_model.polarity_model.spacy_model == "en_core_web_sm" - - # Loading a model with different spacy_model settings - polarity_config_path = str(Path(polarity_tmp_dir) / "config_setfit.json") - with open(polarity_config_path, "r") as f: - config = json.load(f) - assert config == { - "span_context": 5, - "normalize_embeddings": False, - "spacy_model": "en_core_web_sm", - "labels": None, - } - config["spacy_model"] = "en_core_web_bla" - with open(polarity_config_path, "w") as f: - json.dump(config, f) - # Load a model with the updated config, there should be a warning - fresh_model = AbsaModel.from_pretrained(aspect_tmp_dir, polarity_tmp_dir) - assert len(caplog.record_tuples) == 1 - assert caplog.record_tuples[0][2] == ( - "The Aspect and Polarity models are configured to use different spaCy models:\n" - "* 'en_core_web_sm' for the aspect model, and\n" - "* 'en_core_web_bla' for the polarity model.\n" - "This model will use 'en_core_web_sm'." - ) - - logger.propagate = False - - -@pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA must be available to move a model between devices") -def test_to(absa_model: AbsaModel) -> None: - assert absa_model.device.type == "cuda" - absa_model.to("cpu") - assert absa_model.device.type == "cpu" - assert absa_model.aspect_model.device.type == "cpu" - assert absa_model.polarity_model.device.type == "cpu" - - -@torch_cuda_available -@pytest.mark.parametrize("device", ["cpu", "cuda"]) -def test_load_model_on_device(device): - model = AbsaModel.from_pretrained("sentence-transformers/paraphrase-albert-small-v2", device=device) - assert model.device.type == device - assert model.polarity_model.device.type == device - assert model.aspect_model.device.type == device - - -def test_predict_dataset(trained_absa_model: AbsaModel): - inputs = Dataset.from_dict( - { - "text": [ - "But the staff was so horrible to us.", - "To be completely fair, the only redeeming factor was the food, which was above average, but couldn't make up for all the other deficiencies of Teodora.", - "The food is uniformly exceptional, with a very capable kitchen which will proudly whip up whatever you feel like eating, whether it's on the menu or not.", - "The food is uniformly exceptional, with a very capable kitchen which will proudly whip up whatever you feel like eating, whether it's on the menu or not.", - "The food is uniformly exceptional, with a very capable kitchen which will proudly whip up whatever you feel like eating, whether it's on the menu or not.", - ], - "span": ["staff", "food", "food", "kitchen", "menu"], - "label": ["negative", "positive", "positive", "positive", "neutral"], - "ordinal": [0, 0, 0, 0, 0], - } - ) - outputs = trained_absa_model.predict(inputs) - assert isinstance(outputs, Dataset) - assert set(outputs.column_names) == {"pred_polarity", "text", "span", "label", "ordinal"} - - inputs = Dataset.from_dict( - { - "text": [ - "But the staff was so horrible to us.", - "To be completely fair, the only redeeming factor was the food, which was above average, but couldn't make up for all the other deficiencies of Teodora.", - "The food is uniformly exceptional, with a very capable kitchen which will proudly whip up whatever you feel like eating, whether it's on the menu or not.", - "The food is uniformly exceptional, with a very capable kitchen which will proudly whip up whatever you feel like eating, whether it's on the menu or not.", - "The food is uniformly exceptional, with a very capable kitchen which will proudly whip up whatever you feel like eating, whether it's on the menu or not.", - ], - "span": ["staff", "food", "food", "kitchen", "menu"], - } - ) - outputs = trained_absa_model.predict(inputs) - assert isinstance(outputs, Dataset) - assert "pred_polarity" in outputs.column_names - - -def test_predict_dataset_errors(trained_absa_model: AbsaModel): - inputs = Dataset.from_dict( - { - "text": [ - "But the staff was so horrible to us.", - "To be completely fair, the only redeeming factor was the food, which was above average, but couldn't make up for all the other deficiencies of Teodora.", - "The food is uniformly exceptional, with a very capable kitchen which will proudly whip up whatever you feel like eating, whether it's on the menu or not.", - "The food is uniformly exceptional, with a very capable kitchen which will proudly whip up whatever you feel like eating, whether it's on the menu or not.", - "The food is uniformly exceptional, with a very capable kitchen which will proudly whip up whatever you feel like eating, whether it's on the menu or not.", - ], - } - ) - with pytest.raises( - ValueError, - match=re.escape( - "`inputs` must be either a `str`, a `List[str]`, or a `datasets.Dataset` with columns `text` and `span` and optionally `ordinal`. " - "Found a dataset with these columns: ['text']." - ), - ): - trained_absa_model.predict(inputs) - - inputs = Dataset.from_dict( - { - "text": [ - "But the staff was so horrible to us.", - "To be completely fair, the only redeeming factor was the food, which was above average, but couldn't make up for all the other deficiencies of Teodora.", - "The food is uniformly exceptional, with a very capable kitchen which will proudly whip up whatever you feel like eating, whether it's on the menu or not.", - "The food is uniformly exceptional, with a very capable kitchen which will proudly whip up whatever you feel like eating, whether it's on the menu or not.", - "The food is uniformly exceptional, with a very capable kitchen which will proudly whip up whatever you feel like eating, whether it's on the menu or not.", - ], - "span": ["staff", "food", "food", "kitchen", "menu"], - "pred_polarity": ["negative", "positive", "positive", "positive", "neutral"], - } - ) - with pytest.raises( - ValueError, - match=re.escape( - "`predict_dataset` wants to add a `pred_polarity` column, but the input dataset already contains that column." - ), - ): - trained_absa_model.predict(inputs) diff --git a/tests/span/test_trainer.py b/tests/span/test_trainer.py deleted file mode 100644 index dba18047..00000000 --- a/tests/span/test_trainer.py +++ /dev/null @@ -1,96 +0,0 @@ -import logging - -from datasets import Dataset -from pytest import LogCaptureFixture -from transformers import TrainerCallback - -from setfit import AbsaTrainer -from setfit.logging import get_logger -from setfit.span.modeling import AbsaModel - - -def test_trainer(absa_model: AbsaModel, absa_dataset: Dataset) -> None: - trainer = AbsaTrainer(absa_model, train_dataset=absa_dataset, eval_dataset=absa_dataset) - trainer.train() - - metrics = trainer.evaluate() - assert "aspect" in metrics - assert "polarity" in metrics - assert "accuracy" in metrics["aspect"] - assert "accuracy" in metrics["polarity"] - assert metrics["aspect"]["accuracy"] > 0.0 - assert metrics["polarity"]["accuracy"] > 0.0 - new_metrics = trainer.evaluate(absa_dataset) - assert metrics == new_metrics - - predict = absa_model.predict("Best pizza outside of Italy and really tasty.") - assert {"span": "pizza", "polarity": "positive"} in predict - predict = absa_model.predict(["Best pizza outside of Italy and really tasty.", "This is another sentence"]) - assert isinstance(predict, list) and len(predict) == 2 and isinstance(predict[0], list) - predict = absa_model(["Best pizza outside of Italy and really tasty.", "This is another sentence"]) - assert isinstance(predict, list) and len(predict) == 2 and isinstance(predict[0], list) - - -def test_trainer_callbacks(absa_model: AbsaModel) -> None: - trainer = AbsaTrainer(absa_model) - assert len(trainer.aspect_trainer.st_trainer.callback_handler.callbacks) >= 2 - num_callbacks = len(trainer.aspect_trainer.st_trainer.callback_handler.callbacks) - callback_names = { - callback.__class__.__name__ for callback in trainer.aspect_trainer.st_trainer.callback_handler.callbacks - } - assert {"DefaultFlowCallback", "ProgressCallback"} <= callback_names - - class TestCallback(TrainerCallback): - pass - - callback = TestCallback() - trainer.add_callback(callback) - assert len(trainer.aspect_trainer.st_trainer.callback_handler.callbacks) == num_callbacks + 1 - assert len(trainer.polarity_trainer.st_trainer.callback_handler.callbacks) == num_callbacks + 1 - assert trainer.aspect_trainer.st_trainer.callback_handler.callbacks[-1] == callback - assert trainer.polarity_trainer.st_trainer.callback_handler.callbacks[-1] == callback - - assert trainer.pop_callback(callback) == (callback, callback) - trainer.add_callback(callback) - assert trainer.aspect_trainer.st_trainer.callback_handler.callbacks[-1] == callback - assert trainer.polarity_trainer.st_trainer.callback_handler.callbacks[-1] == callback - trainer.remove_callback(callback) - assert callback not in trainer.aspect_trainer.st_trainer.callback_handler.callbacks - assert callback not in trainer.polarity_trainer.st_trainer.callback_handler.callbacks - - -def test_train_ordinal_too_high(absa_model: AbsaModel, caplog: LogCaptureFixture) -> None: - logger = get_logger("setfit") - logger.propagate = True - - absa_dataset = Dataset.from_dict( - { - "text": [ - "It is about food and ambiance, and imagine how dreadful it will be it we only had to listen to an idle engine." - ], - "span": ["food"], - "label": ["negative"], - "ordinal": [1], - } - ) - with caplog.at_level(logging.INFO): - trainer = AbsaTrainer(absa_model, train_dataset=absa_dataset) - assert len(trainer.aspect_trainer.train_dataset) == 3 - assert len(trainer.polarity_trainer.train_dataset) == 0 - # These tests are ignored as the caplog is inconsistent: - # assert len(caplog.record_tuples) == 1 - # assert caplog.record_tuples[0][2] == ( - # "The ordinal of 1 for span 'food' in 'It is about food and ambiance, and imagine how dreadful it will be " - # "it we only had to listen to an idle engine.' is too high. Skipping this sample." - # ) - # assert caplog.record_tuples[0][1] == logging.INFO - - logger.propagate = False - - -def test_train_column_mapping(absa_model: AbsaModel, absa_dataset: Dataset) -> None: - absa_dataset = absa_dataset.rename_columns({"text": "sentence", "span": "aspect"}) - trainer = AbsaTrainer( - absa_model, train_dataset=absa_dataset, column_mapping={"sentence": "text", "aspect": "span"} - ) - trainer.train()