From 365cba6bf0204df120c6c2af5231b0fb27a99ad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Pumar?= Date: Wed, 20 Dec 2023 14:21:47 +0100 Subject: [PATCH 01/14] =?UTF-8?q?=F0=9F=94=96=20New=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/package.json | 2 +- src/argilla/_version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index b10f196704..7e4c5d73df 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "argilla", - "version": "1.21.0-dev", + "version": "1.21.0", "private": true, "scripts": { "dev": "nuxt", diff --git a/src/argilla/_version.py b/src/argilla/_version.py index 959d60618a..fae07982da 100644 --- a/src/argilla/_version.py +++ b/src/argilla/_version.py @@ -13,4 +13,4 @@ # limitations under the License. # coding: utf-8 -version = "1.21.0-dev" +version = "1.21.0" From 3ef731cefc80ad6444ae2b98f5418278f6819d16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Pumar?= Date: Wed, 20 Dec 2023 15:34:12 +0100 Subject: [PATCH 02/14] =?UTF-8?q?=F0=9F=8E=81=20xmas=20gift?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/nuxt.config.ts | 2 ++ frontend/plugins/logo/index.ts | 39 ++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 frontend/plugins/logo/index.ts diff --git a/frontend/nuxt.config.ts b/frontend/nuxt.config.ts index b1a08fd909..0e457a4400 100644 --- a/frontend/nuxt.config.ts +++ b/frontend/nuxt.config.ts @@ -58,6 +58,8 @@ const config: NuxtConfig = { // Plugins to run before rendering page (https://go.nuxtjs.dev/config-plugins) plugins: [ + { src: "~/plugins/logo" }, + { src: "~/plugins/directives" }, { src: "~/plugins/di" }, diff --git a/frontend/plugins/logo/index.ts b/frontend/plugins/logo/index.ts new file mode 100644 index 0000000000..5973a89d84 --- /dev/null +++ b/frontend/plugins/logo/index.ts @@ -0,0 +1,39 @@ +/* + * coding=utf-8 + * Copyright 2021-present, the Recognai S.L. team. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export default ({ $config }) => { + // eslint-disable-next-line no-console + console.log( + `%c + ... ...... + .-=+++++++=-. =******- + .=++++++++++++++:-******+ + -+++++++++++++++++=+******- +:++++++++++++++++++*+*******= +++++++++++++++++++**+-*******+: +++++++++++++++++++**+ :+*******+-. +:++++++++++++++++***- .=**********+=--: + -++++++++++++++***- .=************* + .=++++++++++++*+: :+********** + .-=+++++++=-. .-=+***** + ... .. + +© ${new Date().getFullYear()} Argilla (${$config.clientVersion}) + `, + "color:#F88989" + ); +}; From 2691b2c24776416923e481a118039afbfba6ae35 Mon Sep 17 00:00:00 2001 From: Francisco Aranda Date: Wed, 20 Dec 2023 15:32:08 +0100 Subject: [PATCH 03/14] feature: Add list like aggregation support for metadata (#4414) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description This PR adds list support for term metadata values. Closes #4359 **Type of change** (Please delete options that are not relevant. Remember to title the PR according to the type of change) - [ ] New feature (non-breaking change which adds functionality) - [ ] Refactor (change restructuring the codebase without changing functionality) - [X] Improvement (change adding some improvement to an existing functionality) **How Has This Been Tested** (Please describe the tests that you ran to verify your changes. And ideally, reference `tests`) Tested locally with this code snippet: ```python dataset = rg.FeedbackDataset( fields=[rg.TextField(name="text"), rg.TextField(name="optional", required=False)], questions=[rg.TextQuestion(name="question")], metadata_properties=[ rg.TermsMetadataProperty(name="terms-metadata", values=["a", "b", "c"]), rg.IntegerMetadataProperty(name="integer-metadata"), rg.FloatMetadataProperty(name="float-metadata", min=0.0, max=10.0), ], ) ds = dataset.push_to_argilla("ds", workspace="argilla") records = [ rg.FeedbackRecord(fields={"text": "Hello world!"}, metadata={"terms-metadata": "a"}), rg.FeedbackRecord(fields={"text": "Hello world!"}, metadata={"terms-metadata": ["b", "a"]}), ] ds.add_records(records) ``` **Checklist** - [ ] I added relevant documentation - [X] I followed the style guidelines of this project - [X] I did a self-review of my code - [ ] I made corresponding changes to the documentation - [X] My changes generate no new warnings - [X] I have added tests that prove my fix is effective or that my feature works - [ ] I filled out [the contributor form](https://tally.so/r/n9XrxK) (see text above) - [X] I have added relevant notes to the `CHANGELOG.md` file (See https://keepachangelog.com/) --------- Co-authored-by: David Berenstein Co-authored-by: kursathalat <86690946+kursathalat@users.noreply.github.com> --- CHANGELOG.md | 1 + .../create_update_dataset/metadata.md | 23 +++++++++++-- .../create_update_dataset/records.md | 24 ++++++++++++-- .../end2end_examples/add-metadata-003.ipynb | 27 ++++++++++++++- src/argilla/client/feedback/constants.py | 4 ++- .../client/feedback/schemas/metadata.py | 20 ++++++++--- .../server/models/metadata_properties.py | 14 ++++++-- tests/unit/server/api/v1/test_records.py | 33 +++++++++++++++++++ 8 files changed, 130 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e4e74caa2..ccc7b0c11d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ These are the section headers that we use: - Added strategy to handle and translate errors from the server for `401` HTTP status code` ([#4362](https://github.com/argilla-io/argilla/pull/4362)) - Added integration for `textdescriptives` using `TextDescriptivesExtractor` to configure `metadata_properties` in `FeedbackDataset` and `FeedbackRecord`. ([#4400](https://github.com/argilla-io/argilla/pull/4400)). Contributed by @m-newhauser - Added `POST /api/v1/me/responses/bulk` endpoint to create responses in bulk for current user. ([#4380](https://github.com/argilla-io/argilla/pull/4380)) +- Added list support for term metadata properties. (Closes [#4359](https://github.com/argilla-io/argilla/issues/4359)) - Added new CLI task to reindex datasets and records into the search engine. ([#4404](https://github.com/argilla-io/argilla/pull/4404)) ### Changed diff --git a/docs/_source/practical_guides/create_update_dataset/metadata.md b/docs/_source/practical_guides/create_update_dataset/metadata.md index 0a90dbaaa5..19560a3039 100644 --- a/docs/_source/practical_guides/create_update_dataset/metadata.md +++ b/docs/_source/practical_guides/create_update_dataset/metadata.md @@ -72,7 +72,11 @@ dataset.delete_metadata_properties(metadata_properties="groups") ### Format `metadata` -Record metadata can include any information about the record that is not part of the fields in the form of a dictionary. If you want the metadata to correspond with the metadata properties configured for your dataset so that these can be used for filtering and sorting records, make sure that the key of the dictionary corresponds with the metadata property `name`. When the key doesn't correspond, this will be considered extra metadata that will get stored with the record (as long as `allow_extra_metadata` is set to `True` for the dataset), but will not be usable for filtering and sorting. +Record metadata can include any information about the record that is not part of the fields in the form of a dictionary. If you want the metadata to correspond with the metadata properties configured for your dataset so that these can be used for filtering and sorting records, make sure that the key of the dictionary corresponds with the metadata property `name`. When the key doesn't correspond, this will be considered extra metadata that will get stored with the record (as long as `allow_extra_metadata` is set to `True` for the dataset), but will not be usable for filtering and sorting. For any metadata property, you can define a single metadata value in the form of a string or integer, or multiple metadata values in the form of a list of strings or integers. + +::::{tab-set} + +:::{tab-item} Single Metadata ```python record = rg.FeedbackRecord( @@ -80,10 +84,23 @@ record = rg.FeedbackRecord( metadata={"source": "encyclopedia", "text_length":150} ) ``` +::: + +:::{tab-item} Multiple Metadata +```python +record = rg.FeedbackRecord( + fields={...}, + metadata={"source": ["encyclopedia", "wikipedia"], "text_length":150} +) +``` + +::: + +:::: #### Add `metadata` -Once the `metadata_properties` were defined, to add metadata to the records, it slightly depends on whether you are using a `FeedbackDataset` or a `RemoteFeedbackDataset`. For an end-to-end example, check our [tutorial on adding metadata](/tutorials_and_integrations/tutorials/feedback/end2end_examples/add-metadata-003.ipynb). +Once the `metadata_properties` were defined, to add metadata to the records, it slightly depends on whether you are using a `FeedbackDataset` or a `RemoteFeedbackDataset`. For an end-to-end example, check our [tutorial on adding metadata](/tutorials_and_integrations/tutorials/feedback/end2end_examples/add-metadata-003.ipynb). Remember that you can either define a single metadata value for a metadata property or aggregate metadata values for the `TermsMetadataProperty` in the form of a list for the cases where one record falls into multiple metadata categories. ```{note} The dataset not yet pushed to Argilla or pulled from HuggingFace Hub is an instance of `FeedbackDataset` whereas the dataset pulled from Argilla is an instance of `RemoteFeedbackDataset`. The difference between the two is that the former is a local one and the changes made on it stay locally. On the other hand, the latter is a remote one and the changes made on it are directly reflected on the dataset on the Argilla server, which can make your process faster. @@ -202,4 +219,4 @@ for record in dataset: record.metadata["my_metadata"] = "my_value" modified_records.append(record) rg.log(name="my_dataset", records=modified_records) -``` \ No newline at end of file +``` diff --git a/docs/_source/practical_guides/create_update_dataset/records.md b/docs/_source/practical_guides/create_update_dataset/records.md index 525793d18a..53178f83ca 100644 --- a/docs/_source/practical_guides/create_update_dataset/records.md +++ b/docs/_source/practical_guides/create_update_dataset/records.md @@ -22,7 +22,7 @@ After configuring a `FeedbackDataset`, as shown in the [previous guide](/practic record = rg.FeedbackRecord( fields={ "question": "Why can camels survive long without water?", - "answer": "Camels use the fat in their humps to keep them filled with energy and hydration for long periods of time." + "answer": "Camels use the fat in their humps to keep them filled with energy and hydration for long periods." }, metadata={"source": "encyclopedia"}, vectors={"my_vector": [...], "my_other_vector": [...]}, @@ -46,7 +46,12 @@ record = rg.FeedbackRecord( ``` #### Format `metadata` -Record metadata can include any information about the record that is not part of the fields in the form of a dictionary. If you want the metadata to correspond with the metadata properties configured for your dataset so that these can be used for filtering and sorting records, make sure that the key of the dictionary corresponds with the metadata property `name`. When the key doesn't correspond, this will be considered extra metadata that will get stored with the record (as long as `allow_extra_metadata` is set to `True` for the dataset), but will not be usable for filtering and sorting. + +Record metadata can include any information about the record that is not part of the fields in the form of a dictionary. If you want the metadata to correspond with the metadata properties configured for your dataset so that these can be used for filtering and sorting records, make sure that the key of the dictionary corresponds with the metadata property `name`. When the key doesn't correspond, this will be considered extra metadata that will get stored with the record (as long as `allow_extra_metadata` is set to `True` for the dataset), but will not be usable for filtering and sorting. As well as adding one metadata property to a single record, you can also add aggregate metadata values for the `TermsMetadataProperty` in the form of a list. + +::::{tab-set} + +:::{tab-item} Single Metadata ```python record = rg.FeedbackRecord( @@ -54,6 +59,19 @@ record = rg.FeedbackRecord( metadata={"source": "encyclopedia", "text_length":150} ) ``` +::: + +:::{tab-item} Multiple Metadata +```python +record = rg.FeedbackRecord( + fields={...}, + metadata={"source": ["encyclopedia", "wikipedia"], "text_length":150} +) +``` + +::: + +:::: #### Format `vectors` You can associate vectors, like text embeddings, to your records. This will enable the [semantic search](filter_dataset.md#semantic-search) in the UI and the Python SDK. These are saved as a dictionary, where the keys correspond to the `name`s of the vector settings that were configured for your dataset and the value is a list of floats. Make sure that the length of the list corresponds to the dimensions set in the vector settings. @@ -510,4 +528,4 @@ rg.delete_records(name="example-dataset", query="metadata.code=33", discard_only ``` ::: -:::: \ No newline at end of file +:::: diff --git a/docs/_source/tutorials_and_integrations/tutorials/feedback/end2end_examples/add-metadata-003.ipynb b/docs/_source/tutorials_and_integrations/tutorials/feedback/end2end_examples/add-metadata-003.ipynb index 9ef26e6186..5724277807 100644 --- a/docs/_source/tutorials_and_integrations/tutorials/feedback/end2end_examples/add-metadata-003.ipynb +++ b/docs/_source/tutorials_and_integrations/tutorials/feedback/end2end_examples/add-metadata-003.ipynb @@ -300,7 +300,7 @@ "\n", "### TermsMetadataProperty\n", "\n", - "The `TermsMetadaProperty` is a metadata property that can be used to filter the metadata of a record based on a list of possible terms or values." + "The `TermsMetadataProperty` is a metadata property that can be used to filter the metadata of a record based on a list of possible terms or values." ] }, { @@ -439,6 +439,31 @@ "dataset_remote.update_records(modified_records)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Aggregate Metadata Values\n", + "\n", + "In addition, we have the opportunity to add multiple metadata values for the `TermsMetadataProperty` to a single record. This is quite useful when a record falls into multiple categories. For the example case at hand, let us imagine that one of the records (or any number of them) is to be annotated by two groups. We can simply encode this information by giving a list of the metadata values. Let us see how it is done for the local `FeedbackDataset` and it is just the same process for the `RemoteFeedbackDataset` as above." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dataset[1].metadata[\"group\"] = [\"group-1\", \"group-2\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We have seen an example of how to add aggregate metadata values for `TermsMetadataProperty` here. Please note that this is also applicable for `IntegerMetadataProperty` and `FloatMetadataProperty`, and you can add them in the same way." + ] + }, { "cell_type": "markdown", "metadata": {}, diff --git a/src/argilla/client/feedback/constants.py b/src/argilla/client/feedback/constants.py index 784c12144c..1eb7a3be4a 100644 --- a/src/argilla/client/feedback/constants.py +++ b/src/argilla/client/feedback/constants.py @@ -11,6 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +from typing import List, Union from pydantic import StrictFloat, StrictInt, StrictStr @@ -23,7 +24,7 @@ FIELD_TYPE_TO_PYTHON_TYPE = {FieldTypes.text: str} # We are using `pydantic`'s strict types to avoid implicit type conversions METADATA_PROPERTY_TYPE_TO_PYDANTIC_TYPE = { - MetadataPropertyTypes.terms: StrictStr, + MetadataPropertyTypes.terms: Union[StrictStr, List[StrictStr]], MetadataPropertyTypes.integer: StrictInt, MetadataPropertyTypes.float: StrictFloat, } @@ -32,4 +33,5 @@ StrictInt: int, StrictFloat: float, StrictStr: str, + Union[StrictStr, List[StrictStr]]: (str, list), } diff --git a/src/argilla/client/feedback/schemas/metadata.py b/src/argilla/client/feedback/schemas/metadata.py index 5f88f3fc1b..b4ad1716bf 100644 --- a/src/argilla/client/feedback/schemas/metadata.py +++ b/src/argilla/client/feedback/schemas/metadata.py @@ -145,11 +145,21 @@ def server_settings(self) -> Dict[str, Any]: settings["values"] = self.values return settings - def _all_values_exist(self, introduced_value: Optional[str] = None) -> Optional[str]: - if introduced_value is not None and self.values is not None and introduced_value not in self.values: - raise ValueError( - f"Provided '{self.name}={introduced_value}' is not valid, only values in {self.values} are allowed." - ) + def _all_values_exist(self, introduced_value: Optional[Union[str, List[str]]] = None) -> Optional[str]: + if introduced_value is None or self.values is None: + return introduced_value + + if isinstance(introduced_value, str): + values = [introduced_value] + else: + values = introduced_value + + for value in values: + if value not in self.values: + raise ValueError( + f"Provided '{self.name}={value}' is not valid, only values in {self.values} are allowed." + ) + return introduced_value def _validator(self, value: Any) -> Any: diff --git a/src/argilla/server/models/metadata_properties.py b/src/argilla/server/models/metadata_properties.py index 1e3d098328..7a80aaf36c 100644 --- a/src/argilla/server/models/metadata_properties.py +++ b/src/argilla/server/models/metadata_properties.py @@ -43,9 +43,17 @@ class TermsMetadataPropertySettings(BaseMetadataPropertySettings): type: Literal[MetadataPropertyType.terms] values: Optional[List[str]] = None - def check_metadata(self, value: str) -> None: - if self.values is not None and value not in self.values: - raise ValueError(f"'{value}' is not an allowed term.") + def check_metadata(self, value: Union[str, List[str]]) -> None: + if self.values is None: + return + + values = value + if isinstance(values, str): + values = [value] + + for v in values: + if v not in self.values: + raise ValueError(f"'{v}' is not an allowed term.") NT = TypeVar("NT", int, float) diff --git a/tests/unit/server/api/v1/test_records.py b/tests/unit/server/api/v1/test_records.py index e89e0e067b..2a031afbcc 100644 --- a/tests/unit/server/api/v1/test_records.py +++ b/tests/unit/server/api/v1/test_records.py @@ -276,6 +276,39 @@ async def test_update_record_with_no_metadata( } mock_search_engine.index_records.assert_not_called() + async def test_update_record_with_list_terms_metadata( + self, async_client: "AsyncClient", mock_search_engine: SearchEngine, owner_auth_header: dict + ): + dataset = await DatasetFactory.create() + await TermsMetadataPropertyFactory.create(name="terms-metadata-property", dataset=dataset) + record = await RecordFactory.create(dataset=dataset) + + response = await async_client.patch( + f"/api/v1/records/{record.id}", + headers=owner_auth_header, + json={ + "metadata": { + "terms-metadata-property": ["a", "b", "c"], + }, + }, + ) + + assert response.status_code == 200 + assert response.json() == { + "id": str(record.id), + "fields": {"text": "This is a text", "sentiment": "neutral"}, + "metadata": { + "terms-metadata-property": ["a", "b", "c"], + }, + "external_id": record.external_id, + "responses": [], + "suggestions": [], + "vectors": {}, + "inserted_at": record.inserted_at.isoformat(), + "updated_at": record.updated_at.isoformat(), + } + mock_search_engine.index_records.assert_called_once_with(dataset, [record]) + async def test_update_record_with_no_suggestions( self, async_client: "AsyncClient", db: "AsyncSession", mock_search_engine: SearchEngine, owner_auth_header: dict ): From 34fa9627a4332a2d3750f1ced9054e4b5e43cccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Wed, 20 Dec 2023 16:00:01 +0100 Subject: [PATCH 04/14] feat: add `httpx_extra_kwargs` argument to `init` (#4441) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description This PR adds a new argument `httpx_extra_kwargs` to the `rg.init` method and `Argilla` class that allows to pass arguments to the internal `httpx` client used by `Argilla`. **Type of change** - [x] New feature (non-breaking change which adds functionality) **How Has This Been Tested** ```python import argilla as rg ​ rg.init(api_url="http://localhost:6900", api_key="argilla.apikey", httpx_extra_kwargs={"verify": False}) ``` **Checklist** - [ ] I added relevant documentation - [x] follows the style guidelines of this project - [x] I did a self-review of my code - [ ] I made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] I filled out [the contributor form](https://tally.so/r/n9XrxK) (see text above) - [x] I have added relevant notes to the CHANGELOG.md file (See https://keepachangelog.com/) --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- CHANGELOG.md | 1 + frontend/plugins/logo/index.ts | 14 +++++++------- src/argilla/client/client.py | 5 ++++- src/argilla/client/sdk/client.py | 14 +++++++------- src/argilla/client/singleton.py | 8 +++++++- 5 files changed, 26 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ccc7b0c11d..f8698bb3a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ These are the section headers that we use: - Added `POST /api/v1/me/responses/bulk` endpoint to create responses in bulk for current user. ([#4380](https://github.com/argilla-io/argilla/pull/4380)) - Added list support for term metadata properties. (Closes [#4359](https://github.com/argilla-io/argilla/issues/4359)) - Added new CLI task to reindex datasets and records into the search engine. ([#4404](https://github.com/argilla-io/argilla/pull/4404)) +- Added `httpx_extra_kwargs` argument to `rg.init` and `Argilla` to allow passing extra arguments to `httpx.Client` used by `Argilla`. ([#4440](https://github.com/argilla-io/argilla/pull/4441)) ### Changed diff --git a/frontend/plugins/logo/index.ts b/frontend/plugins/logo/index.ts index 5973a89d84..906e753ed1 100644 --- a/frontend/plugins/logo/index.ts +++ b/frontend/plugins/logo/index.ts @@ -19,13 +19,13 @@ export default ({ $config }) => { // eslint-disable-next-line no-console console.log( `%c - ... ...... - .-=+++++++=-. =******- - .=++++++++++++++:-******+ - -+++++++++++++++++=+******- -:++++++++++++++++++*+*******= -++++++++++++++++++**+-*******+: -++++++++++++++++++**+ :+*******+-. + ... ...... + .-=+++++++=-. =******- + .=++++++++++++++:-******+ + -+++++++++++++++++=+******- +:++++++++++++++++++*+*******= +++++++++++++++++++**+-*******+: +++++++++++++++++++**+ :+*******+-. :++++++++++++++++***- .=**********+=--: -++++++++++++++***- .=************* .=++++++++++++*+: :+********** diff --git a/src/argilla/client/client.py b/src/argilla/client/client.py index c510c85097..e9cad1af72 100644 --- a/src/argilla/client/client.py +++ b/src/argilla/client/client.py @@ -95,6 +95,7 @@ def __init__( workspace: Optional[str] = None, timeout: int = 120, extra_headers: Optional[Dict[str, str]] = None, + httpx_extra_kwargs: Optional[Dict[str, Any]] = None, ): """ Inits `Argilla` instance. @@ -114,7 +115,8 @@ def __init__( timeout: Wait `timeout` seconds for the connection to timeout. Default: 60. extra_headers: Extra HTTP headers sent to the server. You can use this to customize the headers of argilla client requests, like additional security restrictions. Default: `None`. - + httpx_extra_kwargs: Extra kwargs passed to the `httpx.Client` constructor. For more information about the + available arguments, see https://www.python-httpx.org/api/#client. Defaults to `None`. """ from argilla.client.login import ArgillaCredentials @@ -146,6 +148,7 @@ def __init__( token=api_key, timeout=timeout, headers=headers.copy(), + httpx_extra_kwargs=httpx_extra_kwargs, ) self._user = users_api.whoami(client=self.http_client) # .parsed diff --git a/src/argilla/client/sdk/client.py b/src/argilla/client/sdk/client.py index 8e12b2f7d4..a72e5fdc64 100644 --- a/src/argilla/client/sdk/client.py +++ b/src/argilla/client/sdk/client.py @@ -19,7 +19,7 @@ import json import uuid from json import JSONEncoder -from typing import Dict, Optional, TypeVar +from typing import Any, Dict, Optional, TypeVar from urllib.parse import urlparse import httpx @@ -36,6 +36,7 @@ class _ClientCommonDefaults: cookies: Dict[str, str] = dataclasses.field(default_factory=dict) headers: Dict[str, str] = dataclasses.field(default_factory=dict) timeout: float = 5.0 + httpx_extra_kwargs: Optional[Dict[str, Any]] = None def get_headers(self) -> Dict[str, str]: """Get headers to be used in all endpoints""" @@ -94,11 +95,13 @@ def default(self, o): class Client(_ClientCommonDefaults, _Client): def __post_init__(self): super().__post_init__() + self.httpx_extra_kwargs = self.httpx_extra_kwargs or {} self.__httpx__ = httpx.Client( base_url=self.base_url, headers=self.get_headers(), cookies=self.get_cookies(), timeout=self.get_timeout(), + **self.httpx_extra_kwargs, ) # TODO: Remove this NOW!!!! self.__http_async__ = httpx.AsyncClient( @@ -115,17 +118,14 @@ def __hash__(self): return hash(self.base_url) def with_httpx_error_handler(func): - def wrap_error(base_url: str): - err_str = f"Your Api endpoint at {base_url} is not available or not responding." - raise BaseClientError(err_str) from None - @functools.wraps(func) def inner(self, *args, **kwargs): try: result = func(self, *args, **kwargs) return result - except httpx.ConnectError as err: # noqa: F841 - return wrap_error(self.base_url) + except httpx.ConnectError as err: + err_str = f"Your Api endpoint at {self.base_url} is not available or not responding: {err}" + raise BaseClientError(err_str) from err return inner diff --git a/src/argilla/client/singleton.py b/src/argilla/client/singleton.py index 431e71b3e8..9e5ae78f32 100644 --- a/src/argilla/client/singleton.py +++ b/src/argilla/client/singleton.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Dict, Optional +from typing import Any, Dict, Optional from argilla.client.client import Argilla @@ -40,6 +40,7 @@ def init( workspace: Optional[str] = None, timeout: int = 60, extra_headers: Optional[Dict[str, str]] = None, + httpx_extra_kwargs: Optional[Dict[str, Any]] = None, ) -> Argilla: cls._INSTANCE = None @@ -49,6 +50,7 @@ def init( timeout=timeout, workspace=workspace, extra_headers=extra_headers, + httpx_extra_kwargs=httpx_extra_kwargs, ) return cls._INSTANCE @@ -60,6 +62,7 @@ def init( workspace: Optional[str] = None, timeout: int = 60, extra_headers: Optional[Dict[str, str]] = None, + httpx_extra_kwargs: Optional[Dict[str, Any]] = None, ) -> None: """Init the Python client. @@ -78,6 +81,8 @@ def init( timeout: Wait `timeout` seconds for the connection to timeout. Default: 60. extra_headers: Extra HTTP headers sent to the server. You can use this to customize the headers of argilla client requests, like additional security restrictions. Default: `None`. + httpx_extra_kwargs: Extra kwargs passed to the `httpx.Client` constructor. For more information about the + available arguments, see https://www.python-httpx.org/api/#client. Defaults to `None`. Examples: >>> import argilla as rg @@ -93,6 +98,7 @@ def init( workspace=workspace, timeout=timeout, extra_headers=extra_headers, + httpx_extra_kwargs=httpx_extra_kwargs, ) From 3b6f74be475cb14ea7b6d0e54a469ab7873346b7 Mon Sep 17 00:00:00 2001 From: Sara Han <127759186+sdiazlor@users.noreply.github.com> Date: Wed, 20 Dec 2023 16:17:45 +0100 Subject: [PATCH 05/14] docs: UI improve no dataset page to transform it into an onboarding page for first time users developers (#4398) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Onboarding message: - Welcome - Explain FeedbackDataset and other Datasets - Encourage open a quickstart notebook (where indicated how to connect to Argilla and follow the workflow) - References - Encourage contribution and contact to us Note: The links in the notebooks were modified by the complete URL, otherwise they do not work if opened in Colab or Jupyter. Closes #4274 **Type of change** (Remember to title the PR according to the type of change) - [x] Documentation update **How Has This Been Tested** (Please describe the tests that you ran to verify your changes.) - [x] `sphinx-autobuild` (read [Developer Documentation](https://docs.argilla.io/en/latest/community/developer_docs.html#building-the-documentation) for more details) **Checklist** - [ ] I added relevant documentation - [ ] I followed the style guidelines of this project - [ ] I did a self-review of my code - [ ] I made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I filled out [the contributor form](https://tally.so/r/n9XrxK) (see text above) - [ ] I have added relevant notes to the `CHANGELOG.md` file (See https://keepachangelog.com/) --------- Co-authored-by: davidberenstein1957 Co-authored-by: Daniel Vila Suero Co-authored-by: leiyre Co-authored-by: leire Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- docs/_source/_common/snippets/start_page.md | 98 +- .../getting_started/quickstart_workflow.ipynb | 5954 +++++++---------- .../quickstart_workflow_feedback.ipynb | 3149 +++++---- .../datasets-empty/DatasetsEmpty.vue | 137 +- frontend/static/images/logo.svg | 51 + scripts/end2end_examples.py | 25 +- 6 files changed, 4384 insertions(+), 5030 deletions(-) create mode 100644 frontend/static/images/logo.svg diff --git a/docs/_source/_common/snippets/start_page.md b/docs/_source/_common/snippets/start_page.md index 519a4271a4..a40e46b0b5 100644 --- a/docs/_source/_common/snippets/start_page.md +++ b/docs/_source/_common/snippets/start_page.md @@ -1,36 +1,88 @@ -::::{tab-set} +
-:::{tab-item} Feedback datasets +# Welcome to -```python -# install datasets library with pip install datasets -import argilla as rg -from datasets import load_dataset +## Argilla is a platform to build high-quality AI datasets + +If you need support join the [Argilla Slack community](https://join.slack.com/t/rubrixworkspace/shared_invite/zt-whigkyjn-a3IUJLD7gDbTZ0rKlvcJ5g) + +
+ +
+ +Get started by publishing your first dataset. -# load an Argilla Feedback Dataset from the Hugging Face Hub -# look for other datasets at https://huggingface.co/datasets?other=argilla -dataset = rg.FeedbackDataset.from_huggingface("argilla/oasst_response_quality", split="train") +### 1. Open an IDE, Jupyter or Collab -# push the dataset to Argilla -dataset.push_to_argilla("oasst_response_quality") +If you're a Collab user, you can directly use our [introductory tutorial](https://colab.research.google.com/github/argilla-io/argilla/blob/develop/docs/_source/getting_started/quickstart_workflow_feedback.ipynb). + +### 2. Install the SDK with pip + +To work with Argilla datasets, you need to use the Argilla SDK. You can install the SDK with pip as follows: + +```sh +pip install argilla -U ``` -::: -:::{tab-item} Other datasets +### 3. Connect to your Argilla server + +Get your `ARGILLA_API_URL`: + +- If you are using Docker, it is the URL shown in your browser (by default `http://localhost:6900`) +- If you are using HF Spaces, it should be constructed as follows: `https://[your-owner-name]-[your_space_name].hf.space` + +Get your `ARGILLA_API_KEY` you find in ["My settings"](/user-settings) and copy the API key. + +Make sure to replace `ARGILLA_API_URL` and `ARGILLA_API_KEY` in the code below. If you are using a private HF Space, you need to specify your `HF_TOKEN` which can be found [here](https://huggingface.co/settings/tokens). ```python -# install datasets library with pip install datasets import argilla as rg -from datasets import load_dataset -# load dataset from the hub -dataset = load_dataset("argilla/gutenberg_spacy-ner", split="train") +rg.init( + api_url="ARGILLA_API_URL", + api_key="ARGILLA_API_KEY", + # extra_headers={"Authorization": f"Bearer {"HF_TOKEN"}"} +) +``` + +### 4. Create your first dataset + +Specify a workspace where the dataset will be created. Check your workspaces in ["My settings"](/user_settings). To create a new workspace, check the [docs](https://docs.argilla.io/en/latest/getting_started/installation/configurations/workspace_management.html). + +Create a Dataset with two labels ("sadness" and "joy"). Don't forget to replace "". Here, we are using a task template, check the docs to [create a fully custom dataset](https://docs.argilla.io/en/latest/practical_guides/create_update_dataset/create_dataset.html). + +```python +dataset = rg.FeedbackDataset.for_text_classification( + labels=["sadness", "joy"], + multi_label=False, + use_markdown=True, + guidelines=None, + metadata_properties=None, + vectors_settings=None, +) +dataset.push_to_argilla(name="my-first-dataset", workspace="") +``` + +### 5. Add records -# read in dataset, assuming its a dataset for token classification -dataset_rg = rg.read_datasets(dataset, task="TokenClassification") +Create a list with the records you want to add. Ensure that you match the fields with the ones specified in the previous step. -# log the dataset -rg.log(dataset_rg, "gutenberg_spacy-ner") +You can also use `pandas` or `load_dataset` to [read an existing dataset and create records from it](https://docs.argilla.io/en/latest/practical_guides/create_update_dataset/records.html#add-records). + +```python +records = [ + rg.FeedbackRecord( + fields={ + "text": "I am so happy today", + }, + ), + rg.FeedbackRecord( + fields={ + "text": "I feel sad today", + }, + ) +] +dataset.add_records(records) ``` -::: -:::: \ No newline at end of file + +
diff --git a/docs/_source/getting_started/quickstart_workflow.ipynb b/docs/_source/getting_started/quickstart_workflow.ipynb index 91685bde19..5ca3cb9475 100644 --- a/docs/_source/getting_started/quickstart_workflow.ipynb +++ b/docs/_source/getting_started/quickstart_workflow.ipynb @@ -1,3369 +1,2613 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "8b1cd645", - "metadata": { - "id": "8b1cd645" - }, - "source": [ - "# Workflow of Other Datasets\n", - "Welcome! This will cover a default workflow from logging data to preparing for training.\n", - "\n", - "
\n", - "\n", - "Note\n", - "\n", - "This workflow covers the `DatasetForTextClassification`, `DatasetForTokenClassification`, and `DatasetForText2Text`. The workflow for `FeedbackDataset` can be found [here](../getting_started/quickstart_workflow_feedback.html). Not sure which dataset to use? Check out our section on [choosing a dataset](../practical_guides/choose_dataset.html).\n", - "\n", - "
" - ] - }, - { - "cell_type": "markdown", - "id": "EwDfn8E7W7jD", - "metadata": { - "id": "EwDfn8E7W7jD" - }, - "source": [ - "## Install Libraries\n", - "\n", - "Install the latest version of Argilla in Colab, along with other libraries and models used in this notebook." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "CzxpuhdoW-h6", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "CzxpuhdoW-h6", - "outputId": "712e29de-8ce0-45aa-bd1d-f0c55422183d" - }, - "outputs": [], - "source": [ - "!pip install argilla datasets transformers evaluate spacy-transformers transformers[torch]\n", - "!python -m spacy download de_core_news_sm" - ] - }, - { - "cell_type": "markdown", - "id": "UL17lWRKXwOI", - "metadata": { - "id": "UL17lWRKXwOI" - }, - "source": [ - "## Set Up Argilla\n", - "\n", - "You can quickly deploy Argilla Server on [HF Spaces](https://huggingface.co/new-space?template=argilla/argilla-template-space).\n", - "\n", - "Alternatively, if you want to run Argilla locally on your own computer, the easiest way to get Argilla UI up and running is to deploy on Docker:\n", - "\n", - "```\n", - "docker run -d --name quickstart -p 6900:6900 argilla/argilla-quickstart:latest\n", - "```\n", - "\n", - "More info on Installation [here](../getting_started/installation/deployments/deployments.html)." - ] - }, - { - "cell_type": "markdown", - "id": "00b2e199", - "metadata": { - "id": "00b2e199" - }, - "source": [ - "## Connect to Argilla\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "id": "klwYMH_rdv0m", - "metadata": { - "id": "klwYMH_rdv0m" - }, - "source": [ - "It is possible to connect to our Argilla instance by simply importing the Argilla library, which internally connects to the Argilla Server using the `ARGILLA_API_URL` and `ARGILLA_API_KEY` environment variables." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "IiJiKTi-dgLp", - "metadata": { - "id": "IiJiKTi-dgLp" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "# Set your variable here\n", - "os.environ[\"ARGILLA_API_URL\"] = \"your_argilla_URL\"\n", - "os.environ[\"ARGILLA_API_KEY\"] = \"owner.apikey\"\n", - "# os.environ['HF_TOKEN'] = \"your-hf-token\" # if you want to use your private HF Space" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "gG8wH9l7bXEm", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "gG8wH9l7bXEm", - "outputId": "44fe5b13-0ddb-4106-865a-6b6e3afe3c32" - }, - "outputs": [], - "source": [ - "import argilla as rg\n", - "\n", - "rg.init(workspace=\"admin\")\n", - "\n", - "# If you want to use your private HF Space\n", - "# rg.init(workspace=\"admin\", extra_headers={\"Authorization\": f\"Bearer {os.environ['HF_TOKEN']}\"})" - ] - }, - { - "cell_type": "markdown", - "id": "2ZyEUBBjbK7k", - "metadata": { - "id": "2ZyEUBBjbK7k" - }, - "source": [ - "`\"owner.apikey\"` is the default value for `ARGILLA_API_KEY` variable.\n", - "\n", - "`admin` is the name of the default workspace. A **workspace** is a “space” inside your Argilla instance where authorized users can collaborate.\n", - "\n", - "If you want to initialize a connection manually you can use `rg.init()`. For more info about custom configurations like headers and workspace separation, check our [config page](../getting_started/installation/configurations/configurations.html).\n", - "\n", - "If you want to customize the access credentials, take a look at our [user management section](../getting_started/installation/configurations/user_management.html)." - ] - }, - { - "cell_type": "markdown", - "id": "ae0038d1-86b1-4eb9-ada4-ae561ad25aa3", - "metadata": { - "id": "ae0038d1-86b1-4eb9-ada4-ae561ad25aa3" - }, - "source": [ - "## Upload data\n", - "\n", - "The main component of the Argilla data model is called a **record**. Records can be of different types depending on the currently supported tasks:\n", - "\n", - " 1. `TextClassificationRecord`\n", - " 2. `TokenClassificationRecord`\n", - " 3. `Text2TextRecord`\n", - "\n", - "The most critical attributes of a record that are common to all types are:\n", - "\n", - " - `text`: The input text of the record (Required);\n", - " - `annotation`: Annotate your record in a task-specific manner (Optional);\n", - " - `prediction`: Add task-specific model predictions to the record (Optional);\n", - " - `metadata`: Add some arbitrary metadata to the record (Optional);\n", - "\n", - "A [Dataset](../conceptual_guides/data_model.html#other-datasets) in Argilla is a collection of records of the same type." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "866426c8-b3af-4307-a3eb-3d50171e4b7f", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 135, - "referenced_widgets": [ - "83d7b92514e24e3d88f7207ec26a6b76", - "9146f98b81744bceb0083e98aa1c3af9", - "5406303479514972b3e5241ce40e3f70", - "21a09c7691c64e088a74c08095df0057", - "d577c60f0dbe46c1be6fab55672c4f86", - "cf5da266e6ad493da229cf05469b2fc4" - ] - }, - "id": "866426c8-b3af-4307-a3eb-3d50171e4b7f", - "outputId": "164125ed-722a-4e2e-9360-83cf8f2ad847" - }, - "outputs": [], - "source": [ - "# Create a basic text classification record\n", - "textcat_record = rg.TextClassificationRecord(\n", - " text=\"Hello world, this is me!\",\n", - " prediction=[(\"LABEL1\", 0.8), (\"LABEL2\", 0.2)],\n", - " annotation=\"LABEL1\",\n", - " multi_label=False,\n", - ")\n", - "\n", - "# Create a basic token classification record\n", - "tokencat_record = rg.TokenClassificationRecord(\n", - " text=\"Michael is a professor at Harvard\",\n", - " tokens=[\"Michael\", \"is\", \"a\", \"professor\", \"at\", \"Harvard\"],\n", - " prediction=[(\"NAME\", 0, 7), (\"LOC\", 26, 33)],\n", - ")\n", - "\n", - "# Create a basic text2text record\n", - "text2text_record = rg.Text2TextRecord(\n", - " text=\"My name is Sarah and I love my dog.\",\n", - " prediction=[\"Je m'appelle Sarah et j'aime mon chien.\"],\n", - ")\n", - "\n", - "# Upload (log) the records to corresponding datasets in the Argilla web app\n", - "rg.log(textcat_record, \"my_textcat_dataset\")\n", - "rg.log(tokencat_record, \"my_tokencat_dataset\")\n", - "rg.log(tokencat_record, \"my_text2text_dataset\")" - ] - }, - { - "cell_type": "markdown", - "id": "c84db0f9-a9ca-4799-9a26-635d2f3b94d4", - "metadata": { - "id": "c84db0f9-a9ca-4799-9a26-635d2f3b94d4" - }, - "source": [ - "Now you can access your datasets in the Argilla web app and look at your first records.\n", - "\n", - "However, most of the time, you will have your data in some file format, like TXT, CSV, or JSON.\n", - "Argilla relies on two well-known Python libraries to read these files: [pandas](https://pandas.pydata.org/) and [datasets](https://huggingface.co/docs/datasets/index).\n", - "After reading the files with one of those libraries, Argilla provides shortcuts to create your records automatically.\n", - "\n", - "Let's look at a few examples for each of the record types.\n", - "\n", - "**As mentioned earlier, you choose the record type depending on the task you want to tackle.**" - ] - }, - { - "cell_type": "markdown", - "id": "c4137fd2-cc98-4f59-a14e-31cd7489d59b", - "metadata": { - "id": "c4137fd2-cc98-4f59-a14e-31cd7489d59b" - }, - "source": [ - "### 1. TextClassification" - ] - }, - { - "cell_type": "markdown", - "id": "c1004cfb-6fed-4281-950f-1f19495cd114", - "metadata": { - "id": "c1004cfb-6fed-4281-950f-1f19495cd114" - }, - "source": [ - "In this example, we will read a [CSV file](https://www.kaggle.com/datasets/databar/10k-snapchat-reviews) from a Kaggle competition that contains reviews for the Snapchat app.\n", - "The underlying task here could be to classify the reviews by their sentiment.\n", - "\n", - "Let us read the file with [pandas](https://pandas.pydata.org/)\n", - "\n", - "
\n", - "\n", - "Note\n", - " \n", - "If the file is too big to fit in memory, try using the [datasets library](https://huggingface.co/docs/datasets/index) with no memory constraints, as shown in the next section.\n", - " \n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d4ae148b-4d91-49ef-a7d1-6073ce8f2077", - "metadata": { - "id": "d4ae148b-4d91-49ef-a7d1-6073ce8f2077" - }, - "outputs": [], - "source": [ - "import pandas as pd\n", - "\n", - "# Read the CSV file into a pandas DataFrame\n", - "dataframe = pd.read_csv(\"Snapchat_app_store_reviews.csv\")" - ] - }, - { - "cell_type": "markdown", - "id": "2ae293cb-4349-4d04-926f-e3abf0c3afad", - "metadata": { - "id": "2ae293cb-4349-4d04-926f-e3abf0c3afad" - }, - "source": [ - "and have a quick look at the first three rows of the resulting [pandas DataFrame](https://pandas.pydata.org/docs/getting_started/intro_tutorials/01_table_oriented.html):" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3eb22d64-b15c-42d6-a43c-8a6f11c2bf5f", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 143 - }, - "id": "3eb22d64-b15c-42d6-a43c-8a6f11c2bf5f", - "outputId": "1d993f6c-4861-48ce-ec0d-b157acb5b2e9" - }, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - "
\n", - "
\n", - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Unnamed: 0userNameratingreviewisEditeddatetitle
00Savvanananahhh4For the most part I quite enjoy Snapchat it’s ...False10/4/20 6:01Performance issues
11Idek 9-1011123I’m sorry to say it, but something is definite...False10/14/20 2:13What happened?
22William Quintana3Snapchat update ruined my story organization! ...False7/31/20 19:54STORY ORGANIZATION RUINED!
\n", - "
\n", - " \n", - "\n", - "\n", - "\n", - "
\n", - " \n", - "
\n", - "\n", - "\n", - "\n", - " \n", - "\n", - " \n", - " \n", - "\n", - " \n", - "
\n", - "
\n" - ], - "text/plain": [ - " Unnamed: 0 userName rating \\\n", - "0 0 Savvanananahhh 4 \n", - "1 1 Idek 9-101112 3 \n", - "2 2 William Quintana 3 \n", - "\n", - " review isEdited date \\\n", - "0 For the most part I quite enjoy Snapchat it’s ... False 10/4/20 6:01 \n", - "1 I’m sorry to say it, but something is definite... False 10/14/20 2:13 \n", - "2 Snapchat update ruined my story organization! ... False 7/31/20 19:54 \n", - "\n", - " title \n", - "0 Performance issues \n", - "1 What happened? \n", - "2 STORY ORGANIZATION RUINED! " - ] - }, - "execution_count": 146, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "dataframe.head(3)" - ] - }, - { - "cell_type": "markdown", - "id": "24f7560f-0929-478a-ae4e-62274a1f04c5", - "metadata": { - "id": "24f7560f-0929-478a-ae4e-62274a1f04c5" - }, - "source": [ - "We will choose the _review_ column as input text for our records.\n", - "For Argilla to know, we must rename the corresponding column to _text_.\n", - "\n", - "We will choose the _rating_ column as label.\n", - "For Argilla to know, we must rename the corresponding column to _annotation_.\n", - "\n", - "Other columns can be conveniently wrapped in a dictionary and mapped as _metadata_ column, as expected by the [TextClassificationRecord](https://docs.argilla.io/en/latest/reference/python/python_client.html#argilla.client.models.TextClassificationRecord) class." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "OUbcZnHtkZnj", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 206 - }, - "id": "OUbcZnHtkZnj", - "outputId": "05a2ad0a-a377-40fb-eedb-3c2f1a492550" - }, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - "
\n", - "
\n", - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
annotationtextmetadata
04For the most part I quite enjoy Snapchat it’s ...{'userName': 'Savvanananahhh', 'isEdited': Fal...
13I’m sorry to say it, but something is definite...{'userName': 'Idek 9-101112', 'isEdited': Fals...
23Snapchat update ruined my story organization! ...{'userName': 'William Quintana', 'isEdited': F...
35I really love the app for how long i have been...{'userName': 'an gonna be unkown😏', 'isEdited'...
41This is super frustrating. I was in the middle...{'userName': 'gzhangziqi', 'isEdited': False, ...
\n", - "
\n", - " \n", - "\n", - "\n", - "\n", - "
\n", - " \n", - "
\n", - "\n", - "\n", - "\n", - " \n", - "\n", - " \n", - " \n", - "\n", - " \n", - "
\n", - "
\n" - ], - "text/plain": [ - " annotation text \\\n", - "0 4 For the most part I quite enjoy Snapchat it’s ... \n", - "1 3 I’m sorry to say it, but something is definite... \n", - "2 3 Snapchat update ruined my story organization! ... \n", - "3 5 I really love the app for how long i have been... \n", - "4 1 This is super frustrating. I was in the middle... \n", - "\n", - " metadata \n", - "0 {'userName': 'Savvanananahhh', 'isEdited': Fal... \n", - "1 {'userName': 'Idek 9-101112', 'isEdited': Fals... \n", - "2 {'userName': 'William Quintana', 'isEdited': F... \n", - "3 {'userName': 'an gonna be unkown😏', 'isEdited'... \n", - "4 {'userName': 'gzhangziqi', 'isEdited': False, ... " - ] - }, - "execution_count": 147, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "#wrap metadata in a dictionary\n", - "def metadata_to_dict(dataframe):\n", - " metadata = {}\n", - " metadata[\"userName\"] = dataframe[\"userName\"]\n", - " metadata[\"isEdited\"] = dataframe[\"isEdited\"]\n", - " metadata[\"date\"] = dataframe[\"date\"]\n", - " metadata[\"title\"] = dataframe[\"title\"]\n", - " return metadata\n", - "\n", - "dataframe[\"metadata\"] = dataframe.apply(metadata_to_dict, axis=1)\n", - "\n", - "# Drop unused the columns\n", - "dataframe = dataframe.drop(\n", - " [\"Unnamed: 0\", \"userName\", \"isEdited\", \"date\", \"title\"],\n", - " axis=1\n", - ")\n", - "\n", - "# Rename the 'review' column to 'text',\n", - "dataframe = dataframe.rename(\n", - " columns={\"review\": \"text\", \"rating\":\"annotation\"}\n", - ")\n", - "\n", - "dataframe.head()" - ] - }, - { - "cell_type": "markdown", - "id": "a191e8c9-e55a-41b2-ad24-b0860fd31445", - "metadata": { - "id": "a191e8c9-e55a-41b2-ad24-b0860fd31445" - }, - "source": [ - "We can now read this `DataFrame` with Argilla, which will automatically create the records and put them in a Dataset" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f2f63eda-d041-4849-8828-f3de0b25cb1a", - "metadata": { - "id": "f2f63eda-d041-4849-8828-f3de0b25cb1a" - }, - "outputs": [], - "source": [ - "import argilla as rg\n", - "\n", - "# Read DataFrame into a Argilla Dataset\n", - "dataset_rg = rg.read_pandas(dataframe, task=\"TextClassification\")" - ] - }, - { - "cell_type": "markdown", - "id": "ebf825a9-7aaf-4b43-970d-6b4c2d493bb6", - "metadata": { - "id": "ebf825a9-7aaf-4b43-970d-6b4c2d493bb6" - }, - "source": [ - "We will upload this dataset to the web app and give it the name *snapchat_reviews*" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ea1c0cb7-f129-45e7-8784-88908d882104", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 69 - }, - "id": "ea1c0cb7-f129-45e7-8784-88908d882104", - "outputId": "3cf04921-2715-4e56-e919-5e9ecb8b1d47" - }, - "outputs": [], - "source": [ - "# Upload (log) the Dataset to the web app\n", - "rg.log(dataset_rg, \"snapchat_reviews\")" - ] - }, - { - "cell_type": "markdown", - "id": "z4Pn5MTqxJw9", - "metadata": { - "id": "z4Pn5MTqxJw9" - }, - "source": [ - "You can configure labels programmatically by using `configure_dataset_settings` method:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fNIHsEWOwlKy", - "metadata": { - "id": "fNIHsEWOwlKy" - }, - "outputs": [], - "source": [ - "labels = [\"1\", \"2\", \"3\", \"4\" , \"5\"]\n", - "settings = rg.TextClassificationSettings(label_schema=labels)\n", - "rg.configure_dataset_settings(name=\"snapchat_reviews\", settings=settings)" - ] - }, - { - "cell_type": "markdown", - "id": "930cb8c3-5dfc-4e5a-bdf4-3c19f3d5fb00", - "metadata": { - "id": "930cb8c3-5dfc-4e5a-bdf4-3c19f3d5fb00" - }, - "source": [ - "\n", - "\n", - "![Screenshot of the uploaded snapchat reviews](https://github.com/argilla-io/argilla/blob/main/docs/_source/_static/reference/webapp/explore-text-classification.png?raw=1)" - ] - }, - { - "cell_type": "markdown", - "id": "341abb81-2acd-411e-a1d3-7c54cfc257f8", - "metadata": { - "id": "341abb81-2acd-411e-a1d3-7c54cfc257f8" - }, - "source": [ - "### 2. TokenClassification" - ] - }, - { - "cell_type": "markdown", - "id": "59944e44-4202-4890-9a45-f99fc3fb2dd1", - "metadata": { - "id": "59944e44-4202-4890-9a45-f99fc3fb2dd1" - }, - "source": [ - "We will use German reviews of organic coffees in a [CSV file](https://www.kaggle.com/datasets/mldado/german-online-reviewsratings-of-organic-coffee) for this example.\n", - "The underlying task here could be to extract all attributes of an organic coffee.\n", - "\n", - "This time, let's read the file with [datasets](https://huggingface.co/docs/datasets/index)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "502febcb-26f1-4832-8218-4f029ebed697", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "502febcb-26f1-4832-8218-4f029ebed697", - "outputId": "fac8ed08-e753-42ce-b77a-a3c56cc3d3fe" - }, - "outputs": [], - "source": [ - "from datasets import Dataset\n", - "\n", - "# Read the csv file\n", - "dataset = Dataset.from_csv(\"kaffee_reviews.csv\")\n" - ] - }, - { - "cell_type": "markdown", - "id": "d29c2276-7cae-41e5-ba3b-157a8c1a6c6e", - "metadata": { - "id": "d29c2276-7cae-41e5-ba3b-157a8c1a6c6e" - }, - "source": [ - "and have a quick look at the first three rows of the resulting [dataset Dataset](https://huggingface.co/docs/datasets/access):" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1b77947c-ed89-4dce-ba75-158264c8d384", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 143 - }, - "id": "1b77947c-ed89-4dce-ba75-158264c8d384", - "outputId": "05dd4779-1d0f-4f8f-8aab-b696bef2a1eb" - }, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - "
\n", - "
\n", - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Unnamed: 0brandratingreview
00GEPA Kaffee5Wenn ich Bohnenkaffee trinke (auf Arbeit trink...
11GEPA Kaffee5Für mich ist dieser Kaffee ideal. Die Grundvor...
22GEPA Kaffee5Ich persönlich bin insbesondere von dem Geschm...
\n", - "
\n", - " \n", - "\n", - "\n", - "\n", - "
\n", - " \n", - "
\n", - "\n", - "\n", - "\n", - " \n", - "\n", - " \n", - " \n", - "\n", - " \n", - "
\n", - "
\n" - ], - "text/plain": [ - " Unnamed: 0 brand rating \\\n", - "0 0 GEPA Kaffee 5 \n", - "1 1 GEPA Kaffee 5 \n", - "2 2 GEPA Kaffee 5 \n", - "\n", - " review \n", - "0 Wenn ich Bohnenkaffee trinke (auf Arbeit trink... \n", - "1 Für mich ist dieser Kaffee ideal. Die Grundvor... \n", - "2 Ich persönlich bin insbesondere von dem Geschm... " - ] - }, - "execution_count": 78, - "metadata": {}, - "output_type": "execute_result" - } + "cells": [ + { + "cell_type": "markdown", + "id": "8b1cd645", + "metadata": { + "id": "8b1cd645" + }, + "source": [ + "# Workflow of Other Datasets\n", + "Welcome! This will cover a default workflow from logging data to preparing for training.\n", + "\n", + "
\n", + "\n", + "Note\n", + "\n", + "This workflow covers the `DatasetForTextClassification`, `DatasetForTokenClassification`, and `DatasetForText2Text`. The workflow for `FeedbackDataset` can be found [here](https://docs.argilla.io/en/latest/getting_started/quickstart_workflow_feedback.html). Not sure which dataset to use? Check out our section on [choosing a dataset](https://docs.argilla.io/en/latest/practical_guides/choose_dataset.html).\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "EwDfn8E7W7jD", + "metadata": { + "id": "EwDfn8E7W7jD" + }, + "source": [ + "## Install Libraries\n", + "\n", + "Install the latest version of Argilla in Colab, along with other libraries and models used in this notebook." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "CzxpuhdoW-h6", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "CzxpuhdoW-h6", + "outputId": "712e29de-8ce0-45aa-bd1d-f0c55422183d" + }, + "outputs": [], + "source": [ + "!pip install argilla datasets transformers evaluate spacy-transformers transformers[torch] requests\n", + "!python -m spacy download en_core_web_sm" + ] + }, + { + "cell_type": "markdown", + "id": "UL17lWRKXwOI", + "metadata": { + "id": "UL17lWRKXwOI" + }, + "source": [ + "## Set Up Argilla\n", + "\n", + "If you have already deployed Argilla Server, then you can skip this step. Otherwise, you can quickly deploy it in two different ways:\n", + "\n", + "* You can deploy Argilla Server on [HF Spaces](https://huggingface.co/new-space?template=argilla/argilla-template-space).\n", + "\n", + "* Alternatively, if you want to run Argilla locally on your own computer, the easiest way to get Argilla UI up and running is to deploy on Docker:\n", + "\n", + " ```\n", + " docker run -d --name quickstart -p 6900:6900 argilla/argilla-quickstart:latest\n", + " ```\n", + "\n", + "More info on Installation [here](../getting_started/installation/deployments/deployments.html)." + ] + }, + { + "cell_type": "markdown", + "id": "00b2e199", + "metadata": { + "id": "00b2e199" + }, + "source": [ + "## Connect to Argilla\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "klwYMH_rdv0m", + "metadata": { + "id": "klwYMH_rdv0m" + }, + "source": [ + "It is possible to connect to our Argilla instance by simply importing the Argilla library and using the environment variables and `rg.init()`.\n", + "\n", + "* `ARGILLA_API_URL`: It is the url of the Argilla Server.\n", + " * If you're using Docker, it is `http://localhost:6900` by default.\n", + " * If you're using HF Spaces, it is constructed as `https://[your-owner-name]-[your_space_name].hf.space`.\n", + "* `ARGILLA_API_KEY`: It is the API key of the Argilla Server. It is `owner` by default.\n", + "* `HF_TOKEN`: It is the Hugging Face API token. It is only needed if you're using a [private HF Space](https://docs.argilla.io/en/latest/getting_started/installation/deployments/huggingface-spaces.html#deploy-argilla-on-spaces). You can configure it in your profile: [Setting > Access Tokens](https://huggingface.co/settings/tokens).\n", + "* `workspace`: It is a “space” inside your Argilla instance where authorized users can collaborate. It's `argilla` by default.\n", + "\n", + "For more info about custom configurations like headers, workspace separation or access credentials, check our [config page](https://docs.argilla.io/en/latest/getting_started/installation/configurations/configurations.html)." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "ea169237", + "metadata": {}, + "outputs": [], + "source": [ + "import argilla as rg\n", + "from argilla._constants import DEFAULT_API_KEY" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ca4fd7ae-7e31-405e-84c1-974828a903bd", + "metadata": { + "editable": true, + "papermill": { + "duration": null, + "end_time": null, + "exception": null, + "start_time": null, + "status": "completed" + }, + "slideshow": { + "slide_type": "" + }, + "tags": [ + "parameters" + ] + }, + "outputs": [], + "source": [ + "# Argilla credentials\n", + "api_url = \"http://localhost:6900\" # \"https://.hf.space\"\n", + "api_key = DEFAULT_API_KEY # admin.apikey\n", + "# Huggingface credentials\n", + "hf_token = \"hf_...\"" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "728e6af7", + "metadata": { + "editable": true, + "papermill": { + "duration": null, + "end_time": null, + "exception": null, + "start_time": null, + "status": "completed" + }, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Users\\sarah\\Documents\\argilla\\src\\argilla\\client\\client.py:154: UserWarning: Default user was detected and no workspace configuration was provided, so the default 'argilla' workspace will be used. If you want to setup another workspace, use the `rg.set_workspace` function or provide a different one on `rg.init`\n", + " warnings.warn(\n" + ] + } + ], + "source": [ + "rg.init(api_url=api_url, api_key=api_key)\n", + "\n", + "# # If you want to use your private HF Space\n", + "# rg.init(extra_headers={\"Authorization\": f\"Bearer {hf_token}\"})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Enable Telemetry\n", + "\n", + "We gain valuable insights from how you interact with our tutorials. To improve ourselves in offering you the most suitable content, using the following lines of code will help us understand that this tutorial is serving you effectively. Though this is entirely anonymous, you can choose to skip this step if you prefer. For more info, please check out the [Telemetry](../../reference/telemetry.md) page." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " from argilla.utils.telemetry import tutorial_running\n", + " tutorial_running()\n", + "except ImportError:\n", + " print(\"Telemetry is introduced in Argilla 1.20.0 and not found in the current installation. Skipping telemetry.\")" + ] + }, + { + "cell_type": "markdown", + "id": "ae0038d1-86b1-4eb9-ada4-ae561ad25aa3", + "metadata": { + "id": "ae0038d1-86b1-4eb9-ada4-ae561ad25aa3" + }, + "source": [ + "## Upload data\n", + "\n", + "The main component of the Argilla data model is called a **record**. Records can be of different types depending on the currently supported tasks:\n", + "\n", + " 1. `TextClassificationRecord`\n", + " 2. `TokenClassificationRecord`\n", + " 3. `Text2TextRecord`\n", + "\n", + "The most critical attributes of a record that are common to all types are:\n", + "\n", + " - `text`: The input text of the record (Required);\n", + " - `annotation`: Annotate your record in a task-specific manner (Optional);\n", + " - `prediction`: Add task-specific model predictions to the record (Optional);\n", + " - `metadata`: Add some arbitrary metadata to the record (Optional);\n", + "\n", + "A [Dataset](https://docs.argilla.io/en/latest/conceptual_guides/data_model.html#other-datasets) in Argilla is a collection of records of the same type." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "866426c8-b3af-4307-a3eb-3d50171e4b7f", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 135, + "referenced_widgets": [ + "83d7b92514e24e3d88f7207ec26a6b76", + "9146f98b81744bceb0083e98aa1c3af9", + "5406303479514972b3e5241ce40e3f70", + "21a09c7691c64e088a74c08095df0057", + "d577c60f0dbe46c1be6fab55672c4f86", + "cf5da266e6ad493da229cf05469b2fc4" + ] + }, + "id": "866426c8-b3af-4307-a3eb-3d50171e4b7f", + "outputId": "164125ed-722a-4e2e-9360-83cf8f2ad847" + }, + "outputs": [], + "source": [ + "# Create a basic text classification record\n", + "textcat_record = rg.TextClassificationRecord(\n", + " text=\"Hello world, this is me!\",\n", + " prediction=[(\"LABEL1\", 0.8), (\"LABEL2\", 0.2)],\n", + " annotation=\"LABEL1\",\n", + " multi_label=False,\n", + ")\n", + "\n", + "# Create a basic token classification record\n", + "tokencat_record = rg.TokenClassificationRecord(\n", + " text=\"Michael is a professor at Harvard\",\n", + " tokens=[\"Michael\", \"is\", \"a\", \"professor\", \"at\", \"Harvard\"],\n", + " prediction=[(\"NAME\", 0, 7), (\"LOC\", 26, 33)],\n", + ")\n", + "\n", + "# Create a basic text2text record\n", + "text2text_record = rg.Text2TextRecord(\n", + " text=\"My name is Sarah and I love my dog.\",\n", + " prediction=[\"Je m'appelle Sarah et j'aime mon chien.\"],\n", + ")\n", + "\n", + "# Upload (log) the records to corresponding datasets in the Argilla web app\n", + "rg.log(textcat_record, \"my_textcat_dataset\")\n", + "rg.log(tokencat_record, \"my_tokencat_dataset\")\n", + "rg.log(tokencat_record, \"my_text2text_dataset\")" + ] + }, + { + "cell_type": "markdown", + "id": "c84db0f9-a9ca-4799-9a26-635d2f3b94d4", + "metadata": { + "id": "c84db0f9-a9ca-4799-9a26-635d2f3b94d4" + }, + "source": [ + "Now you can access your datasets in the Argilla web app and look at your first records.\n", + "\n", + "However, most of the time, you will have your data in some file format, like TXT, CSV, or JSON.\n", + "Argilla relies on two well-known Python libraries to read these files: [pandas](https://pandas.pydata.org/) and [datasets](https://huggingface.co/docs/datasets/index).\n", + "After reading the files with one of those libraries, Argilla provides shortcuts to create your records automatically. Make sure to match the column names with the required attributes of the record type you want to create.\n", + "\n", + "```python\n", + "# Using a pandas dataframe\n", + "dataset_rg = rg.read_pandas(dataframe, task=\"TextClassification\")\n", + "\n", + "# Using a Dataset\n", + "dataset_rg = rg.read_datasets(dataset, task=\"TokenClassification\")\n", + "```\n", + "\n", + "**As mentioned earlier, you choose the record type depending on the task you want to tackle.**" + ] + }, + { + "cell_type": "markdown", + "id": "c4137fd2-cc98-4f59-a14e-31cd7489d59b", + "metadata": { + "id": "c4137fd2-cc98-4f59-a14e-31cd7489d59b" + }, + "source": [ + "### 1. TextClassification" + ] + }, + { + "cell_type": "markdown", + "id": "c1004cfb-6fed-4281-950f-1f19495cd114", + "metadata": { + "id": "c1004cfb-6fed-4281-950f-1f19495cd114" + }, + "source": [ + "In our example, we're going to work with a section of the [IMDb dataset](https://huggingface.co/datasets/imdb) available on Hugging Face. The underlying task here could be to classify the reviews by their sentiment." + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "id": "4cd38737", + "metadata": {}, + "outputs": [], + "source": [ + "from datasets import load_dataset\n", + "\n", + "dataset = load_dataset(\"imdb\", split=\"train\").shuffle(seed=42).select(range(100))" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'text': 'There is no relation at all between Fortier and Profiler but the fact that both are police series about violent crimes. Profiler looks crispy, Fortier looks classic. Profiler plots are quite simple. Fortier\\'s plot are far more complicated... Fortier looks more like Prime Suspect, if we have to spot similarities... The main character is weak and weirdo, but have \"clairvoyance\". People like to compare, to judge, to evaluate. How about just enjoying? Funny thing too, people writing Fortier looks American but, on the other hand, arguing they prefer American series (!!!). Maybe it\\'s the language, or the spirit, but I think this series is more English than American. By the way, the actors are really good and funny. The acting is not superficial at all...',\n", + " 'label': 1}" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dataset[0]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As we can see, the dataset has two columns: `text` and `label`. We will use the label as the annotation of our record. Thus, to match the required attributes of a `TextClassificationRecord`, we need to rename the columns." + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": {}, + "outputs": [], + "source": [ + "dataset = dataset.rename_column(\"label\", \"annotation\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, we can inspect our dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
textannotation
0There is no relation at all between Fortier an...1
1This movie is a great. The plot is very true t...1
2George P. Cosmatos' \"Rambo: First Blood Part I...0
\n", + "
" ], - "source": [ - "# The best way to visualize a Dataset is actually via pandas\n", - "dataset.select(range(3)).to_pandas()\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "id": "4f2d75b4-c9d7-40c0-b66c-c52e9de7ef1a", - "metadata": { - "id": "4f2d75b4-c9d7-40c0-b66c-c52e9de7ef1a" - }, - "source": [ - "We will choose the _review_ column as input text for our records.\n", - "For Argilla to know, we have to rename the corresponding column to _text_.\n", - "\n", - "Other columns can be conveniently wrapped in a dictionary and mapped as _metadata_ columns as expected by the [TokenClassificationRecord](https://docs.argilla.io/en/latest/reference/python/python_client.html#argilla.client.models.TokenClassificationRecord) class." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "32194771-66a3-4ecd-960e-59a8f8be8c2e", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "32194771-66a3-4ecd-960e-59a8f8be8c2e", - "outputId": "fef65821-c2a5-4527-8824-c93df4bd11c5" - }, - "outputs": [], - "source": [ - "#wrap metadata in a dictionary\n", - "\n", - "def metadata_to_dict(row):\n", - " metadata = {}\n", - " metadata[\"brand\"] = row[\"brand\"]\n", - " metadata[\"rating\"] = row[\"rating\"]\n", - " row['metadata'] = metadata\n", - " return row\n", - "\n", - "dataset = dataset.map(metadata_to_dict, remove_columns=[\"Unnamed: 0\",\"brand\",\"rating\"])\n", - "\n", - "dataset = dataset.rename_column(\"review\", \"text\")\n", - "dataset.select(range(3)).to_pandas()" - ] - }, - { - "cell_type": "markdown", - "id": "fb7d2f14-37a0-4a5a-8ae7-86f8e9304fa9", - "metadata": { - "id": "fb7d2f14-37a0-4a5a-8ae7-86f8e9304fa9" - }, - "source": [ - "In contrast to the other types, token classification records need the input text **and** the corresponding tokens.\n", - "So let us tokenize our input text in a small helper function and add the tokens to a new column called _tokens_.\n", - "\n", - "
\n", - "\n", - "Note\n", - "\n", - "We will use [spaCy](https://spacy.io/) to tokenize the text, but you can use whatever library you prefer.\n", - " \n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3a4664fe-0840-4768-b856-79bdbd1dc178", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 17, - "referenced_widgets": [ - "7e028b1a16394adcac90c011d14d3458", - "7605822dfe30439a8b53c117466e513f", - "13d0949b0cad4cee8f1aa691ebd43476", - "452429879663485da881188f70d20ca5", - "d4bae643e36a4b258b137507d628399d", - "50edec55b45242d3a3bd0b3d9444710e", - "7d817c646854435686ab9b7e317b2f92", - "e9f06d2e0ff645aea882a0958eda2820", - "15c7227ddf8946d7bdd9c5e076407300", - "f47cb9c3e20e48acb6df9bed2f0cb7bf", - "2d9d836276674563893af311d7ceb5cf" - ] - }, - "id": "3a4664fe-0840-4768-b856-79bdbd1dc178", - "outputId": "af22dc01-aa13-4ea7-e57e-9d922e37238a" - }, - "outputs": [], - "source": [ - "import spacy\n", - "\n", - "# Load a german spaCy model to tokenize our text\n", - "nlp = spacy.load(\"de_core_news_sm\")\n", - "\n", - "# Define our tokenize function\n", - "def tokenize(row):\n", - " tokens = [token.text for token in nlp(row[\"text\"])]\n", - " return {\"tokens\": tokens}\n", - "\n", - "\n", - "# Map the tokenize function to our dataset\n", - "dataset = dataset.map(tokenize)" - ] - }, - { - "cell_type": "markdown", - "id": "26e5ee02-3f26-4db2-9729-e664e9740e18", - "metadata": { - "id": "26e5ee02-3f26-4db2-9729-e664e9740e18" - }, - "source": [ - "Let us have a quick look at our extended `Dataset`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "efcd39d2-0cb3-4ce1-b9ec-ca341d4bdb8f", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 143 - }, - "id": "efcd39d2-0cb3-4ce1-b9ec-ca341d4bdb8f", - "outputId": "75f4e97d-19dc-40b6-ff34-ee972213e655" - }, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - "
\n", - "
\n", - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
textmetadatatokens
0Wenn ich Bohnenkaffee trinke (auf Arbeit trink...{'brand': 'GEPA Kaffee', 'rating': 5}[Wenn, ich, Bohnenkaffee, trinke, (, auf, Arbe...
1Für mich ist dieser Kaffee ideal. Die Grundvor...{'brand': 'GEPA Kaffee', 'rating': 5}[Für, mich, ist, dieser, Kaffee, ideal, ., Die...
2Ich persönlich bin insbesondere von dem Geschm...{'brand': 'GEPA Kaffee', 'rating': 5}[Ich, persönlich, bin, insbesondere, von, dem,...
\n", - "
\n", - " \n", - "\n", - "\n", - "\n", - "
\n", - " \n", - "
\n", - "\n", - "\n", - "\n", - " \n", - "\n", - " \n", - " \n", - "\n", - " \n", - "
\n", - "
\n" - ], - "text/plain": [ - " text \\\n", - "0 Wenn ich Bohnenkaffee trinke (auf Arbeit trink... \n", - "1 Für mich ist dieser Kaffee ideal. Die Grundvor... \n", - "2 Ich persönlich bin insbesondere von dem Geschm... \n", - "\n", - " metadata \\\n", - "0 {'brand': 'GEPA Kaffee', 'rating': 5} \n", - "1 {'brand': 'GEPA Kaffee', 'rating': 5} \n", - "2 {'brand': 'GEPA Kaffee', 'rating': 5} \n", - "\n", - " tokens \n", - "0 [Wenn, ich, Bohnenkaffee, trinke, (, auf, Arbe... \n", - "1 [Für, mich, ist, dieser, Kaffee, ideal, ., Die... \n", - "2 [Ich, persönlich, bin, insbesondere, von, dem,... " - ] - }, - "execution_count": 84, - "metadata": {}, - "output_type": "execute_result" - } + "text/plain": [ + " text annotation\n", + "0 There is no relation at all between Fortier an... 1\n", + "1 This movie is a great. The plot is very true t... 1\n", + "2 George P. Cosmatos' \"Rambo: First Blood Part I... 0" + ] + }, + "execution_count": 77, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dataset.select(range(3)).to_pandas()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Once, we checked that everything is correct, we can convert it to an Argilla dataset." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dataset_rg = rg.read_datasets(dataset, task=\"TextClassification\")" + ] + }, + { + "cell_type": "markdown", + "id": "ae4ee6b9", + "metadata": {}, + "source": [ + "We will upload this dataset to the web app and give it the name *imdb*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f19320bf", + "metadata": {}, + "outputs": [], + "source": [ + "rg.log(dataset_rg, \"imdb\")" + ] + }, + { + "cell_type": "markdown", + "id": "cedf9a8c", + "metadata": {}, + "source": [ + "You can configure labels programmatically by using `configure_dataset_settings` method:\n", + "\n", + "```python\n", + "labels = [\"pos\", \"neg\"]\n", + "settings = rg.TextClassificationSettings(label_schema=labels)\n", + "rg.configure_dataset_settings(name=\"imdb\", settings=settings)\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "930cb8c3-5dfc-4e5a-bdf4-3c19f3d5fb00", + "metadata": { + "id": "930cb8c3-5dfc-4e5a-bdf4-3c19f3d5fb00" + }, + "source": [ + "\n", + "\n", + "![Screenshot of the uploaded snapchat reviews](https://github.com/argilla-io/argilla/blob/main/docs/_source/_static/reference/webapp/explore-text-classification.png?raw=1)" + ] + }, + { + "cell_type": "markdown", + "id": "341abb81-2acd-411e-a1d3-7c54cfc257f8", + "metadata": { + "id": "341abb81-2acd-411e-a1d3-7c54cfc257f8" + }, + "source": [ + "### 2. TokenClassification" + ] + }, + { + "cell_type": "markdown", + "id": "59944e44-4202-4890-9a45-f99fc3fb2dd1", + "metadata": { + "id": "59944e44-4202-4890-9a45-f99fc3fb2dd1" + }, + "source": [ + "We will use the [ag_news](https://huggingface.co/datasets/ag_news) from Hugging Face for this example.\n", + "The underlying task here could be to extract the places and people involved in the events described in the headlines." + ] + }, + { + "cell_type": "markdown", + "id": "4cf9a7c0", + "metadata": {}, + "source": [ + "So, we will start by loading the dataset and analyzing it." + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "id": "502febcb-26f1-4832-8218-4f029ebed697", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "502febcb-26f1-4832-8218-4f029ebed697", + "outputId": "fac8ed08-e753-42ce-b77a-a3c56cc3d3fe" + }, + "outputs": [], + "source": [ + "from datasets import load_dataset\n", + "\n", + "dataset = load_dataset(\"ag_news\", split=\"train\").shuffle(seed=50).select(range(100))" + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "id": "1b77947c-ed89-4dce-ba75-158264c8d384", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 143 + }, + "id": "1b77947c-ed89-4dce-ba75-158264c8d384", + "outputId": "05dd4779-1d0f-4f8f-8aab-b696bef2a1eb" + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
textlabel
0Bills' Milloy Ready to Make Season Debut (AP) ...1
1MLB: Atlanta 6, Houston 5 JD Drew extended Atl...1
2PARMALAT: FT, BONDI WANTS 1 BLN DOLLARS FROM I...2
\n", + "
" ], - "source": [ - "dataset.select(range(3)).to_pandas()" - ] - }, - { - "cell_type": "markdown", - "id": "054e40cc-51f4-4321-b42a-2301775c0e9f", - "metadata": { - "id": "054e40cc-51f4-4321-b42a-2301775c0e9f" - }, - "source": [ - "We can now read this `Dataset` with Argilla, which will automatically create the records and put them in a [Argilla Dataset](../guides/features/datasets.ipynb)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0e4ff337-7b12-48fc-b8a1-a829a7800d49", - "metadata": { - "id": "0e4ff337-7b12-48fc-b8a1-a829a7800d49" - }, - "outputs": [], - "source": [ - "# Read Dataset into a Argilla Dataset\n", - "dataset_rg = rg.read_datasets(dataset, task=\"TokenClassification\")\n" - ] - }, - { - "cell_type": "markdown", - "id": "4cb220a7-cb2c-4e38-bd41-4cf96ed478c2", - "metadata": { - "id": "4cb220a7-cb2c-4e38-bd41-4cf96ed478c2" - }, - "source": [ - "We will upload this dataset to the web app and give it the name `coffee_reviews`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5dee85f1-1a37-4850-9bda-c4e54aa5db03", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 69, - "referenced_widgets": [ - "db3e5db8fe3f42a8908e3cd2a029cf56", - "706f8e4861904c33a9468216e3220d9b" - ] - }, - "id": "5dee85f1-1a37-4850-9bda-c4e54aa5db03", - "outputId": "8ecb355c-b251-4758-e108-ce483d6f7e8d" - }, - "outputs": [], - "source": [ - "# Log the dataset to the Argilla web app\n", - "rg.log(dataset_rg, \"coffee-reviews\")" - ] - }, - { - "cell_type": "markdown", - "id": "27826187", - "metadata": {}, - "source": [ - "You can configure labels programmatically by using `configure_dataset_settings` method:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3c6049fb", - "metadata": {}, - "outputs": [], - "source": [ - "labels = [\"PER\", \"ORG\", \"LOC\", \"MISC\"]\n", - "settings = rg.TokenClassificationSettings(label_schema=labels)\n", - "rg.configure_dataset_settings(name=\"coffee-reviews\", settings=settings)" - ] - }, - { - "cell_type": "markdown", - "id": "bd1c2d53-37c6-438a-b447-6dcae3369f55", - "metadata": { - "id": "bd1c2d53-37c6-438a-b447-6dcae3369f55" - }, - "source": [ - "You can also create labels in _Dataset\\Settings_ and start annotating:\n", - "\n", - "![Screenshot of the uploaded coffee reviews](https://github.com/argilla-io/argilla/blob/main/docs/_source/_static/reference/webapp/features-annotate.png?raw=1)" - ] - }, - { - "cell_type": "markdown", - "id": "d66683c8-9ed5-4ab9-9937-39eeac9ccab0", - "metadata": { - "id": "d66683c8-9ed5-4ab9-9937-39eeac9ccab0" - }, - "source": [ - "### 3. Text2Text" - ] - }, - { - "cell_type": "markdown", - "id": "3c17d862-5e64-4c34-8aa5-3941506913c6", - "metadata": { - "id": "3c17d862-5e64-4c34-8aa5-3941506913c6" - }, - "source": [ - "In this example, we will use English sentences from the European Center for Disease Prevention and Control available at the [Hugging Face Hub](https://huggingface.co/datasets/europa_ecdc_tm).\n", - "The underlying task here could be to translate the sentences into other European languages.\n", - "\n", - "Let us load the data with [datasets](https://huggingface.co/docs/datasets/index) from the [Hub](https://huggingface.co/datasets)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cfbce85f-200e-4b54-9650-308395b81770", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "cfbce85f-200e-4b54-9650-308395b81770", - "outputId": "4af259e0-f733-4991-b24b-389d075308ad" - }, - "outputs": [], - "source": [ - "from datasets import load_dataset\n", - "\n", - "# Load the Dataset from the Hugging Face Hub and extract the train split\n", - "dataset = load_dataset(\"europa_ecdc_tm\", \"en2fr\", split=\"train\")\n" - ] - }, - { - "cell_type": "markdown", - "id": "0de908f2-ff08-4a0e-87d9-a278f9ccd452", - "metadata": { - "id": "0de908f2-ff08-4a0e-87d9-a278f9ccd452" - }, - "source": [ - "and have a quick look at the first row of the resulting [dataset Dataset](https://huggingface.co/docs/datasets/access):" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "16d386a5-14cb-46cd-afb2-e8405ddd5232", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "16d386a5-14cb-46cd-afb2-e8405ddd5232", - "outputId": "325d7ced-f987-4e42-e3ea-88e1b103976b" - }, - "outputs": [ - { - "data": { - "text/plain": [ - "{'translation': {'en': 'Vaccination against hepatitis C is not yet available.',\n", - " 'fr': 'Aucune vaccination contre l’hépatite C n’est encore disponible.'}}" - ] - }, - "execution_count": 130, - "metadata": {}, - "output_type": "execute_result" - } + "text/plain": [ + " text label\n", + "0 Bills' Milloy Ready to Make Season Debut (AP) ... 1\n", + "1 MLB: Atlanta 6, Houston 5 JD Drew extended Atl... 1\n", + "2 PARMALAT: FT, BONDI WANTS 1 BLN DOLLARS FROM I... 2" + ] + }, + "execution_count": 79, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# The best way to visualize a Dataset is actually via pandas\n", + "dataset.select(range(3)).to_pandas()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As the label is not needed in this case, we will add it as metadata." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def metadata_to_dict(row):\n", + " metadata = {}\n", + " metadata[\"label\"] = row[\"label\"]\n", + " row['metadata'] = metadata\n", + " return row\n", + "\n", + "dataset = dataset.map(metadata_to_dict, remove_columns=[\"label\"])" + ] + }, + { + "cell_type": "markdown", + "id": "fb7d2f14-37a0-4a5a-8ae7-86f8e9304fa9", + "metadata": { + "id": "fb7d2f14-37a0-4a5a-8ae7-86f8e9304fa9" + }, + "source": [ + "In contrast to the other types, token classification records need the input text **and** the corresponding tokens.\n", + "So let us tokenize our input text in a small helper function and add the tokens to a new column called _tokens_.\n", + "\n", + "
\n", + "\n", + "Note\n", + "\n", + "We will use [spaCy](https://spacy.io/) to tokenize the text, but you can use whatever library you prefer.\n", + " \n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3a4664fe-0840-4768-b856-79bdbd1dc178", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 17, + "referenced_widgets": [ + "7e028b1a16394adcac90c011d14d3458", + "7605822dfe30439a8b53c117466e513f", + "13d0949b0cad4cee8f1aa691ebd43476", + "452429879663485da881188f70d20ca5", + "d4bae643e36a4b258b137507d628399d", + "50edec55b45242d3a3bd0b3d9444710e", + "7d817c646854435686ab9b7e317b2f92", + "e9f06d2e0ff645aea882a0958eda2820", + "15c7227ddf8946d7bdd9c5e076407300", + "f47cb9c3e20e48acb6df9bed2f0cb7bf", + "2d9d836276674563893af311d7ceb5cf" + ] + }, + "id": "3a4664fe-0840-4768-b856-79bdbd1dc178", + "outputId": "af22dc01-aa13-4ea7-e57e-9d922e37238a" + }, + "outputs": [], + "source": [ + "import spacy\n", + "\n", + "# Load a english spaCy model to tokenize our text\n", + "nlp = spacy.load(\"en_core_web_sm\")\n", + "\n", + "# Define our tokenize function\n", + "def tokenize(row):\n", + " tokens = [token.text for token in nlp(row[\"text\"])]\n", + " return {\"tokens\": tokens}\n", + "\n", + "\n", + "# Map the tokenize function to our dataset\n", + "dataset = dataset.map(tokenize)" + ] + }, + { + "cell_type": "markdown", + "id": "26e5ee02-3f26-4db2-9729-e664e9740e18", + "metadata": { + "id": "26e5ee02-3f26-4db2-9729-e664e9740e18" + }, + "source": [ + "Let us have a quick look at our extended `Dataset`:" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "id": "efcd39d2-0cb3-4ce1-b9ec-ca341d4bdb8f", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 143 + }, + "id": "efcd39d2-0cb3-4ce1-b9ec-ca341d4bdb8f", + "outputId": "75f4e97d-19dc-40b6-ff34-ee972213e655" + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
textmetadatatokens
0Bills' Milloy Ready to Make Season Debut (AP) ...{'label': 1}[Bills, ', Milloy, Ready, to, Make, Season, De...
1MLB: Atlanta 6, Houston 5 JD Drew extended Atl...{'label': 1}[MLB, :, Atlanta, 6, ,, Houston, 5, JD, Drew, ...
2PARMALAT: FT, BONDI WANTS 1 BLN DOLLARS FROM I...{'label': 2}[PARMALAT, :, FT, ,, BONDI, WANTS, 1, BLN, DOL...
\n", + "
" ], - "source": [ - "dataset[0]\n" - ] - }, - { - "cell_type": "markdown", - "id": "67e41a33-11c2-422f-a5f4-411dc464f451", - "metadata": { - "id": "67e41a33-11c2-422f-a5f4-411dc464f451" - }, - "source": [ - "We can see that the English sentences are nested in a dictionary inside the _translation_ column.\n", - "\n", - "To extract English sentences into a new _text_ column we will write a quick helper function and [map](https://huggingface.co/docs/datasets/process#map) the whole `Dataset` with it.\n", - "\n", - "French sentences will be extracted into a new _prediction_ column, wrapped in \"[ ]\", as the prediction field of [Text2TextRecord](https://docs.argilla.io/en/latest/reference/python/python_client.html#argilla.client.models.Text2TextRecord) accepts a list of strings or tuples." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b27d93ad-86f0-4d6c-a31f-5cc1d55235a6", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 17, - "referenced_widgets": [ - "9217d6b5c2a0495bbb0724dd296b2af7", - "518e260b7d2f4f90b39668414389c166", - "4635b9f0d42b4172b52b64de660082d1", - "b1df83e2ef6540bbb09098e12d707d6d", - "0a54f0576a9347d5a546ff8b3e371121", - "8f5ca2714e114119adb61c9122afffbb", - "c36638fb29cb4510bfdda737e03c4a0e", - "514672241dad488ca37a5e802553eb35", - "48dbaf59d0be41a6a6d7c30cb621e9ef", - "007a11c0643c45ecbf50c92bb244170f", - "81e07d9401bd40cc82240136a529be2d" - ] - }, - "id": "b27d93ad-86f0-4d6c-a31f-5cc1d55235a6", - "outputId": "1b19c0ec-30ad-4773-bfe3-0eeb150d48fd" - }, - "outputs": [], - "source": [ - "# Define our helper extract function\n", - "def extract(row):\n", - " return {\"text\": row[\"translation\"][\"en\"], \"prediction\":[row[\"translation\"][\"fr\"]]}\n", - "\n", - "\n", - "# Map the extract function to our dataset\n", - "dataset = dataset.map(extract, remove_columns = [\"translation\"])\n" - ] - }, - { - "cell_type": "markdown", - "id": "c9aa9293-64bb-4061-b686-b3115a7942fc", - "metadata": { - "id": "c9aa9293-64bb-4061-b686-b3115a7942fc" - }, - "source": [ - "Let us have a quick look at our extended `Dataset`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "KGbG_olNGU9j", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 143 - }, - "id": "KGbG_olNGU9j", - "outputId": "8d2b53bf-8e10-45d0-8f11-c8be59ea3270" - }, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - "\n", - "
\n", - "
\n", - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
textprediction
0Vaccination against hepatitis C is not yet ava...[Aucune vaccination contre l’hépatite C n’est ...
1HIV infection[Infection à VIH]
2The human immunodeficiency virus (HIV) remains...[L’infection par le virus de l’immunodéficienc...
\n", - "
\n", - " \n", - "\n", - "\n", - "\n", - "
\n", - " \n", - "
\n", - "\n", - "\n", - "\n", - " \n", - "\n", - " \n", - " \n", - "\n", - " \n", - "
\n", - "
\n" - ], - "text/plain": [ - " text \\\n", - "0 Vaccination against hepatitis C is not yet ava... \n", - "1 HIV infection \n", - "2 The human immunodeficiency virus (HIV) remains... \n", - "\n", - " prediction \n", - "0 [Aucune vaccination contre l’hépatite C n’est ... \n", - "1 [Infection à VIH] \n", - "2 [L’infection par le virus de l’immunodéficienc... " - ] - }, - "execution_count": 132, - "metadata": {}, - "output_type": "execute_result" - } + "text/plain": [ + " text metadata \\\n", + "0 Bills' Milloy Ready to Make Season Debut (AP) ... {'label': 1} \n", + "1 MLB: Atlanta 6, Houston 5 JD Drew extended Atl... {'label': 1} \n", + "2 PARMALAT: FT, BONDI WANTS 1 BLN DOLLARS FROM I... {'label': 2} \n", + "\n", + " tokens \n", + "0 [Bills, ', Milloy, Ready, to, Make, Season, De... \n", + "1 [MLB, :, Atlanta, 6, ,, Houston, 5, JD, Drew, ... \n", + "2 [PARMALAT, :, FT, ,, BONDI, WANTS, 1, BLN, DOL... " + ] + }, + "execution_count": 82, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dataset.select(range(3)).to_pandas()" + ] + }, + { + "cell_type": "markdown", + "id": "054e40cc-51f4-4321-b42a-2301775c0e9f", + "metadata": { + "id": "054e40cc-51f4-4321-b42a-2301775c0e9f" + }, + "source": [ + "We can now read this `Dataset` with Argilla, which will automatically create the records and put them in a [Argilla Dataset](https://docs.argilla.io/en/latest/reference/python/python_client.html#argilla.client.datasets.read_datasets)." + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "id": "0e4ff337-7b12-48fc-b8a1-a829a7800d49", + "metadata": { + "id": "0e4ff337-7b12-48fc-b8a1-a829a7800d49" + }, + "outputs": [], + "source": [ + "# Read Dataset into a Argilla Dataset\n", + "dataset_rg = rg.read_datasets(dataset, task=\"TokenClassification\")" + ] + }, + { + "cell_type": "markdown", + "id": "4cb220a7-cb2c-4e38-bd41-4cf96ed478c2", + "metadata": { + "id": "4cb220a7-cb2c-4e38-bd41-4cf96ed478c2" + }, + "source": [ + "We will upload this dataset to the web app and give it the name `ag_news`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5dee85f1-1a37-4850-9bda-c4e54aa5db03", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 69, + "referenced_widgets": [ + "db3e5db8fe3f42a8908e3cd2a029cf56", + "706f8e4861904c33a9468216e3220d9b" + ] + }, + "id": "5dee85f1-1a37-4850-9bda-c4e54aa5db03", + "outputId": "8ecb355c-b251-4758-e108-ce483d6f7e8d" + }, + "outputs": [], + "source": [ + "# Log the dataset to the Argilla web app\n", + "rg.log(dataset_rg, \"ag_news\")" + ] + }, + { + "cell_type": "markdown", + "id": "27826187", + "metadata": {}, + "source": [ + "You can configure labels programmatically by using `configure_dataset_settings` method:\n", + "\n", + "```python\n", + "labels = [\"PER\", \"ORG\", \"LOC\", \"MISC\"]\n", + "settings = rg.TokenClassificationSettings(label_schema=labels)\n", + "rg.configure_dataset_settings(name=\"ag_news\", settings=settings)\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "bd1c2d53-37c6-438a-b447-6dcae3369f55", + "metadata": { + "id": "bd1c2d53-37c6-438a-b447-6dcae3369f55" + }, + "source": [ + "You can also create labels in _Dataset\\Settings_ and start annotating:\n", + "\n", + "![Screenshot of the uploaded coffee reviews](https://github.com/argilla-io/argilla/blob/main/docs/_source/_static/reference/webapp/features-annotate.png?raw=1)" + ] + }, + { + "cell_type": "markdown", + "id": "d66683c8-9ed5-4ab9-9937-39eeac9ccab0", + "metadata": { + "id": "d66683c8-9ed5-4ab9-9937-39eeac9ccab0" + }, + "source": [ + "### 3. Text2Text" + ] + }, + { + "cell_type": "markdown", + "id": "3c17d862-5e64-4c34-8aa5-3941506913c6", + "metadata": { + "id": "3c17d862-5e64-4c34-8aa5-3941506913c6" + }, + "source": [ + "In this example, we will use English sentences from the European Center for Disease Prevention and Control available at the [Hugging Face Hub](https://huggingface.co/datasets/europa_ecdc_tm).\n", + "The underlying task here could be to translate the sentences into other European languages.\n", + "\n", + "Let us load the data with [datasets](https://huggingface.co/docs/datasets/index) from the [Hub](https://huggingface.co/datasets)." + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "id": "cfbce85f-200e-4b54-9650-308395b81770", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "cfbce85f-200e-4b54-9650-308395b81770", + "outputId": "4af259e0-f733-4991-b24b-389d075308ad" + }, + "outputs": [], + "source": [ + "from datasets import load_dataset\n", + "\n", + "# Load the Dataset from the Hugging Face Hub and extract a subset of the train split as example\n", + "dataset = load_dataset(\"europa_ecdc_tm\", \"en2fr\", split=\"train\").shuffle(seed=30).select(range(100))" + ] + }, + { + "cell_type": "markdown", + "id": "0de908f2-ff08-4a0e-87d9-a278f9ccd452", + "metadata": { + "id": "0de908f2-ff08-4a0e-87d9-a278f9ccd452" + }, + "source": [ + "and have a quick look at the first row of the resulting [dataset Dataset](https://huggingface.co/docs/datasets/access):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16d386a5-14cb-46cd-afb2-e8405ddd5232", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "16d386a5-14cb-46cd-afb2-e8405ddd5232", + "outputId": "325d7ced-f987-4e42-e3ea-88e1b103976b" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'translation': {'en': 'Vaccination against hepatitis C is not yet available.',\n", + " 'fr': 'Aucune vaccination contre l’hépatite C n’est encore disponible.'}}" + ] + }, + "execution_count": 130, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dataset[0]" + ] + }, + { + "cell_type": "markdown", + "id": "67e41a33-11c2-422f-a5f4-411dc464f451", + "metadata": { + "id": "67e41a33-11c2-422f-a5f4-411dc464f451" + }, + "source": [ + "We can see that the English sentences are nested in a dictionary inside the _translation_ column.\n", + "\n", + "To extract English sentences into a new _text_ column we will write a quick helper function and [map](https://huggingface.co/docs/datasets/process#map) the whole `Dataset` with it.\n", + "\n", + "French sentences will be extracted into a new _prediction_ column, wrapped in \"[ ]\", as the prediction field of [Text2TextRecord](https://docs.argilla.io/en/latest/reference/python/python_client.html#argilla.client.models.Text2TextRecord) accepts a list of strings or tuples." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b27d93ad-86f0-4d6c-a31f-5cc1d55235a6", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 17, + "referenced_widgets": [ + "9217d6b5c2a0495bbb0724dd296b2af7", + "518e260b7d2f4f90b39668414389c166", + "4635b9f0d42b4172b52b64de660082d1", + "b1df83e2ef6540bbb09098e12d707d6d", + "0a54f0576a9347d5a546ff8b3e371121", + "8f5ca2714e114119adb61c9122afffbb", + "c36638fb29cb4510bfdda737e03c4a0e", + "514672241dad488ca37a5e802553eb35", + "48dbaf59d0be41a6a6d7c30cb621e9ef", + "007a11c0643c45ecbf50c92bb244170f", + "81e07d9401bd40cc82240136a529be2d" + ] + }, + "id": "b27d93ad-86f0-4d6c-a31f-5cc1d55235a6", + "outputId": "1b19c0ec-30ad-4773-bfe3-0eeb150d48fd" + }, + "outputs": [], + "source": [ + "# Define our helper extract function\n", + "def extract(row):\n", + " return {\"text\": row[\"translation\"][\"en\"], \"prediction\":[row[\"translation\"][\"fr\"]]}\n", + "\n", + "\n", + "# Map the extract function to our dataset\n", + "dataset = dataset.map(extract, remove_columns = [\"translation\"])" + ] + }, + { + "cell_type": "markdown", + "id": "c9aa9293-64bb-4061-b686-b3115a7942fc", + "metadata": { + "id": "c9aa9293-64bb-4061-b686-b3115a7942fc" + }, + "source": [ + "Let us have a quick look at our extended `Dataset`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "KGbG_olNGU9j", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 143 + }, + "id": "KGbG_olNGU9j", + "outputId": "8d2b53bf-8e10-45d0-8f11-c8be59ea3270" + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "
\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
textprediction
0Vaccination against hepatitis C is not yet ava...[Aucune vaccination contre l’hépatite C n’est ...
1HIV infection[Infection à VIH]
2The human immunodeficiency virus (HIV) remains...[L’infection par le virus de l’immunodéficienc...
\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + "
\n", + " \n", + "
\n", + "\n", + "\n", + "\n", + " \n", + "\n", + " \n", + " \n", + "\n", + " \n", + "
\n", + "
\n" ], - "source": [ - "dataset.select(range(3)).to_pandas()" - ] - }, - { - "cell_type": "markdown", - "id": "138e0dac-7772-49ae-b57e-52619ebf5899", - "metadata": { - "id": "138e0dac-7772-49ae-b57e-52619ebf5899" - }, - "source": [ - "We can now read this `Dataset` with Argilla, which will automatically create the records and put them in an [Argilla Dataset](../guides/features/datasets.ipynb)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c6f98a37-5fda-4e79-aff1-f3fb770498ea", - "metadata": { - "id": "c6f98a37-5fda-4e79-aff1-f3fb770498ea" - }, - "outputs": [], - "source": [ - "# Read Dataset into a Argilla Dataset\n", - "dataset_rg = rg.read_datasets(dataset, task=\"Text2Text\")\n" - ] - }, - { - "cell_type": "markdown", - "id": "a5236bf1-f98c-411f-81d0-e31740f0fc10", - "metadata": { - "id": "a5236bf1-f98c-411f-81d0-e31740f0fc10" - }, - "source": [ - "We will upload this dataset to the web app and give it the name `ecdc_en`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9da01d6f-2728-4ed0-b6aa-cd2e2531202c", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 69, - "referenced_widgets": [ - "19d173ba40134ea3ba4625977b924c83", - "24b90ef58e0e41cfb668144bc0adb815" - ] - }, - "id": "9da01d6f-2728-4ed0-b6aa-cd2e2531202c", - "outputId": "eeaba929-596e-4c59-9778-a37458a83c37" - }, - "outputs": [], - "source": [ - "# Log the dataset to the Argilla web app\n", - "rg.log(dataset_rg, \"ecdc_en\")\n" - ] - }, - { - "cell_type": "markdown", - "id": "f5a4969a-9132-459d-9f8d-e0006a1d52a0", - "metadata": { - "id": "f5a4969a-9132-459d-9f8d-e0006a1d52a0" - }, - "source": [ - "![Screenshot of the uploaded English phrases.](https://github.com/argilla-io/argilla/blob/main/docs/_source/_static/reference/webapp/explore-text2text.png?raw=1)" - ] - }, - { - "cell_type": "markdown", - "id": "e3139dd1-a939-4baa-8d26-bd1214c8cbcd", - "metadata": { - "id": "e3139dd1-a939-4baa-8d26-bd1214c8cbcd" - }, - "source": [ - "## Label datasets" - ] - }, - { - "cell_type": "markdown", - "id": "5bfeaf75-143e-4543-85f3-2a6e995dcf06", - "metadata": { - "id": "5bfeaf75-143e-4543-85f3-2a6e995dcf06" - }, - "source": [ - "Argilla provides several ways to label your data. Using Argilla's UI, you can mix and match the following options:\n", - "\n", - "\n", - "1. Manually labeling each record using the specialized interface for each task type;\n", - "2. Leveraging a user-provided model and validating its predictions;\n", - "3. Defining heuristic rules to produce \"noisy labels\" which can then be combined with weak supervision;\n", - "\n", - "Each way has its pros and cons, and the best match largely depends on your individual use case.\n" - ] - }, - { - "cell_type": "markdown", - "id": "b85a058b", - "metadata": { - "id": "b85a058b" - }, - "source": [ - "### Annotation guideline\n", - "\n", - "Before starting the annotation process with a team, it is important to align the different truths everyone in the team thinks they have. Because the same text is going to be annotated by multiple annotators independently or we might want to revisit an old dataset later on. Besides a set of obvious mistakes, we also often encounter uncertain grey areas. Consider the following phrase for NER-annotation `Harry Potter and the Prisoner of Azkaban` can be interpreted in many ways. The entire phrase is as the movie title, `Harry Potter` is a person, and `Azkaban` is a location. Maybe we don´t even want to annotate fictional locations and characters. Therefore, it is important to define these assumptions beforehand and iterate over them together with the team. Take a look at [this blog](https://www.superb-ai.com/blog/how-to-write-better-annotation-guidelines-for-human-labelers-4-top-tips) from our friends over at `suberb.ai` or [this blog](https://www.grammarly.com/blog/engineering/annotation-best-practices/?utm_campaign=B2C&utm_medium=social&utm_source=LinkedIn_org&utm_term=blog&utm_content=link) from Grammarly for more context." - ] - }, - { - "cell_type": "markdown", - "id": "cf0bccc4-05c7-4c27-920b-5b76eb4acd22", - "metadata": { - "id": "cf0bccc4-05c7-4c27-920b-5b76eb4acd22" - }, - "source": [ - "### 1. Manual labeling" - ] - }, - { - "cell_type": "markdown", - "id": "3b085744-7c61-4614-b542-c4de2cea9181", - "metadata": { - "id": "3b085744-7c61-4614-b542-c4de2cea9181" - }, - "source": [ - "![Manual annotations of a sentiment classification task](https://github.com/argilla-io/argilla/blob/main/docs/_source/_static/reference/webapp/features-metrics.png?raw=1)\n", - "\n", - "The straightforward approach of manual annotations might be necessary if you do not have a suitable model for your use case or cannot come up with good heuristic rules for your dataset.\n", - "It can also be a good approach if you dispose of a large annotation workforce or require few but unbiased and high-quality labels.\n", - "\n", - "Argilla tries to make this relatively cumbersome approach as painless as possible.\n", - "Via an intuitive and adaptive UI, its exhaustive search and filter functionalities, and bulk annotation capabilities, Argilla turns the manual annotation process into an efficient option. \n", - "\n", - "Look at our dedicated [feature reference](../reference/webapp/features.html) for a detailed and illustrative guide on manually annotating your dataset with Argilla." - ] - }, - { - "cell_type": "markdown", - "id": "e631840b-9cf7-45e6-9dc4-5f6b24cf0e8b", - "metadata": { - "id": "e631840b-9cf7-45e6-9dc4-5f6b24cf0e8b" - }, - "source": [ - "### 2. Validating predictions" - ] - }, - { - "cell_type": "markdown", - "id": "e3c46840-0991-4fc0-bbb0-15df33ee242b", - "metadata": { - "id": "e3c46840-0991-4fc0-bbb0-15df33ee242b" - }, - "source": [ - "![Validate predictions for a token classification dataset](https://github.com/argilla-io/argilla/blob/main/docs/_source/_static/reference/webapp/features-validation.png?raw=1)\n", - "\n", - "Nowadays, many pre-trained or zero-shot models are available online via model repositories like the Hugging Face Hub.\n", - "Most of the time, you probably will find a model that already suits your specific dataset task to some degree.\n", - "In Argilla, you can pre-annotate your data by including predictions from these models in your records.\n", - "Assuming that the model works reasonably well on your dataset, you can filter for records with high prediction scores and validate the predictions.\n", - "In this way, you will rapidly annotate part of your data and alleviate the annotation process.\n", - "\n", - "One downside of this approach is that your annotations will be subject to the possible biases and mistakes of the pre-trained model.\n", - "When guided by pre-trained models, it is common to see human annotators get influenced by them.\n", - "Therefore, it is advisable to avoid pre-annotations when building a rigorous test set for the final model evaluation.\n", - "\n", - "Check the [introduction tutorial](../tutorials//notebooks/labelling-tokenclassification-spacy-pretrained.ipynb) to learn to add predictions to the records.\n", - "And our [feature reference](../reference/webapp/features.md) includes a detailed guide on validating predictions in the Argilla web app." - ] - }, - { - "cell_type": "markdown", - "id": "c2cbd593-e241-4f27-9a58-6932912ea9f1", - "metadata": { - "id": "c2cbd593-e241-4f27-9a58-6932912ea9f1" - }, - "source": [ - "### 3. Weak labeling rules" - ] - }, - { - "cell_type": "markdown", - "id": "87f7ea92-d40f-4d09-a0e8-16b7d7867e6e", - "metadata": { - "id": "87f7ea92-d40f-4d09-a0e8-16b7d7867e6e" - }, - "source": [ - "![Defining a rule for a multi-label text classification task.](https://github.com/argilla-io/argilla/blob/main/docs/_source/_static/reference/webapp/features-weak-labelling.png?raw=1)\n", - "\n", - "Another approach to annotating your data is to define heuristic rules tailored to your dataset.\n", - "For example, let us assume you want to classify news articles into the categories of *Finance*, *Sports*, and *Culture*.\n", - "In this case, a reasonable rule would be to label all articles that include the word \"stock\" as *Finance*.\n", - "\n", - "Rules can get arbitrarily complex and can also include the record's metadata.\n", - "The downside of this approach is that it might be challenging to come up with working heuristic rules for some datasets.\n", - "Furthermore, rules are rarely 100% precise and often conflict with each other. These noisy labels can be cleaned up using weak supervision and label models, or something as simple as majority voting. It is usually a trade-off between the amount of annotated data and the quality of the labels.\n", - "\n", - "Check [our guide](../guides/techniques/weak_supervision.ipynb) for an extensive introduction to weak supervision with Argilla.\n", - "Also, check the [feature reference](../reference/webapp/features.md) for the Define rules mode of the web app and our [various tutorials](../tutorials/techniques/weak_supervision.md) to see practical examples of weak supervision workflows." - ] - }, - { - "cell_type": "markdown", - "id": "90307acf-ba85-4f8c-86d3-ca398be7a496", - "metadata": { - "id": "90307acf-ba85-4f8c-86d3-ca398be7a496" - }, - "source": [ - "## Train a model\n", - "\n", - "The `ArgillaTrainer` is a wrapper around many of our favorite NLP libraries. It provides a very intuitive abstract workflow to facilitate simple training workflows using decent default pre-set configurations without having to worry about any data transformations from Argilla. More info [here](../practical_guides/fine_tune.html)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4D8LKfk1rUvC", - "metadata": { - "id": "4D8LKfk1rUvC" - }, - "outputs": [], - "source": [ - "from argilla.training import ArgillaTrainer\n", - "\n", - "sentence = \"I love Snapchat, but the new update is terrible. I can't find anything anymore.\"\n", - "\n", - "trainer = ArgillaTrainer(\n", - " name=\"snapchat_reviews\",\n", - " workspace=\"admin\",\n", - " framework=\"spacy\",\n", - " train_size=0.8\n", - ")\n", - "trainer.update_config(max_epochs=2)\n", - "trainer.train(output_dir=\"my_easy_model\")\n", - "\n", - "records = trainer.predict(sentence, as_argilla_records=True)\n", - "\n", - "# Print the prediction\n", - "print(\"\\ntesting predicitons...\")\n", - "print(sentence)\n", - "print(f\"Predicted_label: {records.prediction}\")" - ] - }, - { - "cell_type": "markdown", - "id": "29cb1351-6324-4faa-9067-fd50785844f5", - "metadata": { - "id": "29cb1351-6324-4faa-9067-fd50785844f5" - }, - "source": [ - "Argilla helps you to create and curate training data. **It is not a complete framework for training a model but we do provide integrations.** You can use Argilla complementary with other excellent open-source frameworks that focus on developing and training NLP models.\n", - "\n", - "Here we list three of the most commonly used open-source libraries, but many more are available and may be more suited for your specific use case:\n", - "\n", - " - [transformers](https://huggingface.co/docs/transformers/index): This library provides thousands of pre-trained models for various NLP tasks and modalities. Its excellent documentation focuses on fine-tuning those models to your specific use case;\n", - " - [spaCy](https://spacy.io/): This library also comes with pre-trained models built into a pipeline tackling multiple tasks simultaneously. Since it is a purely NLP library, it comes with many more NLP features than just model training;\n", - " - [spark-nlp](https://nlp.johnsnowlabs.com/): Spark NLP is an open-source text processing library for advanced natural language processing for the Python, Java and Scala programming languages. The library is built on top of Apache Spark and its Spark ML library.\n", - " - [scikit-learn](https://scikit-learn.org/stable/): This de facto standard library is a powerful Swiss army knife for machine learning with some NLP support. Usually, their NLP models lack the performance when compared to transformers or spacy, but give it a try if you want to train a lightweight model quickly;\n" - ] + "text/plain": [ + " text \\\n", + "0 Vaccination against hepatitis C is not yet ava... \n", + "1 HIV infection \n", + "2 The human immunodeficiency virus (HIV) remains... \n", + "\n", + " prediction \n", + "0 [Aucune vaccination contre l’hépatite C n’est ... \n", + "1 [Infection à VIH] \n", + "2 [L’infection par le virus de l’immunodéficienc... " + ] + }, + "execution_count": 132, + "metadata": {}, + "output_type": "execute_result" } - ], - "metadata": { - "accelerator": "GPU", + ], + "source": [ + "dataset.select(range(3)).to_pandas()" + ] + }, + { + "cell_type": "markdown", + "id": "138e0dac-7772-49ae-b57e-52619ebf5899", + "metadata": { + "id": "138e0dac-7772-49ae-b57e-52619ebf5899" + }, + "source": [ + "We can now read this `Dataset` with Argilla, which will automatically create the records and put them in an [Argilla Dataset](../guides/features/datasets.ipynb)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c6f98a37-5fda-4e79-aff1-f3fb770498ea", + "metadata": { + "id": "c6f98a37-5fda-4e79-aff1-f3fb770498ea" + }, + "outputs": [], + "source": [ + "# Read Dataset into a Argilla Dataset\n", + "dataset_rg = rg.read_datasets(dataset, task=\"Text2Text\")" + ] + }, + { + "cell_type": "markdown", + "id": "a5236bf1-f98c-411f-81d0-e31740f0fc10", + "metadata": { + "id": "a5236bf1-f98c-411f-81d0-e31740f0fc10" + }, + "source": [ + "We will upload this dataset to the web app and give it the name `ecdc_en`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9da01d6f-2728-4ed0-b6aa-cd2e2531202c", + "metadata": { "colab": { - "provenance": [] - }, - "kernelspec": { - "display_name": "Python 3", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.17" - }, - "vscode": { - "interpreter": { - "hash": "2584bca9d226488c39a669ff1ce19d7ca5f410e2d3aa9b82f20653edd0d96bfc" - } - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "007a11c0643c45ecbf50c92bb244170f": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "0a54f0576a9347d5a546ff8b3e371121": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": "hidden", - "width": null - } - }, - "13d0949b0cad4cee8f1aa691ebd43476": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "FloatProgressModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_e9f06d2e0ff645aea882a0958eda2820", - "max": 464, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_15c7227ddf8946d7bdd9c5e076407300", - "value": 464 - } - }, - "15c7227ddf8946d7bdd9c5e076407300": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "ProgressStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "19d173ba40134ea3ba4625977b924c83": { - "model_module": "@jupyter-widgets/output", - "model_module_version": "1.0.0", - "model_name": "OutputModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/output", - "_model_module_version": "1.0.0", - "_model_name": "OutputModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/output", - "_view_module_version": "1.0.0", - "_view_name": "OutputView", - "layout": "IPY_MODEL_24b90ef58e0e41cfb668144bc0adb815", - "msg_id": "", - "outputs": [ - { - "data": { - "text/html": "
Logging... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━  98% 0:00:01\n
\n", - "text/plain": "Logging... \u001b[38;2;249;38;114m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[38;5;237m╺\u001b[0m \u001b[35m 98%\u001b[0m \u001b[36m0:00:01\u001b[0m\n" - }, - "metadata": {}, - "output_type": "display_data" - } - ] - } - }, - "21a09c7691c64e088a74c08095df0057": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "24b90ef58e0e41cfb668144bc0adb815": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "2d9d836276674563893af311d7ceb5cf": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "452429879663485da881188f70d20ca5": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_f47cb9c3e20e48acb6df9bed2f0cb7bf", - "placeholder": "​", - "style": "IPY_MODEL_2d9d836276674563893af311d7ceb5cf", - "value": " 453/464 [00:04<00:00, 112.79 examples/s]" - } - }, - "4635b9f0d42b4172b52b64de660082d1": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "FloatProgressModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_514672241dad488ca37a5e802553eb35", - "max": 2561, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_48dbaf59d0be41a6a6d7c30cb621e9ef", - "value": 2561 - } - }, - "48dbaf59d0be41a6a6d7c30cb621e9ef": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "ProgressStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "50edec55b45242d3a3bd0b3d9444710e": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "514672241dad488ca37a5e802553eb35": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "518e260b7d2f4f90b39668414389c166": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_8f5ca2714e114119adb61c9122afffbb", - "placeholder": "​", - "style": "IPY_MODEL_c36638fb29cb4510bfdda737e03c4a0e", - "value": "Map: 65%" - } - }, - "5406303479514972b3e5241ce40e3f70": { - "model_module": "@jupyter-widgets/output", - "model_module_version": "1.0.0", - "model_name": "OutputModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/output", - "_model_module_version": "1.0.0", - "_model_name": "OutputModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/output", - "_view_module_version": "1.0.0", - "_view_name": "OutputView", - "layout": "IPY_MODEL_21a09c7691c64e088a74c08095df0057", - "msg_id": "", - "outputs": [ - { - "data": { - "text/html": "
Logging... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   0% -:--:--\n
\n", - "text/plain": "Logging... \u001b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[35m 0%\u001b[0m \u001b[36m-:--:--\u001b[0m\n" - }, - "metadata": {}, - "output_type": "display_data" - } - ] - } - }, - "706f8e4861904c33a9468216e3220d9b": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "7605822dfe30439a8b53c117466e513f": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_50edec55b45242d3a3bd0b3d9444710e", - "placeholder": "​", - "style": "IPY_MODEL_7d817c646854435686ab9b7e317b2f92", - "value": "Map: 98%" - } - }, - "7d817c646854435686ab9b7e317b2f92": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "7e028b1a16394adcac90c011d14d3458": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HBoxModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_7605822dfe30439a8b53c117466e513f", - "IPY_MODEL_13d0949b0cad4cee8f1aa691ebd43476", - "IPY_MODEL_452429879663485da881188f70d20ca5" - ], - "layout": "IPY_MODEL_d4bae643e36a4b258b137507d628399d" - } - }, - "81e07d9401bd40cc82240136a529be2d": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "83d7b92514e24e3d88f7207ec26a6b76": { - "model_module": "@jupyter-widgets/output", - "model_module_version": "1.0.0", - "model_name": "OutputModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/output", - "_model_module_version": "1.0.0", - "_model_name": "OutputModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/output", - "_view_module_version": "1.0.0", - "_view_name": "OutputView", - "layout": "IPY_MODEL_9146f98b81744bceb0083e98aa1c3af9", - "msg_id": "", - "outputs": [ - { - "data": { - "text/html": "
Logging... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   0% -:--:--\n
\n", - "text/plain": "Logging... \u001b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[35m 0%\u001b[0m \u001b[36m-:--:--\u001b[0m\n" - }, - "metadata": {}, - "output_type": "display_data" - } - ] - } - }, - "8f5ca2714e114119adb61c9122afffbb": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "9146f98b81744bceb0083e98aa1c3af9": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "9217d6b5c2a0495bbb0724dd296b2af7": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HBoxModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_518e260b7d2f4f90b39668414389c166", - "IPY_MODEL_4635b9f0d42b4172b52b64de660082d1", - "IPY_MODEL_b1df83e2ef6540bbb09098e12d707d6d" - ], - "layout": "IPY_MODEL_0a54f0576a9347d5a546ff8b3e371121" - } - }, - "b1df83e2ef6540bbb09098e12d707d6d": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_007a11c0643c45ecbf50c92bb244170f", - "placeholder": "​", - "style": "IPY_MODEL_81e07d9401bd40cc82240136a529be2d", - "value": " 1665/2561 [00:00<00:00, 14760.53 examples/s]" - } - }, - "c36638fb29cb4510bfdda737e03c4a0e": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "cf5da266e6ad493da229cf05469b2fc4": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } + "base_uri": "https://localhost:8080/", + "height": 69, + "referenced_widgets": [ + "19d173ba40134ea3ba4625977b924c83", + "24b90ef58e0e41cfb668144bc0adb815" + ] + }, + "id": "9da01d6f-2728-4ed0-b6aa-cd2e2531202c", + "outputId": "eeaba929-596e-4c59-9778-a37458a83c37" + }, + "outputs": [], + "source": [ + "# Log the dataset to the Argilla web app\n", + "rg.log(dataset_rg, \"ecdc_en\")" + ] + }, + { + "cell_type": "markdown", + "id": "f5a4969a-9132-459d-9f8d-e0006a1d52a0", + "metadata": { + "id": "f5a4969a-9132-459d-9f8d-e0006a1d52a0" + }, + "source": [ + "![Screenshot of the uploaded English phrases.](https://github.com/argilla-io/argilla/blob/main/docs/_source/_static/reference/webapp/explore-text2text.png?raw=1)" + ] + }, + { + "cell_type": "markdown", + "id": "e3139dd1-a939-4baa-8d26-bd1214c8cbcd", + "metadata": { + "id": "e3139dd1-a939-4baa-8d26-bd1214c8cbcd" + }, + "source": [ + "## Label datasets" + ] + }, + { + "cell_type": "markdown", + "id": "5bfeaf75-143e-4543-85f3-2a6e995dcf06", + "metadata": { + "id": "5bfeaf75-143e-4543-85f3-2a6e995dcf06" + }, + "source": [ + "Argilla provides several ways to label your data. Using Argilla's UI, you can mix and match the following options:\n", + "\n", + "\n", + "1. Manually labeling each record using the specialized interface for each task type;\n", + "2. Leveraging a user-provided model and validating its predictions;\n", + "3. Defining heuristic rules to produce \"noisy labels\" which can then be combined with weak supervision;\n", + "\n", + "Each way has its pros and cons, and the best match largely depends on your individual use case.\n" + ] + }, + { + "cell_type": "markdown", + "id": "b85a058b", + "metadata": { + "id": "b85a058b" + }, + "source": [ + "### Annotation guideline\n", + "\n", + "Before starting the annotation process with a team, it is important to align the different truths everyone in the team thinks they have. Because the same text is going to be annotated by multiple annotators independently or we might want to revisit an old dataset later on. Besides a set of obvious mistakes, we also often encounter uncertain grey areas. Consider the following phrase for NER-annotation `Harry Potter and the Prisoner of Azkaban` can be interpreted in many ways. The entire phrase is as the movie title, `Harry Potter` is a person, and `Azkaban` is a location. Maybe we don´t even want to annotate fictional locations and characters. Therefore, it is important to define these assumptions beforehand and iterate over them together with the team. Take a look at [this blog](https://www.superb-ai.com/blog/how-to-write-better-annotation-guidelines-for-human-labelers-4-top-tips) from our friends over at `suberb.ai` or [this blog](https://www.grammarly.com/blog/engineering/annotation-best-practices/?utm_campaign=B2C&utm_medium=social&utm_source=LinkedIn_org&utm_term=blog&utm_content=link) from Grammarly for more context." + ] + }, + { + "cell_type": "markdown", + "id": "cf0bccc4-05c7-4c27-920b-5b76eb4acd22", + "metadata": { + "id": "cf0bccc4-05c7-4c27-920b-5b76eb4acd22" + }, + "source": [ + "### 1. Manual labeling" + ] + }, + { + "cell_type": "markdown", + "id": "3b085744-7c61-4614-b542-c4de2cea9181", + "metadata": { + "id": "3b085744-7c61-4614-b542-c4de2cea9181" + }, + "source": [ + "![Manual annotations of a sentiment classification task](https://github.com/argilla-io/argilla/blob/main/docs/_source/_static/reference/webapp/features-metrics.png?raw=1)\n", + "\n", + "The straightforward approach of manual annotations might be necessary if you do not have a suitable model for your use case or cannot come up with good heuristic rules for your dataset.\n", + "It can also be a good approach if you dispose of a large annotation workforce or require few but unbiased and high-quality labels.\n", + "\n", + "Argilla tries to make this relatively cumbersome approach as painless as possible.\n", + "Via an intuitive and adaptive UI, its exhaustive search and filter functionalities, and bulk annotation capabilities, Argilla turns the manual annotation process into an efficient option. \n", + "\n", + "Look at our dedicated [feature reference](https://docs.argilla.io/en/latest/reference/webapp/features.html) for a detailed and illustrative guide on manually annotating your dataset with Argilla." + ] + }, + { + "cell_type": "markdown", + "id": "e631840b-9cf7-45e6-9dc4-5f6b24cf0e8b", + "metadata": { + "id": "e631840b-9cf7-45e6-9dc4-5f6b24cf0e8b" + }, + "source": [ + "### 2. Validating predictions" + ] + }, + { + "cell_type": "markdown", + "id": "e3c46840-0991-4fc0-bbb0-15df33ee242b", + "metadata": { + "id": "e3c46840-0991-4fc0-bbb0-15df33ee242b" + }, + "source": [ + "![Validate predictions for a token classification dataset](https://github.com/argilla-io/argilla/blob/main/docs/_source/_static/reference/webapp/features-validation.png?raw=1)\n", + "\n", + "Nowadays, many pre-trained or zero-shot models are available online via model repositories like the Hugging Face Hub.\n", + "Most of the time, you probably will find a model that already suits your specific dataset task to some degree.\n", + "In Argilla, you can pre-annotate your data by including predictions from these models in your records.\n", + "Assuming that the model works reasonably well on your dataset, you can filter for records with high prediction scores and validate the predictions.\n", + "In this way, you will rapidly annotate part of your data and alleviate the annotation process.\n", + "\n", + "One downside of this approach is that your annotations will be subject to the possible biases and mistakes of the pre-trained model.\n", + "When guided by pre-trained models, it is common to see human annotators get influenced by them.\n", + "Therefore, it is advisable to avoid pre-annotations when building a rigorous test set for the final model evaluation.\n", + "\n", + "Check the [introduction tutorial](https://docs.argilla.io/en/latest/tutorials/notebooks/labelling-tokenclassification-spacy-pretrained.html) to learn to add predictions to the records.\n", + "And our [feature reference](https://docs.argilla.io/en/latest/reference/webapp/features.html) includes a detailed guide on validating predictions in the Argilla web app." + ] + }, + { + "cell_type": "markdown", + "id": "c2cbd593-e241-4f27-9a58-6932912ea9f1", + "metadata": { + "id": "c2cbd593-e241-4f27-9a58-6932912ea9f1" + }, + "source": [ + "### 3. Weak labeling rules" + ] + }, + { + "cell_type": "markdown", + "id": "87f7ea92-d40f-4d09-a0e8-16b7d7867e6e", + "metadata": { + "id": "87f7ea92-d40f-4d09-a0e8-16b7d7867e6e" + }, + "source": [ + "![Defining a rule for a multi-label text classification task.](https://github.com/argilla-io/argilla/blob/main/docs/_source/_static/reference/webapp/features-weak-labelling.png?raw=1)\n", + "\n", + "Another approach to annotating your data is to define heuristic rules tailored to your dataset.\n", + "For example, let us assume you want to classify news articles into the categories of *Finance*, *Sports*, and *Culture*.\n", + "In this case, a reasonable rule would be to label all articles that include the word \"stock\" as *Finance*.\n", + "\n", + "Rules can get arbitrarily complex and can also include the record's metadata.\n", + "The downside of this approach is that it might be challenging to come up with working heuristic rules for some datasets.\n", + "Furthermore, rules are rarely 100% precise and often conflict with each other. These noisy labels can be cleaned up using weak supervision and label models, or something as simple as majority voting. It is usually a trade-off between the amount of annotated data and the quality of the labels.\n", + "\n", + "Check [our guide](https://docs.argilla.io/en/latest/practical_guides/annotation_workflows/weak_supervision.html) for an extensive introduction to weak supervision with Argilla.\n", + "Also, check the [feature reference](https://docs.argilla.io/en/latest/reference/webapp/features.html#weak-labeling) for the Define rules mode of the web app and our [various tutorials](https://docs.argilla.io/en/latest/tutorials/techniques/weak_supervision.html) to see practical examples of weak supervision workflows." + ] + }, + { + "cell_type": "markdown", + "id": "90307acf-ba85-4f8c-86d3-ca398be7a496", + "metadata": { + "id": "90307acf-ba85-4f8c-86d3-ca398be7a496" + }, + "source": [ + "## Train a model\n", + "\n", + "The `ArgillaTrainer` is a wrapper around many of our favorite NLP libraries. It provides a very intuitive abstract workflow to facilitate simple training workflows using decent default pre-set configurations without having to worry about any data transformations from Argilla. More info [here](https://docs.argilla.io/en/latest/practical_guides/fine_tune.html)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4D8LKfk1rUvC", + "metadata": { + "id": "4D8LKfk1rUvC" + }, + "outputs": [], + "source": [ + "from argilla.training import ArgillaTrainer\n", + "\n", + "sentence = \"I love this film, but the new remake is terrible.\"\n", + "\n", + "trainer = ArgillaTrainer(\n", + " name=\"imdb\",\n", + " workspace=\"argilla\",\n", + " framework=\"spacy\",\n", + " train_size=0.8\n", + ")\n", + "trainer.update_config(max_epochs=1, max_steps=1)\n", + "trainer.train(output_dir=\"my_easy_model\")\n", + "\n", + "records = trainer.predict(sentence, as_argilla_records=True)\n", + "\n", + "# Print the prediction\n", + "print(\"\\ntesting predictions...\")\n", + "print(sentence)\n", + "print(f\"Predicted_label: {records.prediction}\")" + ] + }, + { + "cell_type": "markdown", + "id": "29cb1351-6324-4faa-9067-fd50785844f5", + "metadata": { + "id": "29cb1351-6324-4faa-9067-fd50785844f5" + }, + "source": [ + "Argilla helps you to create and curate training data. **It is not a complete framework for training a model but we do provide integrations.** You can use Argilla complementary with other excellent open-source frameworks that focus on developing and training NLP models.\n", + "\n", + "Here we list three of the most commonly used open-source libraries, but many more are available and may be more suited for your specific use case:\n", + "\n", + " - [transformers](https://huggingface.co/docs/transformers/index): This library provides thousands of pre-trained models for various NLP tasks and modalities. Its excellent documentation focuses on fine-tuning those models to your specific use case;\n", + " - [spaCy](https://spacy.io/): This library also comes with pre-trained models built into a pipeline tackling multiple tasks simultaneously. Since it is a purely NLP library, it comes with many more NLP features than just model training;\n", + " - [spark-nlp](https://nlp.johnsnowlabs.com/): Spark NLP is an open-source text processing library for advanced natural language processing for the Python, Java and Scala programming languages. The library is built on top of Apache Spark and its Spark ML library.\n", + " - [scikit-learn](https://scikit-learn.org/stable/): This de facto standard library is a powerful Swiss army knife for machine learning with some NLP support. Usually, their NLP models lack the performance when compared to transformers or spacy, but give it a try if you want to train a lightweight model quickly;\n" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.18" + }, + "vscode": { + "interpreter": { + "hash": "2584bca9d226488c39a669ff1ce19d7ca5f410e2d3aa9b82f20653edd0d96bfc" + } + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "007a11c0643c45ecbf50c92bb244170f": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "0a54f0576a9347d5a546ff8b3e371121": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": "hidden", + "width": null + } + }, + "13d0949b0cad4cee8f1aa691ebd43476": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_e9f06d2e0ff645aea882a0958eda2820", + "max": 464, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_15c7227ddf8946d7bdd9c5e076407300", + "value": 464 + } + }, + "15c7227ddf8946d7bdd9c5e076407300": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "19d173ba40134ea3ba4625977b924c83": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/output", + "_model_module_version": "1.0.0", + "_model_name": "OutputModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/output", + "_view_module_version": "1.0.0", + "_view_name": "OutputView", + "layout": "IPY_MODEL_24b90ef58e0e41cfb668144bc0adb815", + "msg_id": "", + "outputs": [ + { + "data": { + "text/html": "
Logging... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━  98% 0:00:01\n
\n", + "text/plain": "Logging... \u001b[38;2;249;38;114m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[38;5;237m╺\u001b[0m \u001b[35m 98%\u001b[0m \u001b[36m0:00:01\u001b[0m\n" }, - "d4bae643e36a4b258b137507d628399d": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": "hidden", - "width": null - } + "metadata": {}, + "output_type": "display_data" + } + ] + } + }, + "21a09c7691c64e088a74c08095df0057": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "24b90ef58e0e41cfb668144bc0adb815": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "2d9d836276674563893af311d7ceb5cf": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "452429879663485da881188f70d20ca5": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_f47cb9c3e20e48acb6df9bed2f0cb7bf", + "placeholder": "​", + "style": "IPY_MODEL_2d9d836276674563893af311d7ceb5cf", + "value": " 453/464 [00:04<00:00, 112.79 examples/s]" + } + }, + "4635b9f0d42b4172b52b64de660082d1": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_514672241dad488ca37a5e802553eb35", + "max": 2561, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_48dbaf59d0be41a6a6d7c30cb621e9ef", + "value": 2561 + } + }, + "48dbaf59d0be41a6a6d7c30cb621e9ef": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "50edec55b45242d3a3bd0b3d9444710e": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "514672241dad488ca37a5e802553eb35": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "518e260b7d2f4f90b39668414389c166": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_8f5ca2714e114119adb61c9122afffbb", + "placeholder": "​", + "style": "IPY_MODEL_c36638fb29cb4510bfdda737e03c4a0e", + "value": "Map: 65%" + } + }, + "5406303479514972b3e5241ce40e3f70": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/output", + "_model_module_version": "1.0.0", + "_model_name": "OutputModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/output", + "_view_module_version": "1.0.0", + "_view_name": "OutputView", + "layout": "IPY_MODEL_21a09c7691c64e088a74c08095df0057", + "msg_id": "", + "outputs": [ + { + "data": { + "text/html": "
Logging... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   0% -:--:--\n
\n", + "text/plain": "Logging... \u001b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[35m 0%\u001b[0m \u001b[36m-:--:--\u001b[0m\n" }, - "d577c60f0dbe46c1be6fab55672c4f86": { - "model_module": "@jupyter-widgets/output", - "model_module_version": "1.0.0", - "model_name": "OutputModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/output", - "_model_module_version": "1.0.0", - "_model_name": "OutputModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/output", - "_view_module_version": "1.0.0", - "_view_name": "OutputView", - "layout": "IPY_MODEL_cf5da266e6ad493da229cf05469b2fc4", - "msg_id": "", - "outputs": [ - { - "data": { - "text/html": "
Logging... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   0% -:--:--\n
\n", - "text/plain": "Logging... \u001b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[35m 0%\u001b[0m \u001b[36m-:--:--\u001b[0m\n" - }, - "metadata": {}, - "output_type": "display_data" - } - ] - } + "metadata": {}, + "output_type": "display_data" + } + ] + } + }, + "706f8e4861904c33a9468216e3220d9b": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "7605822dfe30439a8b53c117466e513f": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_50edec55b45242d3a3bd0b3d9444710e", + "placeholder": "​", + "style": "IPY_MODEL_7d817c646854435686ab9b7e317b2f92", + "value": "Map: 98%" + } + }, + "7d817c646854435686ab9b7e317b2f92": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "7e028b1a16394adcac90c011d14d3458": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_7605822dfe30439a8b53c117466e513f", + "IPY_MODEL_13d0949b0cad4cee8f1aa691ebd43476", + "IPY_MODEL_452429879663485da881188f70d20ca5" + ], + "layout": "IPY_MODEL_d4bae643e36a4b258b137507d628399d" + } + }, + "81e07d9401bd40cc82240136a529be2d": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "83d7b92514e24e3d88f7207ec26a6b76": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/output", + "_model_module_version": "1.0.0", + "_model_name": "OutputModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/output", + "_view_module_version": "1.0.0", + "_view_name": "OutputView", + "layout": "IPY_MODEL_9146f98b81744bceb0083e98aa1c3af9", + "msg_id": "", + "outputs": [ + { + "data": { + "text/html": "
Logging... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   0% -:--:--\n
\n", + "text/plain": "Logging... \u001b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[35m 0%\u001b[0m \u001b[36m-:--:--\u001b[0m\n" }, - "db3e5db8fe3f42a8908e3cd2a029cf56": { - "model_module": "@jupyter-widgets/output", - "model_module_version": "1.0.0", - "model_name": "OutputModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/output", - "_model_module_version": "1.0.0", - "_model_name": "OutputModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/output", - "_view_module_version": "1.0.0", - "_view_name": "OutputView", - "layout": "IPY_MODEL_706f8e4861904c33a9468216e3220d9b", - "msg_id": "", - "outputs": [ - { - "data": { - "text/html": "
Logging... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╺━━━━━  86% 0:00:01\n
\n", - "text/plain": "Logging... \u001b[38;2;249;38;114m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[38;5;237m╺\u001b[0m\u001b[38;5;237m━━━━━\u001b[0m \u001b[35m 86%\u001b[0m \u001b[36m0:00:01\u001b[0m\n" - }, - "metadata": {}, - "output_type": "display_data" - } - ] - } + "metadata": {}, + "output_type": "display_data" + } + ] + } + }, + "8f5ca2714e114119adb61c9122afffbb": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "9146f98b81744bceb0083e98aa1c3af9": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "9217d6b5c2a0495bbb0724dd296b2af7": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_518e260b7d2f4f90b39668414389c166", + "IPY_MODEL_4635b9f0d42b4172b52b64de660082d1", + "IPY_MODEL_b1df83e2ef6540bbb09098e12d707d6d" + ], + "layout": "IPY_MODEL_0a54f0576a9347d5a546ff8b3e371121" + } + }, + "b1df83e2ef6540bbb09098e12d707d6d": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_007a11c0643c45ecbf50c92bb244170f", + "placeholder": "​", + "style": "IPY_MODEL_81e07d9401bd40cc82240136a529be2d", + "value": " 1665/2561 [00:00<00:00, 14760.53 examples/s]" + } + }, + "c36638fb29cb4510bfdda737e03c4a0e": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "cf5da266e6ad493da229cf05469b2fc4": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "d4bae643e36a4b258b137507d628399d": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": "hidden", + "width": null + } + }, + "d577c60f0dbe46c1be6fab55672c4f86": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/output", + "_model_module_version": "1.0.0", + "_model_name": "OutputModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/output", + "_view_module_version": "1.0.0", + "_view_name": "OutputView", + "layout": "IPY_MODEL_cf5da266e6ad493da229cf05469b2fc4", + "msg_id": "", + "outputs": [ + { + "data": { + "text/html": "
Logging... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   0% -:--:--\n
\n", + "text/plain": "Logging... \u001b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[35m 0%\u001b[0m \u001b[36m-:--:--\u001b[0m\n" }, - "e9f06d2e0ff645aea882a0958eda2820": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } + "metadata": {}, + "output_type": "display_data" + } + ] + } + }, + "db3e5db8fe3f42a8908e3cd2a029cf56": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/output", + "_model_module_version": "1.0.0", + "_model_name": "OutputModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/output", + "_view_module_version": "1.0.0", + "_view_name": "OutputView", + "layout": "IPY_MODEL_706f8e4861904c33a9468216e3220d9b", + "msg_id": "", + "outputs": [ + { + "data": { + "text/html": "
Logging... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╺━━━━━  86% 0:00:01\n
\n", + "text/plain": "Logging... \u001b[38;2;249;38;114m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[38;5;237m╺\u001b[0m\u001b[38;5;237m━━━━━\u001b[0m \u001b[35m 86%\u001b[0m \u001b[36m0:00:01\u001b[0m\n" }, - "f47cb9c3e20e48acb6df9bed2f0cb7bf": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - } - } + "metadata": {}, + "output_type": "display_data" + } + ] + } + }, + "e9f06d2e0ff645aea882a0958eda2820": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "f47cb9c3e20e48acb6df9bed2f0cb7bf": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } } - }, - "nbformat": 4, - "nbformat_minor": 5 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/docs/_source/getting_started/quickstart_workflow_feedback.ipynb b/docs/_source/getting_started/quickstart_workflow_feedback.ipynb index 38b1626ad4..a8d03660af 100644 --- a/docs/_source/getting_started/quickstart_workflow_feedback.ipynb +++ b/docs/_source/getting_started/quickstart_workflow_feedback.ipynb @@ -1,1623 +1,1618 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "9ba716c0", - "metadata": {}, - "source": [ - "
\n", - "\n", - "Note\n", - " \n", - "This tutorial demonstrates a sample usage for `FeedbackDataset`, which offers implementations different from the old `TextClassificationDataset`, `Text2TextDataset` and `TokenClassificationDataset`. To have info about old datasets, you can have a look at them [here](../getting_started/quickstart_workflow.html).\n", - " \n", - "
" - ] - }, - { - "cell_type": "markdown", - "id": "8b1cd645", - "metadata": { - "id": "8b1cd645" - }, - "source": [ - "# Workflow Feedback Dataset\n", - "\n", - "Argilla Feedback is a tool designed to obtain and manage both the feedback data from annotators and the suggestions from LLMs.\n" - ] - }, - { - "cell_type": "markdown", - "id": "fbc7acc7", - "metadata": {}, - "source": [ - "## Install Libraries\n", - "\n", - "Install the latest version of Argilla in Colab, along with other libraries and models used in this notebook." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f808bb22", - "metadata": {}, - "outputs": [], - "source": [ - "!pip install argilla datasets setfit evaluate seqeval" - ] - }, + "cells": [ + { + "cell_type": "markdown", + "id": "9ba716c0", + "metadata": {}, + "source": [ + "
\n", + "\n", + "Note\n", + " \n", + "This tutorial demonstrates a sample usage for `FeedbackDataset`, which offers implementations different from the old `TextClassificationDataset`, `Text2TextDataset` and `TokenClassificationDataset`. To have info about old datasets, you can have a look at them [here]([../getting_started/quickstart_workflow.html](https://docs.argilla.io/en/latest/getting_started/quickstart_workflow.html)). Not sure which dataset to use? Check out our section on [choosing a dataset](https://docs.argilla.io/en/latest/practical_guides/choose_dataset.html).\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "8b1cd645", + "metadata": { + "id": "8b1cd645" + }, + "source": [ + "# Workflow Feedback Dataset\n", + "\n", + "Argilla Feedback is a tool designed to obtain and manage both the feedback data from annotators and the suggestions from small and large language models.\n" + ] + }, + { + "cell_type": "markdown", + "id": "fbc7acc7", + "metadata": {}, + "source": [ + "## Install Libraries\n", + "\n", + "Install the latest version of Argilla in Colab, along with other libraries and models used in this notebook." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f808bb22", + "metadata": {}, + "outputs": [], + "source": [ + "!pip install argilla datasets setfit evaluate seqeval" + ] + }, + { + "cell_type": "markdown", + "id": "EwDfn8E7W7jD", + "metadata": { + "id": "EwDfn8E7W7jD" + }, + "source": [ + "## Set Up Argilla\n", + "\n", + "If you have already deployed Argilla Server, then you can skip this step. Otherwise, you can quickly deploy it in two different ways:\n", + "\n", + "* You can deploy Argilla Server on [HF Spaces](https://huggingface.co/new-space?template=argilla/argilla-template-space).\n", + "\n", + "* Alternatively, if you want to run Argilla locally on your own computer, the easiest way to get Argilla UI up and running is to deploy on Docker:\n", + "\n", + " ```\n", + " docker run -d --name quickstart -p 6900:6900 argilla/argilla-quickstart:latest\n", + " ```\n", + "\n", + "More info on Installation [here](../getting_started/installation/deployments/deployments.html)." + ] + }, + { + "cell_type": "markdown", + "id": "00b2e199", + "metadata": { + "id": "00b2e199" + }, + "source": [ + "## Connect to Argilla\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "a93bc742", + "metadata": {}, + "source": [ + "It is possible to connect to our Argilla instance by simply importing the Argilla library and using the environment variables and `rg.init()`.\n", + "\n", + "* `ARGILLA_API_URL`: It is the url of the Argilla Server.\n", + " * If you're using Docker, it is `http://localhost:6900` by default.\n", + " * If you're using HF Spaces, it is constructed as `https://[your-owner-name]-[your_space_name].hf.space`.\n", + "* `ARGILLA_API_KEY`: It is the API key of the Argilla Server. It is `owner` by default.\n", + "* `HF_TOKEN`: It is the Hugging Face API token. It is only needed if you're using a [private HF Space](https://docs.argilla.io/en/latest/getting_started/installation/deployments/huggingface-spaces.html#deploy-argilla-on-spaces). You can configure it in your profile: [Setting > Access Tokens](https://huggingface.co/settings/tokens).\n", + "* `workspace`: It is a “space” inside your Argilla instance where authorized users can collaborate. It's `argilla` by default.\n", + "\n", + "For more info about custom configurations like headers, workspace separation or access credentials, check our [config page](https://docs.argilla.io/en/latest/getting_started/installation/configurations/configurations.html)." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "215b5b39", + "metadata": {}, + "outputs": [], + "source": [ + "import argilla as rg\n", + "from argilla._constants import DEFAULT_API_KEY" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "d3609e0a", + "metadata": { + "tags": [ + "parameters" + ] + }, + "outputs": [], + "source": [ + "# Argilla credentials\n", + "api_url = \"http://localhost:6900\" # \"https://.hf.space\"\n", + "api_key = DEFAULT_API_KEY # admin.apikey\n", + "# Huggingface credentials\n", + "hf_token = \"hf_...\"" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "19c56015", + "metadata": {}, + "outputs": [ { - "cell_type": "markdown", - "id": "EwDfn8E7W7jD", - "metadata": { - "id": "EwDfn8E7W7jD" - }, - "source": [ - "## Set Up Argilla\n", - "\n", - "You can quickly deploy Argilla Server on [HF Spaces](https://huggingface.co/new-space?template=argilla/argilla-template-space).\n", - "\n", - "Alternatively, if you want to run Argilla locally on your own computer, the easiest way to get Argilla UI up and running is to deploy on Docker:\n", - "\n", - "```\n", - "docker run -d --name quickstart -p 6900:6900 argilla/argilla-quickstart:latest\n", - "```\n", - "\n", - "More info on Installation [here](../getting_started/installation/deployments/deployments.html)." - ] - }, + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Users\\sarah\\Documents\\argilla\\src\\argilla\\client\\client.py:154: UserWarning: Default user was detected and no workspace configuration was provided, so the default 'argilla' workspace will be used. If you want to setup another workspace, use the `rg.set_workspace` function or provide a different one on `rg.init`\n", + " warnings.warn(\n" + ] + } + ], + "source": [ + "import argilla as rg\n", + "rg.init(api_url=api_url, api_key=api_key)\n", + "\n", + "# # If you want to use your private HF Space\n", + "# rg.init(extra_headers={\"Authorization\": f\"Bearer {hf_token}\"})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Enable Telemetry\n", + "\n", + "We gain valuable insights from how you interact with our tutorials. To improve ourselves in offering you the most suitable content, using the following lines of code will help us understand that this tutorial is serving you effectively. Though this is entirely anonymous, you can choose to skip this step if you prefer. For more info, please check out the [Telemetry](../../reference/telemetry.md) page." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " from argilla.utils.telemetry import tutorial_running\n", + " tutorial_running()\n", + "except ImportError:\n", + " print(\"Telemetry is introduced in Argilla 1.20.0 and not found in the current installation. Skipping telemetry.\")" + ] + }, + { + "cell_type": "markdown", + "id": "423d6483", + "metadata": {}, + "source": [ + "## Create a Dataset\n", + "\n", + "FeedbackDataset is the container for Argilla Feedback structure. Argilla Feedback offers different components for FeedbackDatasets that you can employ for various aspects of your workflow. For a more detailed explanation, refer to the [documentation](https://docs.argilla.io/en/latest/practical_guides/practical_guides.html) and the [end-to-end tutorials](https://docs.argilla.io/en/latest/tutorials_and_integrations/tutorials/tutorials.html) for beginners.\n", + "\n", + "To start, we need to configure the FeedbackDatasest. To do so, there are two options: use a pre-defined template or create a custom one." + ] + }, + { + "cell_type": "markdown", + "id": "4146f0d7", + "metadata": {}, + "source": [ + "### Use a Task Template\n", + "\n", + "Argilla offers a set of [pre-defined templates for different tasks](https://docs.argilla.io/en/latest/practical_guides/create_update_dataset/create_dataset.html#task-templates). You can use them to configure your dataset straightforward. For instance, if you want to create a dataset for simple text classification, you can use the following code:" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "96b161c4", + "metadata": {}, + "outputs": [ { - "cell_type": "markdown", - "id": "00b2e199", - "metadata": { - "id": "00b2e199" - }, - "source": [ - "## Connect to Argilla\n", - "\n" + "data": { + "text/plain": [ + "FeedbackDataset(\n", + " fields=[TextField(name='text', title='Text', required=True, type='text', use_markdown=True)]\n", + " questions=[LabelQuestion(name='label', title='Label', description='Classify the text by selecting the correct label from the given list of labels.', required=True, type='label_selection', labels=['joy', 'sadness'], visible_labels=None)]\n", + " guidelines=This is a text classification dataset that contains texts and labels. Given a set of texts and a predefined set of labels, the goal of text classification is to assign one label to each text based on its content. Please classify the texts by making the correct selection.)\n", + " metadata_properties=[])\n", + ")" ] - }, + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dataset = rg.FeedbackDataset.for_text_classification(\n", + " labels=[\"joy\", \"sadness\"],\n", + " multi_label=False,\n", + " use_markdown=True,\n", + " guidelines=None,\n", + " metadata_properties=None,\n", + " vectors_settings=None,\n", + ")\n", + "dataset" + ] + }, + { + "cell_type": "markdown", + "id": "4f9ca558", + "metadata": {}, + "source": [ + "Now that we have our dataset, we can push the dataset to the Argilla space.\n", + "\n", + "
\n", + "\n", + "Note\n", + " \n", + "From Argilla 1.14.0, calling `push_to_argilla` will not just push the `FeedbackDataset` into Argilla, but will also return the remote `FeedbackDataset` instance, which implies that the additions, updates, and deletions of records will be pushed to Argilla as soon as they are made. This is a change from previous versions of Argilla, where you had to call `push_to_argilla` again to push the changes to Argilla.\n", + " \n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dc956805", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " dataset.push_to_argilla(name=\"my-first-dataset\", workspace=\"argilla\")\n", + "except:\n", + " pass" + ] + }, + { + "cell_type": "markdown", + "id": "2847830f", + "metadata": {}, + "source": [ + "### Configure a Custom Dataset\n", + "\n", + "If your dataset does not fit into one of the pre-defined templates, you [can create a custom dataset](https://docs.argilla.io/en/latest/practical_guides/create_update_dataset/create_dataset.html#define-questions) by defining the fields, the different question types, the metadata properties and the vectors settings." + ] + }, + { + "cell_type": "markdown", + "id": "4b099e23", + "metadata": {}, + "source": [ + "## Add the Records\n", + "\n", + "A record refers to each of the data items that will be annotated by the annotator team. The records will be the pieces of information that will be shown to the user in the UI in order to complete the annotation task. In the current dataset sample, it can only consist of a text to be labeled." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "36b9b719", + "metadata": {}, + "outputs": [], + "source": [ + "records = [\n", + " rg.FeedbackRecord(\n", + " fields={\n", + " \"text\": \"I am so happy today\",\n", + " },\n", + " ),\n", + " rg.FeedbackRecord(\n", + " fields={\n", + " \"text\": \"I feel sad today\",\n", + " },\n", + " )\n", + "]\n", + "dataset.add_records(records)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "df7d2540", + "metadata": {}, + "outputs": [ { - "cell_type": "markdown", - "id": "klwYMH_rdv0m", - "metadata": { - "id": "klwYMH_rdv0m" - }, - "source": [ - "It is possible to connect to our Argilla instance by simply importing Argilla library, which internally connects to Argilla Server using the `ARGILLA_API_URL` and `ARGILLA_API_KEY` environment variables." + "data": { + "text/plain": [ + "[FeedbackRecord(fields={'text': 'I am so happy today'}, metadata={}, vectors={}, responses=[], suggestions=(), external_id=None),\n", + " FeedbackRecord(fields={'text': 'I feel sad today'}, metadata={}, vectors={}, responses=[], suggestions=(), external_id=None)]" ] - }, + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dataset.records" + ] + }, + { + "cell_type": "markdown", + "id": "850c8a0e", + "metadata": {}, + "source": [ + "Argilla also offers a way to use suggestions and responses from other models as a starting point for annotators. This way, annotators can save time and effort by correcting the predictions or answers instead of annotating from scratch. " + ] + }, + { + "cell_type": "markdown", + "id": "5a5a3bf3", + "metadata": {}, + "source": [ + "## Train a model\n", + "\n", + "As with other datasets, Feedback datasets also allow to create a training pipeline and make inferences with the resulting model. After you gather responses with Argilla Feedback, you can easily fine-tune an LLM. In this example, we will have to complete a text classification task.\n", + "\n", + "For fine-tuning, we will use setfit library and the [Argilla Trainer](https://docs.argilla.io/en/latest/practical_guides/fine_tune.html#the-argillatrainer), which is a powerful wrapper around many of our favorite NLP libraries. It provides a very intuitive abstract representation to facilitate simple training workflows using decent default pre-set configurations without having to worry about any data transformations from Argilla.\n", + "\n", + "Let us first create our dataset to train. For this example, we will use the [emotion](https://huggingface.co/datasets/argilla/emotion) dataset from Argilla, which was created using Argilla. Each text item has its responses as 6 different sentiments, which are Sadness, Joy, Love, Anger, Fear and Surprise." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "586059d7", + "metadata": {}, + "outputs": [], + "source": [ + "# Besides Argilla, it can also be imported with load_dataset from datasets\n", + "dataset_hf = rg.FeedbackDataset.from_huggingface(\"argilla/emotion\", split=\"train[1:101]\")" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "9286b053", + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "execution_count": null, - "id": "IiJiKTi-dgLp", - "metadata": { - "id": "IiJiKTi-dgLp" - }, - "outputs": [], - "source": [ - "import os\n", - "#set your variable here\n", - "os.environ[\"ARGILLA_API_URL\"] = \"your_argilla_URL\"\n", - "os.environ[\"ARGILLA_API_KEY\"] = \"owner.apikey\"" + "data": { + "text/plain": [ + "FeedbackDataset(\n", + " fields=[TextField(name='text', title='Text', required=True, type=, use_markdown=False)]\n", + " questions=[LabelQuestion(name='label', title='Label', description=None, required=True, type=, labels={'0': 'sadness', '1': 'joy', '2': 'love', '3': 'anger', '4': 'fear', '5': 'surprise'}, visible_labels=6)]\n", + " guidelines=Argilla port of [dair-ai/emotion](https://huggingface.co/datasets/dair-ai/emotion).)\n", + " metadata_properties=[])\n", + ")" ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dataset_hf" + ] + }, + { + "cell_type": "markdown", + "id": "861c3648", + "metadata": {}, + "source": [ + "We can then start to create a training pipeline by first defining `TrainingTask`, which is used to define how the data should be processed and formatted according to the associated task and framework. Each task has its own classmethod and the data formatting can always be customized via `formatting_func`. You can visit [this page](https://docs.argilla.io/en/latest/practical_guides/fine_tune.html#tasks) for more info. Simpler tasks like text classification can be defined using default definitions, as we do in this example." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "04537510", + "metadata": {}, + "outputs": [], + "source": [ + "from argilla.feedback import TrainingTask\n", + "\n", + "task = TrainingTask.for_text_classification(\n", + " text=dataset_hf.field_by_name(\"text\"),\n", + " label=dataset_hf.question_by_name(\"label\")\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "2492e2e2", + "metadata": {}, + "source": [ + "We can then define our ArgillaTrainer for any of the supported frameworks and customize the training config using ArgillaTrainer.update_config.\n", + "\n", + "Let us define ArgillaTrainer with any of the supported frameworks. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8187adc1", + "metadata": {}, + "outputs": [], + "source": [ + "from argilla.feedback import ArgillaTrainer\n", + "\n", + "trainer = ArgillaTrainer(\n", + " dataset=dataset_hf,\n", + " task=task,\n", + " framework=\"setfit\",\n", + " train_size=0.8\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "1bccf880", + "metadata": {}, + "source": [ + "You can update the model config via `update_config`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "332a4f1d", + "metadata": {}, + "outputs": [], + "source": [ + "trainer.update_config(num_train_epochs=1, num_iterations=1)" + ] + }, + { + "cell_type": "markdown", + "id": "ca6d7dc3", + "metadata": {}, + "source": [ + "We can now train the model with `train`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a144a4a8", + "metadata": {}, + "outputs": [], + "source": [ + "trainer.train(output_dir=\"setfit_model\")" + ] + }, + { + "cell_type": "markdown", + "id": "8ae12a61", + "metadata": {}, + "source": [ + "and make inferences with `predict`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7863b92", + "metadata": {}, + "outputs": [], + "source": [ + "trainer.predict(\"This is just perfect!\")" + ] + }, + { + "cell_type": "markdown", + "id": "85ad4ad2", + "metadata": {}, + "source": [ + "We have trained a model with FeedbackDataset in this tutorial. For more info about concepts in Argilla Feedback and LLMs, look [here](https://docs.argilla.io/en/latest/conceptual_guides/llm/llm.html). For a more detailed explanation, refer to the [documentation](https://docs.argilla.io/en/latest/practical_guides/practical_guides.html) and the [end-to-end tutorials](https://docs.argilla.io/en/latest/tutorials_and_integrations/tutorials/tutorials.html) for beginners." + ] + }, + { + "cell_type": "markdown", + "id": "2ZyEUBBjbK7k", + "metadata": { + "id": "2ZyEUBBjbK7k" + }, + "source": [ + "-------------\n", + "\n" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.18" + }, + "vscode": { + "interpreter": { + "hash": "2584bca9d226488c39a669ff1ce19d7ca5f410e2d3aa9b82f20653edd0d96bfc" + } + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "007a11c0643c45ecbf50c92bb244170f": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "211a9ff3", - "metadata": {}, - "outputs": [], - "source": [ - "import argilla as rg\n", - "\n", - "rg.init(workspace=\"admin\")" - ] + "0a54f0576a9347d5a546ff8b3e371121": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": "hidden", + "width": null + } }, - { - "cell_type": "markdown", - "id": "c4e763b4", - "metadata": {}, - "source": [ - "`\"owner.apikey\"` is the default value for `ARGILLA_API_KEY` variable.\n", - "\n", - "`admin` is the name of the default workspace. A **workspace** is a “space” inside your Argilla instance where authorized users can collaborate.\n", - "\n", - "If you want to initialize a connection manually you can use `rg.init()`. For more info about custom configurations like headers and workspace separation, check our [config page](../getting_started/installation/configurations/configurations.html).\n", - "\n", - "If you want to customize the access credentials, take a look at our [user management section](../getting_started/installation/configurations/user_management.html)." - ] + "13d0949b0cad4cee8f1aa691ebd43476": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_e9f06d2e0ff645aea882a0958eda2820", + "max": 464, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_15c7227ddf8946d7bdd9c5e076407300", + "value": 464 + } }, - { - "cell_type": "markdown", - "id": "423d6483", - "metadata": {}, - "source": [ - "## Create Dataset\n", - "\n", - "FeedbackDataset is the container for Argilla Feedback structure. Argilla Feedback offers different components for FeedbackDatasets that you can employ for various aspects of your workflow. To start, we need to define fields, questions and records while we optionally have the opportunity to employ responses and suggestions for our task later.\n", - "\n", - "### Fields\n", - "\n", - "`fields` will store the question and answer structure to be used for each sample." - ] + "15c7227ddf8946d7bdd9c5e076407300": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "52213caa", - "metadata": {}, - "outputs": [], - "source": [ - "import argilla as rg\n", - "from argilla.feedback import ArgillaTrainer, TrainingTask" + "19d173ba40134ea3ba4625977b924c83": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/output", + "_model_module_version": "1.0.0", + "_model_name": "OutputModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/output", + "_view_module_version": "1.0.0", + "_view_name": "OutputView", + "layout": "IPY_MODEL_24b90ef58e0e41cfb668144bc0adb815", + "msg_id": "", + "outputs": [ + { + "data": { + "text/html": "
Logging... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━  98% 0:00:01\n
\n", + "text/plain": "Logging... \u001b[38;2;249;38;114m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[38;5;237m╺\u001b[0m \u001b[35m 98%\u001b[0m \u001b[36m0:00:01\u001b[0m\n" + }, + "metadata": {}, + "output_type": "display_data" + } ] + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "94ecaf52", - "metadata": {}, - "outputs": [], - "source": [ - "fields = [\n", - " rg.TextField(name=\"question\", required=True),\n", - " rg.TextField(name=\"answer\", required=True, use_markdown=True)\n", - "]" - ] + "21a09c7691c64e088a74c08095df0057": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } }, - { - "cell_type": "markdown", - "id": "1e4abff4", - "metadata": {}, - "source": [ - "### Questions\n", - "\n", - "For the dataset, you need to define at least one question type. As of today, the different question types that Argilla offers are `RatingQuestion`, `TextQuestion`, `LabelQuestion`, `MultiLabelQuestion` and `RankingQuestion`. Let us create a `LabelQuestion` for the current example." - ] + "24b90ef58e0e41cfb668144bc0adb815": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "6c5441dd", - "metadata": {}, - "outputs": [], - "source": [ - "label_question = [\n", - " rg.LabelQuestion(\n", - " name=\"relevant\",\n", - " title=\"Relevancy\",\n", - " labels=[\"yes\", \"no\"],\n", - " required=True,\n", - " visible_labels=None\n", - " )\n", - "]" - ] + "2d9d836276674563893af311d7ceb5cf": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } }, - { - "cell_type": "markdown", - "id": "0ac69d4d", - "metadata": {}, - "source": [ - "While `name` is the identifier of the question internally, `title` will be the question/instruction seen on Argilla UI. We also define a dictionary for labels." - ] + "452429879663485da881188f70d20ca5": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_f47cb9c3e20e48acb6df9bed2f0cb7bf", + "placeholder": "​", + "style": "IPY_MODEL_2d9d836276674563893af311d7ceb5cf", + "value": " 453/464 [00:04<00:00, 112.79 examples/s]" + } }, - { - "cell_type": "markdown", - "id": "b962aeee", - "metadata": {}, - "source": [ - "### Annotation guideline\n", - "\n", - "As it is helpful for annotators, we can enrich our task with `guidelines` as well. Clear guidelines will help them to understand the task better and make more accurate annotations. There are two ways to have guidelines: defining it as an argument to the FeedbackDataset or as an argument (`description`) to the question instances above. Depending on the specific task you employ, you may want to use either one of them, so it is good practice to try both.\n", - "\n", - "We can now create our FeedbackDataset instance with the fields, question type as shown above. Do not forget to define `fields` and `questions` as a list, while `guidelines` expects a string." - ] + "4635b9f0d42b4172b52b64de660082d1": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_514672241dad488ca37a5e802553eb35", + "max": 2561, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_48dbaf59d0be41a6a6d7c30cb621e9ef", + "value": 2561 + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "ec92cdf5", - "metadata": {}, - "outputs": [], - "source": [ - "dataset = rg.FeedbackDataset(\n", - " guidelines=\"Annotations should be made according to the policy.\",\n", - " fields=fields,\n", - " questions=label_question\n", - ")" - ] + "48dbaf59d0be41a6a6d7c30cb621e9ef": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } }, - { - "cell_type": "markdown", - "id": "4b099e23", - "metadata": {}, - "source": [ - "## Upload data" - ] + "50edec55b45242d3a3bd0b3d9444710e": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } }, - { - "cell_type": "markdown", - "id": "deb37de2", - "metadata": {}, - "source": [ - "### Records\n", - "\n", - "A record refers to each of the data items that will be annotated by the annotator team. The records will be the pieces of information that will be shown to the user in the UI in order to complete the annotation task. In the current single-label dataset sample, it can only consist of a text to be labeled while it will be a prompt and output pair in the case of instruction datasets.\n", - "\n", - "For Argilla Feedback, we can define a `FeedbackRecord` with the mandatory argument `fields`. Records also offer [other optional arguments](../reference/python/python_client.html#argilla.client.feedback.schemas.records.FeedbackRecord) to further augment each record." - ] + "514672241dad488ca37a5e802553eb35": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "36b9b719", - "metadata": {}, - "outputs": [], - "source": [ - "# A sample FeedbackRecord\n", - "record = rg.FeedbackRecord(\n", - " fields={\n", - " \"question\": \"Why can camels survive long without water?\",\n", - " \"answer\": \"Camels use the fat in their humps to keep them filled with energy and hydration for long periods of time.\"\n", - " }\n", - ")" - ] + "518e260b7d2f4f90b39668414389c166": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_8f5ca2714e114119adb61c9122afffbb", + "placeholder": "​", + "style": "IPY_MODEL_c36638fb29cb4510bfdda737e03c4a0e", + "value": "Map: 65%" + } }, - { - "cell_type": "markdown", - "id": "9febcaa9", - "metadata": {}, - "source": [ - "### Responses\n", - "\n", - "Argilla Feedback can deal with multiple responses per record for each one of the annotators. We can define a list of responses for each record. Each response will be a dictionary with the annotator's name as the key and the response as the value." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "230ad6cc", - "metadata": {}, - "outputs": [], - "source": [ - "record.responses = [\n", - " {\n", - " \"values\":{\n", - " \"relevant\":{\n", - " \"value\": \"yes\"\n", - " }\n", - " }\n", - " }\n", - "]" - ] - }, - { - "cell_type": "markdown", - "id": "c5767608", - "metadata": {}, - "source": [ - "### Suggestions\n", - "\n", - "Argilla Feedback offers a way to use suggestions from LLMs and other models as a starting point for annotators. This way, annotators can save time and effort by correcting the predictions instead of annotating from scratch." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "39e57c76", - "metadata": {}, - "outputs": [], - "source": [ - "record.update(\n", - " suggestions=[\n", - " {\n", - " \"question_name\": \"relevant\",\n", - " \"value\": \"yes\"\n", - " }\n", - " ]\n", - ")" + "5406303479514972b3e5241ce40e3f70": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/output", + "_model_module_version": "1.0.0", + "_model_name": "OutputModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/output", + "_view_module_version": "1.0.0", + "_view_name": "OutputView", + "layout": "IPY_MODEL_21a09c7691c64e088a74c08095df0057", + "msg_id": "", + "outputs": [ + { + "data": { + "text/html": "
Logging... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   0% -:--:--\n
\n", + "text/plain": "Logging... \u001b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[35m 0%\u001b[0m \u001b[36m-:--:--\u001b[0m\n" + }, + "metadata": {}, + "output_type": "display_data" + } ] + } }, - { - "cell_type": "markdown", - "id": "53af531a", - "metadata": {}, - "source": [ - "Now, it is quite simple to add records to the FeedbackDataset we have previously created, in the form of a list." - ] + "706f8e4861904c33a9468216e3220d9b": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "d870269d", - "metadata": {}, - "outputs": [], - "source": [ - "dataset.add_records([record])" - ] + "7605822dfe30439a8b53c117466e513f": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_50edec55b45242d3a3bd0b3d9444710e", + "placeholder": "​", + "style": "IPY_MODEL_7d817c646854435686ab9b7e317b2f92", + "value": "Map: 98%" + } }, - { - "cell_type": "markdown", - "id": "5ad19311", - "metadata": {}, - "source": [ - "Now that we have our dataset with already annotated responses and suggestions as model predictions, we can push the dataset to the Argilla space.\n", - "\n", - "
\n", - "\n", - "Note\n", - " \n", - "From Argilla 1.14.0, calling `push_to_argilla` will not just push the `FeedbackDataset` into Argilla, but will also return the remote `FeedbackDataset` instance, which implies that the additions, updates, and deletions of records will be pushed to Argilla as soon as they are made. This is a change from previous versions of Argilla, where you had to call `push_to_argilla` again to push the changes to Argilla.\n", - " \n", - "
" - ] + "7d817c646854435686ab9b7e317b2f92": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "3cfb8b36", - "metadata": {}, - "outputs": [], - "source": [ - "remote_dataset = dataset.push_to_argilla(name=\"emotion_dataset\", workspace=\"admin\")" - ] + "7e028b1a16394adcac90c011d14d3458": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_7605822dfe30439a8b53c117466e513f", + "IPY_MODEL_13d0949b0cad4cee8f1aa691ebd43476", + "IPY_MODEL_452429879663485da881188f70d20ca5" + ], + "layout": "IPY_MODEL_d4bae643e36a4b258b137507d628399d" + } }, - { - "cell_type": "markdown", - "id": "5a5a3bf3", - "metadata": {}, - "source": [ - "## Train a model\n", - "\n", - "As with other datasets, Feedback datasets also allow to create a training pipeline and make inferences with the resulting model. After you gather responses with Argilla Feedback, you can easily fine-tune an LLM. In this example, we will have to complete a text classification task.\n", - "\n", - "For fine-tuning, we will use setfit library and the [Argilla Trainer](../practical_guides/fine_tune.html#the-trainingtask), which is a powerful wrapper around many of our favorite NLP libraries. It provides a very intuitive abstract representation to facilitate simple training workflows using decent default pre-set configurations without having to worry about any data transformations from Argilla.\n", - "\n", - "Let us first create our dataset to train. For this example, we will use [emotion](https://huggingface.co/datasets/argilla/emotion) dataset from Argilla, which was created using Argilla. Each text item has its responses as 6 different sentiments, which are Sadness, Joy, Love, Anger, Fear and Surprise." - ] + "81e07d9401bd40cc82240136a529be2d": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "586059d7", - "metadata": {}, - "outputs": [], - "source": [ - "#besides Argilla, it can also be imported with load_dataset from datasets\n", - "dataset_hf = rg.FeedbackDataset.from_huggingface(\"argilla/emotion\")" + "83d7b92514e24e3d88f7207ec26a6b76": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/output", + "_model_module_version": "1.0.0", + "_model_name": "OutputModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/output", + "_view_module_version": "1.0.0", + "_view_name": "OutputView", + "layout": "IPY_MODEL_9146f98b81744bceb0083e98aa1c3af9", + "msg_id": "", + "outputs": [ + { + "data": { + "text/html": "
Logging... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   0% -:--:--\n
\n", + "text/plain": "Logging... \u001b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[35m 0%\u001b[0m \u001b[36m-:--:--\u001b[0m\n" + }, + "metadata": {}, + "output_type": "display_data" + } ] + } }, - { - "cell_type": "markdown", - "id": "861c3648", - "metadata": {}, - "source": [ - "We can then start to create a training pipeline by first defining `TrainingTask`, which is used to define how the data should be processed and formatted according to the associated task and framework. Each task has its own classmethod and the data formatting can always be customized via `formatting_func`. You can visit [this page](../practical_guides/fine_tune.html#the-trainingtask) for more info. Simpler tasks like text classification can be defined using default definitions, as we do in this example." - ] + "8f5ca2714e114119adb61c9122afffbb": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "04537510", - "metadata": {}, - "outputs": [], - "source": [ - "task = TrainingTask.for_text_classification(\n", - " text=dataset_hf.field_by_name(\"text\"),\n", - " label=dataset_hf.question_by_name(\"label\")\n", - ")" - ] + "9146f98b81744bceb0083e98aa1c3af9": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } }, - { - "cell_type": "markdown", - "id": "2492e2e2", - "metadata": {}, - "source": [ - "We can then define our ArgillaTrainer for any of the supported frameworks and customize the training config using ArgillaTrainer.update_config.\n", - "\n", - "Let us define ArgillaTrainer with any of the supported frameworks. " - ] + "9217d6b5c2a0495bbb0724dd296b2af7": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_518e260b7d2f4f90b39668414389c166", + "IPY_MODEL_4635b9f0d42b4172b52b64de660082d1", + "IPY_MODEL_b1df83e2ef6540bbb09098e12d707d6d" + ], + "layout": "IPY_MODEL_0a54f0576a9347d5a546ff8b3e371121" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "8187adc1", - "metadata": {}, - "outputs": [], - "source": [ - "trainer = ArgillaTrainer(\n", - " dataset=dataset_hf,\n", - " task=task,\n", - " framework=\"setfit\",\n", - " train_size=0.8\n", - ")" - ] + "b1df83e2ef6540bbb09098e12d707d6d": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_007a11c0643c45ecbf50c92bb244170f", + "placeholder": "​", + "style": "IPY_MODEL_81e07d9401bd40cc82240136a529be2d", + "value": " 1665/2561 [00:00<00:00, 14760.53 examples/s]" + } }, - { - "cell_type": "markdown", - "id": "1bccf880", - "metadata": {}, - "source": [ - "You can update the model config via `update_config`." - ] + "c36638fb29cb4510bfdda737e03c4a0e": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "332a4f1d", - "metadata": {}, - "outputs": [], - "source": [ - "trainer.update_config(num_train_epochs=2)" - ] - }, - { - "cell_type": "markdown", - "id": "ca6d7dc3", - "metadata": {}, - "source": [ - "We can now train the model with `train`" - ] + "cf5da266e6ad493da229cf05469b2fc4": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "a144a4a8", - "metadata": {}, - "outputs": [], - "source": [ - "trainer.train(output_dir=\"setfit_model\")" - ] - }, - { - "cell_type": "markdown", - "id": "8ae12a61", - "metadata": {}, - "source": [ - "and make inferences with `predict`." - ] + "d4bae643e36a4b258b137507d628399d": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": "hidden", + "width": null + } }, - { - "cell_type": "code", - "execution_count": null, - "id": "d7863b92", - "metadata": {}, - "outputs": [], - "source": [ - "trainer.predict(\"This is just perfect!\")" - ] - }, - { - "cell_type": "markdown", - "id": "85ad4ad2", - "metadata": {}, - "source": [ - "We have trained a model with FeedbackDataset in this tutorial. For more info about concepts in Argilla Feedback and LLMs, look [here](../conceptual_guides/llm/llm.html). To see more hands-on tutorials about FeedbackDataset, please look [here](../practical_guides/practical_guides.html)." + "d577c60f0dbe46c1be6fab55672c4f86": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/output", + "_model_module_version": "1.0.0", + "_model_name": "OutputModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/output", + "_view_module_version": "1.0.0", + "_view_name": "OutputView", + "layout": "IPY_MODEL_cf5da266e6ad493da229cf05469b2fc4", + "msg_id": "", + "outputs": [ + { + "data": { + "text/html": "
Logging... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   0% -:--:--\n
\n", + "text/plain": "Logging... \u001b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[35m 0%\u001b[0m \u001b[36m-:--:--\u001b[0m\n" + }, + "metadata": {}, + "output_type": "display_data" + } ] + } }, - { - "cell_type": "markdown", - "id": "2ZyEUBBjbK7k", - "metadata": { - "id": "2ZyEUBBjbK7k" - }, - "source": [ - "-------------\n", - "\n" + "db3e5db8fe3f42a8908e3cd2a029cf56": { + "model_module": "@jupyter-widgets/output", + "model_module_version": "1.0.0", + "model_name": "OutputModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/output", + "_model_module_version": "1.0.0", + "_model_name": "OutputModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/output", + "_view_module_version": "1.0.0", + "_view_name": "OutputView", + "layout": "IPY_MODEL_706f8e4861904c33a9468216e3220d9b", + "msg_id": "", + "outputs": [ + { + "data": { + "text/html": "
Logging... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╺━━━━━  86% 0:00:01\n
\n", + "text/plain": "Logging... \u001b[38;2;249;38;114m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[38;5;237m╺\u001b[0m\u001b[38;5;237m━━━━━\u001b[0m \u001b[35m 86%\u001b[0m \u001b[36m0:00:01\u001b[0m\n" + }, + "metadata": {}, + "output_type": "display_data" + } ] - } - ], - "metadata": { - "accelerator": "GPU", - "colab": { - "provenance": [] - }, - "kernelspec": { - "display_name": "Python 3", - "name": "python3" + } }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.17" + "e9f06d2e0ff645aea882a0958eda2820": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } }, - "vscode": { - "interpreter": { - "hash": "2584bca9d226488c39a669ff1ce19d7ca5f410e2d3aa9b82f20653edd0d96bfc" - } - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "007a11c0643c45ecbf50c92bb244170f": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "0a54f0576a9347d5a546ff8b3e371121": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": "hidden", - "width": null - } - }, - "13d0949b0cad4cee8f1aa691ebd43476": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "FloatProgressModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_e9f06d2e0ff645aea882a0958eda2820", - "max": 464, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_15c7227ddf8946d7bdd9c5e076407300", - "value": 464 - } - }, - "15c7227ddf8946d7bdd9c5e076407300": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "ProgressStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "19d173ba40134ea3ba4625977b924c83": { - "model_module": "@jupyter-widgets/output", - "model_module_version": "1.0.0", - "model_name": "OutputModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/output", - "_model_module_version": "1.0.0", - "_model_name": "OutputModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/output", - "_view_module_version": "1.0.0", - "_view_name": "OutputView", - "layout": "IPY_MODEL_24b90ef58e0e41cfb668144bc0adb815", - "msg_id": "", - "outputs": [ - { - "data": { - "text/html": "
Logging... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━  98% 0:00:01\n
\n", - "text/plain": "Logging... \u001b[38;2;249;38;114m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[38;5;237m╺\u001b[0m \u001b[35m 98%\u001b[0m \u001b[36m0:00:01\u001b[0m\n" - }, - "metadata": {}, - "output_type": "display_data" - } - ] - } - }, - "21a09c7691c64e088a74c08095df0057": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "24b90ef58e0e41cfb668144bc0adb815": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "2d9d836276674563893af311d7ceb5cf": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "452429879663485da881188f70d20ca5": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_f47cb9c3e20e48acb6df9bed2f0cb7bf", - "placeholder": "​", - "style": "IPY_MODEL_2d9d836276674563893af311d7ceb5cf", - "value": " 453/464 [00:04<00:00, 112.79 examples/s]" - } - }, - "4635b9f0d42b4172b52b64de660082d1": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "FloatProgressModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_514672241dad488ca37a5e802553eb35", - "max": 2561, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_48dbaf59d0be41a6a6d7c30cb621e9ef", - "value": 2561 - } - }, - "48dbaf59d0be41a6a6d7c30cb621e9ef": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "ProgressStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "50edec55b45242d3a3bd0b3d9444710e": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "514672241dad488ca37a5e802553eb35": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "518e260b7d2f4f90b39668414389c166": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_8f5ca2714e114119adb61c9122afffbb", - "placeholder": "​", - "style": "IPY_MODEL_c36638fb29cb4510bfdda737e03c4a0e", - "value": "Map: 65%" - } - }, - "5406303479514972b3e5241ce40e3f70": { - "model_module": "@jupyter-widgets/output", - "model_module_version": "1.0.0", - "model_name": "OutputModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/output", - "_model_module_version": "1.0.0", - "_model_name": "OutputModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/output", - "_view_module_version": "1.0.0", - "_view_name": "OutputView", - "layout": "IPY_MODEL_21a09c7691c64e088a74c08095df0057", - "msg_id": "", - "outputs": [ - { - "data": { - "text/html": "
Logging... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   0% -:--:--\n
\n", - "text/plain": "Logging... \u001b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[35m 0%\u001b[0m \u001b[36m-:--:--\u001b[0m\n" - }, - "metadata": {}, - "output_type": "display_data" - } - ] - } - }, - "706f8e4861904c33a9468216e3220d9b": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "7605822dfe30439a8b53c117466e513f": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_50edec55b45242d3a3bd0b3d9444710e", - "placeholder": "​", - "style": "IPY_MODEL_7d817c646854435686ab9b7e317b2f92", - "value": "Map: 98%" - } - }, - "7d817c646854435686ab9b7e317b2f92": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "7e028b1a16394adcac90c011d14d3458": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HBoxModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_7605822dfe30439a8b53c117466e513f", - "IPY_MODEL_13d0949b0cad4cee8f1aa691ebd43476", - "IPY_MODEL_452429879663485da881188f70d20ca5" - ], - "layout": "IPY_MODEL_d4bae643e36a4b258b137507d628399d" - } - }, - "81e07d9401bd40cc82240136a529be2d": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "83d7b92514e24e3d88f7207ec26a6b76": { - "model_module": "@jupyter-widgets/output", - "model_module_version": "1.0.0", - "model_name": "OutputModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/output", - "_model_module_version": "1.0.0", - "_model_name": "OutputModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/output", - "_view_module_version": "1.0.0", - "_view_name": "OutputView", - "layout": "IPY_MODEL_9146f98b81744bceb0083e98aa1c3af9", - "msg_id": "", - "outputs": [ - { - "data": { - "text/html": "
Logging... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   0% -:--:--\n
\n", - "text/plain": "Logging... \u001b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[35m 0%\u001b[0m \u001b[36m-:--:--\u001b[0m\n" - }, - "metadata": {}, - "output_type": "display_data" - } - ] - } - }, - "8f5ca2714e114119adb61c9122afffbb": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "9146f98b81744bceb0083e98aa1c3af9": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "9217d6b5c2a0495bbb0724dd296b2af7": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HBoxModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_518e260b7d2f4f90b39668414389c166", - "IPY_MODEL_4635b9f0d42b4172b52b64de660082d1", - "IPY_MODEL_b1df83e2ef6540bbb09098e12d707d6d" - ], - "layout": "IPY_MODEL_0a54f0576a9347d5a546ff8b3e371121" - } - }, - "b1df83e2ef6540bbb09098e12d707d6d": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_007a11c0643c45ecbf50c92bb244170f", - "placeholder": "​", - "style": "IPY_MODEL_81e07d9401bd40cc82240136a529be2d", - "value": " 1665/2561 [00:00<00:00, 14760.53 examples/s]" - } - }, - "c36638fb29cb4510bfdda737e03c4a0e": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "cf5da266e6ad493da229cf05469b2fc4": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "d4bae643e36a4b258b137507d628399d": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": "hidden", - "width": null - } - }, - "d577c60f0dbe46c1be6fab55672c4f86": { - "model_module": "@jupyter-widgets/output", - "model_module_version": "1.0.0", - "model_name": "OutputModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/output", - "_model_module_version": "1.0.0", - "_model_name": "OutputModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/output", - "_view_module_version": "1.0.0", - "_view_name": "OutputView", - "layout": "IPY_MODEL_cf5da266e6ad493da229cf05469b2fc4", - "msg_id": "", - "outputs": [ - { - "data": { - "text/html": "
Logging... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   0% -:--:--\n
\n", - "text/plain": "Logging... \u001b[38;5;237m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[35m 0%\u001b[0m \u001b[36m-:--:--\u001b[0m\n" - }, - "metadata": {}, - "output_type": "display_data" - } - ] - } - }, - "db3e5db8fe3f42a8908e3cd2a029cf56": { - "model_module": "@jupyter-widgets/output", - "model_module_version": "1.0.0", - "model_name": "OutputModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/output", - "_model_module_version": "1.0.0", - "_model_name": "OutputModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/output", - "_view_module_version": "1.0.0", - "_view_name": "OutputView", - "layout": "IPY_MODEL_706f8e4861904c33a9468216e3220d9b", - "msg_id": "", - "outputs": [ - { - "data": { - "text/html": "
Logging... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╺━━━━━  86% 0:00:01\n
\n", - "text/plain": "Logging... \u001b[38;2;249;38;114m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[38;5;237m╺\u001b[0m\u001b[38;5;237m━━━━━\u001b[0m \u001b[35m 86%\u001b[0m \u001b[36m0:00:01\u001b[0m\n" - }, - "metadata": {}, - "output_type": "display_data" - } - ] - } - }, - "e9f06d2e0ff645aea882a0958eda2820": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "f47cb9c3e20e48acb6df9bed2f0cb7bf": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - } - } + "f47cb9c3e20e48acb6df9bed2f0cb7bf": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } } - }, - "nbformat": 4, - "nbformat_minor": 5 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/frontend/components/features/datasets/datasets-table/datasets-empty/DatasetsEmpty.vue b/frontend/components/features/datasets/datasets-table/datasets-empty/DatasetsEmpty.vue index f41f9a0269..dd7c08ce0f 100644 --- a/frontend/components/features/datasets/datasets-table/datasets-empty/DatasetsEmpty.vue +++ b/frontend/components/features/datasets/datasets-table/datasets-empty/DatasetsEmpty.vue @@ -1,32 +1,15 @@ diff --git a/frontend/static/images/logo.svg b/frontend/static/images/logo.svg new file mode 100644 index 0000000000..9284927348 --- /dev/null +++ b/frontend/static/images/logo.svg @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/scripts/end2end_examples.py b/scripts/end2end_examples.py index 7a8e63997f..7acbfcbe03 100644 --- a/scripts/end2end_examples.py +++ b/scripts/end2end_examples.py @@ -30,25 +30,19 @@ from typing import Dict, Optional import papermill -import typer from argilla._constants import DEFAULT_API_KEY @dataclass class ExampleNotebook: - sort_index: int = field(init=False) src_filename: Path dst_filename: Path + sort_index: int parameters: Dict = field(default_factory=dict) def __post_init__(self): self.src_filename = Path(self.src_filename) - assert self.src_filename.exists(), f"File {self.src_filename} does not exist" - self.sort_index = int(self.src_filename.stem.split("-")[-1]) self.dst_filename = Path(self.dst_filename) - dst_folder = self.dst_filename.parent - if not dst_folder.exists(): - dst_folder.mkdir(exist_ok=True) def run(self): try: @@ -101,6 +95,12 @@ def main( Run the end2end example notebooks. If no arguments are passed, it will try to get the api_key and the hf_token from the environment variables. """ + if isinstance(examples_folder, str): + examples_folder = Path(examples_folder) + + if not examples_folder.exists(): + raise ValueError(f"Folder {examples_folder} does not exist") + if not hf_token: hf_token = get_huggingface_token() @@ -115,9 +115,15 @@ def main( with tempfile.TemporaryDirectory() as tmpdir: examples = [] - for filename in examples_folder.glob("*.ipynb"): + for idx, filename in enumerate(examples_folder.glob("*.ipynb")): + filename = Path(filename) + if "-" in filename.stem: + sort_index = int(filename.stem.split("-")[-1]) + else: + sort_index = idx examples.append( ExampleNotebook( + sort_index=sort_index, src_filename=filename, dst_filename=Path(tmpdir) / output_notebook, parameters=notebook_parameters, @@ -130,4 +136,5 @@ def main( if __name__ == "__main__": - typer.run(main) + main(examples_folder="docs/_source/getting_started") + main() From 3303b1a479cde3f5a2659e49e1f34815387a375f Mon Sep 17 00:00:00 2001 From: Sara Han <127759186+sdiazlor@users.noreply.github.com> Date: Wed, 20 Dec 2023 16:32:11 +0100 Subject: [PATCH 06/14] bug/Update textdescriptives.py (#4444) --- src/argilla/client/feedback/integrations/textdescriptives.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/argilla/client/feedback/integrations/textdescriptives.py b/src/argilla/client/feedback/integrations/textdescriptives.py index 8e177ab9bf..1757cee558 100644 --- a/src/argilla/client/feedback/integrations/textdescriptives.py +++ b/src/argilla/client/feedback/integrations/textdescriptives.py @@ -16,6 +16,7 @@ import re from typing import List, Optional, Union +import numpy as np import pandas as pd import textdescriptives as td from rich.progress import Progress @@ -113,6 +114,8 @@ def _extract_metrics_for_single_field( if basic_metrics is None and self.metrics is None: basic_metrics = self.__basic_metrics field_metrics = field_metrics.loc[:, basic_metrics] + # Convert any None values to NaNs + field_metrics = field_metrics.fillna(value=np.nan) # Select all column names that contain ONLY NaNs nan_columns = field_metrics.columns[field_metrics.isnull().all()].tolist() if nan_columns: From d96089b9b98b3fba0ade6d388443a2837915302a Mon Sep 17 00:00:00 2001 From: Agus <56895847+plaguss@users.noreply.github.com> Date: Wed, 20 Dec 2023 17:09:02 +0100 Subject: [PATCH 07/14] feat: add metrics module aligned with unification (#4271) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description This PR aligns the metric modules for a dataset with the unification strategies. It includes a new `UnifiedAnnotationMetric` class to compute the `AnnotatorMetric` for a *unified* dataset. Should be merged after #4175. Closes #4263, #4034 **Type of change** (Please delete options that are not relevant. Remember to title the PR according to the type of change) - [x] New feature (non-breaking change which adds functionality) - [x] Refactor (change restructuring the codebase without changing functionality) - [ ] Improvement (change adding some improvement to an existing functionality) **How Has This Been Tested** (Please describe the tests that you ran to verify your changes. And ideally, reference `tests`) - [x] `tests/integration/client/feedbac/metrics/test_annotator_metrics.py` **Checklist** - [ ] I added relevant documentation - [x] I followed the style guidelines of this project - [x] I did a self-review of my code - [ ] I made corresponding changes to the documentation - [ ] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [ ] I filled out [the contributor form](https://tally.so/r/n9XrxK) (see text above) - [ ] I have added relevant notes to the `CHANGELOG.md` file (See https://keepachangelog.com/) --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Agustin Piqueres Co-authored-by: David Berenstein Co-authored-by: kursathalat <86690946+kursathalat@users.noreply.github.com> Co-authored-by: plaguss --- CHANGELOG.md | 1 + .../_common/tabs/unfication_strategies.md | 8 +- .../practical_guides/collect_responses.md | 145 ++++- docs/_source/reference/python/index.rst | 2 + .../python/python_annotation_metrics.rst | 48 ++ .../end2end_examples/use-metrics-007.ipynb | 465 +++++++++++++- .../tutorials/tutorials.md | 6 +- src/argilla/client/feedback/dataset/base.py | 5 - .../client/feedback/dataset/local/dataset.py | 52 +- src/argilla/client/feedback/dataset/mixins.py | 164 +++++ .../client/feedback/dataset/remote/dataset.py | 30 +- .../client/feedback/metrics/__init__.py | 26 + .../feedback/metrics/agreement_metrics.py | 343 ++++++++++ .../feedback/metrics/annotator_metrics.py | 608 ++++++++++++++++++ src/argilla/client/feedback/metrics/base.py | 221 +++++++ src/argilla/client/feedback/metrics/utils.py | 215 +++++++ .../client/feedback/training/schemas/base.py | 8 +- src/argilla/client/feedback/unification.py | 36 +- tests/integration/client/conftest.py | 134 ++++ .../feedback/dataset/remote/test_dataset.py | 2 +- .../client/feedback/metrics/__init__.py | 13 + .../metrics/test_agreement_metrics.py | 348 ++++++++++ .../metrics/test_annotator_metrics.py | 358 +++++++++++ .../client/feedback/metrics/test_utils.py | 130 ++++ .../client/feedback/test_unification.py | 11 +- 25 files changed, 3266 insertions(+), 113 deletions(-) create mode 100644 docs/_source/reference/python/python_annotation_metrics.rst create mode 100644 src/argilla/client/feedback/dataset/mixins.py create mode 100644 src/argilla/client/feedback/metrics/__init__.py create mode 100644 src/argilla/client/feedback/metrics/agreement_metrics.py create mode 100644 src/argilla/client/feedback/metrics/annotator_metrics.py create mode 100644 src/argilla/client/feedback/metrics/base.py create mode 100644 src/argilla/client/feedback/metrics/utils.py create mode 100644 tests/integration/client/feedback/metrics/__init__.py create mode 100644 tests/integration/client/feedback/metrics/test_agreement_metrics.py create mode 100644 tests/integration/client/feedback/metrics/test_annotator_metrics.py create mode 100644 tests/integration/client/feedback/metrics/test_utils.py diff --git a/CHANGELOG.md b/CHANGELOG.md index f8698bb3a4..7cf9aae2d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ These are the section headers that we use: ### Added +- Added annotation metrics module for the `FeedbackDataset` (`argilla.client.feedback.metrics`). ([#4175](https://github.com/argilla-io/argilla/pull/4175)). - Added strategy to handle and translate errors from the server for `401` HTTP status code` ([#4362](https://github.com/argilla-io/argilla/pull/4362)) - Added integration for `textdescriptives` using `TextDescriptivesExtractor` to configure `metadata_properties` in `FeedbackDataset` and `FeedbackRecord`. ([#4400](https://github.com/argilla-io/argilla/pull/4400)). Contributed by @m-newhauser - Added `POST /api/v1/me/responses/bulk` endpoint to create responses in bulk for current user. ([#4380](https://github.com/argilla-io/argilla/pull/4380)) diff --git a/docs/_source/_common/tabs/unfication_strategies.md b/docs/_source/_common/tabs/unfication_strategies.md index 53a383a261..7b628d7cdf 100644 --- a/docs/_source/_common/tabs/unfication_strategies.md +++ b/docs/_source/_common/tabs/unfication_strategies.md @@ -9,7 +9,7 @@ dataset = FeedbackDataset.from_huggingface( repo_id="argilla/stackoverflow_feedback_demo" ) strategy = LabelQuestionStrategy("majority") # "disagreement", "majority_weighted (WIP)" -dataset.unify_responses( +dataset.compute_unified_responses( question=dataset.question_by_name("title_question_fit"), strategy=strategy, ) @@ -28,7 +28,7 @@ dataset = FeedbackDataset.from_huggingface( repo_id="argilla/stackoverflow_feedback_demo" ) strategy = MultiLabelQuestionStrategy("majority") # "disagreement", "majority_weighted (WIP)" -dataset.unify_responses( +dataset.compute_unified_responses( question=dataset.question_by_name("tags"), strategy=strategy, ) @@ -46,7 +46,7 @@ dataset = FeedbackDataset.from_huggingface( repo_id="argilla/stackoverflow_feedback_demo" ) strategy = RankingQuestionStrategy("majority") # "mean", "max", "min" -dataset.unify_responses( +dataset.compute_unified_responses( question=dataset.question_by_name("relevance_ranking"), strategy=strategy, ) @@ -64,7 +64,7 @@ dataset = FeedbackDataset.from_huggingface( repo_id="argilla/stackoverflow_feedback_demo" ) strategy = RatingQuestionStrategy("majority") # "mean", "max", "min" -dataset.unify_responses( +dataset.compute_unified_responses( question=dataset.question_by_name("answer_quality"), strategy=strategy, ) diff --git a/docs/_source/practical_guides/collect_responses.md b/docs/_source/practical_guides/collect_responses.md index f1c69fc2fe..ba49f3b65d 100644 --- a/docs/_source/practical_guides/collect_responses.md +++ b/docs/_source/practical_guides/collect_responses.md @@ -94,6 +94,10 @@ You can unify responses by using a `FeedbackDataset` in combination with a `Ques ```{include} /_common/tabs/unfication_strategies.md ``` +```{warning} +Starting from Argilla 1.21.0, `unify_responses` is deprecated. Please use `compute_unified_responses` instead. +``` + Once you have unified your responses, you will have a dataset that's ready for [fine-tuning](fine_tune.md). Remember to save your unified dataset following one of the methods explained in [Export a Feedback Dataset](export_dataset.md). #### Strategies @@ -120,12 +124,151 @@ Once you have unified your responses, you will have a dataset that's ready for [ * *Duplicate the record*: You may consider that the different answers given by your annotation team are all valid options. In this case, you can duplicate the record to keep each answer. Again, this method does not guarantee the quality of the text, so it is recommended to check the quality of the text, for example using a rating question. +### Annotation Metrics + +There are multiple ways to analyze the annotations on a dataset. In this section, we present three different approaches, which will enable us to analyze the agreement between annotators, analyze the quality of responses against suggestions as ground truths, and analyze the quality of the suggestions against the responses as ground truths. + +```{note} +The following metrics only apply to the `FeedbackDataset`. +``` + +#### Agreement Metrics + +After the annotation process, the annotation data should be evaluated to see the quality of the results obtained. The first step for evaluation would be the calculation of the agreement between the annotators. Inter-Annotator Agreement (IAA), as it is commonly known in the literature, is a way to explore the performance of many parameters such as the quality of the annotation guidelines, whether there is uniformity in the annotation process and whether the annotators are consistent in their annotations. + +```python +import argilla as rg +from argilla.client.feedback.metrics import AgreementMetric + +feedback_dataset = rg.FeedbackDataset.from_argilla("...", workspace="...") +metric = AgreementMetric(dataset=feedback_dataset, question_name="question_name") +agreement_metrics = metric.compute("alpha") +# >>> agreement_metrics +# [AgreementMetricResult(metric_name='alpha', count=1000, result=0.467889)] +``` + +With the `compute` function, we have obtained a container that stores the metric name and the value of the metric as well as the number of records that were used to calculate the metric. + +The metrics can also be computed easily from a `FeedbackDataset`: + +```python +import argilla as rg + +#dataset = rg.FeedbackDataset.from_huggingface("argilla/go_emotions_raw") + +agreement_metrics = dataset.compute_agreement_metrics(question_name="label", metric_names="alpha") +agreement_metrics + +# AgreementMetricResult(metric_name='alpha', count=191792, result=0.2703263452657748) +``` + +Currently, the only agreement metric supported is [Krippendorf’s alpha](https://en.wikipedia.org/wiki/Krippendorff%27s_alpha), for all question types except for the `TextQuestion`. This metric can be computed for any number of annotators, even if some of the responses are not submitted. The value from this measure is in the range [0,1] and is usually interpreted in the following way: alpha >= 0.8 indicates a reliable annotation, alpha >= 0.667 allows making tentative conclusions, while the lower values suggest unreliable annotation. + +```{note} +In case you want to dig deeper into the measurement of the agreement between different annotators, take a look at [*Implementations of inter-annotator agreement coefficients surveyed by Artstein +and Poesio (2007), Inter-Coder Agreement for Computational Linguistics*](https://www.researchgate.net/publication/200044186_Inter-Coder_Agreement_for_Computational_Linguistics). +``` + +##### Supported Agreement Metrics + +We plan on adding more support for other metrics so feel free to reach out on our Slack or GitHub to help us prioritize each task. + +| Question type/Metric | alpha | +|:----------------------|:-------| +| LabelQuestion | ✔️ | +| MultiLabelQuestion | ✔️ | +| RatingQuestion | ✔️ | +| RankingQuestion | ✔️ | +| TextQuestion | | + +#### Model Metrics + +In contrast to agreement metrics, where we compare the responses of annotators with each other, it is a good practice to evaluate the suggestions of models against the annotators as ground truths. As `FeedbackDataset` already offers the possibility to add `suggestions` to the responses, we can compare these initial predictions against the verified reponses. This will give us two important insights: how reliable the responses of a given annotator are, and how good the suggestions we are giving to the annotators are. This way, we can take action to improve the quality of the responses by making changes to the guidelines or the structure, and the suggestions given to the annotators by changing or updating the model we use. Note that each question type has a different set of metrics available. + +Here is an example use of the `compute` function to calculate the metrics for a `FeedbackDataset`: + +```python +import argilla as rg +from argilla.client.feedback.metrics import ModelMetric + +feedback_dataset = rg.FeedbackDataset.from_argilla("...", workspace="...") +metric = ModelMetric(dataset=feedback_dataset, question_name="question_name") +annotator_metrics = metric.compute("accuracy") +# >>> annotator_metrics +# {'00000000-0000-0000-0000-000000000001': [ModelMetricResult(metric_name='accuracy', count=3, result=0.5)], '00000000-0000-0000-0000-000000000002': [ModelMetricResult(metric_name='accuracy', count=3, result=0.25)], '00000000-0000-0000-0000-000000000003': [ModelMetricResult(metric_name='accuracy', count=3, result=0.5)]} +``` + +We obtain a `dict` where the keys contain the `user_id` of a given annotator and a list with the metrics requested. For the interpretation of these metrics, we assume here that the predictions correspond to the suggestions given to an annotator and the true labels correspond to the responses given by an annotator. This way, we can interpret the metrics and see whether the model is performing as expected. The metrics are calculated for each annotator individually, so we can see which annotators are giving responses that align with the model and which are not. + +Alternatively, we have the opportunity to compute the metrics directly from the dataset. Let’s use the following dataset for this, and compute the metrics for the suggestions: + +```python +mmodel_metrics = dataset.compute_model_metrics(question_name="label", metric_names=["accuracy", "precision", "recall", "f1-score"]) +suggestions_metrics['00000000-0000-0000-0000-000000000001'] +# [ModelMetricResult(metric_name='accuracy', count=1269, result=0.43341213553979513), +# ModelMetricResult(metric_name='precision', count=1269, result=0.5593881715337764), +# ModelMetricResult(metric_name='recall', count=1269, result=0.6166023130799764), +# ModelMetricResult(metric_name='f1-score', count=1269, result=0.5448304082545485)] +``` + +Keep in mind this dataset is quite big, so it may take some time both to download and compute the metrics. You can check the [dataset](https://huggingface.co/datasets/argilla/go_emotions_raw) for more info. + +##### For Unified Responses + +As we have seen `ModelMetric` allows us to compare the suggestions from models against responses from annotators, which we choose to be the ground truths. The calculation is done separately for each of the responses, which allows us to see the performance of the model against each annotator. However, in some cases, we may want to see the performance of the model against annotators as a whole and not individually. For this, we can use the `UnifiedModelMetric` class, which allows us to calculate the metrics for the unified responses. For this we rely on the unification strategies, defined in the [Unifying Disagreements](#unifying-disagreements). + +```python +import argilla as rg +from argilla.client.feedback.metrics import UnifiedModelMetric + +feedback_dataset = rg.FeedbackDataset.from_argilla("...", workspace="...") +strategy_name = "majority" +unified_dataset = feedback_dataset.compute_unified_responses(question, strategy_name) +metric = UnifiedModelMetric(dataset=unified_dataset, question_name="question_name") +unified_metrics = metric.compute("accuracy") +# >>> unified_metrics +# ModelMetricResult(metric_name='accuracy', count=3, result=0.25) +``` + +We obtain the same container for the metrics result, but in this case, it’s not associated with any specific annotator but their general alignment. + +We can make use of the same methods we saw above directly from the `FeedbackDataset`, but note the use of the strategy argument used here: + +```python +model_metrics_unified = dataset.compute_model_metrics(question_name="label", metric_names=["accuracy", "precision", "recall", "f1-score"], strategy="majority") +model_metrics_unified +# [ModelMetricResult(metric_name='accuracy', count=53990, result=0.8048342285608446), +# ModelMetricResult(metric_name='precision', count=53990, result=0.8085185809086417), +# ModelMetricResult(metric_name='recall', count=53990, result=0.7679974812646655), +# ModelMetricResult(metric_name='f1-score', count=53990, result=0.786466989240015)] +``` + +By default, the responses will not be unified and we will have the responses at the annotator level, but if we ask for a specific strategy (see the strategies available for each question), they will be unified automatically and computed. + +#### Supported Model Metrics + +We plan on adding more support for other metrics so feel free to reach out on our Slack or GitHub to help us prioritize each task. + +| Metric/Question type | LabelQuestion | MultiLabelQuestion | RatingQuestion | RankingQuestion | TextQuestion | +|:----------------------|:---------------|:-------------------|:---------------|:----------------|:-------------| +| accuracy | ✔️ | ✔️ | ✔️ | | | +| precision | ✔️ | ✔️ | ✔️ | | | +| recall | ✔️ | ✔️ | ✔️ | | | +| f1-score | ✔️ | ✔️ | ✔️ | | | +| confusion-matrix | ✔️ | ✔️ | ✔️ | | | +| pearson-r | ✔️ | | | | | +| spearman-r | | | ✔️ | | | +| gleu | | | | | ✔️ | +| rouge | | | | | ✔️ | +| ndcg-score | | | | ✔️ | | + + ## Other datasets ```{include} /_common/other_datasets.md ``` -This guide gives you a brief introduction to Argilla Metrics. Argilla Metrics enable you to perform fine-grained analyses of your models and training datasets. Argilla Metrics are inspired by a a number of seminal works such as [Explainaboard](https://explainaboard.inspiredco.ai/). +This guide gives you a brief introduction to Argilla Metrics. Argilla Metrics enables you to perform fine-grained analyses of your models and training datasets. Argilla Metrics are inspired by a number of seminal works such as [Explainaboard](https://explainaboard.inspiredco.ai/). The main goal is to make it easier to build more robust models and training data, going beyond single-number metrics (e.g., F1). diff --git a/docs/_source/reference/python/index.rst b/docs/_source/reference/python/index.rst index 61cfcbdf00..597128426e 100644 --- a/docs/_source/reference/python/index.rst +++ b/docs/_source/reference/python/index.rst @@ -13,6 +13,7 @@ The Python reference guide for Argilla. This section contains: * :ref:`python_training`: The training integration module * :ref:`python_users`: The Python client module to manage users in Argilla * :ref:`python_workspaces`: The Python client module to manage workspaces in Argilla +* :ref:`python_annotation_metrics`: The Python module to measure annotation metrics in Argilla's datasets .. toctree:: :maxdepth: 2 @@ -27,3 +28,4 @@ The Python reference guide for Argilla. This section contains: python_listeners python_users python_workspaces + python_annotation_metrics diff --git a/docs/_source/reference/python/python_annotation_metrics.rst b/docs/_source/reference/python/python_annotation_metrics.rst new file mode 100644 index 0000000000..13700b7985 --- /dev/null +++ b/docs/_source/reference/python/python_annotation_metrics.rst @@ -0,0 +1,48 @@ +.. _python_annotation_metrics: + +Annotation metrics +================== + +Here we describe the available metrics in Argilla: + +- :ref:`python ref agreement_metrics`: Metrics of agreement on an annotation task +- :ref:`python ref annotator_metrics`: Metrics for annotators. Includes both metrics per annotator and unified metrics + for all annotators. + +.. _python ref metrics: + +Base Metric +----------- + +.. automodule:: argilla.client.feedback.metrics.base + :members: AgreementMetricResult, ModelMetricResult + +.. autoclass:: argilla.client.feedback.metrics.base.MetricBase + :members: __init__, compute, allowed_metrics + +.. _python ref agreement_metrics: + +Agreement Metrics +----------------- + +.. automodule:: argilla.client.feedback.metrics.agreement_metrics + :members: + :exclude-members: kendall_tau_dist, prepare_dataset_for_annotation_task, AgreementMetric + +.. autoclass:: argilla.client.feedback.metrics.agreement_metrics.AgreementMetric + :members: __init__, compute + +.. _python ref annotator_metrics: + +Annotator Metrics +----------------- + +.. automodule:: argilla.client.feedback.metrics.annotator_metrics + :members: + + +.. autoclass:: argilla.client.feedback.metrics.annotator_metrics.ModelMetric + :members: __init__, compute + +.. autoclass:: argilla.client.feedback.metrics.annotator_metrics.UnifiedModelMetric + :members: __init__, compute diff --git a/docs/_source/tutorials_and_integrations/tutorials/feedback/end2end_examples/use-metrics-007.ipynb b/docs/_source/tutorials_and_integrations/tutorials/feedback/end2end_examples/use-metrics-007.ipynb index 05550ca33d..0914cbf6fd 100644 --- a/docs/_source/tutorials_and_integrations/tutorials/feedback/end2end_examples/use-metrics-007.ipynb +++ b/docs/_source/tutorials_and_integrations/tutorials/feedback/end2end_examples/use-metrics-007.ipynb @@ -4,14 +4,72 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Use Metrics" + "# Use Metrics to Evaluate Your Model\n", + "\n", + "In this part of our end-to-end series, we will evaluate the annotation results of our dataset using the `metrics` module. To see the previous steps, you can refer to the tutorials such as [creating the dataset](./create-dataset-001.ipynb), [adding responses and suggestions](./add-resoponses) or [training your model](./train-model-006.ipynb). Feel free to check out the [practical guides](../../../../practical_guides/practical_guides.md) page for more in-depth information.\n", + "\n", + "After having your dataset annotated by the annotators, it is strongly recommended to evaluate the annotation results. Within the `metrics` module, we divide the evaluation metrics into three: Agreement Metrics, Suggestions Metrics, and Responses Metrics. **Agreement Metrics** are the metrics that you can employ to evaluate the agreement between the annotators. Generally referred to as \"Inter-Annotator Agreement\" in the literature, this metric has various implementations developed by different researchers, some notable examples of which are Krippendorff's Alpha, Cohen's Kappa, Fleiss' Kappa, Scott's Pi, and Bennet, Albert and Goldstein's S. With these metrics, you can see how reliable the annotations are and how much the annotators agree with each other.\n", + "\n", + "On the other hand, **Suggestions Metrics** are the metrics that you can employ to evaluate the responses of the annotators against the suggestions given to them. This will demonstrate how good the responses of each annotator are compared to a gold standard. In addition, we have the opportunity to unify the responses given by different annotators for a single record. This way, either the unified responses or responses can be evaluated. In a similar way, **Responses Metrics** are the metrics that you can employ to evaluate the suggestions given to the annotators against the responses given by them. This will give us an insight into how good the suggestions are, whether they are helpful or not, and whether the model needs to be improved." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "This tutorial is still WIP." + "![workflow](../../../../_static/tutorials/end2end/base/workflow_metrics.png)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Table of Contents\n", + "\n", + "1. [Pull the Dataset](#Pull-the-Dataset)\n", + " 1. [From Argilla](#From-Argilla)\n", + " 2. [From HuggingFace Hub](#From-HuggingFace-Hub)\n", + "2. [Unify Responses](#Unify-Responses)\n", + "3. [Annotation Metrics](#Annotation-Metrics)\n", + " 1. [Agreement Metrics](#Agreement-Metrics)\n", + " 2. [Model Metrics](#Model-Metrics)\n", + "4. [Conclusion](#Conclusion)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Running Argilla\n", + "\n", + "For this tutorial, you will need to have an Argilla server running. There are two main options for deploying and running Argilla:\n", + "\n", + "**Deploy Argilla on Hugging Face Spaces:** If you want to run tutorials with external notebooks (e.g., Google Colab) and you have an account on Hugging Face, you can deploy Argilla on Spaces with a few clicks:\n", + "\n", + "[![deploy on spaces](https://huggingface.co/datasets/huggingface/badges/raw/main/deploy-to-spaces-lg.svg)](https://huggingface.co/new-space?template=argilla/argilla-template-space)\n", + "\n", + "For details about configuring your deployment, check the [official Hugging Face Hub guide](https://huggingface.co/docs/hub/spaces-sdks-docker-argilla).\n", + "\n", + "**Launch Argilla using Argilla's quickstart Docker image**: This is the recommended option if you want [Argilla running on your local machine](../../../../getting_started/quickstart.md). Note that this option will only let you run the tutorial locally and not with an external notebook service.\n", + "\n", + "For more information on deployment options, please check the Deployment section of the documentation.\n", + "\n", + "
\n", + "\n", + "Tip\n", + "\n", + "This tutorial is a Jupyter Notebook. There are two options to run it:\n", + "\n", + "- Use the Open in Colab button at the top of this page. This option allows you to run the notebook directly on Google Colab. Don't forget to change the runtime type to GPU for faster model training and inference.\n", + "- Download the .ipynb file by clicking on the View source link at the top of the page. This option allows you to download the notebook and run it on your local machine or on a Jupyter notebook tool of your choice.\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First, let's install our dependencies and import the necessary libraries:" ] }, { @@ -19,9 +77,27 @@ "execution_count": null, "metadata": {}, "outputs": [], + "source": [ + "!pip install argilla\n", + "!pip install datasets transformers" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], "source": [ "import argilla as rg\n", - "from argilla._constants import DEFAULT_API_KEY" + "from argilla._constants import DEFAULT_API_KEY\n", + "from argilla.client.feedback.metrics.annotator_metrics import ModelMetric" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In order to run this notebook we will need some credentials to push and load datasets from `Argilla` and `🤗 Hub`, let's set them in the following cell:" ] }, { @@ -40,16 +116,395 @@ "# Huggingface credentials\n", "hf_token = \"hf_...\"" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Log in to Argilla:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rg.init(api_url=api_url, api_key=api_key)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Enable Telemetry\n", + "We gain valuable insights from how you interact with our tutorials. To improve ourselves in offering you the most suitable content, using the following lines of code will help us understand that this tutorial is serving you effectively. Though this is entirely anonymous, you can choose to skip this step if you prefer. For more info, please check out the Telemetry page." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " from argilla.utils.telemetry import tutorial_running\n", + " tutorial_running()\n", + "except ImportError:\n", + " print(\"Telemetry module is introduced in Argilla 1.20.0 and not found in the current installation. Skipping telemetry.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Pull the Dataset\n", + "\n", + "To employ metrics, we can pull a dataset that consists of multiple annotations per record. We can do this either from HuggingFace Hub. Let us see how we can pull it.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### From HuggingFace Hub\n", + "\n", + "We can also pull the dataset from HuggingFace Hub. Similarly, we can use the `from_huggingface` method to pull the dataset." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dataset = rg.FeedbackDataset.from_huggingface(\"argilla/go_emotions_raw\", split=\"train[:1000]\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + "Note \n", + "\n", + "The dataset pulled from HuggingFace Hub is an instance of `FeedbackDataset` whereas the dataset pulled from Argilla is an instance of `RemoteFeedbackDataset`. The difference between the two is that the former is a local one and the changes made on it stay locally. On the other hand, the latter is a remote one and the changes made on it are directly reflected on the dataset on the Argilla server, which can make your process faster.\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let us briefly examine what our dataset looks like. It is a dataset that consists of data items with the field `text`. For each record, we have multiple annotations that label the text with at least one sentiment. Let us see an example of a text and the given responses. In this example, the record has been annotated by 3 annotators and one of them has labeled the text with one sentiment while the other two have labeled it with two sentiments." + ] + }, + { + "cell_type": "code", + "execution_count": 140, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "text: And not all children's hospitals need the same stuff, so call and ask what they need. But I like your tip. You're correct. \n", + "responses: [['neutral'], ['approval', 'desire'], ['approval', 'love']]\n" + ] + } + ], + "source": [ + "print(\"text:\", dataset[5].fields[\"text\"])\n", + "print(\"responses:\", [dataset[5].responses[i].values[\"label\"].value for i in range(len(dataset[5].responses))])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### From Argilla\n", + "\n", + "We can pull the dataset from Argilla by using the `from_argilla` method. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Unify Responses" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When you have multiple annotations per record in your project, it is a good practice to unify the responses to have a single response per record. This is preferable as it makes the dataset more consistent and easier to work with. Let us see how we can unify the responses with Argilla. First, we create a strategy to unify the responses. We go with the `majority` vote strategy, which means that we will keep the responses that have been suggested by the majority of the annotators. " + ] + }, + { + "cell_type": "code", + "execution_count": 146, + "metadata": {}, + "outputs": [], + "source": [ + "strategy = rg.MultiLabelQuestionStrategy(\"majority\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dataset.compute_unified_responses(\n", + " question=dataset.question_by_name(\"label\"),\n", + " strategy=strategy,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can look at a record to see how the responses have been unified. In our case, the responses have been unified to `approval` as it is the majority vote among the responses." + ] + }, + { + "cell_type": "code", + "execution_count": 148, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'label': [UnifiedValueSchema(value=['approval'], strategy=)]}" + ] + }, + "execution_count": 148, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dataset.records[5].unified_responses" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Annotation Metrics\n", + "\n", + "\n", + "Argilla offers various annotation metrics to evaluate the performance of the annotators. Let us see how we can employ each one of them." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Agreement Metrics\n", + "\n", + "The first step for the evaluation is to contrast the responses given by the annotators with each other, which is commonly known as ***Inter-Annotator Agreement***. This is a crucial step to see how reliable the annotations are and how much the annotators agree with each other. Argilla currently offers only [Krippendorff's alpha](https://en.wikipedia.org/wiki/Krippendorff%27s_alpha) as an agreement metric. Let us see how we can evaluate the agreement between the annotators with Argilla.\n", + "\n", + "To calculate the `alpha`, we only need to call the `compute_agreement_metrics` method, with `alpha` being the argument. We also need to specify the question name in our dataset to calculate the metric. Please note that agreement metrics are available for all question types except for the `TextQuestion`." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "AgreementMetricResult(metric_name='alpha', count=3468, result=0.2459926458269277)" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dataset.compute_agreement_metrics(\"alpha\", question_name=\"label\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The result we have shows that the agreement between the annotators is 0.2459 for the dataset we have, which is a low agreement. For Kripendorff's alpha, the value is in the range [0,1] and is usually interpreted in the following way: alpha >= 0.8 indicates a reliable annotation, alpha >= 0.667 allows making tentative conclusions, while the lower values suggest the unreliable annotation. This indicates we might want to revisit our annotation process and work on a better task design or annotator training." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Model Metrics\n", + "\n", + "For computing model metrics, we will assume that the responses given by the annotators are the gold standard, we compare against. The main advantage of adding suggestions to our dataset is to simplify and shorten the annotation task. By computing the model metrics, we will be able to see if the suggestions work in the way we want and if our models running inference are on apr with our expectations. In case of a low performance, we can consider improving the suggestions by updating and fine-tuning the model to generate better suggestions. This way, we can improve the performance of the annotators as well." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The question type we have in the current dataset is `MultiLabelQuestion`. By using the `allowed_metrics` method, we can see the metrics below, which are the available ones for this question type." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['accuracy', 'f1-score', 'precision', 'recall', 'confusion-matrix']" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "metric = ModelMetric(dataset=dataset, question_name=\"label\")\n", + "metric.allowed_metrics" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "With Argilla, we can calculate the responses metrics easily with the `compute_responses_metrics` method. In this example, we will calculate all allowed metrics for the `MultiLabelQuestion` question type." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model_metrics = dataset.compute_model_metrics(question_name=\"label\", metric_names=metric.allowed_metrics)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[ModelMetricResult(metric_name='accuracy', count=182, result=0.5714285714285714),\n", + " ModelMetricResult(metric_name='f1-score', count=182, result=0.428750352375672),\n", + " ModelMetricResult(metric_name='precision', count=182, result=0.4427905213343358),\n", + " ModelMetricResult(metric_name='recall', count=182, result=0.5377066798941799),\n", + " ModelMetricResult(metric_name='confusion-matrix', count=182, result={'admiration': suggestions_admiration_true \\\n", + " responses_admiration_true 174 \n", + " responses_admiration_false 0 \n", + " \n", + " suggestions_admiration_false \n", + " responses_admiration_true 5 \n", + " responses_admiration_false 3 , 'amusement': suggestions_amusement_true \\\n", + " responses_amusement_true 176 \n", + " responses_amusement_false 1 \n", + " \n", + " suggestions_amusement_false \n", + " responses_amusement_true 3 \n", + " responses_amusement_false 2 })]" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model_metrics[\"00000000-0000-0000-0000-000000000004\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Model Metrics for Unified Responses\n", + "\n", + "We have calculated the given metrics for each comparing the model performance against each annotator individually and obtained the respective results for the metric. However, we may sometimes want to calculate the performance of the model against annotators collectively, where we compare the suggestions against unified responses of annotators . As stated above, Argilla offers us the opportunity to unify the responses of annotators according to different strategies. We can first unify the responses as shown and then calculate the metrics for the unified responses.\n", + "\n", + "To accomplish this, we only need to feed the method above with the `strategy` argument. When this argument is set with the preferred strategy, the responses will first be unified and then, these unified responses will be compared to the suggestions. Let us go with the `majority` strategy." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model_metrics_unified = dataset.compute_model_metrics(question_name=\"label\", metric_names=[\"accuracy\", \"precision\", \"recall\", \"f1-score\"], strategy=\"majority\")" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[ModelMetricResult(metric_name='accuracy', count=1000, result=0.812),\n", + " ModelMetricResult(metric_name='precision', count=1000, result=0.7693494302078528),\n", + " ModelMetricResult(metric_name='recall', count=1000, result=0.7744636775213872),\n", + " ModelMetricResult(metric_name='f1-score', count=1000, result=0.7578965673231839)]" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model_metrics_unified" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Conclusion" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this tutorial, we have seen how we can evaluate the annotation results of our dataset using the `metrics` module. We have first unified the response to have a more comprehensive outlook on the annotations. Then, we have calculated the agreement metrics to see how much our annotators agree with each other. After that, we have calculated the suggestions metrics to see how good the responses of each annotator are compared to a gold standard, which is the suggestions in this case. Similarly, we have calculated the responses metrics to see how good the suggestions are compared to the responses of the annotators. \n", + "\n", + "For both suggestions and responses metrics, we have calculated the metrics per annotator and for the unified responses. If you feel that the annotations are not satisfactory, you can reiterate the annotation process by making changes in the structure of your project. You can refer to the [practical guides](../../../../practical_guides/practical_guides.md) to refine your structure or check out the [advanced tutorials](../../../../tutorials.md) to learn more about the advanced use cases of Argilla." + ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "argilla", "language": "python", "name": "python3" }, "language_info": { - "name": "python" + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.18" } }, "nbformat": 4, diff --git a/docs/_source/tutorials_and_integrations/tutorials/tutorials.md b/docs/_source/tutorials_and_integrations/tutorials/tutorials.md index 31149f584e..b23b94ebc5 100644 --- a/docs/_source/tutorials_and_integrations/tutorials/tutorials.md +++ b/docs/_source/tutorials_and_integrations/tutorials/tutorials.md @@ -55,6 +55,11 @@ Learn how to filter and query your `FeedbackDataset`. Learn how to train your model with `ArgillaTrainer`. ``` +```{grid-item-card} Use Metric to Evaluate Your Model +:link: feedback/end2end_examples/use-metrics-007.html + +Learn how to use the metrics module to evaluate your model. +``` ```` @@ -119,7 +124,6 @@ Learn how to set up a project to curate a public dataset that can be used to fin Learn how to apply multimodality (video, audio and images) to your FeedbackDataset using the Argilla TextFields. ``` - ```` **Other datasets** diff --git a/src/argilla/client/feedback/dataset/base.py b/src/argilla/client/feedback/dataset/base.py index d715ea9511..7d00e3840c 100644 --- a/src/argilla/client/feedback/dataset/base.py +++ b/src/argilla/client/feedback/dataset/base.py @@ -215,11 +215,6 @@ def push_to_argilla(self, *args, **kwargs) -> "FeedbackDatasetBase": """Pushes the `FeedbackDataset` to Argilla.""" pass - @abstractmethod - def unify_responses(self, *args, **kwargs): - """Unifies the responses for a given question.""" - pass - @abstractmethod def add_metadata_property(self, *args, **kwargs): """Adds a new `metadata_property` to the current `FeedbackDataset`.""" diff --git a/src/argilla/client/feedback/dataset/local/dataset.py b/src/argilla/client/feedback/dataset/local/dataset.py index a0263abb13..c5f261644d 100644 --- a/src/argilla/client/feedback/dataset/local/dataset.py +++ b/src/argilla/client/feedback/dataset/local/dataset.py @@ -21,6 +21,7 @@ from argilla.client.feedback.dataset import helpers from argilla.client.feedback.dataset.base import FeedbackDatasetBase, R from argilla.client.feedback.dataset.local.mixins import ArgillaMixin, TaskTemplateMixin +from argilla.client.feedback.dataset.mixins import MetricsMixin, UnificationMixin from argilla.client.feedback.integrations.huggingface.dataset import HuggingFaceDatasetMixin from argilla.client.feedback.schemas.enums import RecordSortField, SortOrder from argilla.client.feedback.schemas.questions import ( @@ -63,7 +64,14 @@ _LOGGER = logging.getLogger(__name__) -class FeedbackDataset(ArgillaMixin, HuggingFaceDatasetMixin, FeedbackDatasetBase[FeedbackRecord], TaskTemplateMixin): +class FeedbackDataset( + ArgillaMixin, + HuggingFaceDatasetMixin, + FeedbackDatasetBase[FeedbackRecord], + TaskTemplateMixin, + MetricsMixin, + UnificationMixin, +): def __init__( self, *, @@ -418,44 +426,6 @@ def delete_metadata_properties( self._metadata_properties = list(metadata_properties_mapping.values()) return deleted_metadata_properties if len(deleted_metadata_properties) > 1 else deleted_metadata_properties[0] - def unify_responses( - self: "FeedbackDatasetBase", - question: Union[str, LabelQuestion, MultiLabelQuestion, RatingQuestion], - strategy: Union[ - str, LabelQuestionStrategy, MultiLabelQuestionStrategy, RatingQuestionStrategy, RankingQuestionStrategy - ], - ) -> "FeedbackDataset": - """ - The `unify_responses` function takes a question and a strategy as input and applies the strategy - to unify the responses for that question. - - Args: - question The `question` parameter can be either a string representing the name of the - question, or an instance of one of the question classes (`LabelQuestion`, `MultiLabelQuestion`, - `RatingQuestion`, `RankingQuestion`). - strategy The `strategy` parameter is used to specify the strategy to be used for unifying - responses for a given question. It can be either a string or an instance of a strategy class. - """ - if isinstance(question, str): - question = self.question_by_name(question) - - if isinstance(strategy, str): - if isinstance(question, LabelQuestion): - strategy = LabelQuestionStrategy(strategy) - elif isinstance(question, MultiLabelQuestion): - strategy = MultiLabelQuestionStrategy(strategy) - elif isinstance(question, RatingQuestion): - strategy = RatingQuestionStrategy(strategy) - elif isinstance(question, RankingQuestion): - strategy = RankingQuestionStrategy(strategy) - elif isinstance(question, TextQuestion): - strategy = TextQuestionStrategy(strategy) - else: - raise ValueError(f"Question {question} is not supported yet") - - strategy.unify_responses(self.records, question) - return self - # TODO(alvarobartt,davidberenstein1957): we should consider having something like # `export(..., training=True)` to export the dataset records in any format, replacing # both `format_as` and `prepare_for_training` @@ -511,12 +481,12 @@ def prepare_for_training( if task.formatting_func is None: # in sentence-transformer models we can train without labels if task.label: - local_dataset = local_dataset.unify_responses( + local_dataset = local_dataset.compute_unified_responses( question=task.label.question, strategy=task.label.strategy ) elif isinstance(task, TrainingTaskForQuestionAnswering): if task.formatting_func is None: - local_dataset = self.unify_responses(question=task.answer.name, strategy="disagreement") + local_dataset = self.compute_unified_responses(question=task.answer.name, strategy="disagreement") elif not isinstance( task, ( diff --git a/src/argilla/client/feedback/dataset/mixins.py b/src/argilla/client/feedback/dataset/mixins.py new file mode 100644 index 0000000000..2ed2d854dd --- /dev/null +++ b/src/argilla/client/feedback/dataset/mixins.py @@ -0,0 +1,164 @@ +# Copyright 2021-present, the Recognai S.L. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import warnings +from typing import TYPE_CHECKING, Dict, List, Optional, Union + +from argilla.client.feedback.dataset.base import FeedbackDatasetBase +from argilla.client.feedback.schemas.questions import ( + LabelQuestion, + MultiLabelQuestion, + RankingQuestion, + RatingQuestion, + TextQuestion, +) +from argilla.client.feedback.unification import ( + LabelQuestionStrategy, + MultiLabelQuestionStrategy, + RankingQuestionStrategy, + RatingQuestionStrategy, + TextQuestionStrategy, +) + +if TYPE_CHECKING: + from argilla.client.feedback.dataset.local.dataset import FeedbackDataset + from argilla.client.feedback.metrics.agreement_metrics import AgreementMetricResult + from argilla.client.feedback.metrics.annotator_metrics import ModelMetricResult + + +class MetricsMixin: + """Mixin to add functionality to compute the metrics directly from a `FeedbackDataset`.""" + + def compute_model_metrics( + self, + metric_names: Union[str, List[str]] = None, + question_name: Union[ + str, LabelQuestion, MultiLabelQuestion, RatingQuestion, TextQuestion, RankingQuestion + ] = None, + strategy: Optional[ + Union[str, LabelQuestionStrategy, MultiLabelQuestion, RatingQuestionStrategy, RankingQuestion] + ] = None, + ) -> Union[Dict[str, List["ModelMetricResult"]], "ModelMetricResult", List["ModelMetricResult"]]: + """Compute metrics for the annotators using the suggestions as the ground truth, and the responses + as the predicted value, or if a strategy is provided, the same but applied to unified responses. + + The metric interpretation is the same whether the responses are unified or not. + + Args: + metric_names: Metric name or list of metric names of the metrics, dependent on the question type. + question_name: Question for which we want to compute the metrics. + strategy: Unification strategy. If given, will unify the responses of the dataset and compute + the metrics on the unified responses vs the suggestions instead on a per user level. + See `unified_responses` method for more information. Defaults to None. + + Note: + Currently, the following types of questions are supported: + - For annotator level questions: all the types of questions + - For unified responses: all the questions except the `TextQuestion`. + + Returns: + metrics_container: If strategy is provided it will unify the annotations and return + the metrics for the unified responses. Otherwise, it will return the metrics for + each annotator as a dict, where the key corresponds to the annotator id and the + values are a list with the metrics. + """ + from argilla.client.feedback.metrics.annotator_metrics import ModelMetric, UnifiedModelMetric + + if strategy: + self.compute_unified_responses(question_name, strategy) + return UnifiedModelMetric(self, question_name).compute(metric_names) + else: + return ModelMetric(self, question_name).compute(metric_names) + + def compute_agreement_metrics( + self, + metric_names: Union[str, List[str]] = None, + question_name: Union[str, LabelQuestion, MultiLabelQuestion, RatingQuestion, RankingQuestion] = None, + ) -> Union["AgreementMetricResult", List["AgreementMetricResult"]]: + """Compute agreement or reliability of annotation metrics. + + This metrics can be used to determine the level of agreement across our annotation team, + or whether the guidelines are clear enough for example. + + Args: + metric_names: Metric name or list of metric names of the metrics, dependent on the question type. + question_name: Question for which we want to compute the metrics. + + Note: + Currently, TextQuestion is not supported. + + Returns: + metrics_result: Agreement metrics result or a list of metrics results if a list of metric + names is provided. + """ + from argilla.client.feedback.metrics.agreement_metrics import AgreementMetric + + return AgreementMetric(self, question_name).compute(metric_names) + + +class UnificationMixin: + def unify_responses( + self: "FeedbackDatasetBase", + question: Union[str, LabelQuestion, MultiLabelQuestion, RatingQuestion], + strategy: Union[ + str, LabelQuestionStrategy, MultiLabelQuestionStrategy, RatingQuestionStrategy, RankingQuestionStrategy + ], + ) -> "FeedbackDataset": + warnings.warn( + "`unify_responses` method is deprecated and will be removed in future releases. " + "Please use `compute_unified_responses` instead.", + DeprecationWarning, + ) + return self.compute_unified_responses(question=question, strategy=strategy) + + def compute_unified_responses( + self: "FeedbackDatasetBase", + question: Union[str, LabelQuestion, MultiLabelQuestion, RatingQuestion], + strategy: Union[ + str, LabelQuestionStrategy, MultiLabelQuestionStrategy, RatingQuestionStrategy, RankingQuestionStrategy + ], + ) -> "FeedbackDataset": + """ + The `compute_unified_responses` function takes a question and a strategy as input and applies the strategy + to unify the responses for that question. + + Args: + question The `question` parameter can be either a string representing the name of the + question, or an instance of one of the question classes (`LabelQuestion`, `MultiLabelQuestion`, + `RatingQuestion`, `RankingQuestion`). + strategy The `strategy` parameter is used to specify the strategy to be used for unifying + responses for a given question. It can be either a string or an instance of a strategy class. + """ + if isinstance(question, str): + question = self.question_by_name(question) + + if not strategy: + strategy = "majority" + + if isinstance(strategy, str): + if isinstance(question, LabelQuestion): + strategy = LabelQuestionStrategy(strategy) + elif isinstance(question, MultiLabelQuestion): + strategy = MultiLabelQuestionStrategy(strategy) + elif isinstance(question, RatingQuestion): + strategy = RatingQuestionStrategy(strategy) + elif isinstance(question, RankingQuestion): + strategy = RankingQuestionStrategy(strategy) + elif isinstance(question, TextQuestion): + strategy = TextQuestionStrategy(strategy) + else: + raise ValueError(f"Question {question} is not supported yet") + + strategy.compute_unified_responses(self.records, question) + return self diff --git a/src/argilla/client/feedback/dataset/remote/dataset.py b/src/argilla/client/feedback/dataset/remote/dataset.py index dc2b79cb47..270c3a7b28 100644 --- a/src/argilla/client/feedback/dataset/remote/dataset.py +++ b/src/argilla/client/feedback/dataset/remote/dataset.py @@ -22,6 +22,7 @@ from argilla.client.feedback.constants import DELETE_DATASET_RECORDS_MAX_NUMBER, PUSHING_BATCH_SIZE from argilla.client.feedback.dataset import helpers from argilla.client.feedback.dataset.base import FeedbackDatasetBase, SortBy +from argilla.client.feedback.dataset.mixins import MetricsMixin, UnificationMixin from argilla.client.feedback.dataset.remote.mixins import ArgillaRecordsMixin from argilla.client.feedback.mixins import ArgillaMetadataPropertiesMixin from argilla.client.feedback.schemas.enums import ResponseStatusFilter @@ -384,7 +385,7 @@ def include_as_query_params(self) -> List[str]: return include -class RemoteFeedbackDataset(FeedbackDatasetBase[RemoteFeedbackRecord]): +class RemoteFeedbackDataset(FeedbackDatasetBase[RemoteFeedbackRecord], MetricsMixin, UnificationMixin): # TODO: Call super method once the base init contains only commons init attributes def __init__( self, @@ -990,33 +991,6 @@ def _create_from_dataset(cls, dataset: "RemoteFeedbackDataset") -> "RemoteFeedba return new_dataset - def unify_responses( - self, - question: Union[str, LabelQuestion, MultiLabelQuestion, RatingQuestion], - strategy: Union[ - str, LabelQuestionStrategy, MultiLabelQuestionStrategy, RatingQuestionStrategy, RankingQuestionStrategy - ], - ) -> "FeedbackDataset": - """ - The `unify_responses` function takes a question and a strategy as input and applies the strategy - to unify the responses for that question. - - Args: - question The `question` parameter can be either a string representing the name of the - question, or an instance of one of the question classes (`LabelQuestion`, `MultiLabelQuestion`, - `RatingQuestion`, `RankingQuestion`). - strategy The `strategy` parameter is used to specify the strategy to be used for unifying - responses for a given question. It can be either a string or an instance of a strategy class. - """ - warnings.warn( - "A local `FeedbackDataset` returned because " - "`unify_responses` is not supported for `RemoteFeedbackDataset`. " - "`RemoteFeedbackDataset`.pull().unify_responses(*args, **kwargs)` is applied.", - UserWarning, - ) - local = self.pull() - return local.unify_responses(question=question, strategy=strategy) - def prepare_for_training( self, framework: Union[Framework, str], diff --git a/src/argilla/client/feedback/metrics/__init__.py b/src/argilla/client/feedback/metrics/__init__.py new file mode 100644 index 0000000000..339f8ea2f7 --- /dev/null +++ b/src/argilla/client/feedback/metrics/__init__.py @@ -0,0 +1,26 @@ +# Copyright 2021-present, the Recognai S.L. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from argilla.client.feedback.metrics.agreement_metrics import AgreementMetric +from argilla.client.feedback.metrics.annotator_metrics import ( + ModelMetric, + UnifiedModelMetric, +) + +__all__ = [ + "ModelMetric", + "AgreementMetric", + "UnifiedModelMetric", +] diff --git a/src/argilla/client/feedback/metrics/agreement_metrics.py b/src/argilla/client/feedback/metrics/agreement_metrics.py new file mode 100644 index 0000000000..97cd833b11 --- /dev/null +++ b/src/argilla/client/feedback/metrics/agreement_metrics.py @@ -0,0 +1,343 @@ +# Copyright 2021-present, the Recognai S.L. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""This module contains metrics to gather information related to inter-Annotator agreement. """ + +from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Union + +from nltk.metrics.agreement import AnnotationTask as NLTKAnnotationTask +from nltk.metrics.distance import binary_distance, interval_distance, masi_distance + +from argilla.client.feedback.dataset import FeedbackDataset +from argilla.client.feedback.metrics.base import AgreementMetricResult, AnnotationTaskMetricBase, MetricBase +from argilla.client.feedback.schemas import ( + LabelQuestion, + MultiLabelQuestion, + RankingQuestion, + RatingQuestion, +) +from argilla.client.feedback.schemas.remote.shared import RemoteSchema + +if TYPE_CHECKING: + from argilla.client.feedback.dataset import FeedbackDataset + from argilla.client.feedback.dataset.remote.dataset import RemoteFeedbackDataset + from argilla.client.feedback.metrics.base import FormattedResponses + from argilla.client.feedback.schemas.enums import ResponseStatusFilter + from argilla.client.feedback.schemas.records import SortBy + + +def prepare_dataset_for_annotation_task( + dataset: Union["FeedbackDataset", "RemoteFeedbackDataset"], + question_name: str, + filter_by: Optional[Dict[str, Union["ResponseStatusFilter", List["ResponseStatusFilter"]]]] = None, + sort_by: Optional[List["SortBy"]] = None, + max_records: Optional[int] = None, +) -> "FormattedResponses": + """Helper function to prepare the dataset for the nltk's AnnotationTask. + + The AnnotationTask class from nltk expects the data to be formatted as a list + of tuples, each containing the annotator id, the task id and the label. + + The AnnotationTask is supposed to deal with sets and hashable objects, but + there are errors transforming the data to sets and using the MASI distance function. + For the moment what we do with those type of questions is create a string + with all the values, and use the edit distance function. + + Note: + We could potentially extend the functionality to a more than a question name. The + requirement would be that all the questions are of the same type, as that would + determine the type of distance function to use. + + Args: + dataset: FeedbackDataset to compute the metrics. + question_name: Name of the question for which we want to analyse the agreement. + filter_by: A dict with key the field to filter by, and values the filters to apply. + Can be one of: draft, pending, submitted, and discarded. If set to None, + no filter will be applied. Defaults to None (no filter is applied). + sort_by: A list of `SortBy` objects to sort your dataset by. + Defaults to None (no filter is applied). + max_records: The maximum number of records to use for training. Defaults to None. + + Returns: + formatted_responses: The responses formatted as a list of tuples of (user_id, question_id, value). + """ + question_type = type(dataset.question_by_name(question_name)) + # Check to assume the remote questions behave just like local ones + if issubclass(question_type, RemoteSchema): + question_type = type(dataset.question_by_name(question_name).to_local()) + + supported_question_types = list(QUESTION_TO_DISTANCE.keys()) + if question_type not in supported_question_types: + raise NotImplementedError( + f"Question '{question_name}' is of type '{question_type}', the supported question types are: {supported_question_types}." + ) + + if filter_by: + dataset = dataset.filter_by(**filter_by) + if sort_by: + dataset = dataset.sort_by(sort_by) + if max_records: + dataset = dataset.pull(max_records=max_records) + + hf_dataset = dataset.format_as("datasets") + + formatted_responses: FormattedResponses = [] + + for row in hf_dataset: + responses_ = row[question_name] + question_text = row["text"] + for response in responses_: + user_id = response["user_id"] + if user_id is None: + raise ValueError( + "Please push your dataset to argilla to have the user_id necessary for this computation." + ) + + value = response["value"] + if value is None: + continue + # To avoid errors with the MASI distance function + if isinstance(value, list): + if len(value) == 0: + continue + if question_type == RankingQuestion: + value = tuple(value["rank"]) + elif question_type == MultiLabelQuestion: + value = frozenset(value) + + formatted_responses.append((user_id, question_text, value)) + + return formatted_responses + + +def kendall_tau_dist(x: List[int], y: List[int]) -> float: + r"""Kendall tau distance. + + https://en.wikipedia.org/wiki/Kendall_tau_distance + + Args: + x: Values of the first annotation. + y: Values of the first annotation. + + Returns: + distance: Kendall tau distance. + + Example: + >>> import itertools + >>> values = (1, 2, 3) + >>> for i, a in enumerate(itertools.permutations(values, len(values))): + ... for j, b in enumerate(itertools.permutations(values, len(values))): + ... if j >= i: + ... print((a, b), kendall_tau_dist(a,b)) + ... + ((1, 2, 3), (1, 2, 3)) 0.0 + ((1, 2, 3), (1, 3, 2)) 0.3333333333333333 + ((1, 2, 3), (2, 1, 3)) 0.3333333333333333 + ((1, 2, 3), (2, 3, 1)) 0.6666666666666667 + ((1, 2, 3), (3, 1, 2)) 0.6666666666666667 + ((1, 2, 3), (3, 2, 1)) 1.0 + ... + """ + from scipy.stats import kendalltau + + coef, _ = kendalltau(x, y) + return 0.5 * (1 - coef) + + +QUESTION_TO_DISTANCE = { + LabelQuestion: binary_distance, + MultiLabelQuestion: masi_distance, + RatingQuestion: interval_distance, + RankingQuestion: kendall_tau_dist, +} + + +class AgreementMetric(MetricBase): + """Main class to compute agreement metrics. + + Example: + >>> import argilla as rg + >>> from argilla.client.feedback.metrics import AgreementMetric + >>> metric = AgreementMetric(dataset=dataset, question_name=question, filter_by={"response_status": "submitted"}) + >>> metrics_report = metric.compute("alpha") + + """ + + def __init__( + self, + dataset: FeedbackDataset, + question_name: str, + filter_by: Optional[Dict[str, Union["ResponseStatusFilter", List["ResponseStatusFilter"]]]] = None, + sort_by: Optional[List["SortBy"]] = None, + max_records: Optional[int] = None, + ) -> None: + """Initialize a `AgreementMetric` object to compute agreement metrics. + + Args: + dataset: FeedbackDataset to compute the metrics. + question_name: Name of the question for which we want to analyse the agreement. + filter_by: A dict with key the field to filter by, and values the filters to apply. + Can be one of: draft, pending, submitted, and discarded. If set to None, + no filter will be applied. Defaults to None (no filter is applied). + sort_by: A list of `SortBy` objects to sort your dataset by. + Defaults to None (no filter is applied). + max_records: The maximum number of records to use for training. Defaults to None. + """ + self._metrics_per_question = METRICS_PER_QUESTION + super().__init__(dataset, question_name) + self._filter_by = filter_by + self._sort_by = sort_by + self._max_records = max_records + + def compute(self, metric_names: Union[str, List[str]]) -> List[AgreementMetricResult]: + """Computes the agreement metrics for the given question. + + Args: + metric_names: name or list of names for the metrics to compute. i.e. `alpha`. + kwargs: additional arguments to pass to the metric. + + Raises: + ValueError: If the metric name is not supported for the given question. + + Returns: + agreement_metrics: A list of `AgreementMetricResult` objects for the dataset. + """ + metric_names = self._check_metrics(metric_names) + metric_classes = self._get_metric_classes(metric_names) + + dataset = prepare_dataset_for_annotation_task( + self._dataset, + self._question_name, + filter_by=self._filter_by, + sort_by=self._sort_by, + max_records=self._max_records, + ) + + distance_function = QUESTION_TO_DISTANCE[self._question_type] + + metrics = [] + for metric_name, metric_cls in metric_classes: + metric = metric_cls(annotated_dataset=dataset, distance_function=distance_function) + result = metric.compute() + metrics.append(AgreementMetricResult(metric_name=metric_name, result=result, count=len(dataset))) + + if len(metric_names) == 1: + return metrics[0] + + return metrics + + +class NLTKAnnotationTaskMetric(NLTKAnnotationTask, AnnotationTaskMetricBase): + """Base class for metrics that use the nltk's AnnotationTask class. + + These metrics make use of a distance function to compute the distance between + + It is often the case that we don't want to treat two different + labels as complete disagreement, and so the AnnotationTask constructor can also + take a distance metric as a final argument. Distance metrics are functions that take two + arguments, and return a value between 0.0 and 1.0 indicating the distance between them. + + By default, the following distance metrics are provided for each type of question: + + For LabelQuestion, binary_distance: + + >>> am.binary_distance("a", "b") + 1.0 + >>> am.binary_distance("a", "a") + 0.0 + + For MultiLabelQuestion, masi_distance: + + >>> label_sets = [ + ... [frozenset(["a", "b"]), frozenset(["b", "a"])], + ... [frozenset(["a"]), frozenset(["a", "b"])], + ... [frozenset(["c"]), frozenset(["a", "b"])], + ... ] + >>> for a, b in label_sets: + ... print((a,b), am.masi_distance(a,b)) + ... + (frozenset({'a', 'b'}), frozenset({'a', 'b'})) 0.0 + (frozenset({'a'}), frozenset({'a', 'b'})) 0.665 + (frozenset({'c'}), frozenset({'a', 'b'})) 1.0 + + For RatingQuestion, interval_distance: + + >>> for a, b in [(1, 1), (1, 2), (3,6)]: + ... print((a,b), am.interval_distance(a,b)) + ... + (1, 1) 0 + (1, 2) 1 + (3, 6) 9 + + For RankingQuestion, kendall_tau_dist: + + >>> for i, a in enumerate(itertools.permutations(values, len(values))): + ... for j, b in enumerate(itertools.permutations(values, len(values))): + ... if j >= i: + ... print((a, b), kendall_tau_dist(a,b)) + ... + ((1, 2, 3), (1, 2, 3)) 0.0 + ((1, 2, 3), (1, 3, 2)) 0.3333333333333333 + ((1, 2, 3), (2, 1, 3)) 0.3333333333333333 + ((1, 2, 3), (2, 3, 1)) 0.6666666666666667 + ((1, 2, 3), (3, 1, 2)) 0.6666666666666667 + ((1, 2, 3), (3, 2, 1)) 1.0 + ((1, 3, 2), (1, 3, 2)) 0.0 + ... + """ + + def __init__(self, annotated_dataset: "FormattedResponses" = None, distance_function: Callable = None) -> None: + AnnotationTaskMetricBase.__init__( + self, annotated_dataset=annotated_dataset, distance_function=distance_function + ) + super().__init__(data=annotated_dataset, distance=distance_function) + + +class KrippendorfAlpha(NLTKAnnotationTaskMetric): + """Krippendorf's alpha agreement metric. + + Is a statistical measure of the inter-annotator agreement achieved when coding a set + of units of analysis. + + To interpret the results from this metric, we refer the reader to the wikipedia entry. + The common consensus dictates that a value of alpha >= 0.8 indicates a reliable annotation, + a value >= 0.667 can only guarantee tentative conclusions, while lower values suggest an + unreliable annotation. + + See Also: + - Take a look at this metric definition: + https://en.wikipedia.org/wiki/Krippendorff%27s_alpha + + - We use the implementation from nltk: + https://www.nltk.org/api/nltk.metrics.agreement.html#nltk.metrics.agreement.AnnotationTask.alpha + """ + + def _compute(self, dataset) -> float: + return self.alpha() + + +METRICS_PER_QUESTION = { + LabelQuestion: { + "alpha": KrippendorfAlpha, + }, + MultiLabelQuestion: { + "alpha": KrippendorfAlpha, + }, + RatingQuestion: { + "alpha": KrippendorfAlpha, + }, + RankingQuestion: { + "alpha": KrippendorfAlpha, + }, +} diff --git a/src/argilla/client/feedback/metrics/annotator_metrics.py b/src/argilla/client/feedback/metrics/annotator_metrics.py new file mode 100644 index 0000000000..963dc7e313 --- /dev/null +++ b/src/argilla/client/feedback/metrics/annotator_metrics.py @@ -0,0 +1,608 @@ +# Copyright 2021-present, the Recognai S.L. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""This module contains metrics for Suggestions Metric and Responses Metric.""" + +import random +import warnings +from collections import defaultdict +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union + +import numpy as np +import pandas as pd + +from argilla.client.feedback.dataset import FeedbackDataset +from argilla.client.feedback.metrics.base import AnnotatorMetricBase, MetricBase, ModelMetricResult +from argilla.client.feedback.metrics.utils import ( + get_responses_and_suggestions_per_user, + get_unified_responses_and_suggestions, + is_multiclass, + map_str_to_int, +) +from argilla.client.feedback.schemas import ( + LabelQuestion, + MultiLabelQuestion, + RankingQuestion, + RatingQuestion, + TextQuestion, +) +from argilla.client.feedback.schemas.enums import ResponseStatusFilter +from argilla.client.feedback.schemas.records import SortBy +from argilla.utils.dependency import requires_dependencies + +if TYPE_CHECKING: + from argilla.client.feedback.dataset import FeedbackDataset + from argilla.client.feedback.metrics.base import Responses, Suggestions + from argilla.client.feedback.schemas.enums import ResponseStatusFilter + from argilla.client.feedback.schemas.records import SortBy + + +class AnnotatorMetric(MetricBase): + """Main class to compute annotator metrics. Annotator metrics refers to the combination of Suggestions Metric and Responses Metric. They are both different from the Agreement Metric (i.e. Inter-Annotator Agreement) and they are utilized to compute metrics contrasting suggestions vs responses. + + Example: + >>> import argilla as rg + >>> from argilla.client.feedback.metrics import AnnotatorMetric + >>> metric = AnnotatorMetric(dataset=dataset, question_name=question) + >>> metrics_report = metric.compute("accuracy") + + """ + + def __init__( + self, + dataset: "FeedbackDataset", + question_name: str, + filter_by: Optional[Dict[str, Union["ResponseStatusFilter", List["ResponseStatusFilter"]]]] = None, + sort_by: Optional[List["SortBy"]] = None, + max_records: Optional[int] = None, + responses_vs_suggestions: bool = True, + ) -> None: + """Initialize an `AnnotatorMetric` object to compute agreement metrics for both Suggestions Metric and Responses Metric. + + Args: + dataset: FeedbackDataset to compute the metrics. + question_name: Name of the question for which we want to analyse the agreement. + filter_by: A dict with key the field to filter by, and values the filters to apply. + Can be one of: draft, pending, submitted, and discarded. If set to None, + no filter will be applied. Defaults to None (no filter is applied). + sort_by: A list of `SortBy` objects to sort your dataset by. + Defaults to None (no filter is applied). + max_records: The maximum number of records to use for training. Defaults to None. + responses_vs_suggestions: Whether to utilize Suggestions Metric (where the suggestions are the ground truths and the responses are compared against them) or Responses Metric (where the responses are the ground truths and the suggestions are compared against them). Defaults to True, i.e. Responses Metric. + """ + self._metrics_per_question = METRICS_PER_QUESTION + super().__init__(dataset, question_name, responses_vs_suggestions=responses_vs_suggestions) + self._filter_by = filter_by + self._sort_by = sort_by + self._max_records = max_records + + def _check_responses_and_suggestions( + self, responses_per_user: Dict[int, "Responses"], suggestions: "Suggestions" + ) -> Tuple[Dict[int, "Responses"], "Suggestions"]: + # Check for possible missing suggestions + df_suggestions = pd.Series(suggestions) + df_responses_per_user = pd.DataFrame(responses_per_user) + df_responses_per_user = df_responses_per_user[df_suggestions.notna()] + df_suggestions = df_suggestions[df_suggestions.notna()] + total_responses = len(suggestions) + + responses_per_user = df_responses_per_user.to_dict(orient="list") + suggestions = df_suggestions.to_list() + + if len(suggestions) == 0: + raise ValueError("All the suggestions are None, the metric cannot be computed.") + elif len(suggestions) < total_responses: + warnings.warn("Some suggestions are None, the metric will be computed without them.") + return responses_per_user, suggestions + + def compute( + self, metric_names: Union[str, List[str]], show_progress: bool = True + ) -> Dict[str, List[ModelMetricResult]]: + """Computes the annotator metrics for the given question. + + Args: + metric_names: name or list of names for the metrics to compute. i.e. `accuracy` + + Raises: + ValueError: If the metric name is not supported for the given question. + + Returns: + metrics: dict with the metrics computed for each annotator, where the + key corresponds to the user id and the values are a list with the + metric results. + """ + metric_names = self._check_metrics(metric_names) + metric_classes = self._get_metric_classes(metric_names) + + responses_and_suggestions_per_user = get_responses_and_suggestions_per_user( + self._dataset, + self._question_name, + filter_by=self._filter_by, + sort_by=self._sort_by, + max_records=self._max_records, + ) + metrics = defaultdict(list) + for user_id, resp_and_suggest in responses_and_suggestions_per_user.items(): + responses = resp_and_suggest["responses"] + suggestions = resp_and_suggest["suggestions"] + as_responses, as_suggestions = self._prepare_responses_and_suggestions(responses, suggestions) + for metric_name, metric_cls in metric_classes: + metric = metric_cls(responses=as_responses, suggestions=as_suggestions) + result = metric.compute() + metrics[user_id].append(ModelMetricResult(metric_name=metric_name, result=result, count=len(responses))) + + return dict(metrics) + + +class ModelMetric(AnnotatorMetric): + """Where suggestions are the ground truths and the responses are compared against them.""" + + def __init__( + self, + dataset: FeedbackDataset, + question_name: str, + filter_by: Optional[Dict[str, Union["ResponseStatusFilter", List["ResponseStatusFilter"]]]] = None, + sort_by: Optional[List["SortBy"]] = None, + max_records: Optional[int] = None, + ) -> None: + super().__init__( + dataset, + question_name, + filter_by=filter_by, + sort_by=sort_by, + max_records=max_records, + responses_vs_suggestions=True, + ) + + +class UnifiedAnnotatorMetric(AnnotatorMetric): + """Main class to compute metrics for a unified dataset. + + Example: + >>> import argilla as rg + >>> from argilla.client.feedback.metrics import UnifiedAnnotatorMetric + >>> metric = UnifiedAnnotatorMetric(dataset=dataset, question_name=question) + >>> metrics_report = metric.compute("accuracy") + """ + + def __init__( + self, + dataset: "FeedbackDataset", + question_name: str, + strategy_name: str = "majority", + filter_by: Optional[Dict[str, Union["ResponseStatusFilter", List["ResponseStatusFilter"]]]] = None, + sort_by: Optional[List["SortBy"]] = None, + max_records: Optional[int] = None, + responses_vs_suggestions: bool = True, + ) -> None: + self._metrics_per_question = METRICS_PER_QUESTION_UNIFIED + super().__init__(dataset, question_name, responses_vs_suggestions=responses_vs_suggestions) + self._filter_by = filter_by + self._sort_by = sort_by + self._max_records = max_records + self._strategy_name = strategy_name + + def _check_responses_and_suggestions( + self, unified_responses: "Responses", suggestions: "Suggestions" + ) -> Tuple["Responses", "Suggestions"]: + # Check for possible missing suggestions + df_suggestions = pd.Series(suggestions) + df_responses = pd.Series(unified_responses) + df_responses = df_responses[df_suggestions.notna()] + df_suggestions = df_suggestions[df_suggestions.notna()] + total_responses = len(suggestions) + + unified_responses = df_responses.to_list() + suggestions = df_suggestions.to_list() + + if len(suggestions) == 0: + raise ValueError("All the suggestions are None, the metric cannot be computed.") + elif len(suggestions) < total_responses: + warnings.warn("Some suggestions are None, the metric will be computed without them.") + return unified_responses, suggestions + + def compute(self, metric_names: Union[str, List[str]]) -> Union[ModelMetricResult, List[ModelMetricResult]]: + """Computes the unified annotation metrics for the given question. + + Args: + metric_names: name or list of names for the metrics to compute. i.e. `accuracy` + kwargs: additional arguments to pass to the metric. + + Raises: + ValueError: If the metric name is not supported for the given question. + + Returns: + metrics: List of annotator metrics results if more than one metric is computed, or the result + container if only one metric is computed. + """ + metric_names = self._check_metrics(metric_names) + metric_classes = self._get_metric_classes(metric_names) + + unified_responses, suggestions = get_unified_responses_and_suggestions( + self._dataset, + self._question_name, + strategy_name=self._strategy_name, + filter_by=self._filter_by, + sort_by=self._sort_by, + max_records=self._max_records, + ) + self._check_responses_and_suggestions(unified_responses, suggestions) + + as_unified_responses, as_suggestions = self._prepare_responses_and_suggestions(unified_responses, suggestions) + metrics = [] + for metric_name, metric_cls in metric_classes: + metric = metric_cls(responses=as_unified_responses, suggestions=as_suggestions) + result = metric.compute() + metrics.append(ModelMetricResult(metric_name=metric_name, result=result, count=len(unified_responses))) + + if len(metric_names) == 1: + return metrics[0] + + return metrics + + +class UnifiedModelMetric(UnifiedAnnotatorMetric): + """""" + + def __init__( + self, + dataset: "FeedbackDataset", + question_name: str, + filter_by: Optional[Dict[str, Union["ResponseStatusFilter", List["ResponseStatusFilter"]]]] = None, + sort_by: Optional[List["SortBy"]] = None, + max_records: Optional[int] = None, + ) -> None: + super().__init__( + dataset, + question_name, + filter_by=filter_by, + sort_by=sort_by, + max_records=max_records, + responses_vs_suggestions=True, + ) + + +class AccuracyMetric(AnnotatorMetricBase): + """Accuracy score. + + Which proportion of the responses are equal to the suggestions offered. + + We use the implementation in: + https://scikit-learn.org/stable/modules/generated/sklearn.metrics.accuracy_score.html#sklearn.metrics.accuracy_score + + In multilabel classification, this function computes subset accuracy: the set of labels predicted for a + sample must exactly match the corresponding set of labels in y_true + """ + + @requires_dependencies("scikit-learn") + def _compute(self, responses, suggestions): + from sklearn.metrics import accuracy_score + + return accuracy_score(y_true=responses, y_pred=suggestions) + + +class PrecisionMetric(AnnotatorMetricBase): + """Compute the precision: tp / (tp + fp) + + We use the implementation in: + https://scikit-learn.org/stable/modules/generated/sklearn.metrics.precision_score.html#sklearn.metrics.precision_score + + In case of multiclass data, calculate metrics for each label, and find their unweighted mean. + This does not take label imbalance into account. + """ + + @requires_dependencies("scikit-learn") + def _compute(self, responses, suggestions): + from sklearn.metrics import precision_score + + if is_multiclass(responses) or is_multiclass(suggestions): + kwargs = {"average": "macro"} + else: + kwargs = {"average": "binary", "pos_label": random.choice(np.unique(responses))} + return precision_score(y_true=responses, y_pred=suggestions, **kwargs) + + +class RecallMetric(AnnotatorMetricBase): + """Compute the recall: tp / (tp + fn) + + We use the implementation in: + https://scikit-learn.org/stable/modules/generated/sklearn.metrics.recall_score.html#sklearn.metrics.recall_score + + In case of multiclass data, calculate metrics for each label, and find their unweighted mean. + This does not take label imbalance into account. + """ + + @requires_dependencies("scikit-learn") + def _compute(self, responses, suggestions): + from sklearn.metrics import recall_score + + if is_multiclass(responses) or is_multiclass(suggestions): + kwargs = {"average": "macro"} + else: + kwargs = {"average": "binary", "pos_label": random.choice(np.unique(responses))} + return recall_score(y_true=responses, y_pred=suggestions, **kwargs) + + +class F1ScoreMetric(AnnotatorMetricBase): + """F1 score: 2 * (precision * recall) / (precision + recall) + + We use the implementation in: + https://scikit-learn.org/stable/modules/generated/sklearn.metrics.f1_score.html#sklearn.metrics.f1_score + + In case of multiclass data, calculate metrics for each label, and find their unweighted mean. + This does not take label imbalance into account. + """ + + @requires_dependencies("scikit-learn") + def _compute(self, responses, suggestions): + from sklearn.metrics import f1_score + + if is_multiclass(responses) or is_multiclass(suggestions): + kwargs = {"average": "macro"} + else: + kwargs = {"average": "binary", "pos_label": random.choice(np.unique(responses))} + + return f1_score(responses, suggestions, **kwargs) + + +class MultiLabelMetrics(AnnotatorMetricBase): + """Parent class for MultiLabel based metrics. It binarizes the data to compute the metrics.""" + + @requires_dependencies("scikit-learn") + def _pre_process(self, responses, suggestions) -> Any: + from sklearn.preprocessing import MultiLabelBinarizer + + classes = sorted(set(responses).union(set(suggestions))) + # Keep the binarizer to access the classes later + self._mlb = MultiLabelBinarizer() + self._mlb.fit(classes) + responses = self._mlb.transform(responses) + suggestions = self._mlb.transform(suggestions) + return responses, suggestions + + def _compute(self, responses, suggestions): + # Child classes are in charge of the implementation + pass + + +class MultiLabelAccuracyMetric(MultiLabelMetrics): + """Computes the accuracy on the binarized data for multilabel classification. + + See Also: + https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.MultiLabelBinarizer.html + `AccuracyMetric` + """ + + @requires_dependencies("scikit-learn") + def _compute(self, responses, suggestions): + from sklearn.metrics import accuracy_score + + return accuracy_score(y_true=responses, y_pred=suggestions) + + +class MultiLabelPrecisionMetric(MultiLabelMetrics): + """Computes the precision on the binarized data for multilabel classification. + + See Also: + https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.MultiLabelBinarizer.html + `PrecisionMetric` + """ + + @requires_dependencies("scikit-learn") + def _compute(self, responses, suggestions): + from sklearn.metrics import precision_score + + return precision_score(y_true=responses, y_pred=suggestions, average="macro") + + +class MultiLabelRecallMetric(MultiLabelMetrics): + """Computes the recall on the binarized data for multilabel classification. + + See Also: + https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.MultiLabelBinarizer.html + `RecallMetric` + """ + + @requires_dependencies("scikit-learn") + def _compute(self, responses, suggestions): + from sklearn.metrics import recall_score + + return recall_score(y_true=responses, y_pred=suggestions, average="macro") + + +class MultiLabelF1ScoreMetric(MultiLabelMetrics): + """Computes the f1-score on the binarized data for multilabel classification. + + See Also: + https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.MultiLabelBinarizer.html + `F1ScoreMetric` + """ + + @requires_dependencies("scikit-learn") + def _compute(self, responses, suggestions): + from sklearn.metrics import f1_score + + return f1_score(y_true=responses, y_pred=suggestions, average="macro") + + +class ConfusionMatrixMetric(AnnotatorMetricBase): + """Compute confusion matrix to evaluate the accuracy of an annotator. + + In case of multiclass classification, this function returns a confusion matrix class-wise. + """ + + @requires_dependencies("scikit-learn") + def _compute(self, responses, suggestions): + import pandas as pd + from sklearn.metrics import confusion_matrix + + unique_responses = sorted(np.unique(responses)) + unique_suggestions = sorted(np.unique(suggestions)) + labels = sorted(set(unique_responses).union(set(unique_suggestions))) + labels_index = [f"responses_{label}" for label in labels] + labels_columns = [f"suggestions_{label}" for label in labels] + result = confusion_matrix(y_true=responses, y_pred=suggestions, labels=labels) + return pd.DataFrame(result, index=labels_index, columns=labels_columns) + + +class MultiLabelConfusionMatrixMetric(MultiLabelMetrics): + """Compute confusion matrix to evaluate the accuracy of an annotator. + + The data is binarized, so we will return a dict with the confusion matrix for each class. + """ + + @requires_dependencies("scikit-learn") + def _compute(self, responses, suggestions): + import pandas as pd + from sklearn.metrics import multilabel_confusion_matrix + + unique_responses = sorted(np.unique(responses)) + unique_suggestions = sorted(np.unique(suggestions)) + labels = sorted(set(unique_responses).union(set(unique_suggestions))) + matrices = multilabel_confusion_matrix(y_true=responses, y_pred=suggestions, labels=labels) + report = {} + for class_, matrix in zip(self._mlb.classes_, matrices): + labels_index = [f"responses_{class_}_{i}" for i in ["true", "false"]] + labels_columns = [f"suggestions_{class_}_{i}" for i in ["true", "false"]] + report[class_] = pd.DataFrame(matrix, index=labels_index, columns=labels_columns) + return report + + +class PearsonCorrelationCoefficientMetric(AnnotatorMetricBase): + def _pre_process(self, responses, suggestions) -> Tuple[List[int], List[int]]: + return map_str_to_int(responses), map_str_to_int(suggestions) + + @requires_dependencies("scipy") + def _compute(self, responses, suggestions): + import scipy.stats as stats + + return stats.pearsonr(x=suggestions, y=responses)[0] + + +class SpearmanCorrelationCoefficientMetric(AnnotatorMetricBase): + @requires_dependencies("scipy") + def _compute(self, responses, suggestions): + import scipy.stats as stats + + return stats.spearmanr(a=suggestions, b=responses)[0] + + +class GLEUMetric(AnnotatorMetricBase): + """ + Improvement of BLEU that takes into account the length of the response. + + BLEU (Bilingual Evaluation Understudy) is an algorithm for evaluating the quality of text + which has been machine-translated from one natural language to another. + The Google-BLEU is an improvement of BLEU that adresses some undesirable properties found on + single sentences. + + https://huggingface.co/spaces/evaluate-metric/bleu + https://huggingface.co/spaces/evaluate-metric/google_bleu + """ + + def _pre_process(self, responses, suggestions) -> Any: + return responses, [[suggestion] for suggestion in suggestions] + + @requires_dependencies("evaluate") + def _compute(self, responses: List[str], suggestions: List[str]): + import evaluate + + gleu = evaluate.load("google_bleu") + return gleu.compute(predictions=responses, references=suggestions)["google_bleu"] # test is symmetrical + + +class ROUGEMetric(AnnotatorMetricBase): + """ + From the evaluate library: + + ROUGE, or Recall-Oriented Understudy for Gisting Evaluation, is a set of metrics and a software package + used for evaluating automatic summarization and machine translation software in natural language processing. + The metrics compare an automatically produced summary or translation against a reference or a set of references + (human-produced) summary or translation. + Note that ROUGE is case insensitive, meaning that upper case letters are treated the same way as lower case letters. + + https://huggingface.co/spaces/evaluate-metric/rouge + """ + + @requires_dependencies("evaluate") + def _compute(self, responses: List[str], suggestions: List[str]): + import evaluate + + rouge = evaluate.load("rouge") + return rouge.compute(predictions=responses, references=suggestions) # test is symmetrical + + +class NDCGMetric(AnnotatorMetricBase): + """Compute Normalized Discounted Cumulative Gain. + + From the Wikipedia page for Discounted Cumulative Gain: + + “Discounted cumulative gain (DCG) is a measure of ranking quality. In information retrieval, + it is often used to measure effectiveness of web search engine algorithms or related applications. + Using a graded relevance scale of documents in a search-engine result set, DCG measures the usefulness, + or gain, of a document based on its position in the result list. The gain is accumulated from the + top of the result list to the bottom, with the gain of each result discounted at lower ranks” + + See Also: + https://scikit-learn.org/stable/modules/generated/sklearn.metrics.ndcg_score.html + https://en.wikipedia.org/wiki/Discounted_cumulative_gain + """ + + @requires_dependencies("scikit-learn") + def _compute(self, responses: List[str], suggestions: List[str]): + from sklearn.metrics import ndcg_score + + return ndcg_score(y_true=responses, y_score=suggestions) + + +METRICS_PER_QUESTION = { + LabelQuestion: { + "accuracy": AccuracyMetric, + "f1-score": F1ScoreMetric, + "precision": PrecisionMetric, + "recall": RecallMetric, + "confusion-matrix": ConfusionMatrixMetric, + "pearson-r": PearsonCorrelationCoefficientMetric, + }, + MultiLabelQuestion: { + "accuracy": MultiLabelAccuracyMetric, + "f1-score": MultiLabelF1ScoreMetric, + "precision": MultiLabelPrecisionMetric, + "recall": MultiLabelRecallMetric, + "confusion-matrix": MultiLabelConfusionMatrixMetric, + }, + RatingQuestion: { + "accuracy": AccuracyMetric, + "f1-score": F1ScoreMetric, + "precision": PrecisionMetric, + "recall": RecallMetric, + "confusion-matrix": ConfusionMatrixMetric, + "spearman-r": SpearmanCorrelationCoefficientMetric, + }, + TextQuestion: { + "gleu": GLEUMetric, + "rouge": ROUGEMetric, + }, + RankingQuestion: { + "ndcg-score": NDCGMetric, + }, +} + + +METRICS_PER_QUESTION_UNIFIED = { + LabelQuestion: METRICS_PER_QUESTION[LabelQuestion], + MultiLabelQuestion: METRICS_PER_QUESTION[MultiLabelQuestion], + RatingQuestion: METRICS_PER_QUESTION[RatingQuestion], + RankingQuestion: METRICS_PER_QUESTION[RankingQuestion], +} diff --git a/src/argilla/client/feedback/metrics/base.py b/src/argilla/client/feedback/metrics/base.py new file mode 100644 index 0000000000..fe04ecbf1d --- /dev/null +++ b/src/argilla/client/feedback/metrics/base.py @@ -0,0 +1,221 @@ +# Copyright 2021-present, the Recognai S.L. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING, Any, Callable, Dict, Hashable, List, Tuple, Union + +import pandas as pd +from pydantic import BaseModel + +from argilla.client.feedback.schemas.remote.shared import RemoteSchema + +if TYPE_CHECKING: + from argilla.client.feedback.dataset import FeedbackDataset + + +# Type aliases +Responses = List[Union[float, int, str]] +Suggestions = Responses +# Expected format for the nltk's AnnotationTask +FormattedResponses = List[Tuple[Any, Hashable, Hashable]] + + +class MetricResultBase(BaseModel): + """Base class for the result of a metric.""" + + metric_name: str + count: int + + +class AgreementMetricResult(MetricResultBase): + """Container for the result of an agreement metric. + + It contains two fields, `metric_name` and `result` with the value of the metric. + """ + + result: float + + +class ModelMetricResult(MetricResultBase): + """Container for the result of an annotator metric. + + It contains two fields, `metric_name` and `result` with the value of the metric. + """ + + result: Union[float, Dict[str, float], pd.DataFrame, Dict[str, pd.DataFrame]] + + class Config: + arbitrary_types_allowed = True + + +class AnnotatorMetricBase(ABC): + """Base class for Annotator metrics.""" + + def __init__(self, responses: Responses = None, suggestions: Suggestions = None) -> None: + """ + Args: + responses: Responses given by the user. + Depending on the type of question it can be a list of strings, or integers. + suggestions: Suggestions offered for the annotators. + Same format as `responses`. + """ + self._responses = responses + self._suggestions = suggestions + + def compute(self, **kwargs): + responses, suggestions = self._pre_process(self._responses, self._suggestions) + return self._compute(responses, suggestions, **kwargs) + + def _pre_process(self, responses: Responses, suggestions: Suggestions) -> Any: + """Optional data preprocessing. By default it just passes the data to the _compute method. + + Args: + responses: Responses given by the user. + suggestions: Suggestions offered for the annotators. + + Returns: + data: tuple with the preprocessed data. + """ + return responses, suggestions + + @abstractmethod + def _compute(self, responses: Responses, suggestions: Suggestions, **kwargs): + """Abstract method where the computation is done. + + Args: + responses: Responses given by the user, as expected by the given metric. + suggestions: Suggestions offered for the annotators, as expected by the given metric. + """ + pass + + +class AnnotationTaskMetricBase(ABC): + """Base class for Agreement metrics.""" + + def __init__(self, annotated_dataset: FormattedResponses = None, distance_function: Callable = lambda x: x) -> None: + """ + Args: + annotated_dataset: Annotated dataset as expected by the metric. + distance_function: Distance function to use for the metric. + Depending on the type of data we need a function to compute the distance. + For example for binary data we can use the binary distance function, + while RatingQuestion works with an interval distance as we are dealing with + numeric values. + """ + self._dataset = annotated_dataset + self._distance_function = distance_function + + def compute(self) -> float: + """General method to obtain the metric. + + Args: + kwargs: Optional arguments that could be passed to the metric. + + Returns: + metric: Metric result that will be stored in the `AgreementMetricResult`. + """ + data = self._pre_process(self._dataset) + return self._compute(data) + + def _pre_process(self, data: FormattedResponses) -> Any: + """Optional data preprocessing. By default it just passes the data to the _compute method. + + Args: + data: annotated dataset. + kwargs: optional arguments to be passed to the metric. + + Returns: + data: dataset prepared for the _compute method. + """ + return data + + @abstractmethod + def _compute(self, data: FormattedResponses): + """Abstract method where the computation is done. + + Args: + data: Data as expected for the given metric. + """ + pass + + +class MetricBase: + _metrics_per_question: Dict[str, Callable] = {} + + def __init__(self, dataset: "FeedbackDataset", question_name: str, responses_vs_suggestions: bool = True) -> None: + """Initializes a `AgreementMetric` object to compute agreement metrics on + a `FeedbackDataset` for a given question. + + Args: + dataset: FeedbackDataset to compute the metrics. + question_name: Name of the question for which we want to analyse the agreement. + responses_vs_suggestions: Whether to compare the responses vs the suggestions, or the + other way around. Defaults to True (the metrics will be compared assuming the + responses are the ground truth and the suggestions are the predictions). + + Raises: + NotImplementedError: If the question type is not supported. + """ + self._dataset = dataset + self._question_name = question_name + self._question_type = type(self._dataset.question_by_name(question_name)) + + # Check to assume the remote questions behave just like local ones + if issubclass(self._question_type, RemoteSchema): + self._question_type = type(self._dataset.question_by_name(question_name).to_local()) + + if allowed_metrics := self._metrics_per_question.get(self._question_type): + self._allowed_metrics = allowed_metrics + else: + raise NotImplementedError(f"No metrics are defined currently for {self._question_type.__name__}") + self._responses_vs_suggestions = responses_vs_suggestions + + def __repr__(self) -> str: + return type(self).__name__ + f"(question_name={self._question_name})" + + @property + def allowed_metrics(self) -> List[str]: + """Available metrics for the given question.""" + return list(self._allowed_metrics) + + def _check_metrics(self, metric_names: Union[str, List[str]]) -> List[str]: + if isinstance(metric_names, str): + metric_names = [metric_names] + + if any([metric not in self._allowed_metrics for metric in metric_names]): + raise ValueError( + f"Metrics allowed for question {self._question_name}: {list(self._allowed_metrics.keys())}" + ) + return metric_names + + def _get_metric_classes(self, metric_names: Union[str, List[str]]) -> List[Tuple[str, Callable]]: + return [(metric_name, self._allowed_metrics[metric_name]) for metric_name in metric_names] + + def _prepare_responses_and_suggestions( + self, responses: Responses, suggestions: Responses + ) -> Union[Tuple[Responses, Suggestions], Tuple[Suggestions, Responses]]: + """Helper function to determine the order in which the responses and suggestions should be passed to the metric, + to avoid duplicating code in the metrics. + + Args: + responses: Responses + suggestions: Responses + + Returns: + Union[Tuple[Responses, Suggestions], Tuple[Suggestions, Responses]] + """ + if self._responses_vs_suggestions: + return responses, suggestions + else: + return suggestions, responses diff --git a/src/argilla/client/feedback/metrics/utils.py b/src/argilla/client/feedback/metrics/utils.py new file mode 100644 index 0000000000..99eb9e880b --- /dev/null +++ b/src/argilla/client/feedback/metrics/utils.py @@ -0,0 +1,215 @@ +# Copyright 2021-present, the Recognai S.L. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections import defaultdict +from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Union + +import numpy as np +from tqdm import tqdm + +from argilla.client.feedback.dataset.remote.dataset import RemoteFeedbackDataset +from argilla.client.feedback.schemas import RankingQuestion, TextQuestion +from argilla.client.feedback.schemas.enums import ResponseStatusFilter +from argilla.client.feedback.schemas.remote.questions import RemoteRankingQuestion + +if TYPE_CHECKING: + from argilla.client.feedback.dataset import FeedbackDataset + from argilla.client.feedback.metrics.base import Responses, Suggestions + from argilla.client.feedback.schemas.enums import ResponseStatusFilter + from argilla.client.feedback.schemas.records import SortBy + + +def get_responses_and_suggestions_per_user( + dataset: Union["FeedbackDataset", "RemoteFeedbackDataset"], + question_name: str, + filter_by: Optional[Dict[str, Union["ResponseStatusFilter", List["ResponseStatusFilter"]]]] = None, + sort_by: Optional[List["SortBy"]] = None, + max_records: Optional[int] = None, + show_progress: bool = True, +) -> Tuple[Dict[int, "Responses"], "Suggestions"]: + """Extract the responses per user and the suggestions from a FeedbackDataset. + + Helper function for the metrics module where we want to compare the responses + in relation to the suggestions offered. + + Args: + dataset: FeedbackDataset or RemoteFeedbackDataset. + question_name: The name of the question to filter from the dataset. + filter_by: A dict with key the field to filter by, and values the filters to apply. + Can be one of: draft, pending, submitted, and discarded. If set to None, + no filter will be applied. Defaults to None (no filter is applied). + sort_by: A list of `SortBy` objects to sort your dataset by. + Defaults to None (no filter is applied). + max_records: The maximum number of records to use for training. Defaults to None. + + Raises: + NotImplementedError: + When no user_id is given. We need that information to compute the metrics. + + Returns: + Tuple containing the responses per user as a dict, with keys the user id and values the responses, + and the suggestions. + """ + if filter_by: + dataset = dataset.filter_by(**filter_by) + if sort_by: + dataset = dataset.sort_by(sort_by) + if max_records: + dataset = dataset.pull(max_records=max_records) + + hf_dataset = dataset.format_as("datasets") + question_type = type(dataset.question_by_name(question_name)) + is_ranking_question = (question_type == RankingQuestion) or (question_type == RemoteRankingQuestion) + + responses_and_suggestions_per_user = defaultdict(lambda: defaultdict(list)) + + for responses_, suggestion in tqdm( + zip(hf_dataset[question_name], hf_dataset[f"{question_name}-suggestion"]), + desc="Extracting responses and suggestions per user", + total=len(hf_dataset), + disable=not show_progress, + ): + if is_ranking_question: + suggestion = suggestion["rank"] + + if isinstance(suggestion, list): + # To make it hashable + suggestion = tuple(suggestion) + + for response in responses_: + user_id = response["user_id"] + if user_id is None: + raise NotImplementedError( + "In order to use this functionality the records need to be assigned to a user." + ) + + if is_ranking_question: + value = response["value"]["rank"] + else: + value = response["value"] + + if value is None: + continue + # To avoid errors with the MASI distance function + if isinstance(value, list): + if len(value) == 0: + continue + + if isinstance(value, list): + # To make it hashable + value = tuple(value) + + responses_and_suggestions_per_user[user_id]["responses"].append(value) + responses_and_suggestions_per_user[user_id]["suggestions"].append(suggestion) + + return responses_and_suggestions_per_user + + +def get_unified_responses_and_suggestions( + dataset: Union["FeedbackDataset", "RemoteFeedbackDataset"], + question_name: str, + strategy_name: str = "majority", + filter_by: Optional[Dict[str, Union["ResponseStatusFilter", List["ResponseStatusFilter"]]]] = None, + sort_by: Optional[List["SortBy"]] = None, + max_records: Optional[int] = None, +) -> Tuple["Responses", "Suggestions"]: + """Extract the unified responses and the suggestions from a FeedbackDataset. + + Helper function for the metrics module where we want to compare the responses + in relation to the suggestions offered. + + Args: + dataset: FeedbackDataset or RemoteFeedbackDataset. + question_name: The name of the question to filter from the dataset. + strategy_name: The name of the strategy to use to unify the responses. + filter_by: A dict with key the field to filter by, and values the filters to apply. + Can be one of: draft, pending, submitted, and discarded. If set to None, + no filter will be applied. Defaults to None (no filter is applied). + sort_by: A list of `SortBy` objects to sort your dataset by. + Defaults to None (no filter is applied). + max_records: The maximum number of records to use for training. Defaults to None. + + Raises: + NotImplementedError: + When asked for a TextQuestion. + ValueError: + If the dataset hasn't been unified yet. + + Returns: + Tuple containing the unified responses and the suggestions. + """ + if filter_by: + dataset = dataset.filter_by(**filter_by) + if sort_by: + dataset = dataset.sort_by(sort_by) + if max_records: + dataset = dataset.pull(max_records=max_records) + + question_type = type(dataset.question_by_name(question_name)) + if question_type == TextQuestion: + raise NotImplementedError("This function is not available for `TextQuestion`.") + + if isinstance(dataset, RemoteFeedbackDataset): + dataset = dataset.pull() + + dataset.compute_unified_responses(question_name, strategy=strategy_name) + + unified_responses = [] + suggestions = [] + + if not dataset.records[0].unified_responses: + raise ValueError("Please unify the responses first: `dataset.compute_unified_responses(question, strategy)`.") + + # Get the position of the suggestion from the first record to avoid the nested loop, + # and the type of the value to cast the unified responses. + for idx_suggestion, suggestion in enumerate(dataset.records[0].suggestions): + if suggestion.question_name == question_name: + value_type = type(suggestion.value) + break + + if value_type == list: + # To make MultiLabelQuestion hashable + value_type = tuple + + for record in dataset.records: + unified_response = value_type(record.unified_responses[question_name][0].value) + unified_responses.append(unified_response) + suggestions.append(value_type(record.suggestions[idx_suggestion].value)) + + if question_type == RankingQuestion: + unified_responses = [ + tuple(ranking_schema.rank for ranking_schema in response) for response in unified_responses + ] + suggestions = [tuple(s["rank"] for s in suggestion) for suggestion in suggestions] + + return unified_responses, suggestions + + +def map_str_to_int(values: List[str]) -> List[int]: + """Helper function to work with label questions as numerical values. + + Args: + values: responses or suggestions with the string labels. + + Returns: + values: corresponding values as integers to compute the metric + """ + unique_values = np.unique(values) + map_values = {value: i for i, value in enumerate(unique_values)} + return [map_values[value] for value in values] + + +def is_multiclass(data) -> bool: + """Helper function to check if a list of responses from LabelQuestion is also multiclass.""" + return len(np.unique(data)) > 2 diff --git a/src/argilla/client/feedback/training/schemas/base.py b/src/argilla/client/feedback/training/schemas/base.py index 50678f9263..6b96da9478 100644 --- a/src/argilla/client/feedback/training/schemas/base.py +++ b/src/argilla/client/feedback/training/schemas/base.py @@ -806,8 +806,8 @@ def _format_data(self, dataset: "FeedbackDataset") -> List[Dict[str, Any]]: else: return super()._format_data(dataset) - def unify_responses(self, responses: List[FeedbackRecord]): - self.defaults.label.strategy.unify_responses(responses=responses, field=self.defaults.label.question) + def compute_unified_responses(self, responses: List[FeedbackRecord]): + self.defaults.label.strategy.compute_unified_responses(responses=responses, field=self.defaults.label.question) @requires_dependencies("scikit-learn") def _train_test_split(self, data: List[dict], train_size: float, seed: int) -> Tuple[List[dict], List[dict]]: @@ -1460,8 +1460,8 @@ def _format_data(self, dataset: "FeedbackDataset") -> List[Dict[str, Any]]: return outputs - def unify_responses(self, responses: List[FeedbackRecord]): - self.label.strategy.unify_responses(responses=responses, field=self.label.question) + def compute_unified_responses(self, responses: List[FeedbackRecord]): + self.label.strategy.compute_unified_responses(responses=responses, field=self.label.question) @requires_dependencies("scikit-learn") def _train_test_split( diff --git a/src/argilla/client/feedback/unification.py b/src/argilla/client/feedback/unification.py index e04de558bb..03f9139ffb 100644 --- a/src/argilla/client/feedback/unification.py +++ b/src/argilla/client/feedback/unification.py @@ -51,11 +51,11 @@ class UnifiedValueSchema(ValueSchema): class RatingQuestionStrategyMixin: - def unify_responses( + def compute_unified_responses( self, records: List[FeedbackRecord], question: Union["RatingQuestionStrategy", "RankingQuestionStrategy"] ): """ - The function `unify_responses` takes a list of feedback records and a rating question, and + The function `compute_unified_responses` takes a list of feedback records and a rating question, and returns a unified value based on the specified unification method. Args: @@ -68,7 +68,7 @@ def unify_responses( actual question Returns: - The method `unify_responses` returns the result of either the `_majority` or + The method `compute_unified_responses` returns the result of either the `_majority` or `_aggregate` method, depending on the value of `self.value`. """ UnifiedValueSchema.update_forward_refs() @@ -100,8 +100,8 @@ class RatingQuestionStrategy(RatingQuestionStrategyMixin, Enum): MAX: str = "max" MIN: str = "min" - def unify_responses(self, records: List[FeedbackRecord], question: RatingQuestion): - return super().unify_responses(records, question) + def compute_unified_responses(self, records: List[FeedbackRecord], question: RatingQuestion): + return super().compute_unified_responses(records, question) def _aggregate(self, records: List[FeedbackRecord], question: str): """ @@ -178,7 +178,7 @@ class TextQuestionStrategy(Enum): DISAGREEMENT = "disagreement" - def unify_responses(self, records: List[FeedbackRecord], question: str): + def compute_unified_responses(self, records: List[FeedbackRecord], question: str): UnifiedValueSchema.update_forward_refs() unified_records = [] for rec in records: @@ -214,8 +214,8 @@ class RankingQuestionStrategy(RatingQuestionStrategyMixin, Enum): MAX: str = "max" MIN: str = "min" - def unify_responses(self, records: List[FeedbackRecord], question: RankingQuestion): - return super().unify_responses(records, question) + def compute_unified_responses(self, records: List[FeedbackRecord], question: RankingQuestion): + return super().compute_unified_responses(records, question) def _aggregate(self, records: List[FeedbackRecord], question: str): """ @@ -376,9 +376,11 @@ def _majority(self, records: List[FeedbackRecord], question: str): class LabelQuestionStrategyMixin: - def unify_responses(self, records: List[FeedbackRecord], question: Union[str, LabelQuestion, MultiLabelQuestion]): + def compute_unified_responses( + self, records: List[FeedbackRecord], question: Union[str, LabelQuestion, MultiLabelQuestion] + ): """ - The function `unify_responses` takes a list of feedback records and a question, and returns a + The function `compute_unified_responses` takes a list of feedback records and a question, and returns a unified value based on the specified unification method. Args: @@ -389,7 +391,7 @@ def unify_responses(self, records: List[FeedbackRecord], question: Union[str, La `MultiLabelQuestion` object. It represents the question for which you want to unify the responses. - Returns: The method `unify_responses` returns the result of one of the following methods: + Returns: The method `compute_unified_responses` returns the result of one of the following methods: `_majority`, `_majority_weighted`, or `_disagreement`. The specific method that is called depends on the value of `self.value`. """ @@ -460,7 +462,7 @@ class LabelQuestionStrategy(LabelQuestionStrategyMixin, Enum): Examples: >>> from argilla import LabelQuestion, LabelQuestionStrategy >>> strategy = LabelQuestionStrategy("majority") - >>> records = strategy.unify_responses(records, question=LabelQuestion(...)) + >>> records = strategy.compute_unified_responses(records, question=LabelQuestion(...)) """ MAJORITY: str = "majority" @@ -560,7 +562,7 @@ def _majority(self, records: List[FeedbackRecord], question: str): if not majority_value: majority_value = [random.choice(list(counter.keys()))] - rec._unified_responses[question] = [UnifiedValueSchema(value=majority_value, strategy=self.value)] + rec._unified_responses[question] = [UnifiedValueSchema(value=list(majority_value), strategy=self.value)] return records @classmethod @@ -644,18 +646,18 @@ class LabelQuestionUnification(BaseModel): question: Union[LabelQuestion, MultiLabelQuestion] strategy: Union[str, LabelQuestionStrategy, MultiLabelQuestionStrategy] = "majority" - def unify_responses(self, records: List[FeedbackRecord]): + def compute_unified_responses(self, records: List[FeedbackRecord]): """ - The function `unify_responses` takes a list of `FeedbackRecord` objects and returns the unified + The function `compute_unified_responses` takes a list of `FeedbackRecord` objects and returns the unified responses using a strategy and a specific question. Args: - records The "records" parameter is a list of FeedbackRecord objects. - Returns: The method `unify_responses` returns the result of calling the `unify_responses` method + Returns: The method `compute_unified_responses` returns the result of calling the `compute_unified_responses` method of the `strategy` object, passing in the `records` and `question` as arguments. """ - return self.strategy.unify_responses(records, self.question) + return self.strategy.compute_unified_responses(records, self.question) @root_validator def strategy_must_be_valid_and_align_with_question(cls, values: Dict[str, Any]) -> Dict[str, Any]: diff --git a/tests/integration/client/conftest.py b/tests/integration/client/conftest.py index fdc88b6201..aa0e2dd426 100644 --- a/tests/integration/client/conftest.py +++ b/tests/integration/client/conftest.py @@ -557,6 +557,140 @@ def feedback_dataset_records() -> List[FeedbackRecord]: ] +@pytest.fixture +def feedback_dataset_records_with_paired_suggestions() -> List[FeedbackRecord]: + # This fixture contains the same records as `feedback_dataset_records` but with suggestions + # for each question so that we can test the annotator metrics. + # Generates 4 records from 3 annotators. + + import random + import uuid + + q1_options = ["positive", "negative"] + q2_options = [1, 2] + q3_options = ["a", "b", "c"] + q4_options = [["a", "b"], ["b", "c"], ["a", "c"]] + q5_options = [ + [{"rank": 1, "value": "a"}, {"rank": 2, "value": "b"}], + [{"rank": 2, "value": "a"}, {"rank": 1, "value": "b"}], + [{"rank": 1, "value": "a"}, {"rank": 2, "value": "b"}], + ] + + records = [] + + for record_id in range(1, 5): + responses = [] + for annotator_id in range(1, 4): + # Make the random seed depend on the record_id and annotator_id for reproducibility. + random.seed(123 + record_id + annotator_id) + idx1 = random.randint(0, len(q1_options) - 1) + random.seed(123 + record_id + annotator_id + 1) + idx2 = random.randint(0, len(q2_options) - 1) + random.seed(123 + record_id + annotator_id + 2) + idx3 = random.randint(0, len(q3_options) - 1) + random.seed(123 + record_id + annotator_id + 3) + idx4 = random.randint(0, len(q4_options) - 1) + random.seed(123 + record_id + annotator_id + 4) + idx5 = random.randint(0, len(q5_options) - 1) + + response_q1 = q1_options[idx1] + response_q2 = q2_options[idx2] + response_q3 = q3_options[idx3] + response_q4 = q4_options[idx4] + response_q5 = q5_options[idx5] + + if annotator_id == 1: + # Always answer like the suggestion + suggestion_q1 = response_q1 + suggestion_q2 = response_q2 + suggestion_q3 = response_q3 + suggestion_q4 = response_q4 + suggestion_q5 = response_q5 + elif annotator_id == 2: + # Never answer like the suggestion + suggestion_q1 = q1_options[idx1 - 1] + suggestion_q2 = q2_options[idx2 - 1] + suggestion_q3 = q3_options[idx3 - 1] + suggestion_q4 = q4_options[idx4 - 1] + suggestion_q5 = q5_options[idx5 - 1] + elif annotator_id == 3: + # Sometimes answer like the suggestion + if record_id % 2 == 0: + suggestion_q1 = response_q1 + suggestion_q2 = response_q2 + suggestion_q3 = response_q3 + suggestion_q4 = response_q4 + suggestion_q5 = response_q5 + else: + suggestion_q1 = q1_options[idx1 - 1] + suggestion_q2 = q2_options[idx2 - 1] + suggestion_q3 = q3_options[idx3 - 1] + suggestion_q4 = q4_options[idx4 - 1] + suggestion_q5 = q5_options[idx5 - 1] + + responses.append( + { + "values": { + "question-1": {"value": f"{response_q1} example"}, + "question-2": {"value": response_q2}, + "question-3": {"value": response_q3}, + "question-4": {"value": response_q4}, + "question-5": {"value": response_q5}, + }, + "status": "submitted", + "user_id": uuid.UUID(int=annotator_id), + }, + ) + + records.append( + FeedbackRecord( + fields={"text": f"This is a {response_q1} example", "label": f"{response_q1}"}, + responses=responses, + suggestions=[ + { + "question_name": "question-1", + "value": suggestion_q1, + "type": "human", + "score": 0.0, + "agent": f"agent-{annotator_id}", + }, + { + "question_name": "question-2", + "value": suggestion_q2, + "type": "human", + "score": 0.0, + "agent": f"agent-{annotator_id}", + }, + { + "question_name": "question-3", + "value": suggestion_q3, + "type": "human", + "score": 0.0, + "agent": f"agent-{annotator_id}", + }, + { + "question_name": "question-4", + "value": suggestion_q4, + "type": "human", + "score": 0.0, + "agent": f"agent-{annotator_id}", + }, + { + "question_name": "question-5", + "value": suggestion_q5, + "type": "human", + "score": 0.0, + "agent": f"agent-{annotator_id}", + }, + ], + metadata={"unit": "test"}, + external_id=str(annotator_id + record_id), + ) + ) + + return records + + @pytest.fixture def feedback_dataset_records_with_metadata() -> List[FeedbackRecord]: records = [] diff --git a/tests/integration/client/feedback/dataset/remote/test_dataset.py b/tests/integration/client/feedback/dataset/remote/test_dataset.py index de44e75988..70825ff52e 100644 --- a/tests/integration/client/feedback/dataset/remote/test_dataset.py +++ b/tests/integration/client/feedback/dataset/remote/test_dataset.py @@ -827,7 +827,7 @@ async def test_warning_local_methods(self, role: UserRole) -> None: UserWarning, match="A local `FeedbackDataset` returned because `unify_responses` is not supported for `RemoteFeedbackDataset`. ", ): - ds.unify_responses(question=None, strategy=None) + ds.compute_unified_responses(question=None, strategy=None) with pytest.raises(ValueError, match="`FeedbackRecord.fields` does not match the expected schema"): with pytest.warns( diff --git a/tests/integration/client/feedback/metrics/__init__.py b/tests/integration/client/feedback/metrics/__init__.py new file mode 100644 index 0000000000..55be41799b --- /dev/null +++ b/tests/integration/client/feedback/metrics/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2021-present, the Recognai S.L. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/tests/integration/client/feedback/metrics/test_agreement_metrics.py b/tests/integration/client/feedback/metrics/test_agreement_metrics.py new file mode 100644 index 0000000000..d2cef9f226 --- /dev/null +++ b/tests/integration/client/feedback/metrics/test_agreement_metrics.py @@ -0,0 +1,348 @@ +# Copyright 2021-present, the Recognai S.L. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import uuid +from typing import TYPE_CHECKING, FrozenSet, List, Tuple, Union + +import pytest +from argilla import User, init +from argilla.client.feedback.dataset import FeedbackDataset +from argilla.client.feedback.metrics.agreement_metrics import ( + AgreementMetric, + AgreementMetricResult, + prepare_dataset_for_annotation_task, +) +from argilla.client.feedback.schemas import FeedbackRecord + +from tests.factories import UserFactory, WorkspaceFactory + +if TYPE_CHECKING: + from argilla.client.feedback.schemas.types import AllowedFieldTypes, AllowedQuestionTypes + + +@pytest.mark.parametrize( + "question, metric_names", + [ + # RatingQuestion + ("question-2", {"alpha"}), + # LabelQuestion + ("question-3", {"alpha"}), + # MultiLabelQuestion + ("question-4", {"alpha"}), + # RankingQuestion + ("question-5", {"alpha"}), + ], +) +@pytest.mark.usefixtures( + "feedback_dataset_guidelines", + "feedback_dataset_fields", + "feedback_dataset_questions", + "feedback_dataset_records_with_paired_suggestions", +) +def test_allowed_metrics( + feedback_dataset_guidelines: str, + feedback_dataset_fields: List["AllowedFieldTypes"], + feedback_dataset_questions: List["AllowedQuestionTypes"], + feedback_dataset_records_with_paired_suggestions: List[FeedbackRecord], + question: str, + metric_names: Union[str, List[str]], +): + dataset = FeedbackDataset( + guidelines=feedback_dataset_guidelines, + fields=feedback_dataset_fields, + questions=feedback_dataset_questions, + ) + dataset.add_records(records=feedback_dataset_records_with_paired_suggestions) + + metric = AgreementMetric(dataset=dataset, question_name=question) + assert set(metric.allowed_metrics) == metric_names + + +@pytest.mark.parametrize( + "question, num_items, type_of_data", + [ + ("question-1", None, None), + ("question-2", 12, int), + ("question-3", 12, str), + ("question-4", 12, frozenset), + ("question-5", 12, tuple), + ], +) +@pytest.mark.usefixtures( + "feedback_dataset_guidelines", + "feedback_dataset_fields", + "feedback_dataset_questions", + "feedback_dataset_records_with_paired_suggestions", +) +def test_prepare_dataset_for_annotation_task( + feedback_dataset_guidelines: str, + feedback_dataset_fields: List["AllowedFieldTypes"], + feedback_dataset_questions: List["AllowedQuestionTypes"], + feedback_dataset_records_with_paired_suggestions: List[FeedbackRecord], + question: str, + num_items: int, + type_of_data: Union[str, int, FrozenSet, Tuple[str]], +): + dataset = FeedbackDataset( + guidelines=feedback_dataset_guidelines, + fields=feedback_dataset_fields, + questions=feedback_dataset_questions, + ) + dataset.add_records(records=feedback_dataset_records_with_paired_suggestions) + + if question in ("question-1",): + with pytest.raises(NotImplementedError, match=r"^Question '"): + prepare_dataset_for_annotation_task(dataset, question) + else: + formatted_dataset = prepare_dataset_for_annotation_task(dataset, question) + assert isinstance(formatted_dataset, list) + assert len(formatted_dataset) == num_items + item = formatted_dataset[0] + assert isinstance(item, tuple) + assert isinstance(item[0], str) + assert item[0].startswith("00000000-") # beginning of our uuid for tests + assert isinstance(item[1], str) + assert item[1] == feedback_dataset_records_with_paired_suggestions[0].fields["text"] + assert isinstance(item[2], type_of_data) + + +@pytest.mark.parametrize( + "question, metric_names", + [ + # TextQuestion + ("question-1", None), + # RatingQuestion + ("question-2", "alpha"), + ("question-2", ["alpha"]), + # LabelQuestion + ("question-3", "alpha"), + # MultiLabelQuestion + ("question-4", "alpha"), + # RankingQuestion + ("question-5", "alpha"), + ], +) +@pytest.mark.usefixtures( + "feedback_dataset_guidelines", + "feedback_dataset_fields", + "feedback_dataset_questions", + "feedback_dataset_records_with_paired_suggestions", +) +def test_agreement_metrics( + feedback_dataset_guidelines: str, + feedback_dataset_fields: List["AllowedFieldTypes"], + feedback_dataset_questions: List["AllowedQuestionTypes"], + feedback_dataset_records_with_paired_suggestions: List[FeedbackRecord], + question: str, + metric_names: Union[str, List[str]], +): + dataset = FeedbackDataset( + guidelines=feedback_dataset_guidelines, + fields=feedback_dataset_fields, + questions=feedback_dataset_questions, + ) + dataset.add_records(records=feedback_dataset_records_with_paired_suggestions) + + if question in ("question-1",): + with pytest.raises(NotImplementedError, match=r"^No metrics are defined currently for"): + AgreementMetric(dataset=dataset, question_name=question) + else: + metric = AgreementMetric(dataset=dataset, question_name=question) + # Test for repr method + assert repr(metric) == f"AgreementMetric(question_name={question})" + metrics_report = metric.compute(metric_names) + if isinstance(metric_names, str): + metrics_report = [metrics_report] + elif isinstance(metric_names, list): + if len(metric_names) == 1: + metrics_report = [metrics_report] + assert isinstance(metrics_report, list) + assert all([isinstance(m, AgreementMetricResult) for m in metrics_report]) + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "question, metric_names", + [ + # TextQuestion + ("question-1", None), + # RatingQuestion + ("question-2", "alpha"), + ("question-2", ["alpha"]), + # LabelQuestion + ("question-3", "alpha"), + # MultiLabelQuestion + ("question-4", "alpha"), + # RankingQuestion + ("question-5", "alpha"), + ], +) +@pytest.mark.usefixtures( + "feedback_dataset_guidelines", + "feedback_dataset_fields", + "feedback_dataset_questions", + "feedback_dataset_records_with_paired_suggestions", +) +async def test_agreement_metrics_remote( + feedback_dataset_guidelines: str, + feedback_dataset_fields: List["AllowedFieldTypes"], + feedback_dataset_questions: List["AllowedQuestionTypes"], + feedback_dataset_records_with_paired_suggestions: List[FeedbackRecord], + question: str, + metric_names: Union[str, List[str]], + owner: User, +): + init(api_key=owner.api_key) + workspace = await WorkspaceFactory.create(name="test_workspace") + # Add the 4 users for the sample dataset + for i in range(1, 4): + await UserFactory.create(username=f"test_user{i}", id=uuid.UUID(int=i)) + + dataset = FeedbackDataset( + guidelines=feedback_dataset_guidelines, + fields=feedback_dataset_fields, + questions=feedback_dataset_questions, + ) + dataset.add_records(records=feedback_dataset_records_with_paired_suggestions) + remote = dataset.push_to_argilla(name="test-metrics", workspace=workspace.name) + + if question in ("question-1",): + with pytest.raises(NotImplementedError, match=r"^No metrics are defined currently for"): + AgreementMetric(dataset=remote, question_name=question) + else: + metric = AgreementMetric(dataset=remote, question_name=question) + # Test for repr method + assert repr(metric) == f"AgreementMetric(question_name={question})" + metrics_report = metric.compute(metric_names) + if isinstance(metric_names, str): + metrics_report = [metrics_report] + elif isinstance(metric_names, list): + if len(metric_names) == 1: + metrics_report = [metrics_report] + assert isinstance(metrics_report, list) + assert all([isinstance(m, AgreementMetricResult) for m in metrics_report]) + + +@pytest.mark.parametrize( + "question, metric_names", + [ + # TextQuestion + ("question-1", None), + # RatingQuestion + ("question-2", "alpha"), + ("question-2", ["alpha"]), + # LabelQuestion + ("question-3", "alpha"), + # MultiLabelQuestion + ("question-4", "alpha"), + # RankingQuestion + ("question-5", "alpha"), + ], +) +@pytest.mark.usefixtures( + "feedback_dataset_guidelines", + "feedback_dataset_fields", + "feedback_dataset_questions", + "feedback_dataset_records_with_paired_suggestions", +) +def test_agreement_metrics_from_feedback_dataset( + feedback_dataset_guidelines: str, + feedback_dataset_fields: List["AllowedFieldTypes"], + feedback_dataset_questions: List["AllowedQuestionTypes"], + feedback_dataset_records_with_paired_suggestions: List[FeedbackRecord], + question: str, + metric_names: Union[str, List[str]], +): + dataset = FeedbackDataset( + guidelines=feedback_dataset_guidelines, + fields=feedback_dataset_fields, + questions=feedback_dataset_questions, + ) + dataset.add_records(records=feedback_dataset_records_with_paired_suggestions) + + if question in ("question-1",): + with pytest.raises(NotImplementedError, match=r"^No metrics are defined currently for"): + dataset.compute_agreement_metrics(question_name=question, metric_names=metric_names) + else: + metrics_report = dataset.compute_agreement_metrics(question_name=question, metric_names=metric_names) + + if isinstance(metric_names, str): + metrics_report = [metrics_report] + elif isinstance(metric_names, list): + if len(metric_names) == 1: + metrics_report = [metrics_report] + assert isinstance(metrics_report, list) + assert all([isinstance(m, AgreementMetricResult) for m in metrics_report]) + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "question, metric_names", + [ + # TextQuestion + ("question-1", None), + # RatingQuestion + ("question-2", "alpha"), + ("question-2", ["alpha"]), + # LabelQuestion + ("question-3", "alpha"), + # MultiLabelQuestion + ("question-4", "alpha"), + # RankingQuestion + ("question-5", "alpha"), + ], +) +@pytest.mark.usefixtures( + "feedback_dataset_guidelines", + "feedback_dataset_fields", + "feedback_dataset_questions", + "feedback_dataset_records_with_paired_suggestions", +) +async def test_agreement_metrics_from_remote_feedback_dataset( + feedback_dataset_guidelines: str, + feedback_dataset_fields: List["AllowedFieldTypes"], + feedback_dataset_questions: List["AllowedQuestionTypes"], + feedback_dataset_records_with_paired_suggestions: List[FeedbackRecord], + question: str, + metric_names: Union[str, List[str]], + owner: User, +) -> None: + init(api_key=owner.api_key) + workspace = await WorkspaceFactory.create(name="test_workspace") + # Add the 4 users for the sample dataset + for i in range(1, 4): + await UserFactory.create(username=f"test_user{i}", id=uuid.UUID(int=i)) + + dataset = FeedbackDataset( + guidelines=feedback_dataset_guidelines, + fields=feedback_dataset_fields, + questions=feedback_dataset_questions, + ) + dataset.add_records(records=feedback_dataset_records_with_paired_suggestions) + + remote = dataset.push_to_argilla(name="test-metrics", workspace=workspace.name) + + if question in ("question-1",): + with pytest.raises(NotImplementedError, match=r"^No metrics are defined currently for"): + remote.compute_agreement_metrics(question_name=question, metric_names=metric_names) + else: + metrics_report = remote.compute_agreement_metrics(question_name=question, metric_names=metric_names) + + if isinstance(metric_names, str): + metrics_report = [metrics_report] + elif isinstance(metric_names, list): + if len(metric_names) == 1: + metrics_report = [metrics_report] + assert isinstance(metrics_report, list) + assert all([isinstance(m, AgreementMetricResult) for m in metrics_report]) diff --git a/tests/integration/client/feedback/metrics/test_annotator_metrics.py b/tests/integration/client/feedback/metrics/test_annotator_metrics.py new file mode 100644 index 0000000000..301a2ededd --- /dev/null +++ b/tests/integration/client/feedback/metrics/test_annotator_metrics.py @@ -0,0 +1,358 @@ +# Copyright 2021-present, the Recognai S.L. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import uuid +from typing import TYPE_CHECKING, List, Union + +import pytest +from argilla import User, init +from argilla.client.feedback.dataset import FeedbackDataset +from argilla.client.feedback.metrics.annotator_metrics import AnnotatorMetric, UnifiedAnnotatorMetric +from argilla.client.feedback.metrics.base import ModelMetricResult +from argilla.client.feedback.schemas import FeedbackRecord + +from tests.factories import UserFactory, WorkspaceFactory + +if TYPE_CHECKING: + from argilla.client.feedback.schemas.types import AllowedFieldTypes, AllowedQuestionTypes + + +@pytest.mark.parametrize( + "question, metric_names", + [ + # TextQuestion + ("question-1", {"gleu", "rouge"}), + # RatingQuestion + ("question-2", {"accuracy", "f1-score", "precision", "recall", "confusion-matrix", "spearman-r"}), + # LabelQuestion + ("question-3", {"accuracy", "f1-score", "precision", "recall", "confusion-matrix", "pearson-r"}), + ], +) +@pytest.mark.usefixtures( + "feedback_dataset_guidelines", + "feedback_dataset_fields", + "feedback_dataset_questions", + "feedback_dataset_records_with_paired_suggestions", +) +def test_allowed_metrics( + feedback_dataset_guidelines: str, + feedback_dataset_fields: List["AllowedFieldTypes"], + feedback_dataset_questions: List["AllowedQuestionTypes"], + feedback_dataset_records_with_paired_suggestions: List[FeedbackRecord], + question: str, + metric_names: Union[str, List[str]], +): + dataset = FeedbackDataset( + guidelines=feedback_dataset_guidelines, + fields=feedback_dataset_fields, + questions=feedback_dataset_questions, + ) + dataset.add_records(records=feedback_dataset_records_with_paired_suggestions) + + metric = AnnotatorMetric(dataset, question) + assert set(metric.allowed_metrics) == metric_names + + +@pytest.mark.parametrize("responses_vs_suggestions", [True, False]) +@pytest.mark.parametrize( + "question, metric_names", + [ + # TextQuestion + ("question-1", ["gleu"]), + # RatingQuestion + ("question-2", "accuracy"), + ("question-2", ["accuracy", "f1-score", "precision", "recall", "confusion-matrix", "spearman-r"]), + # LabelQuestion + ("question-3", "accuracy"), + ("question-3", ["accuracy", "f1-score", "precision", "recall", "confusion-matrix", "pearson-r"]), + # MultiLabelQuestion + ("question-4", ["accuracy", "f1-score", "precision", "recall", "confusion-matrix"]), + # RankingQuestion + ("question-5", "ndcg-score"), + ], +) +@pytest.mark.usefixtures( + "feedback_dataset_guidelines", + "feedback_dataset_fields", + "feedback_dataset_questions", + "feedback_dataset_records_with_paired_suggestions", +) +def test_annotator_metric( + feedback_dataset_guidelines: str, + feedback_dataset_fields: List["AllowedFieldTypes"], + feedback_dataset_questions: List["AllowedQuestionTypes"], + feedback_dataset_records_with_paired_suggestions: List[FeedbackRecord], + question: str, + metric_names: Union[str, List[str]], + responses_vs_suggestions: bool, +): + dataset = FeedbackDataset( + guidelines=feedback_dataset_guidelines, + fields=feedback_dataset_fields, + questions=feedback_dataset_questions, + ) + dataset.add_records(records=feedback_dataset_records_with_paired_suggestions) + + metric = AnnotatorMetric(dataset, question, responses_vs_suggestions=responses_vs_suggestions) + # Test for repr method + assert repr(metric) == f"AnnotatorMetric(question_name={question})" + metrics_report = metric.compute(metric_names) + assert len(metrics_report) == 3 # Number of annotators + assert isinstance(metrics_report, dict) + user_id = str(uuid.UUID(int=1)) + metric_results = metrics_report[user_id] + assert isinstance(metric_results, list) + metric_result = metric_results[0] + assert isinstance(metric_result, ModelMetricResult) + if isinstance(metric_names, str): + metric_names = [metric_names] + + assert all([result.metric_name == name for result, name in zip(metric_results, metric_names)]) + + +@pytest.mark.parametrize("responses_vs_suggestions", [True]) +@pytest.mark.parametrize( + "question, metric_names", + [ + # RatingQuestion + ("question-2", "accuracy"), + ("question-2", ["accuracy", "f1-score", "precision", "recall", "confusion-matrix", "spearman-r"]), + # LabelQuestion + ("question-3", "accuracy"), + ("question-3", ["accuracy", "f1-score", "precision", "recall", "confusion-matrix", "pearson-r"]), + # MultiLabelQuestion + ("question-4", ["accuracy", "f1-score", "precision", "recall", "confusion-matrix"]), + # RankingQuestion + ("question-5", "ndcg-score"), + ], +) +@pytest.mark.usefixtures( + "feedback_dataset_guidelines", + "feedback_dataset_fields", + "feedback_dataset_questions", + "feedback_dataset_records_with_paired_suggestions", +) +def test_annotator_metric_from_feedback_dataset( + feedback_dataset_guidelines: str, + feedback_dataset_fields: List["AllowedFieldTypes"], + feedback_dataset_questions: List["AllowedQuestionTypes"], + feedback_dataset_records_with_paired_suggestions: List[FeedbackRecord], + question: str, + metric_names: Union[str, List[str]], + responses_vs_suggestions: bool, +): + dataset = FeedbackDataset( + guidelines=feedback_dataset_guidelines, + fields=feedback_dataset_fields, + questions=feedback_dataset_questions, + ) + dataset.add_records(records=feedback_dataset_records_with_paired_suggestions) + if responses_vs_suggestions: + metrics_report = dataset.compute_model_metrics(question_name=question, metric_names=metric_names) + + assert len(metrics_report) == 3 # Number of annotators + assert isinstance(metrics_report, dict) + user_id = str(uuid.UUID(int=1)) + metric_results = metrics_report[user_id] + assert isinstance(metric_results, list) + metric_result = metric_results[0] + assert isinstance(metric_result, ModelMetricResult) + if isinstance(metric_names, str): + metric_names = [metric_names] + + assert all([result.metric_name == name for result, name in zip(metric_results, metric_names)]) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("responses_vs_suggestions", [True]) +@pytest.mark.parametrize( + "question, metric_names", + [ + # TextQuestion (Tested only once for speed) + # ("question-1", ["gleu"]), + # RatingQuestion + ("question-2", "accuracy"), + ("question-2", ["accuracy", "f1-score", "precision", "recall", "confusion-matrix", "spearman-r"]), + # LabelQuestion + ("question-3", "accuracy"), + ("question-3", ["accuracy", "f1-score", "precision", "recall", "confusion-matrix", "pearson-r"]), + # MultiLabelQuestion + ("question-4", ["accuracy", "f1-score", "precision", "recall", "confusion-matrix"]), + # RankingQuestion + ("question-5", "ndcg-score"), + ], +) +@pytest.mark.usefixtures( + "feedback_dataset_guidelines", + "feedback_dataset_fields", + "feedback_dataset_questions", + "feedback_dataset_records_with_paired_suggestions", +) +async def test_annotator_metric_from_remote_feedback_dataset( + feedback_dataset_guidelines: str, + feedback_dataset_fields: List["AllowedFieldTypes"], + feedback_dataset_questions: List["AllowedQuestionTypes"], + feedback_dataset_records_with_paired_suggestions: List[FeedbackRecord], + question: str, + metric_names: Union[str, List[str]], + responses_vs_suggestions: bool, + owner: User, +): + init(api_key=owner.api_key) + workspace = await WorkspaceFactory.create(name="test_workspace") + # Add the 4 users for the sample dataset + for i in range(1, 4): + await UserFactory.create(username=f"test_user{i}", id=uuid.UUID(int=i)) + + dataset = FeedbackDataset( + guidelines=feedback_dataset_guidelines, + fields=feedback_dataset_fields, + questions=feedback_dataset_questions, + ) + dataset.add_records(records=feedback_dataset_records_with_paired_suggestions) + remote = dataset.push_to_argilla(name="test-metrics", workspace=workspace.name) + if responses_vs_suggestions: + metrics_report = remote.compute_model_metrics(question_name=question, metric_names=metric_names) + + assert len(metrics_report) == 3 # Number of annotators + assert isinstance(metrics_report, dict) + user_id = str(uuid.UUID(int=1)) + metric_results = metrics_report[user_id] + assert isinstance(metric_results, list) + metric_result = metric_results[0] + assert isinstance(metric_result, ModelMetricResult) + if isinstance(metric_names, str): + metric_names = [metric_names] + + assert all([result.metric_name == name for result, name in zip(metric_results, metric_names)]) + + +@pytest.mark.parametrize("responses_vs_suggestions", [True]) +@pytest.mark.parametrize( + "question, metric_names, strategy_name", + [ + # TextQuestion + ("question-1", None, None), + # RatingQuestion + ("question-2", "accuracy", "majority"), + ("question-2", ["accuracy", "f1-score", "precision", "recall", "confusion-matrix", "spearman-r"], "majority"), + # LabelQuestion + ("question-3", ["accuracy", "f1-score", "precision", "recall", "confusion-matrix", "pearson-r"], "majority"), + # MultiLabelQuestion + ("question-4", ["accuracy", "f1-score", "precision", "recall", "confusion-matrix"], "majority"), + # RankingQuestion + ("question-5", "ndcg-score", "majority"), + ], +) +@pytest.mark.usefixtures( + "feedback_dataset_guidelines", + "feedback_dataset_fields", + "feedback_dataset_questions", + "feedback_dataset_records_with_paired_suggestions", +) +def test_annotator_metrics_unified( + feedback_dataset_guidelines: str, + feedback_dataset_fields: List["AllowedFieldTypes"], + feedback_dataset_questions: List["AllowedQuestionTypes"], + feedback_dataset_records_with_paired_suggestions: List[FeedbackRecord], + question: str, + metric_names: Union[str, List[str]], + strategy_name: str, + responses_vs_suggestions: bool, +): + if not strategy_name: + return + dataset = FeedbackDataset( + guidelines=feedback_dataset_guidelines, + fields=feedback_dataset_fields, + questions=feedback_dataset_questions, + ) + dataset.add_records(records=feedback_dataset_records_with_paired_suggestions) + + if question in ("question-1",): + with pytest.raises(NotImplementedError): + UnifiedAnnotatorMetric(dataset, question) + else: + metric = UnifiedAnnotatorMetric( + dataset, question, strategy_name=strategy_name, responses_vs_suggestions=responses_vs_suggestions + ) + metrics_report = metric.compute(metric_names) + + if isinstance(metric_names, list): + assert isinstance(metrics_report, list) + else: + assert isinstance(metrics_report, ModelMetricResult) + assert str(list(dataset.records[0].unified_responses.values())[0][0].value) in str( + dataset.records[0].responses + ) + metrics_report = [metrics_report] + metric_names = [metric_names] + + assert all([result.metric_name == name for result, name in zip(metrics_report, metric_names)]) + + +@pytest.mark.parametrize("responses_vs_suggestions", [True]) +@pytest.mark.parametrize( + "question, metric_names, strategy_name", + [ + # RatingQuestion + ("question-2", "accuracy", "majority"), + ("question-2", ["accuracy", "f1-score", "precision", "recall", "confusion-matrix", "spearman-r"], "majority"), + # LabelQuestion + ("question-3", ["accuracy", "f1-score", "precision", "recall", "confusion-matrix", "pearson-r"], "majority"), + # MultiLabelQuestion + ("question-4", ["accuracy", "f1-score", "precision", "recall", "confusion-matrix"], "majority"), + # RankingQuestion + ("question-5", "ndcg-score", "majority"), + ], +) +@pytest.mark.usefixtures( + "feedback_dataset_guidelines", + "feedback_dataset_fields", + "feedback_dataset_questions", + "feedback_dataset_records_with_paired_suggestions", +) +def test_annotator_metrics_unified_from_feedback_dataset( + feedback_dataset_guidelines: str, + feedback_dataset_fields: List["AllowedFieldTypes"], + feedback_dataset_questions: List["AllowedQuestionTypes"], + feedback_dataset_records_with_paired_suggestions: List[FeedbackRecord], + question: str, + metric_names: Union[str, List[str]], + strategy_name: str, + responses_vs_suggestions: bool, +): + if not strategy_name: + return + dataset = FeedbackDataset( + guidelines=feedback_dataset_guidelines, + fields=feedback_dataset_fields, + questions=feedback_dataset_questions, + ) + dataset.add_records(records=feedback_dataset_records_with_paired_suggestions) + + if responses_vs_suggestions: + metrics_report = dataset.compute_model_metrics( + question_name=question, metric_names=metric_names, strategy=strategy_name + ) + + if isinstance(metric_names, list): + assert isinstance(metrics_report, list) + else: + assert isinstance(metrics_report, ModelMetricResult) + + metrics_report = [metrics_report] + metric_names = [metric_names] + + assert all([result.metric_name == name for result, name in zip(metrics_report, metric_names)]) diff --git a/tests/integration/client/feedback/metrics/test_utils.py b/tests/integration/client/feedback/metrics/test_utils.py new file mode 100644 index 0000000000..59d66f7665 --- /dev/null +++ b/tests/integration/client/feedback/metrics/test_utils.py @@ -0,0 +1,130 @@ +# Copyright 2021-present, the Recognai S.L. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import TYPE_CHECKING, List, Optional, Union + +import pytest +from argilla.client.feedback.dataset import FeedbackDataset +from argilla.client.feedback.metrics.utils import ( + get_responses_and_suggestions_per_user, + get_unified_responses_and_suggestions, +) +from argilla.client.feedback.schemas import FeedbackRecord + +if TYPE_CHECKING: + from argilla.client.feedback.schemas.types import AllowedFieldTypes, AllowedQuestionTypes + + +@pytest.mark.parametrize( + "question, status, num_responses", + [ + ("question-1", "submitted", 4), + ("question-2", "submitted", 4), + ("question-3", "submitted", 4), + ("question-4", "submitted", 4), + ("question-5", "submitted", 4), + ("question-1", "draft", 0), + ], +) +@pytest.mark.usefixtures( + "feedback_dataset_guidelines", + "feedback_dataset_fields", + "feedback_dataset_questions", + "feedback_dataset_records_with_paired_suggestions", +) +def test_responses_and_suggestions_per_user( + feedback_dataset_guidelines: str, + feedback_dataset_fields: List["AllowedFieldTypes"], + feedback_dataset_questions: List["AllowedQuestionTypes"], + feedback_dataset_records_with_paired_suggestions: List[FeedbackRecord], + question: str, + status: str, + num_responses: int, +): + dataset = FeedbackDataset( + guidelines=feedback_dataset_guidelines, + fields=feedback_dataset_fields, + questions=feedback_dataset_questions, + ) + dataset.add_records(records=feedback_dataset_records_with_paired_suggestions) + + responses_and_suggestions_per_user = get_responses_and_suggestions_per_user( + dataset, question, filter_by={"response_status": status} + ) + num_users = 3 + if status != "submitted": + assert len(responses_and_suggestions_per_user) == num_users + else: + assert len(responses_and_suggestions_per_user) == num_users + assert isinstance(responses_and_suggestions_per_user, dict) + assert all( + [ + set(user_data.keys()) == {"responses", "suggestions"} + for user_id, user_data in responses_and_suggestions_per_user.items() + ] + ) + user_data = responses_and_suggestions_per_user[list(responses_and_suggestions_per_user.keys())[0]] + assert len(user_data["responses"]) == len(user_data["suggestions"]) == num_responses + + +@pytest.mark.parametrize( + "question, expected_unified_responses, value_type, strategy", + [ + # TextQuestion + ("question-1", None, None, None), + # RatingQuestion + ("question-2", [1, 1, 1, 2], int, "majority"), + # LabelQuestion + ("question-3", [1, 1, 1, 2], str, "majority"), + # MultiLabelQuestion + ("question-4", [("a", "c"), ("a", "c"), ("c", "b"), ("b", "c")], tuple, "majority"), + # RankingQuestion + # TODO(plaguss): Activate this test when we have a strategy for RankingQuestion (#4295) + # ("question-5", [1, 1, 1, 2], str, "majority"), + ], +) +@pytest.mark.usefixtures( + "feedback_dataset_guidelines", + "feedback_dataset_fields", + "feedback_dataset_questions", + "feedback_dataset_records_with_paired_suggestions", +) +def test_get_unified_responses_and_suggestions( + feedback_dataset_guidelines: str, + feedback_dataset_fields: List["AllowedFieldTypes"], + feedback_dataset_questions: List["AllowedQuestionTypes"], + feedback_dataset_records_with_paired_suggestions: List[FeedbackRecord], + question: str, + expected_unified_responses: Optional[Union[List, str, int]], + value_type: type, + strategy: str, +): + dataset = FeedbackDataset( + guidelines=feedback_dataset_guidelines, + fields=feedback_dataset_fields, + questions=feedback_dataset_questions, + ) + dataset.add_records(records=feedback_dataset_records_with_paired_suggestions) + + if question == "question-1": + with pytest.raises(NotImplementedError, match="^This function is not available"): + get_unified_responses_and_suggestions(dataset, question) + else: + unified_dataset = dataset.compute_unified_responses(question, strategy) + unified_responses, suggestions = get_unified_responses_and_suggestions(unified_dataset, question) + import pdb + + pdb.set_trace() + assert len(unified_responses) == len(suggestions) == len(expected_unified_responses) + assert all([isinstance(response, value_type) for response in unified_responses]) diff --git a/tests/integration/client/feedback/test_unification.py b/tests/integration/client/feedback/test_unification.py index dede6008b5..8d0741cb68 100644 --- a/tests/integration/client/feedback/test_unification.py +++ b/tests/integration/client/feedback/test_unification.py @@ -61,7 +61,7 @@ def test_rating_question_strategy(strategy, unified_response): } question = RatingQuestion(**question_payload) strategy = RatingQuestionStrategy(strategy) - strategy.unify_responses([record], question) + strategy.compute_unified_responses([record], question) unified_response = [UnifiedValueSchema(**resp) for resp in unified_response] assert record._unified_responses[question_name] == unified_response assert RatingQuestionUnification(question=question, strategy=strategy) @@ -95,7 +95,7 @@ def test_ranking_question_strategy(strategy, unified_response): } question = RankingQuestion(**question_payload) strategy = RankingQuestionStrategy(strategy) - strategy.unify_responses([record], question) + strategy.compute_unified_responses([record], question) unified_response = [UnifiedValueSchema(**resp) for resp in unified_response] assert record._unified_responses[question_name] == unified_response assert RankingQuestionUnification(question=question, strategy=strategy) @@ -134,7 +134,7 @@ def test_label_question_strategy(strategy, unified_response): } question = LabelQuestion(**question_payload) strategy = LabelQuestionStrategy(strategy) - strategy.unify_responses([record], question) + strategy.compute_unified_responses([record], question) unified_response = [UnifiedValueSchema(**resp) for resp in unified_response] assert record._unified_responses[question_name] == unified_response assert LabelQuestionUnification(question=question, strategy=strategy) @@ -173,7 +173,7 @@ def test_multi_label_question_strategy(strategy, unified_response): } question = MultiLabelQuestion(**question_payload) strategy = MultiLabelQuestionStrategy(strategy) - strategy.unify_responses([record], question) + strategy.compute_unified_responses([record], question) unified_response = [UnifiedValueSchema(**resp) for resp in unified_response] assert record._unified_responses[question_name] == unified_response assert MultiLabelQuestionUnification(question=question, strategy=strategy) @@ -190,7 +190,6 @@ def test_multi_label_question_strategy_without_overlap(strategy, unified_respons records_payload = { "fields": {"text": "This is the first record", "label": "positive"}, "responses": [ - {"values": {question_name: {"value": ["label1"]}}}, {"values": {question_name: {"value": ["label1"]}}}, {"values": {question_name: {"value": ["label2"]}}}, ], @@ -204,7 +203,7 @@ def test_multi_label_question_strategy_without_overlap(strategy, unified_respons } question = MultiLabelQuestion(**question_payload) strategy = MultiLabelQuestionStrategy(strategy) - strategy.unify_responses([record], question) + strategy.compute_unified_responses([record], question) unified_response = [UnifiedValueSchema(**resp) for resp in unified_response] assert record._unified_responses[question_name] == unified_response assert MultiLabelQuestionUnification(question=question, strategy=strategy) From 4bee06a398825f168ed336a41fa55961de78ce81 Mon Sep 17 00:00:00 2001 From: Sara Han <127759186+sdiazlor@users.noreply.github.com> Date: Wed, 20 Dec 2023 17:49:11 +0100 Subject: [PATCH 08/14] docs: add text descriptives tutorial (#4445) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Please include a summary of the changes and the related issue. Please also include relevant motivation and context. List any dependencies that are required for this change. Closes #4391 **Type of change** (Remember to title the PR according to the type of change) - [x] Documentation update **How Has This Been Tested** (Please describe the tests that you ran to verify your changes.) - [x] `sphinx-autobuild` (read [Developer Documentation](https://docs.argilla.io/en/latest/community/developer_docs.html#building-the-documentation) for more details) **Checklist** - [ ] I added relevant documentation - [ ] I followed the style guidelines of this project - [ ] I did a self-review of my code - [ ] I made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I filled out [the contributor form](https://tally.so/r/n9XrxK) (see text above) - [ ] I have added relevant notes to the `CHANGELOG.md` file (See https://keepachangelog.com/) --------- Co-authored-by: David Berenstein --- .../text-descriptives.PNG | Bin 0 -> 101420 bytes .../add_text_descriptives_as_metadata.ipynb | 556 +++++++++++++ .../annotation_workflows.md | 6 + .../add_text_descriptives_as_metadata.ipynb | 741 +++++++++++++----- .../integrations/integrations.md | 6 + 5 files changed, 1125 insertions(+), 184 deletions(-) create mode 100644 docs/_source/_static/tutorials/add-text-descriptives-as-metadata/text-descriptives.PNG create mode 100644 docs/_source/practical_guides/annotation_workflows/add_text_descriptives_as_metadata.ipynb diff --git a/docs/_source/_static/tutorials/add-text-descriptives-as-metadata/text-descriptives.PNG b/docs/_source/_static/tutorials/add-text-descriptives-as-metadata/text-descriptives.PNG new file mode 100644 index 0000000000000000000000000000000000000000..b817f2b0522ade8ba241e050711030fccebe41a9 GIT binary patch literal 101420 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%0Db8k`4r)R*wyA% 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 z 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

    +=l5tFPX49%?u-?T(e}TwWn&x-o-5Kl66lG{ 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 Mp2\n", + "\n", + "Tip\n", + " \n", + "This tutorial is a Jupyter Notebook. There are two options to run it:\n", + "\n", + "- Use the Open in Colab button at the top of this page. This option allows you to run the notebook directly on Google Colab. Don't forget to change the runtime type to GPU for faster model training and inference.\n", + "- Download the .ipynb file by clicking on the View source link at the top of the page. This option allows you to download the notebook and run it on your local machine or on a Jupyter Notebook tool of your choice.\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Set up the Environment\n", + "\n", + "To complete this tutorial, you will need to install the Argilla client and a few third-party libraries using `pip`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# %pip install --upgrade pip\n", + "%pip install argilla -qqq\n", + "%pip install datasets" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's make the needed imports:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "import argilla as rg\n", + "from argilla.client.feedback.integrations.textdescriptives import TextDescriptivesExtractor\n", + "\n", + "from datasets import load_dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you are running Argilla using the Docker quickstart image or a public Hugging Face Spaces, you need to init the Argilla client with the `URL` and `API_KEY`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Replace api_url with the url to your HF Spaces URL if using Spaces\n", + "# Replace api_key if you configured a custom API key\n", + "# Replace workspace with the name of your workspace\n", + "rg.init(\n", + " api_url=\"http://localhost:6900\", \n", + " api_key=\"owner.apikey\",\n", + " workspace=\"admin\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you're running a private Hugging Face Space, you will also need to set the [HF_TOKEN](https://huggingface.co/settings/tokens) as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# # Set the HF_TOKEN environment variable\n", + "# import os\n", + "# os.environ['HF_TOKEN'] = \"your-hf-token\"\n", + "\n", + "# # Replace api_url with the url to your HF Spaces URL\n", + "# # Replace api_key if you configured a custom API key\n", + "# rg.init(\n", + "# api_url=\"https://[your-owner-name]-[your_space_name].hf.space\", \n", + "# api_key=\"admin.apikey\",\n", + "# extra_headers={\"Authorization\": f\"Bearer {os.environ['HF_TOKEN']}\"},\n", + "# )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Enable Telemetry\n", + "\n", + "We gain valuable insights from how you interact with our tutorials. To improve ourselves in offering you the most suitable content, using the following lines of code will help us understand that this tutorial is serving you effectively. Though this is entirely anonymous, you can choose to skip this step if you prefer. For more info, please check out the [Telemetry](../../reference/telemetry.md) page." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " from argilla.utils.telemetry import tutorial_running\n", + " tutorial_running()\n", + "except ImportError:\n", + " print(\"Telemetry is introduced in Argilla 1.20.0 and not found in the current installation. Skipping telemetry.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Load the Dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For this example, we will use the [squad](https://huggingface.co/datasets/squad) dataset from Hugging Face, which is a reading comprehension dataset composed of questions on a collection of Wikipedia articles, the given context and the answers." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# Load the dataset and select the first 100 examples\n", + "hf_dataset = load_dataset(\"squad\", split=\"train\").select(range(100))" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Dataset({\n", + " features: ['id', 'title', 'context', 'question', 'answers'],\n", + " num_rows: 100\n", + "})" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hf_dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Create the FeedbackDataset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To create a FeedbackDataset, we choose a `TaskTemplate` for question answering with the default configuration, so no metadata is added." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "FeedbackDataset(\n", + " fields=[TextField(name='question', title='Question', required=True, type='text', use_markdown=True), TextField(name='context', title='Context', required=True, type='text', use_markdown=True)]\n", + " questions=[TextQuestion(name='answer', title='Answer', description='Answer the question. Note that the answer must exactly be in the context.', required=True, type='text', use_markdown=True)]\n", + " guidelines=This is a question answering dataset that contains questions and contexts. Please answer the question by using the context.)\n", + " metadata_properties=[])\n", + ")" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Create a FeedbackDataset\n", + "dataset = rg.FeedbackDataset.for_question_answering(\n", + " use_markdown=True,\n", + " guidelines=None,\n", + " metadata_properties=None,\n", + " vectors_settings=None,\n", + ")\n", + "dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We will also define our initial list of records, matching the featured of the dataset with those of the task template. But for the purpose of this tutorial, we will not yet add them to our dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "# Create our list of records\n", + "records = [\n", + " rg.FeedbackRecord(\n", + " fields={\"question\": record[\"question\"], \"context\": record[\"context\"]},\n", + " )\n", + " for record in hf_dataset\n", + "]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Add Text Descriptives" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Our dataset currently lacks metadata. To address this, we will add the text descriptives as metadata using the `TextDescriptivesExtractor`, which has the following arguments:\n", + "\n", + "* *model*: the language of the model.\n", + "* *metrics*: the metrics to be extracted.\n", + "* *fields*: the field names to extract metrics from.\n", + "* *visible_for_annotators*: whether the metadata is visible for annotators.\n", + "* *show_progress*: whether to show the progress bar.\n", + "\n", + "For more information about the `TextDescriptivesExtractor`, please check the [practical guide](./practical_guides/create_update_dataset/metadata.md).\n", + "We can add metadata to local or remote [records](#to-records) or [datasets](#to-a-dataset). Let's see how to do both." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### To Records" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First, we will add the text descriptives as metadata to the records we have defined above. To do so, we will initialize the `TextDescriptivesExtractor` where we will compute the default metrics only for the `question` field. Note that as this happens at a record level, the metadata won't be visible for annotators in the UI." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "# Initialize the TextDescriptivesExtractor\n", + "tde = TextDescriptivesExtractor(\n", + " model = \"en\",\n", + " metrics = None,\n", + " fields = [\"question\"],\n", + " visible_for_annotators = False,\n", + " show_progress = True,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Update the records\n", + "updated_records = tde.update_records(records)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As we can see below, the default metrics for the indicated field have been added to the records as metadata." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'question_n_tokens': 13,\n", + " 'question_n_unique_tokens': 12,\n", + " 'question_n_sentences': 1,\n", + " 'question_perplexity': 1.27,\n", + " 'question_entropy': 0.24,\n", + " 'question_flesch_reading_ease': 89.52}" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "updated_records[0].metadata" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Thus, now we can add the updated records with the metadata to our dataset. And we will push it to Argilla." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "# Add the updated records to the dataset\n", + "dataset.add_records(updated_records)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Push the dataset to Argilla\n", + "remote_dataset = dataset.push_to_argilla(name=\"squad_tutorial\", workspace=\"argilla\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### To a Dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, we will update our dataset with the text descriptives for the context. In this case, we will initialize the `TextDescriptivesExtractor` indicating that we want to extract the metrics related to `descriptive_stats` and `coherence`. We will also set the `visible_for_annotators` argument to `True` so that the metadata is visible for annotators in the UI." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "# Initialize the TextDescriptivesExtractor\n", + "tde = TextDescriptivesExtractor(\n", + " model = \"en\",\n", + " metrics = [\"descriptive_stats\", \"readability\"],\n", + " fields = [\"context\"],\n", + " visible_for_annotators = True,\n", + " show_progress = True,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Update the dataset\n", + "tde.update_dataset(remote_dataset)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this case, it is a remote dataset so it will be updated directly on Argilla. As we can see below, the metrics have been added to the dataset as metadata and they are visible to the annotators." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'question_n_tokens': 13,\n", + " 'question_n_unique_tokens': 12,\n", + " 'question_n_sentences': 1,\n", + " 'question_perplexity': 1.27,\n", + " 'question_entropy': 0.24,\n", + " 'question_flesch_reading_ease': 89.52,\n", + " 'context_flesch_reading_ease': 76.96,\n", + " 'context_flesch_kincaid_grade': 6.93,\n", + " 'context_smog': 8.84,\n", + " 'context_gunning_fog': 9.34,\n", + " 'context_automated_readability_index': 8.43,\n", + " 'context_coleman_liau_index': 8.75,\n", + " 'context_lix': 34.65,\n", + " 'context_rix': 3.0,\n", + " 'context_token_length_mean': 4.46,\n", + " 'context_token_length_median': 4.0,\n", + " 'context_token_length_std': 2.55,\n", + " 'context_sentence_length_mean': 17.71,\n", + " 'context_sentence_length_median': 14.0,\n", + " 'context_sentence_length_std': 7.46,\n", + " 'context_syllables_per_token_mean': 1.32,\n", + " 'context_syllables_per_token_median': 1.0,\n", + " 'context_syllables_per_token_std': 0.69,\n", + " 'context_n_tokens': 124,\n", + " 'context_n_unique_tokens': 68,\n", + " 'context_proportion_unique_tokens': 0.55,\n", + " 'context_n_characters': 572,\n", + " 'context_n_sentences': 7}" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "remote_dataset.records[0].metadata" + ] + }, + { + "attachments": { + "text-descriptives.PNG": { + "image/png": "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LbLi0adz2bYddOvWrd7a0Vj4wEd9QF5405//xOPzMbnwkv8IZ9dZBT7GFWs0SNGVA4wNSMua3OE2xesOqyr1SV/55ZqU9NGnpSXneKcJfNS/eEJa3vImxJ0YkxvvtMgT3vw4hQOemQOfZfeY7V7MrbTJDzrk6f2281ikJQ+Dc3XqLTg3jPK5qbs3X5CHAj1v8upXw7VnzDLvz+A18sDWbkkHLkTnWDO9Z7LH+8COCzLmhybK5zfk7NZD3jaD9bJCnjB1FVw3d5tFBj5me08fNZ+VzPayvYzyjnPsgjRqGOj9mXOcOeU35fWGTWqvoMZ3cs+3rU6LCnz8MrtLXSYG5NBL4Tog8EFERERExMVlpMBHraqq8pozxfPRRx/JN77xDet2w/785z+Xq1f9x+pMT7yBzyy957vyQ+1F8rffDQ3vitcV9//Q6Unzw/tnG/R4Ok9cCjSaS3KFfPdvtQfNQ/LAX9heL0WvAf3uC5bXitOvs4d+EJizZpa6w+BCc86U4l884JZthuvmWz/Q+n3Ae4T79OGCrjvzefiWPKD7zWxzOr11C9RfJiQpSDgYcrdnK6Oeq2jXtnvN2dct5tgiOk+fb0RERERExLiMHPioP/jBD+R3v/ud14iLxnvvvSf/6T/9J+v2Cvlv/s2/keeff96Z82c6FlXgU6Y6w56cSYLtr8+131p7SNIfdMuvQo3/b2294PSqKLXn0dy6wp1X6UaHPGR9fa5dXL1JntAeME7vrEJekEbL+xAREREREXHuLCrwUf/dv/t3smfPHq+5OT06ROtf/at/Zd1OFL/1rW85j2N/++23vS3mQuBT/uqj053+ITqXzZuHpGV/h/PIa2cITXCyYAzI8CFERERERESc3qIDH99//+//vTz55JPS2trqzM9z5coV5/9fe+01Z1iW9tKxva9U//zP/1zWrl0rv/nNb6S5uVna29sJfCrEFX/3gnRcGsv2CBnT8OdleSK24WGV5q/kkE4ebDzkPXkKERERERERMWjJgQ8iIiIiIiIiIi5OCXwQEREREREREStMAh9ERERERERExAqTwAcRERERERERscIk8EFERERERERErDAJfBARERERERERK0wCH0RERERERETECpPABxERERERERGxwiTwQURERERERESsMAl8EBERERERERErTAIfRERERERERMQKk8AHEREREREREbHCJPBBRERERERERKwwCXwQEREREREREStMAh9ERERERERExAqTwAcRERERERERscIk8EFERERERERErDAJfBARERERERERK0wCH0RERERERETECpPABxERERERERGxwiTwQURERERERESsMAl8EBERERERERErTAIfRERERERERMQKk8AHEREREREREbHCJPBBRERERERERKwwvzQ1NSWIiIiIiIiIiFg5EvggIiIiIiIiIlaYBD6IiIiIiIiIiBUmgQ8iIiIiIiIiYoVJ4IOIiIiIiIiIWGES+CAiIiIiIiIiVpgEPoiIiIiIiIiIFSaBDyIiIiIiIiJihUngg4iIiIiIiIhYYRL4ICIiIiIiIiJWmAQ+iIiIiIiIiIgVJoEPIiIiIiIiImKFSeCDiIiIiIiIiFhhEvggIiIiIiIiIlaYBD6IiIiIiIiIiBUmgQ8iIiIiIiIiYoVJ4IOIiIiIiIiIWGES+CAiIiIiIiIiVpgEPoiIiIiIiIiIFSaBDyIiIiIiIiJihUngg4iIiIiIiIhYYRL4ICIiIiIiIiJWmEs28Em/uU3ObvlLOb7m/5IjD/8fWEHqOdVzq+fYdu4RERERERERK90lF/jcunpGejaukLd/c59cP/+KfDY+IlBZ6DnVc6vnWM+1nnPbtYCIiIiIiIhYqS6pwEcb/tr748M3G7xoACodPdd6zgl9EBERERERcSm5pAIf7e1B2LP00HOu5952TSAiIiIiIiJWoksm8NH5XHSIDyxN9Nwzpw8iIiIiIiIuFZdM4KOT+Oq8LrA00XOv14Dt2kBERERERESsNJdM4KPzuDBB89JFz71eA7ZrAxEREREREbHSXDKBjz6uG5Y2eg3Yrg1ERERERETESpPAB5YMBD6IiIiIiIi4VCTwgSUDgQ8iIiIiIiIuFQl8YMlA4IOIiIiIiIhLxXkJfD788EO5cOGC461bt6zrzLUEPrBkA5/JYUm2H5TugTH76xjJzz77TD7++GPra7N3QsbGxlzHba8jIiIiIiIW55wGPn19fbJhwwb5q7/6q4x/8zd/I9u2bZOPPvrI+p65ksAnOte7t8jZ9nPyyV1vQYVQcYHPjW55LvDZyvP5bhk16431bnP//ulBGfTe29+i6zwn3TcC2xt3A4cJ/2/McceOHfKrX/1KJicnra+X5pj0t22VNQ8HzpvxsY0vS3d6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IiIiIiJWoksq8Ll19Ywzjwuhz9JBz7Wecz33tmsCERERERERsRJdUoGPqg1/7e2hQ3x0Xhcmcq489JzqudVzrOeasAcRERERERGXmksu8PHV+Vx0El/t/aGP68bKUc+pnlvm7EFERERERMSl6pINfBARERERERERK1UCH0RERERERETECpPABxERERERERGxwiTwQURERERERESsMAl8EBERERERERErTAIfRERERERERMQKk8AHEREREREREbHCJPBBRERERERERKwwCXwQEREREREREStMAh9ERERERERExAqTwAcRERERERERscIk8EFERERERERErDCLCnw+/fRT+fjjj+X27dvyu9/9DitQPbd6jvVc264BRERERERERFz8Rg58NASwBQRYueo5t10LiIiIiIiIiLi4jRT4EPYsXQl9EBEREREREcvPGQMfHdpjCwJw6cjwLkRERERERMTycsbAh949SC8fRERERERExPJyxsCHCZpRrwHbtYGIiIiIiIiIi9MZAx9bAIBLT9u1gYiIiIiIiIiLUwIfjKTt2kBERERERETExSmBD0bSdm0gIiIiIiIi4uKUwAcjabs2EBEREREREXFxSuCDkbRdG4iIiIiIiIi4OCXwwUjarg1EREREREREXJwS+GAkbdcGIiIiIiIiIi5OCXwwkrZrAxEREREREREXp7EGPu8fq5WVK1fKylU7pXfEvo7rJTn0jFlP1939tuX1mR0ZGpKhoRHra7Px7d1uufacs78+rR9pmYZkxPZamWu7NhARERERERFxcTo3gY+x/sSQdR3HC/vlZ956pQU+b8sefe/Go/K+9fXSnU3g4763Vo5esb9eztquDURERERERERcnM5Z4LNy/SG5ZFnnd78bkd5/WZVdj8CnLLRdG4iIiIiIiIi4OJ2TwGfd2nXm/38m+y9Y1vvwhNSbdVaZdZxePuHAZ6hfTuyul6cf11DoYVn3/B45+k62t9D7XTvk6WfWycP63lWPmX8/LU9v7w4EP0PSf2KP1D/1mKzS/Tz+tNTvPiH9Q/7rWd/v3e+tt0oee3aHHL04UiDwmWGb73fLDlOOdQl9r9nWU6ZMz+yQ7vcD23i/V177Ta23jjmujQ3yWu/72dcXubZrAxEREREREREXp3MS+Ow54YU6/9KbN5/NpdfXm3V+JvtPHJVas05O4DNo3ucFIut/s1P2NzXIei9EqTt2yVln+sDnfTnxnNt76OH1DbJz337ZueVpJ6RZuXaPpALzCl06VucuN9vQfe143mxzVZ3UbdT9BQOfCNucIfAZeWePrPP2Vde4X/bv2yG1/nF1xhP6jIyMOEZdXqy2awMRERERERERF6dzE/icG5ITz2ugUS8nPgyuk5L9PzPLnzkkl66EA58h6f6NBivrzPsDAcXNS3LICWGC2yowpGvwhDRsfFrWm20Gg6bUvp855dpx2tuu18to5c92Sm+g58/IOS+YMWYCn6jbNNqHdI1I6tV6eXptvRwdDCz3y6B1kVm3dHft2iU1NTUyOjqaWaZBzz/+4z/Kyy+/nLNuKdquDURERERERERcnM5R4PM7Gend6fSCWf+62zNHzVkWDnyGuqVB/27M7xU0cnqHs92GLn9oV3Fz+Fw64par9pjbm+b9zjrn72DZXP2gKtjDx254m2pxc/i8LTv1GFbukbetrxenhjtPPfWUPPPMM07oo+q/dRk9fBARERERERGXlnMW+GR68/xsv6Sc10O9fsKBzzt73GFSiXXuMK2gax8OhSvTBD43h6S/96i8tm+H1AfeG3x/qtkdomULdS4dfDr/tQjbVKcLfEaupOTE6/tl529qnWN6bJX7/rgCH1WDHQ151q9f76j/jiPsUW3XBiIiIiIiIiIuTucw8PHn61klO3tH5HcDh2S9eS0zr0848Dm3xw1A1jfI/n06z02+h87N0MPnw17ZsdYNUh5e+7TU63w5r5+Q7n25vXHsEzO7+j13Mq9F3KZaKPDpN/Xgzxf09EadB+g1Odp7yO3RFGPgo/o9feLq2eNruzYQERERERERcXE6p4FPZp6a5zUg0TlvAk/uCgc+g97fTf6cPtNpD3xSr+g+VknDiexTvVS/XH4444c69aH1nEfGN2oIkz2GqNtUrYGPP1Rt/X5JfRRY7h9DzIGPqkFPnGGPars2EBEREREREXFxOreBjwYo/+IOn3JcH5igOG/S5ktyaL35e1Vd7uTGxkude2THvteke8APMbywJDThsRu4PC2HBrLL3BDHLUMmnLmw330kfLA8qh86Gf1jiLzNQuv6xxmem8jr8TQXgc9caLs2EBEREREREXFxOseBj9EPV4w5PWryAp/fZZ+SlaiVPce6pbe3V07sq3UfwZ7zWHUvHFq5StZv3y/7j6WcMGXozQZn6NSqZ/fICfPe3t5uObRd59xZ5+w/G85kA5tVT9XLayd6pfvYHqlNrJO653Ln8Im+TX8Im667Q/bvO+qVt19ec4aErZOG191j6j2xX2ofXyfrdI4jAh9EREREREREjNm5D3wyPXd2Sm8msDFaAh916J3XpO7xQK+glavksbr98nbO492Ng92y4ylvvczE0PoIdC8g8t+75ahc6nXnBwqGM/q49xON67PrrnpadnRdkkuWXkqRt2mOtXv70+58PcHha8Gyqon1sv9svxx1HjdP4IOIiIiIiIiI8Rpr4BOnI0NDMqQGQyKbHw3JyM3Qspsj7ntz5swpoLdu3jbCFrtNy3r+MYUfO18O2q4NRERERERERFycLtrABxeXtmsDERERERERERenBD4YSdu1gYiIiIiIiIiLUwIfjKTt2kBERERERETExSmBD0bSdm0gIiIiIiIi4uKUwAcjabs2EBEREREREXFxSuCDkbRdG4iIiIiIiIi4OJ3zwOfWrVtYAX722WeIuIi03a8RERERERF95yzwsYUGWL7aGpyIuPDa7tuIiIiIiIhFBz62MAArX1tDExHLR9v9HRERERERK9dIgY8tAMClo14DtgYkIpavtvs9IiIiIiJWjtMGPtooGBsbs4YAuHTUayDcWETE8td230dERERExMowJ/CxNQgmJiasIcBcefPmTVxkfvLJJzI5OYmIc6jt/jvfBr8PEBERERGxvM0EPrYf/76l9vKxhQdYXt6+fdvaOEXEhdd2v56twS8IREREREQsX79k+8Fvs1DoYwsJsDIk7EEsX2338WK0fWEgIiIiImL5GDnwUXV4l4YABD2Vq55bPccM40KsLG339Jm0fWkgIiIiImJ5GDnwsTUgZvLOnTuIiDhH2u67M2m7v0+n7YsDEREREREXv9MGPrbGgk1bQwQRERdG233apu2+b9P25YGIiIiIiItba+BjaxgEtTUwCvnpp58iImLM2u63hbTdx4Pavgds2r5EEBERERFxcZoT+NgaAr62RkRQW4Mkqjo3ECIiutruk1G13Z+D2u7vvsHvg5m0faEgIiIiIuLiMRP42H78q7YGg6+tseFra8QgImK82u6/vrb7tq/tfq8GQ52ZtH2pICIiIiLi4vBLth/8qq2BYGtQqLZGSFB94hMiIs5e2z02rO0+rdru67b7v2oLeGzavlgQEREREXHhzQt8bA0CW8PB1siwNU7Cjo+PIyJikdrup2Ft92Xb/dt2nw9/F6i2gMem7csFEREREREX1pzAJ9wACDcSbI0JW6PD1lix+fHHHyMiYgFt902btvuw7X4dvqeH7/nB7wNfW8AT1vblgoiIiIiIC2sm8An/8A82CsKNhnDDwtYAsTVewo6NjSEiYgFt982wtvtv+B4dvocH7+/he38w7PG1hTxBbV8uiIiIiIi4sH4p/GM/2BAINxKCDYhwA8PWELE1YHxv376NiIgzaLt/+truu+F7c/C+Hb6nB+/34e8CQh9ERERExPI2J/AJ/vgPNgr8xsLnn38uAABQfuj92xb8BO/7we8DldAHEREREeNSf0/6uUPw92hc+r9ndT+2/ZeiblP/I6r+x1bbf5ydrbpd3b7ux7b/2ZoJfHQHvn6F+Y0DFQAAyp/gfT345ejrfyf4BgMf1Rb0+Nq+ZBARERFxaau/E4M5w3yo+5vN71P93au9520hzVyp+9P92spTqk7g4//QD1aQ3yDQtAkAACqH4FCv4H3f/y4g9EFERETEONTfh+GsYb7U/Zby+1R/785Vj56Z1P3q/m3lKsUv+T/wgxUTDHsYxgUAUFnofX2m0Ecl8EFERETE2RjOGuZb3b+tXNM53z17wur+beUqRSfwCVZIMOyhdw8AQGXi3+OnC33o5YOIiIiIpaq/GYO/MRdKLYetfDb1N7AthJlvSwmqbH4pWBHBoEdTJRUAACoP/x4fDH6C3weEPoiIiIg4G/X3Y/D35UKp5bCVz6b+LrYFMPOtlsNWvmLNBD62sEfHjwEAQOWh9/diQx8CH0RERESMqv6ODP62XCi1HLby2VyouXvCajls5StWJ/DRH/l+4BMMe3RHAABQefhfJMHQx/8uCH45BgOfYkIf2xcOIiIiIi4d/d+Ui0Fb+WwGQ5eF1la+Yv3SdGGPCgAAlYd/j59t6GMLe1TbFw4iIiIiLh2DgctCayufTf838mLQVr5idQIf/ZHvBz7BsOd3v/ud1zQAAIBKQu/v/r3eD33874KZQp9g4EPog4iIiIg2g4HLQmsrn03/9/Fi0Fa+Yv1SOOzxAx9tDNy6dctrGsTLF1984VS67uPGjRsyPDws165dQ0Rc8ur9UO+Len/U+6TeL+cCvb/7oY9/7w+HPv4XZKm9fFTbFw8iIiIiFuMd9z/I3bG9tnj1f0suBm3lsxkMXBZaW/mK1Ql89Ed+sHePH/bcvHnTaxrEgzYMdJvaqNH/1waEFkIbNL///e+9tQAAljZ6P/z888+d4GVoaMi5X+r9M050m+HQx/8u8AOfqL18bEGPb/hLBxEREbFi/WRELqXOybm+a/KJ7fUSvHPzopxs3ibbthlPp+WOZZ2sn8jIYErOnXtPrn2S//onNy7Jua4jcvDQEek+d0lGLOsUs95MBgOXhdZWPpvBwGWhtZWvWL80XdijxoE2XHRb+l+utQFBuAMAEJ1g8KP30zjw7/FRQ59Se/nYvngQERERK82xD87JkeZdsmvXNtl27KKMWdYp3k/kStc22XX6inzymflbta5nHEvLuWN7zP53ybZtR+Ti7dzXP7naLXu27ZEjZy/KlasXJWnW3dbcLVfGS1sviuHQpVT1t+nx48fl/ffft74eRVv5bAYDl2Ls7e2VZ555xumpb3u9FG3lK1Yn8PHDHjUY+IyOjnpNg9LRytUePbrtu3fveksBAKAY9P6p91G9n+p9dbbo/T0c+Kh+6KOBjx/6+F+UpfTysX3xICIiIlaSY4MnZVfzETn3wZikz8YZ+IzJxWPb5MhvZ3hE99glOblrjxw5l5axD5L5gc+dtPTu2iUdv70VeN8tudi5S3b1BnoNRV0vov5vyNmov0s7Ojpk//79cuXKFes6UbSVz6b/m7hYu7q65Pvf/7784he/iC30sZWvWHMCn3DYMzIy4jUNSkO3rf9VWhsDAAAwe/R+qvdVvb/OBr2/20IfWy8f/4tyusBHtQU+qu3LBxEREbFSHBtOZ4Y9XYsc+NyRW1dT0n3soBw8dFBOnn5P0mOB14dTzvJ9u7bJrv3uOqcuFwh+xj6S9I1P3H8P5Qc+d94/Jdt2JeWav76vWXfXtlOS9uYGirpeVIOBSynq71E/7BkcHLSuE1Vb+WwGA5diPXHihDzwwAOxhT628hXrl6YLe7SQpaIn5/r167ENPwAAABe9r3700UezCn30/j5T6MOwLkRERMTijBb43DHrvSrbtFfQYFrSH1yR904dlF27OuTiLW+d8RGz/JL0HtomB9+6ZP6dlo9u3wltx6Il8PnofIts67qSP6/QJ1eke1uLnLte3HpRDYcuxeiHPfv27Zt12KPaymczGLiU4rFjx2ILfWzlK9Y5CXy0QnW+nrl6ugwAwFJH7696n9X7bSlEDXxsvXwIfBARERHtRgp8rp+Tlm2H5b2bweV35NpbLbLtxKVA4BJxSFdQS+DjlOnstdz1HK9Jcts2SQ4Vt15U/d+PxRoMewYGBqzrFKutfDaDgUupHj58OJbQx1a+Yv2SvzH9oR8Oe7SHTrHof3nW4Qb6Ix8AAOYOvc/q/baUnpR6fw+HPn7go4Z7+fhfltMN6wqGPEFtXz6IiIiIlWiUwOfWuwft6zhBUIdcygztWpqBTzKZdMKeN9980/p6KdrKZ9P/LTxb6+rqZOXKlfLP//zP1tejaCtfsTqBT6HePTpkoFh0G9pIAACAuUeDGb3vFove36P28pluWFcw8CkU+ti+fBAREREr0SiBT8Fg5fZFOZIT1sQY+LxlD3J6w4FPhPWiGg5dolruPXziGtZlK1+x5gU+wd49xQY++mNfhxiU+th1HaKg4/PeffddR30aDQAAFEbvt3rf1ftvMej9PdjLZ7rAZ7bDumxfPoiIiIiVaJTA56PkLtl22vLUq1tzE/iMDXTItkPvya3geurN9+RgoEdR1PWiGgxcijUY+jCHT+l+abrePdqIKAbdhlZmsWiD5b333pMDBw7I3r17czx06JAzZAEAAOxoGKP332LQ+3uxvXz8L8xih3XZvnwQERERK9EogY/7NKzevKdefTJ4Ura9ck4+yiyLJ/DRwOawZdLlj86/KtteDwQ8UdeLqP/bsVT90Ge2j2RXbeWzGQ5dinFRPqUrHPhowfzePcUEPto7p9RgRsfmabjz1ltvOWXR+Si04aBl0YRMX9MTXDKf3ZCBC+/KuzYHbog729Bt6T+8R/a99aH392dyY+BdGbgx33MRuft994Nx728AgOnR0Fzvv8VMlO8HPn4vn7iGdRH4ICIi4lI2SuAzNfWRnHt1m7zafUlueaHPJ9ffk45du6RjIBjuxBT4+BNCv9orV259Inc+/URuXe2VV7e1SO+HwSd/RV0vmuHQpRSDoc/7779vXSeKtvLZDIcuUe3q6pLvf//7sYU9qq18xZoT+ASHc2ljoJgARyux2P/CrOiYPA109P9taENGA6GWlhangVESE5els7FRjp4OhT05gc+n8uGZTunuvyluk+lTuXy8UTovB/Y52i+H2/ul+KMsBne/jedLfyQ+ACw99P6r9+Go6P1d7/NRh3UR+CAiIiLObLTAx/jJR5I6tke2bTPrq7sOyqnfjoSGecUV+Ki35Mrpw7LH31/zYTl1+VZonWLWm1k/bJmt+ntUe88s5sDn9OnT8o//+I+xhT2qrXzF6gQ++iPfNpyrmMBHt6MVWQwa5ugwLj1506ENBl3v7bff9pYUiRf4vFNUhmIJfG68I42N78jcRjEEPgBQPBrG6H04Kn7gYxvWZQt8gsO6CHwQERERY/JT9z+q5c3nM1d+dsfsL0JvnajrTWMwcFlobeWzGQ5dFlJb+Yr1S8HhXPqDPzicq5hJk7XBoBssBm1UTNe7J0h3d7e8/vrr3l9FEinw+VQ+fOuwvPWBH/DkBj43+w7L4YP7pLFxnxxsN/9+60Ozhs8XMv5hv5w+bpab17rPX5abOfOn+ts2jaYP35FufX9foX5C2cDni9uX5a3MNt+X25nRZZ/K+6fNssH8YV833zXrvz3s9VICgKWCBit6H46K3t/9wEfv+8HAx+/l4wc+wV4+/pdmMYGPGv7yQURERMTKNhi4LLS28tm0BS8Lpa18xZoT+GhjwQ989L/+FhP4aMOhmPkjFN2+Bj66v5lIpVLOuiURMfDJ7dGT+/dnt4dluL9bGhu7pd8c6/Dop16o8oXcOH9QGvd3yrvvm+XDH8rAW4dlz56TcjmTx7jbOnz0qLQcf0cufzgsH97MxkW5eIHP8ZPSffQtGTDrDg+/L+8e3yeNB9+RG597a13ulMbX3s0dXvbFsCT3NMpbQ8Q9AEsNvf/qfTgqev/V+7wf+Oj9Pxz46BfNdBM3+4EPvXwQERERMWwwcFlobeWzGQ5dFlJb+Yo1E/j4w7n0h782GrQh8OGHH3pNg5nRxoMO0SoG3Z+GOFevXvWWFEYndG5tbfX+KhIv8Onu1/Ak19uZnjjTBz4OtiFdN9+VgznhjmIaXmf2yJ6k39PGD3Euy8xTMXvr7nlLhr1wx2VcBo42ytEBbwuT70t342vybiDx+WI4KXv2JGWYvAdgyaH332JCer2/+8O6/MBHvweKCXzo5YOIiIiIhQwGLgutrXw2w6HLQmorX7F+ye/d4wc+weFccx346NO4NPA5c+aMt8SObvfgwYNy8uRJb0mReIHPvoPu8Kig/aPeOiUGPuO/PSyNZ/KHUH0x9FagB463LcsQrHy8wOft/O5I44OdTmjklugz+fB0o7x2wU98vpDh5B7Zw9w/AEuSUgIfXV/v93rf9wMfv5dPocDHD30IfBARERFxOsOhy0JqK5/NcOiykNrKV6w5gU94OFcxgY82Good0qVozx0NfbQMhXj33XeddbR8JRHDkC4HS+Bz43yjWVZIf13LtgoyTTh0PRnYZihU+mJY3gr1+AGApYMG6Hofjoof+ASHdQUDH+3lo180BD6IiIiIWIr6WzEcvCyEWg5b+Wzqb99w8LIQajls5SvWaQOfDz74wGsazIz+12H9QV8s+h6djFkfu3758mVvqYsGSPpkLg179CldpQRKDnMY+Nx8Z4+1N04uxQc+tqd0OfP2dAaGhTlz9rghT26PIgBYaui9VO/DUdH7O4EPIiIiIs6V+hsxHL4shFoOW/ls6m/ecPiyEGo5bOUr1kzgow2F2QQ+2v1fGwKloA0EHa6lwY4GP8eOHZPDhw/L/v37Zd++fc4z7fX/Ozs7nf+KXTRzGPh89sFpa9DyxeSn8lkmnyoh8Gnvl9veEpfP5P3uxrwhWzfO6zCuYfnwTGB+HwBYcuj9V+/DUbEFPvo9EAx8dHvBwMcPffSLk8AHEREREadTfxeGw5eFUMthK59N/X1rC2DmWy2HrXzFmhf46JAAbQBod/9iAh+tSN3WbND/utzT0yMnTpyQU6dOyTvvvOM0NBQtU8mhT1yBz8135bXGg/LO8Kem8eKnOTfl3YONcrAn+yj2LyY+lKQuy4yvKj7wee3gQTmc/FA+1WmRfv+Z3PztSdnTeFQG3OrIomXas8f6mj5Kft/hd+WmX9TJD+X0/n3S/b5fjs/kw559sq/7fbNXAChn9D6u9+Go6P1d7/N6b9X7fqHAR79wig18VAIfRERERNTfi37wshCWEpzob99wADOf6v5t5SrF2AIfHW6l75tL/NCno6OjuOFdcQU+8oXc7OuUfTo/T/tAdmjVZzel/2SL7NHljvuk88INyQ5wKz7w6bx8W268czi7TX3s+3XbkLmb8u5r5vXu9wP7U76QG2/vk8Y9p+VD/4WPTT3saZSDfX7foXG53LlHGg+GexMBQDmhEzbr/bGY+2IxgY/28iHwQURERMRi1d+ACxX66H5L+Q2qv2UXai4f3a/u31auUnQCH+1ZM9vAR9FtacXOJVq+V155xamMheML+cL2QLIv3EaN9bVS+Vy3OV0jblwG2hul+/3i508CgMpAQxi9/xZDocBHvw9sgY/fy4fABxERERGLUX8HznfoU2rY46u/Z+e7p4/uT/drK0+pxhr4aOF0LohiH88OpfPZcFJe25OU4SI6PAFA5aD3W71v6/23GAh8EBEREXE+1d+I+rtxrsIf/7ep7se2/1LUbepv4Lnq8aPb1e3rfmz7n605gY+GNfrDXyfy1IZAOp32mgbR0e1pgWGO+XhADjvDvQ7K6Q+ob4Clit5v9b5bLHp/1/u83u/1vq/3/2ICn+CXKoEPIiIiIuLiM/bARydU1v9irBuHOeT3X7iNKHr2ACxZ9D6r99tSnl5I4IOIiIiIWNnGHvgo2hDQ7RQ1sTIAAERG7696n9X7bSkQ+CAiIiIiVrZf0ieyxB34KNo40DkhCH0AAOJF76t6f9X7bKlMF/jo9wKBDyIiIiJieTtngY+iDQTdnv7QBwCA2aP3U72vzibsUQh8EBEREREr2zkNfBRtFOj2tNFw9+5dbykAABSD3j81gNH7qd5XZwuBDyIiIiJiZTvngY+iE4rqnBC6bW0s8Nh2AIDoaNCikzPrfbSUCZptEPggIiIiIla28xL4+GgjQBssun39f20saCGY5wcAIIuG4hrsBIMevX/GCYEPIiIiImJlO6+Bj48GPNpY0MaE7tffJyLiUlfvh3pf1Puj3ifnKhAn8EFERERErGwLBj4ffPDBnAU+AACwsOj9Xe/zBD6IiIiIiJUpgQ8AwBLk3Llzcv78eUmlUvLuu+/Ke++9J/39/Y4XL16UgYEBuXTpkgwODsrly5flypUrcvXqVXn//fcz+qGRqr2Fgur3SFgdnoaIiIiIuBT1e/Lrf1jV/5BqC2jilsAHAGAJEuzVo98DOk9QsEdPsDeP35PH77njf4HoPEM65EzVp4gF1XmIwgIAAAAALFX097D+htbf1/rbW3+L6+9u/7f1XEjgAwCwBCHwAQAAAABYOPT3tPb20d/j+nvb/40dpwQ+AABLEAIfAAAAAICFR39v629z/f9gWBOHBD4AAEsQAh8AAAAAgMWB/tbW3+dx9/Qh8AEAWIIQ+AAAAAAALB70d7f+Ng8GNrOVwAcAYAlC4AMAAAAAsLjQOX30t3gwtJmNBD4AAEsQAh8AAAAAgMWF/sbW3+j+7+3ZSuADALAEIfABAAAAAFh86O9y/S0eDG5KlcAHAGCB0GAlbqNC4AMAAAAAsPjQ3986tCsY3JQqgQ8AwAIRDmviMCoEPgAAAAAAiw/9na2/0f3f3LORwAcAYIEIhzVxGBUCHwAAAACAxYf+btbf6sHgplQJfAAAFohwWBOHUSHwAQAAAABYnAwNDeUEN6VK4AMAsECEw5o4jAqBDwAAAADA4oTABwCgzAmHNXEYFQIfAAAAAIDFCYEPAECZEw5r4jAqBD4AAAAAAIsTAh8AgDInHNbEYVQIfAAAAAAAFicEPgAAZU44rInDqBD4AAAAAAAsTgh8AADKnHBYU8iXXnrJutxmVAh8AAAAAAAWJwQ+AABlTjissalhj6/t9bBRIfABAAAAAFicEPgAAJQ54bAmbDDsiRr6RIXABwAAAABgcVJZgc/UhNOoUKfuessAFgOTI9KX6pORSe9vgBgJhzVBbWGPr21936gQ+AAAAAAALE4qI/AZ65P236yVRFWVVGVcLfWtKRmZj+DHCZqmvD/mjikNs6Ls5laP1OfURcitPeI057z12gaddy0QY9Kz1ZSptcRCFDzW1VKzo10Gx7315pnBVi1Dm/hHNdJd55Srvjd6QzoWol4LUNaEwxpfW8gT1vY+NSoEPgAAAAAAi5OyD3ym0u1Sl6iSxIZG6UymZURDkdG0JDsapdZZbhrdc9yrYqy33jSes437uWFQ2kwDPVJg4DXyaw8kpa+/L9+rY+LkRhUU+OQda7JTGjckpCrRJKkFCH3CgY9MDkmyOylD893DJ+q1AGVNOKyJw6gQ+AAAAAAALE7KO/CZSktbTZUktvXYe/KM9khDokpqjsxtD6PFGvjMuG4FBT7WYx1PSqOGHSdHvAXzR17gs1BEvRZgQdAQJA7CYU0cRoXABwAAAABgcVLWgc/E+SbTqK6TruveAgvpw9VSlQg1vCeGJNnaKLXrEub9q6Vma7P0XAs1vK52Su3GZkndmpC09hbSdRNrpTZnmFBaOjfWZrej/9b3BNtKwX0572+TZGBfE+ebzXuaJDnqLfAYSzaZ5Q3Sdc3821mnRlabhntine7D2DFNnc428JmhzD4T15LStqNW1iaqpGpNjdTv7cnvwXJ3TPo6mqS+ZrVX103S2R8sVyDwGU2Z7fnHWSfNvUMyY3N42mP1tr0tmTNsaay/U5q2uvtZXVMvTR19MpYXGE7IULJNGje4QwULrzflruccX0LWPmeupeGp/MDHv578gkS6vrJMDJgyP+eXpVHaB0zNhLdpo6jAJ+Ix55xTc8wbvPKECF4fet02tiZlaMYTunS4dOmSrFq1yvn/2RIOa+IwKgQ+AAAAALAU+OTCbjm793W5/rm3oAwo48BnSvr2mkZ1sXOQTA66vYKcIWA6rCUpnS/VmgZuQhp6Az1BBttMY9Y0eHfWSu1LnZLs75NUb4szfKyqpl3cIzINZLM8eaDWrGu25wyTScuYP0am4L5qpG3QX2lEuurMOjtT2XBjsk+aAz2Tpm6lzXs73d4q/tCc4WlazrMJfCKV2ZTpapvUmDqr3dMlKS1PqkuadQhVXZc5Ip8R6dmmQYZp7HdouVPS01ovq3Pq2gtldjVL87p6aetNBfZZJXUz9c6JEvgE6nakt8FsV+d36nHKneptk/o15ni1l5i3jr4vqeU2x9x42F/PPfc558lcg4OtNWa97PacoYRrGqRB9xsMfLzrqedW7t/TX18ubplNXWfW0zKb89GqgWdgmzYiBz5Rj9lbb029tDvXhznmw+45bX4vcH2Y43OuD6/MmSF2NaZOmLg6w+7du2MJfcJhTRxGhcAHAAAAAErm+m458fD/IUfy/EM5tnGTXJqhOThn+OWq2S03nQXn5Ow/uGXr6rjtLCkHyjjwKWUo0JT0vWIandUteY3O9GHTcA/O9+I0yKuk+sBgzhwnUwMtUm0assGQxD6ky9tXXgN3SgYPVJsyBBr16XbTOK6WlgHd05SkD5qymPelgzs2Wy96SNfJIaeRFTaz2bzAJ3qZBw+Yuq8PhiSG8T7pPJztxeFOVFwvPaHeSyMndbmpa2c97zzmraeBnp6r3PAjj+kCjdEu57W6bq+U17ukLids8hh1t9GY9Aru9HBqkvZAwKVMpTRgaZCkv6tC23OWRwl8IlxfEylp0vDPXOc5pfFCwciBT6Frwe+9E/WYvbpqGfD+9kj3tkiX33Nrqk9arGUelJZqc8yH5+J+UL5o6POjH/1oVqFPOKyJw6gQ+AAAAABAyfjBylM/kbM7N3mukzef+mM3+EmslNSwt+58khf4iNxO7pCze1vk2qfegjKgjAOftLSvKTLwMQ3RZm382sIBbVib15rOe41+p0FeLe15RTeN1tA2rIGPt69gr4cM17ukNmfbfsjTLulhDQtqpO1q+H3FBz45T2PKGAgIwoFPEWVOH3SDs75s148QI9K1sUoStvNzd8oNG5w/8nvh+Lj12pJbr2G8Y6je1iLth9uz7m1wh5oFgrORk7VStTHYA8nH6y1mKUMOw52mDrL15WwvPFzQYUJSO7WuZwp8Zr6+3GGLjZK0DPNKH6nO3aaNaa8Fy3C+MKFjlrGkNJi/6/0QzcLUe81m283SZ7mMnDqbKcRbguzcudMJfS5evOgtKY5wWBOHUSHwAQAAAICS8YOVxtPegiwjh+53Q58Xj8tn3rJ5wxL4lCNLq4dPOODIYUR66qskcdAra7iBniE/eLEGPt6+Vtd4c+4E9eZIySmHPwF1IpHfM8JhHnr4FFPm0aQ0auCm8/LsaJbO3j4ZGg+WOmp5C59Ht16jBRqZuY1UZ24ZHf6WO/+MM6+OzpMTPC7PGj2WnOGBUzKWTklPR7u07Kw367hz/gRDEmd7BYYUOsHGjIHPzNeXvfeYixusRKufGXv4OMx8zLrO4EF3uJ3Os9R0oEuS6ZGc7bhl9ue0CunMd2U/nqWM3ogffPBB2bp1q7ekOMJhTRxGhcAHAAAAYGkxfHa3nHnuG3LsH/5PR/23LiuJaQIfmTwub+prP9uU8x+Mb57ZIm/WfM0Ng356n7yp8+oEe92MvC5vPnmvdO49J59e3i2n/XV//qD0dl+R8BQ8dz/ul9QL95tjMev8w9ek6+XTcns4P/BJ7zXbfPJx6ff/23ffFvP3vfLmGx/J7eQm6VqrvZK+Ksee+omcv3BbwlOhfj5yXM5uvFeOJdxyn27vl08DZZ0Lyn8OH2uPjQKEA44cQsFDTIFP3c5Ar5OQqZzJpke8oU1m26dsR1RC4DPTuuH6KLbMU2My2NspzTu8wETnbNmT8s7H/AY+uftJS3u12WYojHECmuoGabEcl2P3oNvDR+cx0vlmvDCr/XCn9KQGZSjtDhEry8BnpvMQ8Zh9JoZT0nWgSeq9IFDn9On0ulK5Za6TJlsdO/rXCCjXr1+XRx55RDZv3uwEHqXghzQnTpyYtQQ+AAAAAFCI3x543A1PLOprRTNd4HP3tJx2tr1eLjsL7sjlnfe6+/vpSjm9c5Oc/qU39OvnZh0/9PG3ufEn0pX4QzmmgcqTXujz8Fel643AHDyfnpben+ty3caDcnr7T6Tzp1+VYxtWSqcuCwQ+lxt1vfuzbeJ31jvvO7H5J3Is8cfufpzQR9f7mpwNdN6/qwGSBj1eIHT6hQfl2D98VU40rit8/DFQxoGPafD2t5jGZo1lWEyWnKd0hYdt5eANpfHDltkGPt6+rMOjLIyccsON9iNN5pjMfkPz3tj2W5BSA58iyxxGnyZWnXm/G7pUz/hI/LkIfNyhUDrRcfBYnCFQEQLCsTMN7n7D58Cbv8avL3dYm214khdGxhD4xDWka6ZrIeoxW5kYkk49h17dumW2D+mCXPS+qb36NOzRcKNUCHwAAAAAYK7RXjxumFHYonv6TBP4fPbOejmmr21+XT42f9+9uMn9e8NuGQn8dP7kzDpn+bGXz7m9avxtPny/nP/QWcXh06S7Xk6vnZf/zCn3ibZAz5+7VyS14avuMUUIfI78bJ1c0gJ6ZIaimWNyi/mRpGr0vX8mp88Eev4EwyYCHxtpaa8xDc0CT/5xnxQUfNLThCR3mPW3hiYbNmhviUTwEe9FBz7hBq67r9wnHHkMp6SzdzD7NK9bSWlI+OV0A5Dcp0Yp7n5rZ3pqlVJq4BO5zBOS7u0MPV7dEJojyQnbLBNkT/W3ifM4cSfEmJvAR3tM6dPPcp54lW6XamtAOCGDJ7sk6T35zBrgGdzl2fpyA0d/su0A4+5Ey3EEPv628ob5TaSKm7R5hmsh8jHf6pOuAz15IVdOj6bxpDRWJazB6tD5TukZGMs9liWK3jMfe+wxqa+vn1XYoxD4AAAAAMBco0O3nHBiGnWdovDDmRdfl48/vu364Tn5bfs66XR6xPyZ9L5zx1k1vUfDmXvl/FXnzwBX5PyTZl1/6Je/TS8oynJaep1y+j2Gzkmv7iOxTi6F2qx3+7xwKULgc2xPaDiWv3//vSMt7t9P7ZDggBnl4xMPOtsg8CnEaI8TluiQEueR3mZf6czjvy3BifNELNOA3mUarWM6j8mYDCVbpDbcqC4i8HGDhITUHdRHjw9lw5LMvrpkcNSdM2VksEuadK6eTLl0gt9EbjDhP/3pTLDR5U0EXNMkXak+s71pmswlBz6GYsqcaJDOwRFnnYmxtPTs0keUB+rMCysSz7VJanjM3VZ/m/uo7719Xl3PVeAjTuCnPY6yr/nlrpO21JCMOeUektTBOqenWOax8975bOgYlBFdx1wj6d4mqa2pcbaXrS/vEeW6vX63HsaGk9KyoVYatsUzpEsZOdPoXMurt+pQq3ZpP9AkdWvqpHlv9PrJPM4/T+96jXrM/vXxSlKGnM9P9pxWBz4/6SN6LdRIU7e/vREZ7G4y77U81WyJooHIK6+8MuuwRyHwAQAAAIC5RufrCYY7NnWdovDDEZuJe+XNE37Pm4/k3Q26/KtybK0O0crVmRfHD3L8beaFKKHAxw9iAqFOBn/+oAiBz4n2j7wFHuHAx+8JZAt1ru5wh44R+EzDxJD07K3LTDDraBrETabxmt/HQGRquEeanflKvHUTa01DN527bjGBj2nmpjvcgKmqqjbbS8iQty/T4K3d0yNDXoLo9izKfyqX02BONOQOsRlNZbeVCUwszCbwMcxUZodJU+d73Ml7/fUS6xqk82qoxkeT0lznTqLsulrq9iYDXfDmLvDJbDvRLH2Zsk/I4GH/XHmuqZf2gWC5p2Sot9kJAf11Vm/tlLSpl7z6yquHGmk6PyIj4R4zswh8lIlrSWl3JlKuldodbZIcniqqfjLHmqdfxujHPDHQLvXOnE2+4XOq5G+vKlErzb3mhuOtAfFB4AMAAAAAc82cBj6btsil5PGMV69+JJ/mzK6cHRZ14kX/8e1hX3d70EQNfMLBTBB//iACH8eFDXwyuI/7npiI1qScctYNPLVq1pj9h7qCZZj09jX7/5jvbGteGs1RyjzlrjNjncd5/LEQ4VrxHh8f6Xry6mE+j88NfJokZUs1S6WIY858fmY4Zne9eblilywEPgAAAAAw18zpkK4Igcfl7bqP+yU17C0oRNTAxw91dChYuE3jBzFxBD7T9CT6+I2fONsg8AFYkkzIUHeztPWHU50JSe1NFJg0GpYaBD4AAAAAMNfM96TNYT47s87ZxzGzbvAp7Dr58dsvrJOzbafdOXuiBj5yW/o3699flTd73HmCXO7I5e3uZM6xBD46x9BT+t4/kzdPZSdtvvuxKQ+TNgMsZaZksFXnw1kt9Xu7JNnfJyl9FP5WHSYXmHcIljQEPgAAAAAwH8zrY9nDZJ6e9VXp3LRF+nX416kd8uZad9mJo17wEjnwMZv0e/I8/IdyonG3/DbZIm9vuleObFgZCm1mE/iY/QQfy+7NQXQswWPZAcAw1t8pTVtr3Tl8jI17u8yNJs6xXFDOEPgAAAAAwHyhvXh06JbO16Pqv4vu2eNTTOCjfH5F3n3hPidoyfgP98np9v5sr58iAh/lkwtb5MRPvcewqzVbJK2PTHf+HU/go3w+clzOv/CgN9H0g3L2zEfyebHHXyQEPgAAZQ6BDwAAAAAsKT6/4z3G/Y73FK/Z89knZnuf3MkMuZoXLnqPfyfwAQAAGwQ+AAAAAACLldty7eh6OfHzB0MTTt+Ry41fc3oJdb1x21sWLwQ+AABlDoEPAAAAAMDi5dN31rs9eRJ/LMc2Pi5nt/9EOv1hZM/skOE56lZE4AMAUOYQ+AAAAAAALG5y5/Ax1vxEzraflptxjUmzQOADAFDmEPgAAAAAAEAYAh8AgDKHwAcAAAAAAMIQ+AAAlDkEPgAAAAAAEIbABwCgzCHwAQAAAACAMAQ+AABlDoEPAAAAAACEIfABAChzCHwAAAAAACAMgQ8AQJlD4AMAAAAAAGEIfAAAyhwCHwAAAAAACEPgAwBQ5hD4AAAAAABAGAIfAIAyh8AHAAAAAADCEPgAAJQ5BD4AAAAAABCGwAcAoMwh8AEAAAAAgDAEPgAAZQ6BDwAAAAAAhCHwAQAocwh8AAAAAAAgDIEPAECZQ+ADAAAAAABhCHwAAMocAh8AAAAAAAhD4AMAUOYQ+AAAAAAAQBgCHwCAMofABwAAAAAAwlRW4DM14TQq1Km73jKAIJMj0pfqk5FJ7++FYq7LMclnYSlB4AMAAAAAAGEqI/AZ65P236yVRFWVVGVcLfWtKRmZj8auEzRNeX/MHVPagI+ym1s9Up9TFyG39ojTnPPWaxt03rVAjEnPVlOm1hIL4R1Dfa+lgTo5KG01Zts1bdI37i4a6a5z6sC6/jxSTDkGW/W8tUmkGhpNSdvW1dlzrSbWSsN8fRY8xnrrzb7rpeeWtwDmFAIfAAAAAAAIU/aBz1S6XeoSVZLY0CidybSMaCgympZkR6PUOstNQ3mOe3O4jduIDfKSGZS2qEGFF4LUHkhKX39fvlfHxMmNKjnwmRySrm0JqUo0SM+ot0wxy5PdSRla8B4+0csRNfCZGmzzrvmF+yz4EPjMLwQ+AAAAAAAQprwDn6m004Mjsa3H3nthtEcaTEO35sjc9jBarIHPjOtWbOAzIj22sKdMiRT4TJnro7rwZ0HDoBpTT3P9WfAh8ImGhiBxQOADAAAAAABhyjrwmTjfZBqVddJ13VtgIX242jT8Q43liSFJtjZK7bqEef9qqdnaLD3XQg2vq51Su7FZUrcmJK09JHTdxFqp3dEug97wILN16dxYm92O/lvfE2wrBfflvL9NkoF9TZxvNu9pkmQomBhLNpnlDdJ1zfzbWadGVpsGe2Kd7sPYMU2dzjbwmaHMPhPXktK2o1bWJqqkak2N1O/tye+xcndM+jqapL5GhxlpXTdJZ3+wXIHAR4cj7fCPs06ae4dkxuZw3rFOyWBrjSl3nbSnLePf/PPqrx7pPGcJHrOei8bWpAyFC5lzzAlZu6FR2pKhYwmXw2FKhpJt0ui/7zlzXQ7r8cwc+Mz8WdDtmM9CVbP0edWS7jDX0Z6UjIwmpfk5d0hk9lqYcMuywV2+uqZemjr6ZMwSJk0MdEqT9/7VNY3SPjAhIwUCn7F+s+5W9xxPt82lwKVLl2TVqlXO/8+WhQx89P6u93m93wfDH/0+0O+FW7du5QRAH3/8cU4IFAyC/DAoqAZDYW1fQoiIiIiIi1H/P27qf8ycb8o48JmSvr2mIezPRxMVb14Xd9iLDnFKSudLtaaxmpCG3hFvJcNgm9NgbdppGsUvdUqyv09SvS3O8LGqmnZxj8g0is3y5IFas67ZnjNkKi1jfs5QcF81pmHtrzQiXXVmnZ2pbCAw2SfNgZ5JU7fS5r2d0mgayZlhWsPTRCGzCXwildmU6ar2GElI7Z4uSWl5Ul3SvCEhVXVd5oh8/J42tdLYoeVOSU9rvWnsB+vaC3x2NUvzunpp600F9lkldScD58RGzrF6YU+orDl45zUTREQ6zy5+L5nVW9ukJ2WOOWnOiR5zTWColNfrTAOn5m73XOmQqrzeNeFyZMquc0/1OHXqDMVa0yANWj/TBj6lfRacIGljvdTXrJWGvV2STJpz6QRGY5LU82bqsfGwWxa/TnKuU8NIb4Pz2cnWXZvUr6mRhm36mcgNfNx1s8fnrmu2qb2SvHWWGrt3744l9CHwQUREREQsD+cz+CnjwKeUoUCmYfyKachWt+TNZZI+rL1CmiTl9+pwGuRVUn1g0Lwry9RAi1SbBm4wJLEP6fL2FQwDHEzD/kC1KUMgTEi3S01VtbQM6J6mJH3QlMW8L7eDSglDuk4OZRpUQTObzQt8opd58ICp+/pQQ328TzoPZ3u8uBMTm0Z/qPfSyEldburaWc87j3nraYih5yo3dMkjE/iMeIFCtTS7G7ZjDXwinOepPmlJ5K/nh3PVfphzy9TBzua8HltDx2vN9WXOqfd3Xjmud0ldThDm4SzX+pku8CltWJzbc8gSjjk9vJqkPbR8KqW9iBok6V+CXp3UmP3a6iTS8Y26568xOc05q3A09PnRj340q9CHwAcRERERsXzUXj/zMVVBGQc+aWlfU2Qj1zRQm51wwNKYmUhJk3mt6bzX8HQa5NXSnlf0QWkJbcMa+Hj7an4v1JhWTOO3NmfbfsjTLulhbRibRvjV8PuKD3xyntSUMdAIDwc+RZQ5fdANzvoKttNHpGtjlSRs5+fuVCB48sKKUM8Rxa3XlmmCDoN3DNXP1Tm9aPQY8wKIINbAZ+bzPPVec+77AjhPTwsPZQvhhiXh/Wb/HjnpBkL5xzohqZ16XNMFPoU/CyPn26X9cK5uLx5TBKeHT7BH1gwMd5prIHu9TFcn6SM6fCx0fNZ9eb2TLOd/KbFz504n9Ll48aK3pDgIfBARERERy8+5Dn2WVg+fvB4tQUakp75KEge9soaDgQz5wYs18PH2tbrGm3MnqDcvSk45/AmoE4kCgcU89PAppsyjSWnUkEHn5dnRLJ29fTI0Hix11PIWPo+RJv71yuwHPSPnm5whRtbQSrEGPjOfZ3svrgLcnZChwaR0HW6X5h3Zuptuv074UmBIlhOWlNjDx5mnxz+H3txAbVfd16bbpwYxY+mU9HS0S8vOevN+d94drWf/GpiuTsJhkLMvnRspeE151uh1VOzQzApDb8QPPvigbN261VtSHAQ+iIiIiIjl6VyGPuU/h08xPRTCAUcOoUZzTIFP3c78Hha+fk8LlxF3/7rtU7YjKiHwmWndcH0UW+apMRns7XRCDafRXqVz+qS88zG/gU/1gT6vh4g3F06hJ3TNdeDjB2GJtaYeW0yddUlS53V6r2Xa/c4u8In4WYi6T53HSecm8sK89sOd0pMalKF0V871UnTgU90gLZZryrF7cMn28Ll+/bo88sgjsnnzZmcyt1Ig8EFERERELE91eNdcUcaBj2lU9reITiacPxwnS85TusLDtnLwhvD4YctsAx9vXwV7moQYOeWGG+1HtIeK2W9eWDEPgU+RZQ6jTxOrzrw/Le3VgbltChJP4JN7rGbfOnFy3jxIhhIDH/cpWP68Q4Xxh7qF54iSgekDH/d9tvmKvDBnhrDJLV+Ez0LgKV2FAp+xMw1u2cLXoDffTibwcdaz14kbUgWOT4d4FRPOLhH0vqm9+jTsmc3kbQQ+iIiIiIjl61xN5FzWgY9pamQa9nkNbIP/VKXsk54mJLnDbeSGG57aIyERfKx10YFPtiHt4u4r/FQjh+GUdPYOZp/mdSspDQm/nG4Akv/kIne/tTM9tUopNfCJXOYJSfd2hh6vbgjNkeQEDJbwY6q/TZxHkjsTZM9F4GMY7XHqNG94XImBj4wnnaek5fe+GpGeHbVSd9y9xu0hivY6yp3TJrxfN7z0J+4OMJ6SJmcC5OkDn5zPgi2UGnV751QH6qNg4FOg5467PHC9eBNK59eJV5bg8abbpdoaSE3I4MkuSU731LkKRe+Zjz32mNTX18/6Bk/gg4iIiIhY3s4FZR74GLyGfdUa75HeZl/pzOO/LcGJ80SsKqnZ1SPpMW1sjMlQskVqw+FAEYGP25hNSN1BfQz3UDYsyeyrSwZH3YbNyGCXNOlcPZly6aS8CXfCZudvg/9EozPBRpc3eW9Nk3Sl+sz2prkgSg58DMWUOdEgnYMjbqNtLC09u/Sx4oE688KKxHNtkhoec7fV3+Y+3ntvn1fXcxT4GLTXS6HH7We2WcR5Th/R46uRpu5BGck55mxQOJFsdNZpSQ7JmK4zMSJ9B+uktiZUN3n79R6FnqiTtn63TseGk9KyodZ7xPlMgY8h77MwJEODfZI83CBrtc435AajBYd0eddzQ4d3nOYzku5tco5Be3BlrxdvsnGtk960e7yjg9K5Y60pc/jc+deMOb6UVzdjQ5IydRN+5P9SQQOQV155JZY0n8AHEREREbG8nYtePuUf+CgTQ9Kzty4zqazjmjppMg1WW7+BqeEeaXbmKPHWTZgGakc6d91iAh9t+Ha4AVNVVW22l5Ahb1+mIV27p0eGvIa327Mo/6lcTrgQnodmNJXdViYwsTCbwMcwU5kdJk2d76k1ZffXqZLEugbpvBqq8dGkNNfpZMH+equlbm9SRjLX8twFPnpenPl8zDa6/HqcReCjoUW6ww1PCh/zhAwe9q8F1ZvXaCDCfvPqtEaazuvj5u09bqzYPgt6fR/uk7HQ/aNg4GPqbai32QlB/W2s3topaXNd5F8v+cdbdyTt9K7Lr9fwusY19dI+YPuUQjEQ+CAiIiIilrelzuc5HZUR+GRwH/c9MRGtt4DzOG1jfH0LzP4tQ8scJr19xRHamW3NS3+IKGWecteZsc7jPP4FJ8J15tdLoethOrz3zq6uivssWPEenx9pG/66kXYXQ9kgBwIfRERERMTydi4mb66wwAcAYOlB4IOIiIiIWP7GDYEPAECZQ+CDiIiIiFj+xg2BDwBAmUPgg4iIiIhY/sYNgQ8AQJlD4IOIiIiIWP7GDYEPAECZQ+CDiIiIiFj+xg2BDwBAmUPgg4iIiIhY/sYNgQ8AQJlD4IOIiIiIWP7GDYEPAECZQ+CDiIiIiFj+xg2BDwBAmUPgg4iIiIhY/sYNgQ8AQJlD4IOIiIiIWP7GDYEPAECZQ+CDiIiIiFj+xg2BDwBAmUPgg4iIiIhY/sYNgQ8AQJlD4IOIiIiIWP7GDYEPAECZ44c0cRoVAh9ERERExHiMGwIfAIAyJxzWxGFUCHwQEREREeMxbgh8AADKnHBYE4dRIfBBRERERIzHuCHwAQAoc8JhTRxGhcAHERERETEe44bABwCgzAmHNXEYFQIfRERERMR4jBsCHwCAMicc1sRhVAh8EBERERHjMW4IfAAAypxwWBOHUSHwQURERESMx7gh8AEAKHPCYU0cRoXABxERERExHuOGwAcAoMwJhzVxGBUCH0RERETEeIwbAh8AgDInHNbEYVQIfBARERER4zFuCHwAAMqccFgTh1Eh8EFEREREjMe4IfABAChzwmFNHEaFwAcRERERMR7jprICnym38aBO3fWWLXmmMnUyMektAoCKIhzWxGFUCHwQEREREeMxbioj8Bnrk/bfrJVEVZVUZVwt9a0pGZmP4McJmuI/OWGmtHEUeTcTMni4QdYmgnVSJau3tsvguLcKAFQE4bAmDqNC4IOIiIiIGI9xU/aBz1S6XeoSVZLY0CidybSMaANiNC3JjkapdZa3yeAc92wZ662XqiqzH+/vuWFQ2qqqpL43SkNsSgZba6QqUSvN3YNunRhHBrukeUPCLG+WPnr7AFQM4bAmDqNC4IOIiIiIGI9xU96Bz1Ra2mqqJLGtx96TZ7RHGhJVUnNkbnsYLbrA51aP1Jt1G5MT3oIA40lpjBwcAcBcomFHHITDmjiMCoEPIiIiImI8xk1ZBz4T55ukqqpOuq57CyykD1dLVSIUxkwMSbK1UWrXJcz7V0vN1mbpuRZqeF3tlNqNzZK6NSFp7S2k6ybWSu2O4JCotHRurM1uR/+t7wm2lYL7ct7fJsnAvibON5v3NEly1FvgMZZsMssbpOua+bezTo2srqqSxDrdh7FjmjodbDPlqZK2AgmUMzTM7+EzlpJms73Oq1My1NssdVrONTXSeHhQnFKOD0r7jlpnaNjqmnppTo44b8shSn06mH0k26SxZrVZLyFrnzPrDU95x9dpahNg6XDp0iVZtWqV8/+zJRzWxGFUCHwQEREREeMxbso48JmSvr1VUrW1R4rqqzI56PYKcoaA9Ulff1I6X6qVRFVCGnoDYYYTmtRL085aqX2pU5L9fZLqbXGGj1XVtHvhxIQMmeXJA7VmXbM98+++/rSM+eep4L5qpG3QX2lEuurMOjtTbsCiTPZJc6Bn0tSttHlvp9Mzp/ZA0vzbbGvYFqh4TKSkyby/em9Kxmaaw8jvDbSzSWp3uMeZPFzvhEs1re3SUlMnLb0pp+ztW92gpqU/cCFGrU9/mJkzt1KPpHQ/GqStaZC2A/PRQwpg8bF79+5YQp9wWBOHUSHwQURERESMx7gp48BnTHq2VklVazExwZT0vZKQquqWvHl90od1zpsmSfm9d7xeMtUHBs27skwNtEh1VSKn94x9SJe3rxqzPGdfUzJ4oNqUwQ+NDOl2qamqlpYB3dOUpA+aspj3pXPOdzFz+IiMnG8y2zT1s6ZOmg73SCo9Yn9ymRf45AROhqHjGmIlpPm9YCGGpHNjsM6LqM9rnaY84RDIcL1L6rScBD6wRNHQ50c/+tGsQp9wWBOHUSHwQURERESMx7gp48AnLe1rigx8pvqkuVBoor1izGtN573Ywwl8qqU9r+iD0hLahjXw8faVG5h4XO+S2pxt+yFPu6SHNQCpkbar4fcVF/g4jKclebjJHaZl3qu9a+r29shQMNnxAp9wOd1jqpeeW94Cj8FWsx2/V1UR9Zk+Egq5MkxIaieBDyxtdu7c6YQ+Fy9e9JYURzisicOoEPggIiIiIsZj3CytHj5euGGf22ZEeuqrJHHQK6s3pCsceNiCF2vg4+1rdY03507QDe4j5HPK4U9AnUhIjTmm/FNdQuATYGp8SPp626ReQ7JEgyT94ypQJ5ECnyLqM+d9IUZOam8iAh9YuuiN+MEHH5StW7d6S4ojHNbEYVQIfBARERER4zFuyn8On41dYplG2M60AUUoQIop8Knb2S7th+2mciabHnH3r9s+ZTui2QU+GSYHpaU6EGzNWeCTW58EPgB2rl+/Lo888ohs3rxZvvjiC29pcYTDmjiMCoEPIiIiImI8xk0ZBz4iU/0tzgTI+cOusuQ8pSs8bCsHb6iWH7bMNvDx9mUd0mVh5JQbsLQfaTLHZPYbemqXbb+FmBjWyZOHcubkyTIlqV0zhzaRAp8i6pMhXQD56H1Te/Vp2HP37kwzrBcmHNbEYVQIfBARERER4zFuyjrwMU0Naa+pskyM7DI12OZMXFx30u8xMyHJHW5gEe5DM/VesySCj3gvOvBplr6c8+PuKzwZssNwSjp7B7NP87qVlIaEX063Z0xiW7iM7n5rM8dSGLc8BR5X7z0BrNp7AtisAp9i6tOZmNoyafNwJ5M2w5JE75mPPfaY1NfXzyrsUcJhTRxGhcAHERERETEe46bMAx/DaI8TllStqZc2fXy42Ve6PyU9re6jxfOCEyd4qJKaXT2SHtPGxpgMJVukVh+DHpw7p4jAR7epT+6qO6iPTA/0rMnsq0sGR92GzchglzTpXD2ZcmkPF32aV6D3i/PkqoQ0nAk2uryeMDVN0pXqM9ub5mLw5gOqSugj1ftkyDnOEUknO6XRWR7THD5K1Po0/xo86D6uvfalFndY294GWbumWZq1xxGBDywxNAB55ZVXZh32KOGwJg6jQuCDiIiIiBiPcVP+gY8yMSQ9e+ucgMd9GpVRH0feMZjfu8YwNdwjzRv8J1cZE2uloSOdu24xgY9MSbrDDZiqqmpzetbk7UsDjz09MuT1SHJ7wuQ/lSt9RB9r3pA7tGs0ld3W3r5AmGLBVidm32t/0y59waLPNvAxRKpPj7H+Lmne4U5eXb+zUwbHvW0S+ACUTDisicOoEPggIiIiIsZj3FRG4JNhymtARKuoKa+xEV+1mv1bhpY5THr7mv1/zHe2FbnMd/06McZ//eRQan06gY91fh8AiEI4rInDqBD4ICIiIiLGY9xUWOADi5a7Y5I60Jw/GfXdIemsq5Iq21xHABCJcFgTh1Eh8EFEREREjMe4IfCBeWJEerYlpCpRK42HeyTV3yfJ7hZp1KFg4aFrAFAU4bAmDqNC4IOIiIiIGI9xQ+AD88iEDCXbpHGjO4dP7cZ6aTrcI4PR25YAYCEc1sRhVAh8EBERERHjMW4IfAAAypxwWBOHUSHwQURERESMx7gh8AEAKHPCYU0cRoXABxERERExHuOGwAcAoMwJhzVxGBUCH0RERETEeIwbAh8AgDInHNbEYVQIfBARERER4zFuCHwAAMqccFgTh1Eh8EFEREREjMe4IfABAChzwmFNHEaFwAcRERERMR7jhsAHAKDMCYc1cRiV6QOfd+TVjZtkU8uFQODTJwfrnpPnWi8S+Mybo3Lk5ytk2T2PSmva9no8Dr3RJFue3yJNvaPW16fuDEnP/u1S/cj9cv//eFSqX9wnPUOW9RwvyxGzLd2ezUL7GB04Ik3PrpGq/3G/VK1eL01HL8uoZT2r14/ImnuWyYpHWmXI9vosvPy6lvuIXLa8Vi6O9ur5bZKeUfvr86Fbj9NZ3nUc1eB1/uhT22Vfbzw/5hERceGNGwIfAIAyJxzWxGFUpg98OuSXX/mK/MH644HA5w3Z8Ad/IH/wqy4Cn3mzT7Z/e5ksW7ZcNiVtr8/S0T7Z9/P7zPZ1H8ukar/lh8WVVnn0Hvf1Fd/WwOc+WeGsf5+sef1y/vrpfVLlbc9m/j7GpWfz/bJcX1/+dbN9s49vLHfWXf79LXLudnDdAl7YLvc5798k52yvz8KejVruTdJjea1cHNpfZY6hSvbNYWg4k249Tmd51/HMBq7ze+4LXOfL5f7NPTJufQ8iIpaTcUPgAwBQ5oTDmjiMCoFPmXhn3JyPcftrs3Do6Hq5f7k2Pqtky8ZHnUZ3fhhzWZq+Z9ZZvkr2XQksH+2Tpr/XxupKaRoILFeTm5xGbXVntDKPv7HeWf++jT05PXq0fBriLK85Ga0xfHtURscty2cpgU88VkI9zsbx3k3yzbzrfNzUiwau35Qt53PXR0TE8jNuCHwAAMqccFgTh1Eh8FnK9sgm7Vnw5D7p0x40pjFqDXy83jr3/4ulJ89Ak9xvec94Z7XZVvQeSW4QUC0n88KacTn51MKHBAQ+8bjUA59zmzUgNecgGJyqo0dkjfkcffOFc7nLERGx7IwbAh8AgDInHNbEYVTcwKdPunc8Lvd/7Y/lK1/5inzlT+6Rv9lwTK5ECnzelz1/8wey7D8/L0lb4PP+Xics+OY/vT1z4DPaJ60bH5X7vKFDy5atkPse2SSt/aFeIl4AoSHDeP8+qf7eisD6W+Rk3rwyGmyY1zf2yNT1Htny46+7QyqWLZevf79a9l0oMGfN1Kj07a+Wld7QosLb99Zt2ySPftsviw59elQ2tfWFeqZkyzJ+frus8rad36sm1/yGcuCYrrRK9fcDx/TjTXIk3KC0ell6TgWOfYbAx1rGAq8VGy5MFwREDwkCdeIvy5TvsowmtwfOzwpZubpJzkWcz6b4oMJ27ViuZWPhbQ/JvgfNaw/uy85JFDiey6+b7XuflU29gfcNnZQtj/hD7sx1+D1zjZv9Fj4nxVzn46Hr3PsMWY7LZlH1GPFYt69eKV/XXmpanuVfN+d1u/RcD6xjzBz7lVE592LgHnPPSlmz61yBeaKin8Pi6tCiF/gs30zgg4hY7sYNgQ8AQJkTDmviMCrp9CU59Itvype//Mfy53/1U3nh5VZ55aVa+bv/9ifyF7W/luoIPXw+evUR08D5U9l4Oj/wGWxaaV77ptSfm6GHz23TWHfmqVkhVU81SesbJ+XI/k1SpQ2z5atyJyv2G4Kbt8ia5f76R2Tf82vcRmF46JEfBNRskS3fXi73P7ZF9h09Ka27qr3A5T7TiAw34obkiDOvTe76bnnuD63vD8kwjesHq6Wp7aScPLpPNj2ojeLlsupA8IvaK8sja5yyr1y93pmstuBEyZ75DWVvOz+vlup7vi6rnDpolaanqtyG/vJqORll3pughQKfqXOy5V4tc2tew/hyszak84eiuOVdLz3jpiHca87Ni1q+czJUYLjVaJsOJ7MMaTHXxfoC+853msDnKQ0MTOP++X1yxJybLY9586h8rynSJMHFBT6XpfUR99xnr5318uh37NdaSYHP6jVy33I97+5kx0f8IXVX9skqZ4ied6z+NbF8jWzZbAt8irnOp+TcC/66253P6MmjTVLtHVeU4UglBT4zHasp56POsZqyv/ioO0Tx25vkXOBa8wOf6qdWyorvmbrYf8TcX7Z452SZrMzrvWY7h1695J3D4urQpvs5Wi7r35h5XUREXNzGDYEPAECZEw5r4jAqlw5Vy9e//GX5ZvXr8n7OkK7z8tIPvuL0+JlxSNdHr8vPTKPpTzecCgU+l2Snzv3yvZ0y6IU9hQIfnatl5TcsjSPTqNNGX85QB68hqAHB+jdCQcn1VlmjDb6chrwXBBirdoUadrfPyRZnfpr1OcOJRl9fI8vN9quPhrbvB1PB7Q8dkfXf/7rcr712guuaRqPTYL93S2ASYb8stpCpsAUDH8t23LIvCwVNESwY+Lhzj+hcOvc9Yhr65y/L6JVzTuPaWZZ33KNyZLWWbYWscBrIAU0DuNo2ybPWlc4HtHylrN9/UvrMj5u+N/aZetVl4QCvkIUDH9s2/PAiyrCzYoIKt/6Xy6r9oeO8c1ma9HoIXWulBD72OvHWt7w2fmq96Nwx4cCnqOvc1O96s43lT4XmU9J1v7dS1jSHe7PlW0rgYz/Wcen7F3P9faPwsa55PXtMbuBjyv73+0IBn/n863GGJvoueA79ADJQL8XVYVjtceSGtPllQ0TEcjRuCHwAAMqccFgTh1FpefzL8uU/+rm8esUyh0/nevk3kebw+Z0cf+ZPZdmf/qMcHwsEPhd3ykrTkFnZNJgJewr28Cmo14h/1tKIDzaEA17epb2K7g9MJOxtwzTqbA1Nf8Lg7ATDXsO5QK8Sd36aaBOsug3c9flBzeojEXqsZC0Y+NjqYPykVOtrweAjitMEPs6QlQPV7lOwAt73ZKv05Q2LGpWeHWuk6sE1sv1onwzpZNN3xmXo/D6vN8g3Zf0pS9iV1uF2udtftmyVbIn8yGqvTiyBz3JbXRSYf8hm9KDCm+D6f2yXPtvr57fkhRGlBD7WuV6841kZDjUdvW3lBD5FXuf+sKNir6uA7rEWNme41nTHOp3e+6qas/XgBj7Lc7fveflf7jevBetl+nM42n9STmpvtTv692zuFV7Iacq1cnPuZOWIiFi+xg2BDwBAmRMOa+IwKr/8D1+WL1f9i6TM/T4v8Bk6JD+PFPh8KrdP/KP86bI/lZoTY5nA59JL3zGNmUfltY+yYU+UwGd8dEj6et1hUdv9IUpRG71qXqN6hpAlPGGqF5h8/ckm07DTxl3IXdXy9ZztB7w9KkP9PWY9HcbkDwEJNiYtoUQECwY+1u2Uto/CgY8/vGWFVD3fKueujJpro0962ra4x3fPo9Ka1wOjgLdN3eb1wDLn3Oxbh+Is/061NL2hIdGoXD5/RJqe1KFXyyM+srpw4GMNdaZ7LWTkwMe7dgrPxeL2kgmWsZTAx1Zm7WkyXY+lvGCj6Otce265AYUzpKvXnKcihw26x6rDzdzhWWEzw7XUyOdHn2B3Wc5pmduaZL0/XC9Qx4XnL7K8NuM5DDiLe4V7vszxBYIpREQsf+OGwAcAoMwJhzVxGJXqL39ZvvyLQ879Pi/wGS3mKV2nZOOfmkbSPx6XMX8413dNo+uR12QkEPYUDnxGpef5VdnJV9XlX5f7H6lyeglFbfTaX58pAAm97r0/U44CBvc/empLZgJm1+Xy9e88KlXaU6DMA5+Cw1tUbx6V5T+P3mOpZ6PW0yppzUxo6w2r+bY5vrwAwZ8f6T7ZfiH8WljLcU93rUQOFIoIfGbcZn4Z4wp8pgs1rK+XcJ1P3RmSk6HP6fJvrLJMTm43cj2qM9Xl7T7Zp3PyeOVwvOc+qfrxSifwDdZxUYFPEddFSXXo6daF7cl0iIhYzsYNgQ8AQJkTDmviMCpOD5+H9sjFWfbw0UeyJ5+71zRgfuEO67q4U75jGjq/6Pg4J+yxBz7jcm6zO5/Kys1eD5JMI8hrIFsavdbHhKve0JbsHDbeNsJzj/iG/4u+1+Pnvhd6RL8bC+qVcTzpzm+z/Pub3PltdAiTt223UVfOgY//WPTgsLRce56d/vWweQ1sf2hVoZ4O3jxOM/eEsBz3dI33Ihr2kYOKGR+vnV/GwtsupYdP4aGGeT18irzOcx135nHSiY/XeE/Jy5/4ON/I9ahOe36GpNUZDnWfrNmlcz6NyrgzxMrovS9Yx0UFPjOew4Al16F3bqPWBSIilo1xQ+ADAFDmhMOaOIzK7od0Dp+n5HVb4PPmr+U/FRH43Hn7ebnXNH5+cWxMklvvlWXLa+TEx7lhjz3wOSebtMeAdR4Mr4FsafQWmjfDbcAFG77eNnImTw7Yu8kZApIddlH4qVQ2z23Whuej0hp6FLRa/oGPPwHzTIHPGjniz+Vz+5zs0yE61smZ/QAp0LPBG4I3U+AzcwPcctzThQbTBgq5Rg8qvGsneL0Gtcwb5G7b1tPjsjT9j9C2piuzX4/W4xmV1kd0P8FrsbjrvLB9sl3LWejzFTC2wGeoVVaZ1775vOWa8O8PpQY+M53D226I44bHcdUhIiJWinFD4AMAUOaEw5o4jMp7u/5e/ujLX5b//eKZUODTL3se+ZNoT+nyA587A7Lzf5vGz8O/kF/8Z3d418ehsKdQ4FOo0aSTnjrzcdgCH9uTrvwn43zb9mQs27Ak/+lQa+RIILBxJ362P0nr8r9UyX2PbJKT3pCkcy9806xrCXz8+WrKOvDx62J5/lOI1NEjljl5vElv712fP0TrSpNz7pbXBHpbjZ+U9c42tkuf30sjYN8Od/8zP7LactzThQbTvRaymKCi8LWjYVd+ODh0YJVzfOFrc7RNh9KZ/UYNfPRzpNe+rd7Pb/Em3M4NPYq5zseT26Xq27bHjHvB1P+Y7mlUrrEFPn4vHEvgc/lf9JjMfkoOfPx6sU0u7l/b2ftLMXUYdPxKj5w8P/O1h4iI5WXcEPgAAJQ54bAmDqOSTvfIi1V/JF/+8r+T//3sLjnSk5TeY7vl+R//F/mTRx6Vh4oKfO7IpabvuY2tZfdK/duTeWGPPfDJPiI7M6TLf+z3t+9zG6qWRu/9T1VLlT7me9cR857Lcu5ok/cUqHDjywsCHquW6nvdiYd7+i9LX2+rbHlQh6NYgqDxc25wZLb/6Iu6/pAzifC+mpVOI3x5cAJorzGdHdJlytK2XR7Vsus2yjzwydSFTtr8VJO0ehPStr64RlbqpM2mjrYkcxu7/mPcl91TJVva/PUfdSZmdiZ5DjW8NfRw6vU7j8qW/UfcCW+P7pNNP/66s3zFI6323hY5znXgU3iy4S27Ak9ZMvW1Ra/DzLWpk5C3yvZH3Gs871q7fkTWOMGg1u922bffm3j422tkzV+b5ZEDn2y9O5NfHz0nl51hV+tl5XJT9tXhp1EZi7nOM+uulE1tZtvmd6MzsfbPvePKDKEsbGyBT2YC6eyQrqH+k065V5jP3azm8FEz1/x9Xr3k3i9yJmEupg59NbByznnhSbYREbE8jRsCHwCAMicc1sRhVPT+/sH7vbLzp9+Rf/eVrzg9etQ/+3GDvPVBMZM2u4HP5PBrktDG1n+ul+Rk9MBHG3AnN+ZOwKqN1iNXvEa8tdF72Xl0vBMi+O/TgOFUuCdKNggY72+SR50nZ3lqo3x/gQlnbZPCLv+6rHo+/xHKo52b3PAjuN3XL3sN3DIPfNQ7l+XIxtCk2qax+vUfb7H2XlBHL+yTam9+l+D6PZahb876yabMfDAZ71npnJ+8BrNVy3HHGvhMY/D6VK0TCptjabMPWxvv9x9Z76674kGtp8tFzeHjO5rcnjuBuLkW13eOyuVCoUcR1/nU9R4v9Ais652jhZi0uekxNxD0y7Liwe1yztSlvi94HRQd+KjWc2i7vxRYt1AdOp6T7U4ouCov/ERExPI2bgh8AADKnHBYE4dRcQIfc5/X+/3w+7+VgYEBGRz80Pk+0O+FW7duye9+97tA4POxjI+PyyeffDJt4POdly45j2aPHvh4jvsTneYPj8gYbgje0ccyT/eecBDgr+/PwzGDme3PtH5gPcvQpEpxvNhj9OY8sU/+azHKNVAuFnssWldR62kGiz5Pka9zo39OF8M5muuyFHMOi6lDtYLvE4iIS9W4qazAZ8ptPKhTd71lIHJ3KlMvE5PeskXE1GifpPpHJP7LG2BpEA5r4jAqOYFPzhw+pQU+l17SIV3fk50X9dHsJQQ+USyiZ4ZriT1eEBERERGLMG4qI/AZ65P236yVRFWVVGVcLfWtKRmZj+DHCZrmPq6Y0sZRMbu5OyKp1npZnVMvCVn7mzZJjXrrLDgj0lWn5aqXnlveonlirLc+UC/51vdGb/QCLCThsCYOoxJP4JOWU68dlld/k5BvL1smy6uPy9gkgQ8iIiIiLi3jpuwDn6l0u9QlqiSxoVE6k2kZ0QbEaFqSHY1S6yxvk8E57tXiBgdmP97fc8OgtBUTQkya9TckpCpRK40dSUmPasNqRNLJTmn0lrcNLo4+NVPDSelKmgvR+3u+cM9brbSc6ZO+/nzTtxZH/QDMRDisicOoxBL4XN4jf62ByrI/le+s2yvv3nLDHgIfRERERFxKxk15Bz5TaWmrqZLEth57T57RHmlIVEnNkbntYbT4Ap8pGWytlqpEg/TYevJoGGTqraqmXeZpsN2ixD1v89+zCMBHw444CIc1cRiVeHr4BObw8YKeOQ18EBEREREXoXFT1oHPxPkm02Cvk67r3gIL6cMafITCmIkhSbY2Su26hHn/aqnZ2iw910INr6udUruxWVK3JiStvYV03cRaqd3RLoPj3jqSls6Ntdnt6L/1PcG2UnBfzvvbJBnY18T5ZvOeJkmGgpmxZJNZ3iBd18y/nXVqnKFZiXW6D2PHNHU6kZIms27dyRFvQT5Tg21SbdZpfs+7qPzjDbfzxlLSbPbXeTX/75Fks9TXrM7W4XDwAh2T1B6vnKNJad7qln91Tb009+b25kl3mPX2pMw7PCLVfZaJgU5pes4d0re6plHaB0z9FjqeAMUEPhPXktK2o1bWJqqkak2N1O/tkSFbW33SPd81a8x6psx1ut5koC4APC5duiSrVq1y/n+2hMOaOIwKgQ8iIiIiYjzGTRkHPlPSt9c0qrf2ZIOCKHi9W9whYDp0JymdL9VKoiohDb2BgGSwzQkDmnaahvpLnZLs75NUb4szfCzbM2ZChszy5IFas67ZnjMUKC1j/nkquK+awHAqdw6bxM6U2ZrHZJ80B3omTd1Km/d2SmNVldQeSDrDjfqGbWmDy9R7zZGDjAze8ea951aP1Jv9tvmJmfd3484mWbu1TXpSpiw6TEx7DCWaJJUJZMakZ6tZtqNJmtbVS1tvyj3+HTVmPwlpOp8t/2Br6DxGqnuXkd4G59xl12uT+jWmfls1DJy+DqIGPhqO1QT24RyvMywu1IPK7zm1JnC85nyv3tYmLfVmeevc9gGD8mP37t2xhD7hsCYOo0Lgg4iIiIgYj3FTxoGPFygU1Yiekr5XTEO9uiVvXp/04ZrcwMIJHaqk+sBgTm+UqYEWqTaN/0wAYrAP6fL2VROeQ2hKBg9UmzIEgot0u9RUVUvLgO5pStIHTVnM+9I55zv6kK6ShpgVGfhoQJPTf2jUXZ4tn3d+dJs5vZdG3OWBgMce+ESoe+3JpMGYuQZyqsoLzKIGPl3X3EZnrt4Wp/qkxWwrXBa5m5Z2DfMCQd1Qh15D+cPoRk7WOcdD4AM2NPT50Y9+NKvQJxzWxGFUCHwQEREREeMxbso48DENbh02U0wj2jTem3NCiQDeMKhMzxMndKiW9ryiD0pLaBvWgMXbV2bIVJDrXVKbs20/5GmX9HCX1GkPoKvh90UPfNKHdZiVJfC5npL2w+25nvdimyIDn2APHZcR6cnpxeIFPsGeSx4jp3Lryx74zFz37pC+Rklahnmlj1TbjyeAe97Mvm165Zmut5S7/yZJOQdorsfqKqk+bLnWvWuLwAcKsXPnTif0uXjxorekOMJhTRxGhcAHERERETEe42Zp9fAJhxc5uIFF4qBX1kIBiCV4sQY+3r5W19S6c+4E3eDON5NTDn8C6kQiv8eKQww9fJx5bfxyeHMCzXS8BQKf/DoMn4/C5ydcvkJDumaq+4LHaYgyrM19//Q9fKbbh9urKSFtzvxG052fEenaSOADhdEb8YMPPihbt271lhRHOKyJw6gQ+CAiIiIixmPclP8cPhu7cocWTce0gU8ooIgp8KnbGepREzCVM9m0N9RJt33KdkTRAx837KiddjLrvO0t4cBn5nUKBD45dUHgA6Vx/fp1eeSRR2Tz5s3yxRdfeEuLIxzWxGFUCHwQEREREeMxbso48DGN+v4WZwLk/KE/WXKe0hUetpWDN1zID1tmG/h4+7IO6bLgDnOql/YjTeaYzH7zHqcePfDx9z3t4+jT7blP6fKPN7zfRRz4xDOka/p1codthRhocd/v1BlDuqB49L6pvfo07Ll79663tHjCYU0cRoXABxERERExHuOmrAMfp5GtT0XKmxjZxX26UvDx5BOS3OGGC+E+NNojJBF8xHvRgU+z9OWcH3dfOU/f8hlOSWfvYPZpXreS0pDwy+kGJYlt4TK6+62d5lHrQdJH9GlYNdKmjygPc3dEujSMqTb15pfBm3S5MZm7vhtELc7AR8ZT9kmbJ1JFTdo83ToynnSejpbf60p7mCWkqi7bw8ypc8ukzUPHmbQZ8tF75mOPPSb19fWzCnuUcFgTh1Eh8EFEREREjMe4KfPAxzDa44QlmUdhm32l+1PS01rvzlETDk6cJ2JVSc2uHkmPaWNjTIaSLVIbDg6KCHzc3jIJqTuoj0wfygY8mX11yeCo27AZGeySJp2rJ1OuCUnt1Kd5BZ7adV0nbk5Iw5lgo0vXM8dZ0yRdqT6zvZkuhhHp2Wa2W7Va6lt7JNWflqHhQelLtkvDOrM8USvZR8Mr7uPhdXlztz5S3K3DtXV1UmuOYVEGPoaRM43OeV69tdkdKnegSerW1Enz3pnDnEiBj8EPz5p60zKmDdSxIedR/LmP1zfoY9mdx7XXSuMBd9he82/Wyuq9zfTwgTw0AHnllVdmHfYo4bAmDqNC4IOIiIiIGI9xU/6BjzIxJD1765yGf+YpS6bR39QxmN+7xjA13CPN2jD3102slYaOdO66xQQ+MiXpDjdgCs+dk7evqoTU7umRIa9HktuzKP+pXNbeIqOp7Lb29uX2arEyIUO9zVKnTzML7H/tb9qlz9aeG++Ttjp9wlegnKb8i3VIl8/EtaS076x3J6Pe0SbJ4alIYU7UwEfPr9ajhoJ+PSbWNUjnVVvvqTHp626WRmdi7HrvGnTLTeADc0U4rInDqBD4ICIiIiLGY9xURuCTYcprQESrqCmvsRFftZr9W4aWOUx6+5r9f8x3tlVsmYs61qmYyrmAuGFOgbl3Sqa46yuLG/hUTzenEsAsCIc1cRgVAh9ERERExHiMmwoLfGBpMSFD3c3S1h9OdSYkpfPrVAeGyc0HYylp2dMjI+GwbLhT6qoKTRYOMHvCYU0cRoXABxERERExHuOGwAfKmCkZbNX5dVZL/d4uSfb3Saq3U5q36rC00Pw684E3n1RiQ6O063xS/UnpOtDoDAXLn4QbID7CYU0cRoXABxERERExHuOGwAfKnrH+TmnaqnPmuDbu7ZLU9QXqTTMxJMnWxkxZarc2Sbs+ka3Mh8jB4iYc1sRhVAh8EBERERHjMW4IfAAAypxwWBOHUSHwQURERESMx7gh8AEAKHPCYU0cRoXABxERERExHuOGwAcAoMwJhzVxGBUCH0RERETEeIwbAh8AgDInHNbEYVQIfBARERER4zFuCHwAAMqccFgTh1Eh8EFEREREjMe4IfABAChzwmFNHEaFwAcRERERMR7jhsAHAKDMCYc1cRgVAh9ERERExHiMGwIfAIAyJxzWxGFUCHwQEREREeMxbgh8AADKnHBYE4dRIfBBRERERJy9n3/+ufcLOz4IfAAAypxwWBOHUSHwQUREREScvV988YX3Czs+CHwAAMqccFgTh1Eh8EFEREREnL137971fmHHB4EPAECZEw5r4jAqBD6IiIiIiLN3LiDwAQAoc8JhTRxGhcAHEREREXF2zkXvHoXABwCgzAmHNXEYFQIfRERERMTSnYvJmn0IfAAAypxwWBOHUSHwQUREREQs3d///vfeL+v4IfABAChzwmFNHEaFwAcRERERsTTnMuxRCHwAAMqccFgTh1Eh8EFERERELE4dxjXXYY9C4AMAUOaEw5o4jAqBDyIiIiJidOdqgmYbBD4AAGVOOKyJw6gQ+CAiIiIiFlZ783zxxRfzGvT4EPgAAJQ54bAmDqMSDHn0e+DmzZs5AU8w3PGDHT/I8b9A/C9B/4swqHZ1DQsAAAAAADND4AMAUOaEw5o4jAqBDwAAAADA4oTABwCgzAmHNXEYFQIfAAAAAIDFSWUFPlPufBDq1PwPjwNY8kyN9kmqf0SmvL/zmZB0KiVD496fEAvhsCYOo0LgAwAAAACwOKmMwGesT9p/s1YSVVVSlXG11LemZGQ+gh8naCrcxI2LKQ2zSthN+nC1UyeNyQlvySyYnJ9jnTVzVM6x3npTl/XSc8tbYONWj9TnXIvZa7JmR7sMLlDYEansNrzjqe+dKQQYka46Pc7APgbbcv6e6m9266J10F2gxP75GZS28D4qnHBYE4dRIfABAAAAAFiclH3gM5Vul7pElSQ2NEpnMi0jGoqMpiXZ0Si1zvI2GZz0Vp4j3Ia02Y/399zgNmJnbnSHSUt7tRc4bO2RYt8dZrA1nu3MNXNVzmICn9oDSenr78ua7JTGDQmpSjRJagFCn7kPfMzncTgpXUlzU/H+Dgc+2sNnsLsrJ/SK//ND4BOHUSHwAQAAAABYnJR34DOVlraaKkls67H35BntkYZEldQcmdseRos58Jnqb5FEVZ10dbe5/3/de6FECHyiBz7WczWelEYNg06OeAvmj/kIfPLIC3zyIfCZPeGwJg6jQuADAAAAALA4KevAZ+J8k2koTh9iOMOZEqHG5MSQJFsbpXZdwrx/tdRsbZaea6HhTlc7pXZjs6RuTUhaewvpuom1UpszJCctnRtrs9vRf+t7gm2l4L6c97dJMrCvifPN5j1Nkhz1FniMJZvM8gbpumb+7axTI6tNIzaxTvdh7IhSp1PS94rZrxN8mEZwokrqui1Bg3+s4TbeWEqazb46r2b/XbPGNKT1OJxj7TQ1kGXiWlLadtTKWrOfqjU1Ur+3R4ZC1ZruMO/bk5Kx8UFp36HHlJC1z5n6H9Y+IVMy1Nss9TWrvboy2w+PQrs7Jn0dTe46+t4NjdKWHJLMakWWU+uzsTWZV05lYqBTmp5zhwqurmmU9oEJGZlt4GPORM9WU7ZtyZwwKnKZwnW8s1P6LLuJXvYJGUq2SeMGf916aerok7FggJo5nhGz3XZp9Ov+uSbpNNsNkjm/3t95gU/wmpru8zPcJQ3m77YBy1Cvq+1mPfPZGPb+zsMS+ET6PM8vZ86ckS1btjihxmwJhzVxGBUCHwAAAACAxUkZBz5T0rfXNOqK7cUxaRqD2ivIGQKmQ22S0vlSrWnsJqTBNGgzeA3Vpp2mEfpSpyT7+yTV2+IMH6uqafcCBNNYNsuTB2rNumZ7ztCdtIz5bdSC+6qRtkF/JXfek8TOVDa0mOyTZrMfv2fS1K20eW+n2zPEHyY0bEkDwkykpMm8x5+7xwm/qv2yByjUC8Nr6Ldpu3lqTNJmv50vmePf2OLUR19/NmiZMtuoMXXo11V2+FKD9ATCLKfnTX2TNG2ok5belPSluqTZG+bUfrheanZ4dd3d7AzJq6rrMjXk4fXoqkrUSXO3Ww86dK/GlDHTi6vUcta05Qz9G+ltcK6J7Llvk/o1NdKwTc91DIFP4HxHLdPYGS2TOdYd7dKTMuuleqTlObfugkPEopd9TJLbzPvN9dh4uEdSzrruNZ5zPXrHU7utQWrW1EubnrcCn5u8nlXhayt4TZk9FP78mM/FRlOOV/pCE0B7IabtOs5gCXy8ckz/eZ5fPv74Y3niiSdk8+bNTtAxG8JhTRxGhcAHAAAAAGBxUsaBj9dwLmrYht9YbMmb1yd9uCa34ew0EKuk+sBgToNzaqBFqk0j122wutiHpHj7CjXadfnggVDwkm43Df5qaXF6M0xJ+qApi3lfOqel6zZiixlWM5FsdBrSSf+YzH6qzX7aw6cj3Cj3yWmcu1iHSk31SYtpOIfrSu6mpV0Dr0B44Ly/qia3DJNuMJUT7himUtqDKxgW9Ennzua83lBDx2vNuTP15f2tTFfOGnPN5JRzclBaqk35D3tbKLieG8RZ6yrIdIHPaJfzWqanVdQyaThypk2aDofXc+uu4Yy3r2LK7vQ+a5L2TPjo4tZ7gyT94nvHU5Volr7wtdzqfW68E1xc4ONSaEjXSHed2XaL9AWL5x2ftadahkKBT7TP83yioc+aNWtk48aNswp9wmFNHEaFwAcAAAAAYHFSxoFPWtp12E6wUTcTprHYbBp91oa41xum6bzfctUGoiUcMY3JltA2rA1Wb1/N7+U2ph2ud0ltzrb9kKdd0sNdUqc9gK6G31ds4JPfk8Sps5wQwWOWgc/Ue/rkJXsI4g67CwUCG3ODHf/Y8s6lZf828oIhQ+FyNucGCB4jJ2szIdx0x5M+ok88s7+WwSt39bYWaT/cnnVvgzsUKxDmRS1TYYakc2O27mZddmW401yfgXr3j8c2F5b3mn+dxxn4hLetZOakmnYuqkKBT7TP83yjoYgf+mjwUQrBoCYuo0LgAwAAAACwOFlaPXwsjc0sI9JTXyWJg15ZC4UgluDF2mD19rW6RuclCenNl5JTDn8C6kQiv3eGQ5GBz3UNjqqk6cyY09DyHTyiPTJCvSZmGfgUbLAro7qNhLQ5c7bY3+8fW6TA5+6EDA0mpetwuzTvyNZluPyFy+nPFRPSmUfGPYbpjme6QCWDV+7MfEuqM++NDsnLnR8napl8dHhfqrdT2g80Sb1Zx5mrKFB3xZd9SsbSKenpaJeWnfVmv+5cUbrNTL17x5MJQ3PIvS5jDXxkQpI7zPb2+sO6vGGcO5KBENNGocDHdt6K/FzNEQcOHJAf/vCHMjhoO3MzEw5r4jAqBD4AAAAAAIuT8p/DJ6+3yDTYAoQMoQAppsCnbmegh0fIVE4vhRF3/7rtU7YjKq5h6gyHMevbTUhLfyDxmcvAJ7SNWQU+o0lp9CZjrtupvWe6JKlzvrzXklf+wuWskybLuXBNOddS8aFJCK/cuefKezx+iWXS633woM6Z407C3Gxe6+xNyeBwWroC121RZdc5pnSuIA2cdjSbfXVKT2pQhtLusLNMvXvHY+2t5l+X3jUbb+AT6iHm9Zrz56QqjOV6WsSBz9GjR+Vv//Zv5b333vOWFE84rInDqBD4AAAAAAAsTso48DHtP2d4R2g+mBA5T+kKD9vKwRva4Yctsw18vH3ZG8n5jJzSbdRL+5Emc0xmv6F5aoprmLrhQmJvSsYCvXtc09JZZxrDwaFe/rGG92lpnNuClPCwrRwGvDDG2/ZsAp/0Qfv8S5l9BM5V4XLah08F0cmRCx2PM8wqtK88rIGPu3+d5Dh4TUQtk4wlpUG3mRcGuj3TMoFPEWV317Wcd6dXVn7gY32UvHed+3MIxR34+HP26Gc2cl3ZrqdFGvj4Yc/Fixe9JaURDmviMCoEPgAAAAAAi5OyDnycYEOf2pQ3MbKL+/SjKqnLNFS9ISKmQRpuumrvh5y5QYoOfMINUXdfOU878hlOSWfvYPZpXrdMY14nonXK6fY0SmwLl9Hdr7XRHcaZnLnwRLRueQOTOXsN/HDPCTeEsgQ+4V5V40nnCWL5YYT2wkrkTMY8m8DH/l6dODh/bprC5UxYA7+h853SMzBmtmbwhsPlH493vVmviwAFAh8NZ/SJbDlPhYpaJktQ4uAtz9RdEWUvFLS4y/MDH9vTrNxrJPu5KT3wKRTkeJOf7+iSrp3mc5H31C4bswx8piZkIrwTs2wq/PT0ScuyIjh16pSsWrVKBgYGvCWlEw5r4jAqBD4AAAAAAIuTMg98DKM9TlhS5T8u2uwr3Z+SntZ6Zy6SvODEeSJWldTs6pH0mPZ4GZOhZIvzCPCcuXOKaSB6AUvdQX1UePYR4Nl9dcngqNvDZmSwS5p0rp5MuSYktdM0aIONaafRnsg+eclB19NGd5N0pfrM9go3ewdbzfb8Xk02vIAmG/B4QUSiVpq79ZHbbv2travLnbzX4PayqJEmXW9wJFNfaZ0bSJf3pt1eRWNDzuO2cx9Br2ULBQIO0QIf96ljNdKSHPJ6Lo1I30FTxhrdd+65mrGc3YMy4m1jsLvJeSx69vHi3iTaweMZHZTOHWulYZuGE7brIoBXbluvEQ0hq0OvRSuT12trW2fmWhpL90jThhqp0aFimborouzeddvQ4e93TNK9TU59ahnDgU/9tgZZu8Pfv7uuc30fTGfqt5TAp+Dnx8d53WzXOumyjVkEPl6Poqo1LZK5bLUXky6r6ZQhb5Fc63SO3RroRkTDj8uXL3t/zY5wWBOHUSHwAQAAAABYnJR/4KNMDEnP3rrMZLOOa+qkyTRkbY2xqeEeaXbmLvHWTZjGcEc6d91iAh9tZHe4AVNVVW3OE4Ty9mUatrV7emTI65Hk9izKfyqXEwIkGnKH24ymstvKTGQbwmuw5j2JKwevp1PwMejjfdJW504snCmjKXte49y8I7XHnUsmt1fGlAz1NjvBmX+siXUN0nk19wzMJvDRcg8e9utZ1XKmZGTAdq6il9MJunrNB8FbwyV/X3VH0k5gY78uAkwT+JimuTtXU84jzqOVKe9aWlMvnWlz7QfnnnKIWvb8/a7e2inp8HnPnIfgda6ulvrDuZ+xkgIfU45Cnx8XN+ya+YllPrMJfNzJ03MC2AnzuVtjrucNgcDnujkOU2+zCXziJBzWxGFUCHwAAAAAABYnlRH4ZJhyGhUTeeMx7Ew5607kNKpnh9m/ZWiZw6S3r1kMAcmgQ0m8f8aObeiKjbvmWK2FKO4clIQOudF9FKrrIAXL6Z//Gcqp79f15vBwgkQpU+TrNmrZ/fWiHqRX/7FcyzmYcljP6Yh0bYw4nHE+if34Sycc1sRhVAh8AAAAAAAWJxUW+ABApTE10CLVwfm1II9wWBOHUSHwAQAAAABYnBD4AMCiZCLVIrUba2VtIjS/FuQRDmviMCoEPgAAAAAAixMCHwBYhEzJ0Jl2aT/cLp1Jy0TOkEM4rInDqOj9Xe/zer8Phj/6faDfC7du3coJgD7++OOcECgYBPlhUFANhsLavoQQERERERej/n/c1P+YOd8Q+AAAlDnhsCYOo0Lgg4iIiIgY3fkMfgh8AADKnHBYE4dRIfBBRERERCxO7fUzH1MVEPgAAJQ54bAmDqNC4IOIiIiIWJpzHfoQ+AAAlDnhsCYOo0Lgg4iIiIhYunMZ+hD4AACUOeGwJg6jQuCDiIiIiFi6OrxrriDwAQAoc8JhTRxGhcAHEREREXF2ztVEzgQ+AABlTjisicOoEPggIiIiIs7euYDABwCgzAmHNXEYFQIfRERERMTZOxe9fAh8AADKnHBYE4dRIfBBRERERJy9X3zxhfcLOz4IfAAAypxwWBOHUSHwQUREREScvXMxeTOBDwBAmRMOa+IwKgQ+iIiIiIjxGDcEPgAAZU44rInDqBD4ICIiIiLGY9wQ+AAAlDnhsCYOo0Lgg4iIiIgYj3FD4AMAUOaEw5o4jAqBDyIiIiJiPMYNgQ8AQJkTDmviMCoEPoiIiIiI8Rg3BD4AAGVOOKyJw6gQ+CAiIiIixmPcEPgAAJQ54bAmDqNC4IOIiIiIGI9xQ+ADAFDmhMOaOIwKgQ8iIiIiYjzGDYEPAECZEw5r4jAqBD6IiIiIiPEYNwQ+AABlTjisicOoEPggIiIiIsZj3BD4AACUOeGwJg6jQuCDiIiIiBiPcUPgAwBQ5oTDmjiMCoEPIiIiImI8xk1lBT5TbuNBnbrrLQPDVKZeJia9RVAyU6N9kuofMbUKsDgIhzVxGBUCH0RERETEeIybygh8xvqk/TdrJVFVJVUZV0t9a0pG5iP4cYKmuW/+T2njqJjd3B2TvsMNsjYRrJcqWb21TVKj3jrFcqtH6s022ga9vyuIsd56Uz/10nPLW2BlRLrqtB5nWi9eopWtWAalTa+J1go8mUuMcFgTh1Eh8EFEREREjMe4KfvAZyrdLnWJKklsaJTOZFpGtAExmpZkR6PUOsvbZHCOe7W4jXGzH+/vucFtnNf3RmyITaal/bmEVCVqpbEjKelRbViNSDrZKY0b3OVtgyVcUEs+8DHX3HBSupLmg+P9PR8Q+MB0hMOaOIwKgQ8iIiIiYjzGTXkHPlNpaaupksS2HntPntEeaUhUSc2Rue1htPgCnylJH6yRqkSD9Fh78oxIz7aEVNW0S9E1Q+CzIBD4VCYadsRBOKyJw6gQ+CAiIiIixmPclHXgM3G+yTSC66TrurfAQvpwtVQlQmHMxJAkWxuldl3CvH+11Gxtlp5roYbX1U6p3dgsqVsTktbeQrpuYq3U7miXwXFvHUlL58ba7Hb03/qeYFspuC/n/W2SDOxr4nyzeU+TJEPBzFiyySxvkK5r5t/OOjWy2jTOE+t0H8aOaep0IiVNZt26kyPeAgvpdqmuSuQGNzoErKNJ6mtWm+NJyNoNjdLeH2r4FQp8ZjhOcxSS2uOWe2q4R5qf0yF4ps4y9Tkhg4e996+pkfq9yfwQb6xPOnfWS82aqgL7MIfVYfaxJyVjE+bc7Kh1hrNpnTUeHjR7mJ6ooUpmH97fDsWUzfvbxz2/ndOGb/lly9anjCaleat7fayuqZfmXkvvo0n3/Pjlq9vbI0OTBQKfmc7leEqazTXYdCZ0fd1KSpNZ3nByyFsA03Hp0iVZtWqV8/+zJRzWxGFUCHwQEREREeMxbso48JmSvr2msbq1J68BPS3ayNVeQc4QsD7p609K50u1kqhKSENvoAE72OY0sJt2mkb1S52S7O+TVG+LM3ws2zNmQobM8uSBWrOu2Z75d19/Wsb881RwXzWB4VTunDCJnalsIDHZJ82BnklTt9LmvZ3SaBrntQeS5t9mW8OF44up95ojBRe5eL1+1tRLW2/K7CMlPa31TohQ0zqYDRBsgU+k4xyTnq2m7nY0SZO/XrJd6jWAqGmT9gM1UvdKj6S0Pg+7+0280pfZ79TVNqnRZc81S5e/jx015jhrpD1weQ22mu3V6z7coWzOcbxS58zvNFNPr6iBj7OPwHVXVNks16u73+l7iNkCn0x9rvPPmb/fhDSdD1wf3vnJnlv3/Kze1uCcy5zAJ9K5NFfLyTqpSjRJKhN+6ufRXD+l9BpbwuzevTuW0Ccc1sRhVAh8EBERERHjMW7KOPDxGrxFDUcxjdJXTKO0uiVvXp/0YR0CFWjAOoFPlVQfCIQdhqmBlryeMfYGu7evGrM8Z19TMnig2pQh0DBOt0tNVbW0DOievOFY5n3pnPNtGuKmPFGGdEUJEMKMdJsGvAYKlp5G2tjPBBd5gU/U4/TOV05IYBjulFoNS/Zmwx1l6LiGaNljGOvvlCbt9eP97TIknRvNew9mry8nVMnUpY9XlnBPrxClBj5FlS3uwCfvnI24ywP7cc6tZXifE9rkBD5FXLPmX+012c+HG3rVSNvVnIsWIqChz49+9KNZhT7hsCYOo0Lgg4iIiIgYj3FTxoGPaXBq75BiAp+pPmkuFJp4w6AyPSOcwKc6p4eGy6C0hLZhbbB7+2p+z3LSrndJbc62/ZDHNKiHu6TO2nCOHvikD+uQrOkDhFxGpEvDCWtdmnquNg17v3dMOPCJfJxeQBEKdvzt5R2X18Nq+vBlSlK7csMNJ1TJCSY8Blpm3F6pgY+dAmWLO/AJ9gzzGDkV3N4059a75jOfoaKuWbO6F/K0p4ecXmo1B9O55xYis3PnTif0uXjxorekOMJhTRxGhcAHERERETEe42Zp9fApNP+Mw4j01Ad6ZBQMHPKDF2uD3dvX6ppad86doBvcR8jnlMOfgDqRyB1ClWEue/hMv+3BA6aetyXdoCJch5GPs8D5KibwGR+SwWSXtB9ulkbdvjN3ktlmhFAlSoA0q8BnFmWbVeBjuf5ztzfduXXDoMw2ir1mzVXqTg6ekEReryAoBr0RP/jgg7J161ZvSXGEw5o4jAqBDyIiIiJiPMZN+c/hs7HLNFsjEg4rcgg1oGMKfOp2tkv7YbupnMmmvaE4uu1TtiOKHvi4c/jUTjuZdS4zBD7BoCJch5GPc3aBz8iZRm/S6jppOmC2252UvvSY9GkYFSFUmcvAZ7ZlW2yBT/Rr1mzB6U3kHlfkzyHkcP36dXnkkUdk8+bN8sUXX3hLiyMc1sRhVAh8EBERERHjMW7KOPARmepvyZ1fxkLOU7rCw7Zy8IZq+WHLbAMfb1/W4TEW3IZzvbQf0TlzzH7zHqcePfDRoTktMz2OPucpXaFhWzm4PZ+qDuSGApnAJ/JxzibwSUubOZ7wfEqK0/soQqgyd4FPkWWrzw9G5jbw8cpnO7feEK7MNoq8ZmXUO3eH26XJ7MMeVMJ06H1Te/Vp2HP3bvixdNEJhzVxGBUCH0RERETEeIybsg58nKBCnz5UYDjJlGnk69OTso8nn5DkDrcRHm6aaq+YRPAR70UHPs3Sl3N+3H3lPH3LZzglnb2D2ad53UpKg2kwu+V0G/KJbeEyuvutne5R6wHSR/RpTblPVsrgP7WpLts7ygnGLJNZ69wtdVUJaen3thMOfCIf52wCnwJh15RZXu2ez5xQZV4Dn+hlc0O9RkkGJ63WQE3rZc4Cn8ITlU+c13AxuI0irlmz/6Q+1c27hpx9WiaGhsLoPfOxxx6T+vr6WYU9SjisicOoEPggIiIiIsZj3JR54GMY7XHCkswjp82+0oFHiucFJ84TsaqkZlePpMe0sTEmQ8kWqTXbyJk7p4jAx+8tU3dQHwM+lG0sZ/bVJYOjbsNmZLBLmnSunky5JiS1U5+MFJho2AtZGs4EG126njnOmibpSvWZ7c10MYy4j1mvWi31rfq4c1MvaX20fJv7KPRw43w85fTSSDzXJn2ZsnZKo1k3pw7zAh9DpOOcTeDjBXU1LZIcHnO2PzHaJ23P1UqNBlexBj610nJGH0ee75AX1OTuI3rZ3PNq6mRDs3MO+1I90rZ1rdTV5T6RzEbpgY/BCxSz59a75jc0SENwSJcS6Vyao3bComDvuhFn4uaEP9cTzIgGIK+88sqswx4lHNbEYVQIfBARERER4zFuyj/wUSaGpGdvnRPwOPOJqGvqpKljMBu+BJga7pHmDd6kumpirTR0pHPXLSbwkSlJd7gBU3junLx9VSWkdk+PDHm9LdyeRflP5XJ66IRDmdFUdlvhp11ZmZCh3map04Ans//VUrezUwZzepl4jA9K+1Z9wldgXX3ceLA9agt8DDMdZ8GAIlLgYwiXLVErzedH8gKe2Qc+3vYt+sect4+IZVMm3mvLng9dr3fInKNQQGNhVoGPIe/81DRJatTrXRTaxozncrJPmjUgDT+VywmLEtLQmxOxwjwQDmviMCoEPoiIiIiI8Rg3lRH4ZJjyGhDRKmrKa2zEV61m/+EhUT6T3r5m/x/znW0VW+aijnU2ZY3zOC1kjmOOtj8biinbVMRrNG7cMkbc9xyfS4iPcFgTh1Eh8EFEREREjMe4qbDABwBg6REOa+IwKgQ+iIiIiIjxGDcEPgAAZU44rInDqBD4ICIiIiLGY9wQ+AAAlDnhsCYOo0Lgg4iIiIgYj3FD4AMAUOaEw5o4jAqBDyIiIiJiPMYNgQ8AQJkTDmviMCoEPoiIiIiI8Rg3BD4AAGVOOKyJw6gQ+CAiIiIixmPcEPgAAJQ54bAmDqNC4IOIiIiIGI9xQ+ADAFDmhMOaOIwKgQ8iIiIiYjzGDYEPAECZEw5r4jAqBD6IiIiIiPEYN3Me+Hz44YcEPgAAc0g4rPH99re/PaO296lR0fu73ucJfBARERERZ2fcEPgAAJQ54bAmqC3k8bWt7xsVAh9ERERExHiMGwIfAIAyJxzWhC027FGjQuCDiIiIiBiPcUPgAwBQ5oTDGpvFhD1qVAh8EBERERHjMW4IfAAAypxwWFPIqGGPGhUCH0RERETEeIwbAh8AgDInHNbEYVQIfBARERER4zFuYgt8bt68SeADALAAhMOaOIzKdIGPfi8Q+CAiIiIiRjNuCHwAAMqccFgTh1Eh8EFEREREjMe4IfABAChzwmFNHEaFwAcRERERMR7jZk4Cn+vXrzs//HXj2hD44IMPvN0BAEDchMOaOIyK3t/1Pq/3e73v6/2/mMBHwx4CH0REREREAh8AAAgRDmviMCoEPoiIiIiI8Rg3sQY++kQWAh8AgPklHNbEYVQKBT76fWALfDTsIfBBRERExHn1zrjz+3R0dNz++iIxbgh8AADKnHBYE4dRmSnwufDKJtn069fk3du3M7174gt8LsuR57fIltcvZ5ed3y73L18u9z9/TsYz682vo71NsuX5JukZtb+Oc+D1HmnSa2H/wp332B11j6mpd9T++hx5+XVTj2a/+85H+0Hc561fcjmvH5E19yyTFY+0ypDt9VnoH0uu22Xf0R7pu25/T1Fa7jdFf/4t5/nci/fL8uX3y5bk/DVKnLra1SOjltcWpUV/PizfFyXqXldH5LLltbJzEXxnLoRxfE6xvLz8erW51pfJsmXqo9Iax3fAHBk3cxb46MSdunGdyJPABwBg7rAFNrM1Knp/v9azV371y1/J/jMf5QU+x2u+In/wB8/KSbPNYgMfW9ijZr98emSTfnFv7MksGz26Rpbrsr+Pv/EY1aH9VebHRJXsS9tfL1cXcyPn8q6V7o+45evl5Lh9HXXxHcOo9Owq0NBO75Mqc0xV++P5oRbVno3eD+JHWmdu/I+flPXeD+iSy3lhu9znnLtNcs72+izMHIvVFVL14uwaubb7TdGf/7zzPCpHfr7cKeOqA/N37p26enDfgt03i7boz0f+90WputfVJumxvFZuLobvzIVw9p/TMjDOkGrgiPnu3CJHBiyvlYMDTbLSnL/l398krW+clJNv9C3qcDtuYg98dN4GAh8AgKWBE/gcflq+/OUvyz8edQMf/R6YLvDRsGeuAh913Ox7/E727/m2UgOfxdvIuSxN3zNlW77cabiseb3wj9vFdwxDsu9BUyZbQ3uhA59lK6Vphh/3453V3rqzLOftURmdJqgr1YLn+/o52ffz+8xry2X9G7PrRRO+38TTkNRhB/M75MCpKwKfSC7ee2FpLvR35kK4JAKfOMvcu8nU1zLZ1Gt5rRz0yr/+lOW1RWjczEvgo939AQCg8tD7+2ILfBZaAp959vwW+aa5Dta8flK23GvKOE3PlMV3DIs58Fkm33z+nHUd11FpfSS77mJsCE17vv3eSU+djHUoS7k2JJ26IvCJ5KK9F2JkCXyKtEICn3Ipf9zEFvjoxJx+4KPd+TXwCc7jAwAAlcdTX/6yE/Z85StfyfqPx5zvA/1eOL7+D7zA50N5Y9Pfytf+VP82/vn/lnXNKfmdLfC58a7srf6efN0fa33PffJo7Wvy3u8iBD7T/MAZemOLPPrtFe42ly2Xr39/jWw/VUxX51Hp218tK7/hDrfQISH3PbJJWvtz/0t89ofkuLv+Pd5xLP+6rHq+wBwZQydl++qV2WM2665cvV16QmPMM9u+clmOPLVSVjjlyG14DL2xXdZ8/+tuN33j8m+slDUvFpqbI8IxeT+UwoZ/OOXXb7Xsu5Bfv5nG0u1zsv3HXjln0dA89/w3zfaqnaFcl//lfvNvy4/4ko/Bfo0UPMd6rT5/UoYi/NdydxvZsrgGyh64lsf790n19wLl+vGWvGsjo7mWtjxyn3dtuOe/en/07utuw79JmmrMNTHdEDmvi/zKmvUFPnPm2mrbFKjPZbLi24/Kpra+UMAy3ef4sowmtwe2scJ8LprkXMR5N6ZvmF+WfX+txxq89qYJBWz3Fsuygg3JO0Ny8vlH5b7MteKel/GI28gcy53L0mo++/69Yvk3VsmmowXmpQldCyu+Zz6T5rNdcPt5n8NR6XlxTej+MM21Z3H0grl2M/ej7LVb8NyM9sm+wPE5n6mNrdJ3O7Se7Xw4hu5pmftu8YHPnJU9UJacz7aW1R9meL1Htvj3R2ff2wtc97Z7+BY5OWRb12KhejT7z/lOcu5tRczxZOqidWPgei/wfTmtRXxmfIu/h+cuL2WfUe+5JX2Gbc54brz/kOCVJ2P48x3pd4d3rYbNfI6KvGd6Rv29MK2Wes/7reOVIafsahH3gYUwbmINfPS/5vqBj/5XXgIfAIDKpnn9s/Krv/+OE/R89+Faqd1QKxv3nXe+D7KBz2Pyq1/+pfx/qtZKY8sxObTnOXnk//1Ts/xP5Uf707mBz+Brkvhz82W8/Dvy6D/tlfaTr8nOXz7qTrT37V/L6d+VEviMS8/m+50fziserJamtpNyZL/5sfEd/YG8XFaZRmV23UKaH2iP6I+T5XL/Y1tk39GT0rqrWqqcH4X3yabe7I9Y/4fk+o2rZMV3HpUt+4/IybYmqX7Q/XFz3+bQvCFX9skqPb7l95sfbfvkyBtm2y/6x7xJzgUa3P6216y+z/mRWK2TkAbmpLm8f5VznMv9/b7RKtsfc489b79Rj8kbu79Gh00tMz+onH0Gx/KPy7kXVxao3xXy6IHc+nV/9D5qjmG5aYCukfW6vZInizXXgNaT30vDDyF2hc5phGPIv0a2e+cs/xrxz8Om59fI8nuqpHpXq5w8uk+2mB/P+uNz+d/vm3GeIHfS0PWySnsl3bvKrYfgJKL+tfzsJll1j39ttErTU1XuD9zQtaGOn98uK7U+gmXyzr9Oihxl7iK/4f//Z+9/3OQ4qkNv/P0/yA37D5D3fR48932eJBvdoMRByr1hl/tN0MYB7VXw3ohoHwxir4O8uiCPAporjJgIrydOlDUgzwbkWTCMgvCssZk1yCOIGBGZFdjMgoBJZAYQzM1X+XKTnG/97KmuPlXdNdOzu5JOP8/neaTp3u6qc06dOnW6qrqjZk4tnMeDcJloW4D6FUebO8WXTA3kyctS1vKM7U3jacfHeTKN64vV3ajLxP5q9rq4Ej6s3fFn7H3cnMUUOHhBfsMHkh2oPSDbw+wRs60VWN3Kme4h67IIxeOTbPAvZTporwUoPmcNpLVf0fLTtlNgg8uTyfv3rjEdXe4aPqIHDdZGeZnnTkm/1Dxfkc8rLEIjQ9Knf6ks9mcSvsq0R2a75aO83JZuNuuwIO5v2PvJgS9smYkTl68XdscTJHFfNM3aV4ldn3Wg5y47a6cnRiy7trOjRSjytn2G3T/qI1j7OMv+XdByN9p8wu670FBLExM+nJXD7JecYHLkG6krP1IW/Qi775IsQ+FII91X32T1m2J/z23neFXsmdJYZXYuyjUPdTvJguJuM/OnsCTzcD58lHbKCfG5wW0YI5Nu1N5wJ+aFD9/7QEn0d7F+NnPcoTY7Z30b9+dcLuJe0ebngT4zMF5wwdunKKsh92XVTmL9r9rHyF3+nUneR+4JH/5WF9u4mQ466KCDjjvv4P69+8wHRMLng88mP8kuEz6vhd/7X034kbGk659/9GX4wBvZuTcuwWWV7Ll16wY8817W6RcOwVPfsZZ0dZ4UgUPhhLn8wjNQNAMMNnDlgfs0u85OeIi3YCkb/XJ6F/jGlsmgkQe2JT5gNwJxGUhaQYdAPY8FfI3oTW0fNs4uwDQbVNQ2zWtZQHOxpJYqDQbc7nsz+htQPTwNuxPnWCB8kg/OzeeG1YnjHDwr+c49Yc/cUMGzJV95H0wf4fTXS6IOg31Y1H4+joRAWh2SZepBU8guvp+N1sPEHlY3661777zcBDWRdEJJX9LFB0i2bXTOyefH9ytqQ4UPsg4uJ2YUyERgtv1qhIxEeTyyVMuhRHvE2ly3AaUDu2HG1eb2VIwNmt3tGKt7+3E5wC1fjv+OIfVdYnbPP8M7oHOpCotMVoV9joG4WRYNVk/kN2wgKX/ztLUM93C2Gz0AjG26q/e1QvyKSmQkB7sWKiE2e9YqM/t9gc/UWjfkgKGXzCWSHYPEdLwtqgQTUuZfbFZFWWL+H5E9rxv3mU5fz5+J6damz+yA6wUru2p7I5Vd21linyylN2Yr9uB/4wk+e3Emdr304XuhuGYlZXXCxeEHYyBylHVM7uHVOcf6qsNlSJs91F0rwey9SMIJTbLiyI348TYjk0lWWxzKh4/WTkN9blgbxgnSDeazBGFxh4C1LV725JKoQJ8ZGC/gKLkj7bPHYgJe/kT/6yz/ziTvYywJH3MfHz7Lhw8I/vVf/1U9kg466KCDjjvh+Pd//3fh3/+xMUj4mPv3DBI+B+BvX/55bA8fvn/P1x7bw84dgS/cULN7vvUk7Gcd8r7ll+LJHsXXH7WTFkigkQgw+tDkS1P431mBAad/vQ3N9RZ0rEF7HBWE38eCOuS8eDO+3o6W8eigEQsseIA+wQL0ypXkuQS6LucGgYu8d8a/N4iC2yi4C6sTx5UsaZ1i8o0N4A1ersIMq4MZPMr7xJNPw6F0awWIPhnhdfDbyC9u1GGB1cEcpMhnYIE0R8s2w2ArQ8KngAXSPTZAsMrEA1o++MNtg5XpPvacDG/nhYxUeXrnF1g944NMjvxdPcs5qMCROigZOnC3Y7TuyqayPE8+C2f3g8vQSiQ8AgcvyG9RW4vurWTvGHxjtpS8h64LlqRh9nucnzPsWskITzrqvZewexmwgRkfOGXVq43c0NuVZGQDNjGAjpeZz86bYQPB5PV6RpnhNxKyV3JwDRhF+2DnMd1a+MvObIQPzkcpu7YzpD1KPcslqubvyQGr8h2OPctkHTL0FYgNtx/n5U2xj6FQ9T6ZpgN/m5H+x9L9UD58tHYa6nOD2rCDIN1gPisN/TdG3CFwJkzCfGZovIAhX/Sw69aw9qn8m/0MZ/l3JnkfuSV8fvrTn8YSPvY+PvwcHXTQQQcdd87BEzfcv+uEz4lnX40lfHi/8CWR8PkQrCOfZL9+7u3s3Nvh3KZM+Pzs2YdZh1yAj3wN37D5FxdLVoeNBBqJAEMFXaN8crbfhCK7Z+F0+ltJDjZYi/AGHfzLPB1oi6UTVSjp5StG/bz3Nun3oLfJk1l8ynhJTZc2nhtYJ44MVu2AVMn3YEUu+bBZqwh9mIE2fp8hUEmPRFJAB/fIhsPeOjhtpJPY60XqwT2YkkF5lqTWsJs2J21f719UWUP0sN6ASmJmDY6QkS6PspO4LJm8eEJLD4zSBhU3e9C91hJlqJ3RyxdMG87Sjg3Snmcg9b0Ay7Y8zi+rNjHNBg3mfcIGL9hviTaq7dTV1pAEFtbOfe3GPpeWWHbudRVDvUXXS7qudIK+pOZvA8kBrk6yOGdu2f4/IXuVRHK1Y2XLqG4tZNnnoY7OZMmh7B47c+rZ7jtUfXYfq8ZtW7NShN3sfNrgGbVrNQtDLxtqb/as2RjZ6fe6sHGJlcdYcoP6OxMsoW2SaDPD+vDR2mmozw1pw05CdJPJV6bHHYJcEj7h8QKGlLurffLZTkicRAkf9DmhiISPnuWD7ePzD//wD/Bv//Zv6rF00EEHHXTczsf/Yf5cLOdCEj56dg+W8OHJHlfC5we1+1mHfD889V1HwifRYWcZKHqCkaxkCpoGeJMyWNBxU270KQJhza5pmDs0K96wm2X33pvRj23uK5mcmoP5A3wAYzw3sE4cPCBV8k3DqEPmwDYF/ZZ35sGyXJMfUYS5N7BnFJLP8NbBaSPJpEyaHtLOD8gv4SPrlka63MV9ovKoN+fmrAk18yPa28dRzt7FCsxHG8lyCrB73wLMiSUrpmyytGODANv125pa5hObEeKxBey5yG8J3aeVN8s9GL662Odys0+1CbHpm/i+W1k2u/fLPrzMCd+ZkFtaO047P2DsZfeUxflsR/21Xlw47U7jsE+xYXWsL+H72iQ/JIDTg9bS/GAzYE5hN8wcnhMzoVITPsFtJk23GXx48DO1rtIY6NJnV2k2Z5JZN746BcQdAix2EYT4THVtGiltNE1WaHt0ln9nkvcxloQPf7tr7uPDkz484cN/o6QPHXTQQcftffzoZ/9feIl1HmI5F/Pv/7T2wSjhYy7nCk34vPr5h1iHvAce+3qeCR/11jfLRpMu0t42WniD/0QdulAXm0ROw+JKEza6PejrZVSqLmb9vPe+XpebME4tQnV9A7q9wXRn+XfGcwPrxMGDLC3fOntefJ+UGMY0+5DA1o0K4Pkg4r6ZJOLrH8klGf46uGzENVhILnXSpL2BHJBfwkfPqKhfR+QfgU2BjyNkZJYnluBBEkBIOfuX1Ya3B8pQ5zNDjOdKHZg2nKUdG3jlEifN1uTMjBSfosGei/yWaKPBsxXwdu6ri31OzvBxzzjJNsPH4FZfzNLiG8zKJF76BrOhs2RSl7s6Eh4DuaW04+AZPtlnJwWX3WNnTj3b91B2Nf14y2rjFmmzstLa000+Q4dvHK0SOIUiNLFlUxF9aJ+W+2zNnq6LGSiDMqh6pyV8gmfbDOvDR2unoT43pA1nIk03Tt2GxR2ChA1rQnxmeLyA4W+fuP90l39nkvcxtoSPvazr6tWr0f/5F1n43g900EEHHXTcPse///v/ga+vrMB/mD0D/6Bn95gJny/G9+9xJXx4sgdL+PzL1x+DPaxDvr/2AzThkxykZBko8s00eSDkCKT40idW5ijYQVFBiitIvanuof6PBhsaO+jo1mGe/R9bfoQFXr57d5+eZ+fwQYf8OzPYCasTBw9IlXwzLBXSDBXY2rDgm78pdgbneslabKPUlDo4bUTdK6EH1xes1OAik0zyS/gE7Q/lQcgoVh5jCRe2XA4pZ/s0H0wsQB2ZDSB1ENqODbxyiZNqa66lNvqrbybIgA8rS7KN+ttacj8SvJ376pI4p8qK7ymj9OnyUWnclAPjtCS6Tqah+2ywNiU2dDbLnLJnUML/J2Sf0o7V/dGBqUWUMEMHh0qfo5TdM0h26jkxYFXlcOzhk5mA9tRf43JJWybWlnscoeVS9Xb1OxFq6Y9jjzm5oXOA7p0+fLR2Gupzg9pwIKhuXLoNjDsEaQmfTD4zPF7A8LdPlZC1bYESPuhzQvm/bt68KYJ7nfQxl3XxJM9LL72UmPXDH84/2f79738frl+/Dt/73vfgu9/9Lmxubgo6nQ585zvfEbzyyivw8ssvC7797W8L+D0xeHJpWNZO/i685jW/C488g5+/evUynD38OnbNQfibF/Vvn4KHXvMaeM17P2Vcp1mDR6bYud94D3zqq9a5Zx6BafZ3ux7+TPTbp97Lrn3NQ/Ap8zrF+uk/YOf+AE4/p39zPPfcQ+y618B7Pmn8JlBl+S12f7ss/NzMb8Mfv/csrOvfLp+F97yOXT9zGtZi116Fyx9/J7yOPePgX784+N353HWo7Gf3ed0fwelnzd9fhMp/57J8DfzB6fXB78+dhj+wf9O8+DdwkJ3b9b6BzDRrJ6fFvUx5JGXmk7HS7W+8E97539g1U48k6h2KfBbTfeKcz2bW4fQMO8fkHukiVHcYlz8DH2TX/S575mXrXMLuQ3Tv0xdWT3Xv1+1P3jtqE4Z+Lz/5Hva818EfnV6LX8uQOt8Fx54e/IbpHCWw3J95eBe77x9D5cvWtV89C+/ksjKeeXn1g/AHv/W78NC5y/Frua7+C7v2v/ht68W/Ppiol0TZASvbQ+fscwMif/i1c/DQb7HrX/dWeKTegm9+85uCjY0NuHbtGlz72ufg+O//CvzSL+2Dx5rfVj727+AD/+mX4JfuX4YrzAdzX8z9MvfPr9QehP/nP/wH+A+zfyMSPiLZw/z5jWdlwudYI76ci/cLzf+lEj7W/j064cOXcEUJn395BZ58G+uo3yQ/vx5L+Pz0S1BMBJHZBop6g7/EJoQiMOAD0/S9VmSAuRdKF+3Bixo8GQEMNliLsIMO/VYRCbw6Z2VQa9bPd2938KkHePFgJ6ROHBmQmpvtSvSXshJfNuHnnivB9IFFqF3N8paTfz2pCe3UWTF6AOWeYSN0qzfxNHQ7TB2kHuJylXpg90K+EqK/gjSdafaUSvjcj3x1zTsQQ2xfDaLRL7jdZOemZmHxnP1llCRCRtagR2/SPP8Al4X1hRiknPINLJLwYeUQ7Tht8Ouru1cucfyDKPVVmFg5+Ztv9jeJry31oC4+UW49FykL1kbll8WQtnaLDTKRLw5h9/DVJXmOD6xYeVk9qtfiz+yc01/IcvgoRff8Iszwr/jY16jBMzrAi6HqNlWCZswOeqy8XB52mZXfQdpUZDem/0dkz30g7uuZXpX+EgNZDP3VJNYO7LbE5SLlN0LZMZtXOPWMDFilD59mv9k+nPutuUxf1ErKkcnq6Iz42qOd+NBJPP/sLnciiv+9kF1qwkfrkvnks5bP0p8Tj5V5WB8+WjsN9blhbRgjUDdat3Z7CIw7BMr+SheN3wRhPjM0XkDxyV2/DLLrRgkf9DmhJBI+9iwfPvjA9vbhSwLspI+Z+OFJH5344UkfM/HDBy16AOMiGvhk4mk49htscDT9CDyLnpd8vbogBrxvf7ylfmODKj44PHIudp3m659+CH6bn/+tt8MHHz8Hn//i5+Hc48fgLf+RD8TeActfGVx77gj77TUPwTnj7zUvfPQt7Nxb4PTz+jfHc9mg+c3qecc+ehqWn9LlHJTldb+ryvLCs/D0E6dh4b/yxMvr4B1nBtdyXnjsraKur/uvC3D6iafh2S8+Dcsffif8Zz7A/a1j8LRxre+5rTPvEPd5zX98C7zzA6fh9IcX4O2/+zr47T95B7yF/f6Wj74wuM/zp5O/RbRg+U94WX8b3vHhJ1n5W/DChSfh9HveDPf81m/DLv4MQx5JmfllrHXLB9QD/Zq8IJMxr3soXncHz374zeJev33gGJz+6DKca+lzPptRz5g5DS8Yv4fqLsnX4emjvy2uffPRv4Snv9iCltDnO+R99/9l7HmZde/VF17PFx5/O9zDfr9n5hj85VOfhxf4vT+6AG8W92a6+Zp5jxfgLw/cw8p9D7zl/X8J5y68AM9+dlnonJfvt9m9v27eG9E5Smi5P3tMyum/PgR/+dlnodVi8n/8g/B2Zne/zZMq5jO//rRKtLwZHnqcya7VEmV+5B1S/m99DHumwVeW4R1CFu+AR6pMPq0X4PNVrut72LN44uk18NBT8b/B/J/gucfh7dzXvOZX4Fff+i4oPfoxWPn4o/C/3v02+NVf+SX4pV/6HTj0xJeET+X+lfvaCyd+h/3+f8PvF5+AZy7+A1x9sQEfZ3ay5417YK9I+CzDVT27h/n3V//+r+EPf/mX4Zf/8zvhI0+ehc8+/z004WMu5+IzPc2Ez7/8y78Ifva1x2Af78j3PQxPNr4O3/nhN+HF+t/AAg+2EgFF1oEiD0h44FGA2RNy89HOlQZUj8nNCTMNzPtt9SnYaVg4U4fWtY6YSl05KJcOmW/VsEAyIhF0qIGZMbW6e60JtROzMDk1nXgj7b23HqRES7q6sLFeg9KBSZie4gNwK9gJqBNHv1WdPlyB2moDWlEiRct3sLmrWP5xZkFuLjmFJY6Sga1MWLFzqW//1IDC8TUVjU70mbNwvHU4zOvN6rDEZdEVNsL1IGyE6cAcdEg9zEDx+Byz1SJU19rQ2WxDY6UIM0IHyMAPRSemmG0eq7IyNQd1Qm1Zgw8Y+SwvXt7Jg2Wo8TJ1N6DFNyhWes7yJlroxx6QqQBb6MceyGHlvCI3Fh0s6epAW5Rjmtkiv49pw+NO+LBBV2yPJ8bJRZhVS5Nm2MDA1K1OGvBNUYtnalBTm55PH1lMPhcpC9pG9aekC8xmVvhGq0wea7xtMr2z+9ozh7B7BA8WN5k/ELIuwG42iCqxOs/xpY7MNmtPePyIJrFEVC4fKbMyiyTD5ZRBGaPP7GBW2A3zQYeLUDm+ADNM7nygVjuZLHP/ckW0n6hNmfZr+3/UDnQST7dj6dOWD09DgcmZz05LDGQd6E/HTx6sQP3SBnS0TysswuKDI5Y9p4RP5MOZXUkfHvdbhSxLmTEbVn5k+mgVmuyePVGXstQls592yjIxmTgxlnQx3yhkx9s/r3eGhA/XZeOoTAwW7mVt+GQJFg9OwyS3vdVlRPfD+PDR2qm4T4DPDW7DCEG60X67MMvqw9rcuk6yhMUdApVImZhagMoq6+cvDfrVIJ8ZGC+46J5fEPsP6fbZ5f3vaimSQ6L/pYQP+pxQRMJHJ33shA9P8PCBh/7NnuVjJ33M2T5m4kfP9tHJH/GGWsGTPyPzuSL8JzaQevOp5/DzETU4+itsAPWHj8JX9P/54PChmnXdgCvPPArvejMftPKBl+SePyxC7cX4dbWH+LmjUDN+03zl0X3s3D54tKl/cz/3q2ePwpvFII/xrhW4YpzDyvIrv/42KJ77auwemq+eK8Lbfv1XjOt/Bf7zOx+FL1xGrnU+9wp84dF3wX/mchP3uAf2HavBV59/FPax/+979CuD+zSR30wufwFOzf0q/Iq4j+SePyzB51i9+N+Z8kjKzC/jjY3PQXEXP/92+CtLN/q80P3sXyndp/FVWHnozSK5wcv5rrNX1O8+m/kKPPqH7FxkXwNCdZfkq1A7ti8qj+QeePO7HPrMonuvvjw2itz7V9/K9PjV+HUSXm6eoNDXMn7lV+FtH/wcfNW6FtM5yhDljtm3KMN/hnf9zXPKpqxnvliD4h/GdfWa//hmeNejX4i1RxdXnjkFb4/Jh7UZVt8viPq9Bo7W8L+zkTN5vgCPvvu/qgSPhsn7bf8Tzjz7UpRM575VJtmvQO19vw//L0/uKP6fqQfhU1/9PBzn//9vy/CSnt3D/Dn38536Cbjv139ZzPR57f98Bm6oPsFO+OjZPa6ED+fnG0/Bw/bGw/sfhnosYOaEDBSxTSRZULeaPuMhAt3kcA4q1gam3qQMFnSw+1Yf3C0DJsXkwWVoX5N1MevnvTejf60KC7GNcllQdaYNG+LvkGAnY50kPWieGlwbf8uLyJc9e/Y4a0NW4OUKbKN9X9K+qKaWT+CfnDZh9sHLExtcBNahsBvmmfzsQVOkBzaYaZ5UXzVR8AA028amipj+jVlL3sSGe8CY3CyZtx8Wb1gzPVwI/SADMvlpaeRT1Y5y9p5jgxDxRS4Fb28XOkr/pg2HtOOUcxbyWQh876eDi7C8jt2jz9qLStyJ6/kAssUGrchzkbI422ivDcuHzHZegJmTTfS+2D18A0LnuVtd8bWmBbW31cKpOmz00v2IRmwCr77wpyncO+/wDw56G1BfYgM/UYY5WDzThO4tjx/ANp5n9pvw/y47uNWBur3R9OEq80PuNuOic74Yt+FdC2LG1Mhlzyvhw8F8OPdb3GbN61ygcrTbAKcAuw9l9W1xP8vhibDGpqp3poSPpLteheJhtTfb4TLUrzLbc/qAIXz4CO1Uk9XnDtWGE4TpJhYT3Ge8JAmIOzQxnx6b4RfgMwXZ4wUf6IcBDrE6YDO2KeGDPieU/+tnP/tZlPDBZvnwgQe21MtO+rhm+9gzfjjf+ta3UMxE0E7jG1/7GnyN83X8fN7w530D+V3wdVWWr30DP28Rlf0b+HkT33O9ZQohsPzZ4AkfNhh+9wpexq99DP6UDZb/yyPPJ8/5eOkb+ZZz1LqL8sh7fOMl5LxFiO5DCbq3WW7s/JYQJrtRdTWM7DG/yPn2t6/C3//93wteuiZn9OhEj0726CVcnO+x6+XsoVeEX+b+WX+Zy0z2cL/O/T338f/EfPYNNbuH9ws/Rz7HrhM+fBmXxkz46GVcP2f35ff+kVrehXU+wdzinyBN7k8ThNr3J8sGuEGofXPyuG9f1THzZ5RD6iRk6LrOkK93X6RtxleHDDaSGCxo3QUEqwm4Dkb5e4vIBnK8Zzi3iT044DIc2k+4yMNWRiRrwkcT2VKOPi9tgDuy/ebmp9P9gc22tL08+rYEg3uGfJI/Ylx9ZVrSNw9ZDNFOt1bvYboRvgzzwUPEHeJeyO9p55IYehqhf4jkPoyN7lDyPrY84WP+Zid9zMQPH1z4Zvxw9NIuvadPCNhgiCD+ofpu+JVf+hV48JP/gJ7/1vnj8J9+6T/B8fPIOYLYAjB/lob2lRo7yaMTPTrZo5PtHO5/7WSPXsqFJXzM/XvMhI+9nCtLwscG63wIYjsIHTATxJazWYflC9gsuB7UD0+Ac4PbHOldrkIV2V8mbYNcghAztc400GW7ck8x16a9BHH7k/eRW8KHB/W+pA9/64wlgsykjznbx5f40ckf/XbaxB7YEEQ6L8HfrZyBs5U/g/8PX/LyX8vQRK97GVp/tR9+6T/9Ofwdco4gdhKYf+SEJHrySPaYCR9zdo+Z8DGTPZTwIW4HKOFD7Gz0Hh18H5WG2KOD++neZits77KR0Bvd8n1CWtDhz2d0rzWc+4QRhEbvC8P3AWvwvWqE/XSgFe2Tlm2vF4K4Hcn7GEvCB0v68ISPa/YPHzi4ZvvoxA+W/OGDFT14CQUbCBF3K0/D+/5vua/Jf/yjD8DTf49dI1l/8gPwgY8/i54jiK0A82dZ0AkeO8ljJ3p0skcneoZJ9pgJH9dyLlfCB0v0cLCOhyC2C0r4EDsfvk/GXHxvF07o3mWjwPeXUQmmWBmc+4QRxAC+R8ucuYeSoAAzx8L2eiGI2428j1wTPmbSx0zucPhSBCwRxAcM9mwfe8aPTv7owYdGD1pszIENQRDE3QbmFzk6weNK8uhEjz2rJy3ZYyd8sNk9tJyLIAhiGxD7mXSgzb9kw3z2liR6bPheLvxrQuKLbTnv50Lc8fA9WjpXWnKm2h20TwtBuMj7yC3hw4N6X9KH7yPhSgaZs31ciR87+cMxByvmQIYgCIKQmH7SRid5sESPTvboRE8eyZ5hZ/dwsI6HIAiCIAiCIO4k8j5yTfjopA8P+O3kDk/4YMkgnfjhgwcs8eNK/nCwAQxBEASBgyV4dJIHS/RwdKIna7JHJ3x0n+BL9lDChyAIgiAIgiAG5H3klvDhQb2d9OEDAJ3c4ZuI8t/tpI9O/OjBg5n48SV/OOaAhWMOZgiCIO52bB9pgiV57ESPTvZoP+xL9uiEj53syTq7x5fwwTodgiAIgiAIgrjTyPvINeGjkz52YofDNxt1nfMlfrDkj0YPWELABj4EQRC3G5h/S0Mnd8wEj5nk8SV6siZ7OFmSPVln92AdDkEQBEEQBEHcieR95Jbw4QG9L+nDvxKjz2VJ/JjJHzMBpAceHHPAYmIOagiCIO42ML9oYyd4dJInS6In72QPJXwIgiAIgiAIYocnfHTSB0vq8C/H2EkhfQ0/jyV+zOSPnQDimG+lCYIgCD9mcsdM8JhJHjvRo5M9ZqJHJ3u07zaTPRwz2cOxEz5Zkz0crMMhCIIgCIIgiDuRvI/cEz4cLOnDEz484Pddw7GTP74EkDlY0dgDGoIgiLsRzD+aYAkeO8mjMZM92i8Pm+yhhA9BEARBEARB4OR95JbwMZM5HB70m0mdzc1NEejbSR/7Oiz5YyeAOHzgYQ5YCIIgiHTM5I6Z4PEleTTa/7oSPRyzH6BkD0EQBEEQBEFkJ+8j14SPL+nz3e9+Nwr4067VmMkfjpkA4uhBij2AIQiCIAaYSR0TM8FjJnnMRI+ZaNe+V/tk7au1/zZ9uvbzHEr2EARBEARBEEQ6eR+5JXzSEjk84aOD/CyJH40vAWQPVnxggx2CIIjbmhtd4Vu/+91/RP2eDzO5YyZ4QhI9HO2rTT+ufbv29Roz2cPBEj0arKOJcVPJoI+cIwhiPNzqK//Tx88TBEGkQX6EILzkfeSa8OHoQN9M5PDBwPe+9z0RxJvBvu9vzL+10QMNe8Ayfq7CZ0+Vofzpq8i5cfAyfKHCnrf0BXgZPW9w+bNQZmV78oV/Ev//ytLvw+tf//tQVv/PnYsV+P3Xvx5+/9RX4J+w80PzPXh+mdX5VAW+sIGdt2HX/zW/vgyfvYKd38FckTq77co9BFc/zXX0Wbga/fY9+OyDvwq//OvvhKcy6XmcjKddj7sNXv30UdYGfxl++Zc5TI4d/DoMO7ljJ3jMJE9aooej/bXpy7V/15i+n2MneEywTmZABxrHZqAwMQETnMN16KHXZaDXgupSBaqXevj5MdG5UIEKe27tSrZgd0NdP3Q5bzRgcdcETDJZdbHzI6DrEmcZamst2LiB/00QV5ZhplCAmaU29NVvvUtV9owqtHrWtS4QPbfPMBsqzEDl8t074JC6a0AHOYfRuVBkulDtbmIB6nno10kPGkcnYWIXe8517PzWMVZbQeybIO5khvcjPeis12D55CLM3TcDc0dKsLzagi760ofFCbwvutBBzoWyc3xRZoL9Sp7yIvIg7yP3hA/HDPr1QEAnfDRm4O/6W/seNtigJQ+ufoYN/j7yObiaOPc8nHzta+G1peet38fHxdO/A6997e/D8jfw85Ib8MzDr2fXPQBPbfL/X4fP/Rn//2vhTz75inVtPlyvPwiv57L406fgFeT88LwC1bez+7J7/85HLiLnLb6xDH/Ey8E4+SXk/E7mSydvz3IPwfMlrqOT8Hz022Wo/Bf+2+vh5Jfj12bnOjz/BGurTzwP19HzWRlHux5zG1R2//q3nYSnGs/AM43LmWRgJ3U0ZnLHTvJkSfRwhL++dgGWProEX7g28Okc099zzL4AA+tkNJ2VWRYkFmD2dB2a601oXktLgvSgtcICmZVWMjF0vQZzLOicW82nI8xK65QKdrMkq/pNKKngeOhyXl2Gaf68Qhna2PkRiOqCMglzZ0YbyPbWFmVy74FBsqq7OsfuPQe1rMF3Qs88gC+IMs4/vbW630lI3ZWhhZxL8HIVZpm8CgfKUOftbn1j+ERrJjZgeYqXrwDly9j5rcJlKx6/EgBm38QdRo4vFkKTtDuOIf1I/1oNFoU/QCjMQDGRqGhBmZ871bJ+H4bRfdFW683lV9zlyFNeRB7kfYwl4cOxEzY84aMvNgN7eyBg38e+lwkflGCDl1H5khqcfilx7ktqYPgl6/cxwgZ2f8Ce+Qd/8xJ+nnPjGTj+elauhTp8P/r9Bnz/+zfi1+XMje9/H278GD83PN+B6v1c/ozXH4dnbmDXDHjpb/5AXss42cSv2bE0VcLndiv3EKBt6sej2qiylfur8B30fFbG1a7H2AaV7Rx/DjnnwE7omJjJHSzJ40v0RMkezsVTIgA79eLAf9s+3vT/GGbHgiEHqaVsg1RBF2oH2d8crCUHVNud8JmYherL+DWa/nNFde2I5eRL4Maw/M2ZNLjRhtrRaXauAKX10WZG9Hs96N8a/H/0hA+HLym4u5cTBCV8LpWFDZYuIufGhVj2sRN0hJXD41cCse2buMPIsZ8JarM7kWH8yGYN5vlLj8IslNc7sbbSu1aH0j6ekJ2G8iWzjeacwBjRF22H3jC/4i4HJXx2GnkfuSV8dDBvJmo4eiBw/fr1xB/Zgb45KHDdz0QPTLABzCg0/xcfnH4ImolzTfgQHxj+r6b1+zj5JjzxR+yZ956GFnr+Z/Dqhf/Jyvt6OL72Knr+9qIDKzrhw3jXp3+AXKNpwel7B9d+aB27Zgez/qHbs9xD4G5To6Bs5f4V6KDns7Id7XpEAm3HTOS4sBM8dpLHm+hhCL/84iDhg/lz2+fb2H0ERnjgtJMTPhOwd6mNXiPpQf3w4NqtLmcWvPrQs5OON3NdrpJPwocIaktqoFa+hJy7K8kv4UPc4VDCZ0CwH1HtrDAPtU3sPONmC8p8Bs4eJpfopcbOSmDsFL25y0EJn51G3kfuCR+OnZjRCZ//83/+jwC7ARb8m/fE0IMSbPAyFC/IQZTNh17Q16zLgeHJdfj59XU4ffA35bImxq/90fvgb6/8KH4/zeYanH7n78GvqWtf/1tvhfd9sg0/xK5F2PzkH7O/eyOcvoSd/yGc/x/svq//c1j70eB3+Td/DH/78uC39ZP8+R+C9X9qweO67HN/C5vqPL9X+5Pvg7f+llyK8trX/ya8o7zOymnUW1/78t/CH7Pf/viTm9Fv0TOv/RBalXfB703y5zEm3wp/9vFWxvpuwt/O8Wd9DD7GE11/9DHYQK/7Ofzo2T9ndXg9/Pnx94nnDPSk+GEbVk8a5Xjtr8HvvfNDsGrrKarLBmx8ltVfXS/up869sdyK/43Cr5sUlL2Z5TZluF5+B/wmn7nFruH2df4av+ZHUkeGbN/36Y3BPTlGfX74pdPwDq1Pfu1n5bU/uvK38L4/+jX5O5PLW9+/Chs/Ne4RYdmEkOFpWNvErv05/PDv2X3363bxevjNg6dh/bphe8a12G+czWcfhz+L7iHby59VuB0a1wg5yfMD4vYu73Ua3vVfdD1Zefazdvr3P4xdE2vX4v/KBn/nNLRi1ylSbELjbYM/3YDV97810u/rf+sd8KG/s/SIoZ4drzcjKvugLj+69Hik+z/+5HcHiZzuFfhb/ux71N9OvgneffLTcOWGneB5AT7Mz3/oy/DPL38G3j/7BriH//+198Ab3nEKLrxiJnsuwikeLNh8+GLkrzEfr/lh8zFYmJpUf1eA3QcWYflifPq7HORb92f4Akf8b4xEgRGI8ynjxf1GGQ5VoOXaW6DbhMrhaZhU9yzcOwvF1exLXETQdbAK1RMFFsiWoOmaeaOmv8+eKDkGDD3YOF82ZDcBk1MLUD6/YSVYkGAuqnsHepeXjXtMwuyRKrQz7o/jD2Q7ULuf19UcFHsCS2xghPzmTPjc6kJzaQGmd/EyMXZJvfQz3iOqy60O1I/Pwm61lK5w7zyU1xx7G1i2MLm/CLVr/cCk1BB63KxD8cButZcVt9cyNBwDot5VZtuxa6VtZxqEKNnpckXE9Je1/IreBtQM+U7smoaFU3XYuBm/Llm+bag/w9Zlql8JAbN5zo0WLB+xZLQUsHyMybh+ymgLrF1PHy5Dndkmer2D7nol1TeHXjuQZx82VoswG7VXXscmdO3ZToaM4n6a16kCza51vQazM+T+UXk2O9Bg18u2nG4X6TpSyQpxPwM7Sch8SOw+hd3MBy/H+x+VKLEZ9H+BflWR1BnzX1cDl54h/eHiGctWM/kRhCsV2Muumz3r31umd36B3a+Ay4PpqXJIt/+Bj47fY3B9/8oyzN8rl3FqmeG+gtlvzO8p+Zn3TtUbgpLVTKLOHajex/8e8TVKTosXlO5snYfYTyZ5ecjqe6IydoDv66T9gCyToQ+zzbO2Ma+XicfKyf36Mh63jOpLt4m8j7EkfDh20uf73/9+lPAxwW6GDQhs+DOiAUxevPR38NG/+Ci8lycaXvtWeC/7N///372kr1mHR/iA58gj8Mjv/Rr88fs/Bp/+4t/B3/7Fu+EtfPB0zzvg068Y92P0vvpX8FZ+bvKP4X0f/zQ8+/m/hY++5y1i4PRrD3warhnXOvn+p+Hd7Po3/sUl57l72ADP/P27erBplEcONt8N733wHvi1P3ov/Dmv38fXoSvO99j5N4nB3G8efB987DPPwt998qPw7j+4B9508hF4L6+3+YxXdFJhMJDUz3wfG0zy+3/0k38X3YMPLN+6fG3w906+qxI+rFyfeTf7uzfCR7+KXdeFTz/Arvudj8Kl5iPi/o+8YJy/wXT1e7y+Wk+8Po/AH/NEia0nXZcH3wtvuuc34R3vN/WunnPPI7Cur4+4JpNSvAyJcxl4IVnumAznHoG//bxhX7/3CHxs6a1wzx+8G/7K0A/X2Z9/sTe4r67P+3li6I/hkZge3gSPfPyjzCbfAu/+y7g93nP8Wejpewi+C393RNrEW97zUVaWZ+HTH3+fkuFbWLmNZzJ6rD5v4vf5rXfEbf33mDyPs795bVyG0h7jv1375DtkWVgduf08+8VPw1+p8r3p1KWofN0XPsZ09Ofwjt/h8n+HtOW/+Bisd/W9enDpL5ms2N/92lzcnrlNvPsp0xZVuzbsW9rePXGbUlz7+FvZOZddDnC3wfcy/f5a1M4imbLnve+ZuEwTdNfhY7yuD/IysDb1oLTVj35O10fV5YH3wnvv+TV2/s/F+Y+9wAJlfv5bzF/8BjvP9f8Xn4TPf/Ez8PET2r4+DC+8as7k+bJM+Dz0fnj/b7wB/vThT8DTzz8Nn3j47fDr/Pd7jsHzP9Y+/mX4wkeXYOm9+0WHuv+9j8FjjzI+/0pKsufn8OJfyA2YJw8+DNXzTWisLkPxIO/cCzDPAgHdL8iNeiuwuJ932iyg5BsMMhqeJVHyb0owv4f9zZ55KIm/MTb71QHHyTLM75phQUANGut1qB6fk8HrVBnaVjKGB4OzPHDYNQfFlTo012pQeVDV4XA90xp9ETyyoL+jgrSF83iA3V7ay+q6APUrVvAm6LP78CVTXHZFITtelrKWXWy/ESOY07/puh/ngRaXJ6u7UZeJ/dXsdXENjjblM/Y+bs5iQsqisYNUx294MqUDtQd4kM4TVhWorTWhvlKEuV0FVrdypnvIuixC8fgkCx6lTOU9+O8FKD5nBax6qYGWn7adwjyUT2JlxBhCj0eLUNzFAt/jVaib9looQtNKmvRZoM/3b+JJq5i9TrG2cILXK2Vgq/YfqbCAmZeRy1ZszB3tmRFSfsZmHRa4PAt4e2sZ5U/aVp71L0P5qH1/HNtWUv1KCJjN803WlY8przbEXmX1JVnHwpFG+kBFz3hgbWFOyIj7VdYGhNznM248y/R6WvtmqdfGagUWxPKZuG/Gr8X9OEfLs7y0CAXTjzIbE3V8gPlG4/pIRqeZ/y/oOjWgxv5eDBKx2R/XmQy5DBA7K+yL25kuz+KRaWknQp8p+61k0pHa5+nEvPDzex8oybZj7vsULVfS5WT3ObMgNzU2+5+XG+Jv3f1foF9lOmufmXXodxIWnraTDTi8fYmyGnpc1nI29ZjqR3Daj/M+MIsftdG+grXzKdNmlGwT7UBdf3hR2NjsEakrve8S1s+1H5dLlmceXBZtrLlWhaJaXla5oq5L1RuGSuzYiUGlRy6/KLGjkHIyNsC2dZ7VfjLLy0GI79FlPLII0zyRc9wsky4P9/WsbZxhtnWeyVf7lLPs39wXnLL6EDtuGdWXbiN5H2NL+GjMhM+//uu/Cuykjwa7sYk9WJBvofPnhQ+xwcxr2eAncU698WaD5g9/+cexcz9ufgDeyM69cemrxu9fhY/ypMN//yv4hhpIab71KT6wvQc+8MX4fXC68PkH+QArWabu0yop8rX479/71NvZ72+HT35n8JusFxs4f+gF+LFxLefHX/yAHFQnzn0LPjkvEzavZeei37/zSXg7++3tn/pe9Jt8Jhusz38SvqWvEyg5sPJ/NfY7xvfgk/9dPevHz8IH2ED0nj9/NlHe//3Nj8Pb2LPe9olvwf/+8ofFcz/85cH57134ALztt96S0NP//rYsd0xPqi48EfTJbxvXKrr197L73xO7v8Asg/l7VrByaxkei9f5x433C/289q0fj8v21c/LZJx5fVSf98Ozpt29+iy8nw/sX/s2+Pg3jd//94/h80f47+z6Hw9+5/W+h9nW+y90jWsZr7J2wPVpluXH7DeefDGSBppvrcg62W0q0c5+/A34+ANvgt9M2M+P4YUTb2TXvhc+3zV/V7by3z8J34tdz/jaR0Xy6e1//Q3cnu/5gFFXPZPFsO+ulOs95m+Cb8HH38qutfWAENQGf8Cex3Uz/5lkXTAQ25G4fZT0I7zu0s5jy7Ne/oSwmXv+/Ivwk+j3L8Mpca8pOHXxp9FsHs6Nv3sI7mEd5qFPfz/y8cLnv/gREYx85MVBH2D77Rjtx8RgbPrDL1ozAXrQPMkDmOQeN94EA0r6ki5swNA5xwcBdmDVhgoPZg4uJ2YjdFbnWfCcbb8aUQdRHhbY8QAMS66o5VCFE010hsovug0oHdgNM/wNmPl3PPHB67unYmzQrIInJOGD1V0Hslk2qJT6KEFDfGJ3QIcNivmAyx5coWXRYPVEfsOSNfK35OCSB6AlPjDPcA9ZF2aPtkx10BjbAFPpDpGfTjJkGqgMo8fEfhW/gN4FuUlnLMHSZ9fzuluJFI6278xtSb0hTryZDip/DxpH2IAIk9nFkhgUz64M9Jds66H1V0sKsfqL9pqt/pit5LakC7FvqZuk7+ucWxBvyp0zWhTdtRLM3juTkBGegHVwpSJ9s0uv5sxE57W4H5fyZPfYw+5hJcl656UeTTuIfNXEXuZfreT4jbpsmzEfquxsTxEa1ixN3TbN++vyJBJNHoJ0hPk1QR82zrLr73W3B3tg7+7/Av2q0tncE/YsPJU49808jVD9IdK+euvJ9ixw+RGUPjSPu+qbhttXaNmiLyKQ6zmYLyqx6wv2cmWe8Ng/C4vn4nJ16w2nc3aGXb/I+tXBb9zH8fJNc5nHnoskiBw2l2o/meWFE+R7dLtG+oNBeew2pvpd1tfbL2A2nuAym4ldP6ov3U7yPnJL+OgAXgf5NmbCx8ZM+thgD9PEBiw58mU1EP1y4px64/3fPwXfS5z7Gjz6Rnbuzz4P/6h/+8qH2YB5Dzz69+Z1mm/DJ2as6z385DmekLkHPvwV8/d/hM+8i93jjY/C14xrOd87Jwebn+oMfpP1OgKf/8f4tf/8zz+BLx5j59gg+Is/sc8x/v5R2MPrzZd26N86n5IJn3Pfi36Tz7TLKPn2x96SKA/O9+BTIuEjn/W103vY370/US75+2H4zA/Y/5mcxeAXeW4SpcMTybrseexrxnUGP/miSjyZg2FWp7NvY899G3xiw7g2BKTcUoaIzagyvuVj347/ruVl2qSzPuramU/At2O/Y/airn3XZ1D7/Enj/bFyyv/fAx947ieJa4XMRaIp3qbc7SxJVL6Xzd+Ruiu+/KF70HYh2PgEvIXJ50j9H9VvyiZM++Zt4s9lYihme+xvRZLvrK2HJO42iLUD1QYzysNt86oumF9RZX/LmX+I/674+4/yNnUELvyTTux8RSZ8mHy/r5I6ETefh2O8M35ksGRLYCR8YokdlJ/Dlz7I34axoMYKFgUskF+wAwRGaOCUJeFTwILkHhvk289nwWqBDTqit3cxVNCV4a2RqIMqj5yKHg9SOPJ39SzngAFHysjc2FoFT2Y9fXV/uQozGZ8nn4Wz+8FlaCUSHkhZNFg9kd+SA3Ale8esJD2g899D1wVL0iADDyWjxIBGoPdeypDw8eDUI2bL/SYU+TlDrnLDb1cSkt2LD5SztqWggZokUX61RHGGDTLta7nMNvhb8isDHcm/N8uXZ/3ZYFUkAtPrj9nKOBM+w89qSEPJ7yTS9mIwe+fLTR2+uX+9Dc31FnTEYNR/LebHsfY4QA3o7jPaspKRS9byy42GD1XX4zMnVVs2EpGyPC6/jhOkI8yvpaH/5lzcvyTbhCbMr7ZO8YSYmYw1UL7NTjbZ9NdLIjm3uIa1L+UD7WcE+ZFR2pjHV+i2H+ur1fWO/jshdxUfoP0ngltvDtTs30FSQ9ktK1+LJ4NMuSr9YklS2+ZS7SezvEJBfI8qI55IcutD1qGYTEgitjU+Xzp+8j5yT/hwYgMAxQ9+8AP4t3/7NzThY2InfHyYb5w52CBmGFITPrGBoSY5+NRJjkcvfBG++JzN5+FRfr1rUGqDJR3UwB4bfLoHm1i9kGSVCXv2++16OxM+2GDWfy6OkqN+lhqcH35aD84ZtixSEj4/+cfvwT98hcn8wqfgr4+p5ShIgsSsSxxs8K8Sdo6ESCacCR9ETs4yIkmPkGsViecqnb/hf34CsV3G2ffDG9h5nTT52mM8WfCn8Jnvxe8rwZMZ3oTPT/4R/vHlr4lnfebsB+CwWhYY17GrPko3//1R+Lxdbs6FR4V8BgkxvF3rJKuZxJJtWiUajWsxwtpgijxs0hI+qi6mf/zp2jH2N/fAqa/G/WbECx8U9zx1Uf4/2pfnkYvxZI/gInyEnzP26BHohE/LTOy4+A48yQfpzs8Rd5D9X4YInDIkfPBAXAUcRkAn37rNQWWNDUz54DRGAyr8Oa4g2kDUQZdHDVTjmzerQY9OYHjLybjZg+61lihD7YxegmQGOcm6eO+Z9jwDqY8FWLblcX5ZLQ+YZgMC8z5IWTTYc5HfEgNwHXyfxgJHBpLAcid8cNuyz8m3re5BoraVoEBzGD1GJM/JYHce6uhbTCSJ5SPLQC2l/DoBk/XTxkl9DFP/+FvyAdnrv9UJHz37Qi9DaG/2rFkY2en3urBxibVHY6lNeplVAjXTp+LTrk368bQES0JvSkb4wJChBsc6QSHtbDcUVyyfpKge2x27P67fFEJ0lMmf8q9AdaDNy3i+CiW9tNaydbePCvGrSmcHK2IJWUJGaxW/vBXSx7n8C59Fgch1mIRPhj41ic9XYG3Xdz0mdzWLjPkzsaTr0gZ0sYSnwte34MgkyyChxMpXUDoU9j5IcMq+yHpp5LC5oexnSF+X6nu87cJdHmcdMNvK0ZduNXkfY0n4aMyBgE74mGAJHxd2soeTGLDkxFce4QOvU/CVxDn1xvuRr1i/c65Hg8/r6jd5nzSw5+B85RGedPggPPdT+f+Xz+5nf78fPnEtee31aLA5+G24ejnOR0mF69Fv2DOznIuj5Bg962X4xNvY/9/2CXhZXfNPnz3M7rUHli6rv7l4iv1/MFCV/BN85aN/Cm8QM0sU97wB9r3r7bCf/9vQE1aXBOwZfPD/wefV0pZrn4B97G8Of/afktdmBSm3U07OMibtLuhaReK56h6R7BzoZ7htS4Kdx3776UufgmNv+/Xo/pxff9Pb4U9neULJ1rGrPspe04hszGX/7HeeWPzAc/BT8X9mi3/IrnvX0/BPsetwwtpgugxjoDbPcbdll21FSRzjC1s6qWMnfAY+HU/4/P9a2RI+suPwB1iuICM8cPIEK4EBh3x2GullE/eJyqPekFtLJPgAJnpD7Shn72Il2lxSUoDd+xZgTkx5TkkU+OrulUscvz6Q5R8+vWPPRX5LDNDSypvlHgxfXexzaYPEkEHkSHqMcNmr2x7TzsfwDNSylj90YJ0s3/bUHy/3cIOgBA7bFRtNR5sTc/i+ItZGvk560FqaH2xSyinshpnDc2KGVXqZfXK2Sbs2KafgthPYvuXfs2d6Gd4uNZl15Cv/TbmxtBgMa3ZNw9yhWdEH2HJ126xHD4nnq2vTSNF/WvtB5RqU8NHPQGZzpOKzS6zt+u0Yrav4SEC8nYkN/pHN6rP6GpPYLKxYkoeVVSd/dOLaToo5bG4o+wnydQG+x9uu3eVx1sFhW6P50u0j72OsCR8NHwhgCR8bLNHjIxqo5MzFR7hBnIKLiXO+N97fh3N/ws79yblo+cPlyh52n4fgs5s34MYNFzet+3j4+hLsYc8/9iz/m5fh7NvizzP5/lP3s2ffD+c2B7+563UZlt7Izr3zs+i9zOUb0W+b5+B+9tv9Tw328MCemeVcHCVH41k3Pvce9rd7YOnr/P+q3m87Cy/rv0kMVG/C5fIU++0e2F/+LFx+hcn5pj6ndGjKDalLEiWjh5+Hm+z/L398H7v/MXg+uu8QJMrtkZOzjEm7C7pWkXjujS/AQ+weU5WLiM0aqPprW//CjcE9B9yE5x9mz7VsL2GPm5+FQ/ew3970EJz90lX4vtE2ZPnisnLXR+nqvcyesTJrog2H3e368kd5vZSer52Ffew62f7i12FgunS3Qf+5BIjtSNx14fvuDNoRApLwQWfxCOLnIn+fkvCJdxxp04TxICM8cPIEK4EBh37zXL8e368mDja9PY6og1meWIIHSQAh5exfVpvRHihD/Uon9lwpIzPQRoInX929comTpg/5tt0MwjyBJfZc5LfEQAJbfmcythk+7tkqWWf4jKzHCLe9jjrDReAIpkPKnzYryiapj2HqP/oMJzwhEDII8pDW1m7yt+R8U1I1iEI2po7Th/ZpuQfX7Om6eKPds5OtqWUOWcIR7selPJPLWDWJmSNKRsmvFilU+9b7N+m9TiqXbN8cRw/Kcf0GkKYjp467UBcbzU/D4koTNrqsTPoLYupvbFt3+yhP20g8X+usDl1ELhFeO0vzLw65BiZ8tJ9N3RtPbUpcu6yv8/kKrO36rvfJndOH3mZbbHq9qBIL9lfF/H+PI5fMyXYi2oSR1BHJINHmWLmZzSX6PofNDWU/mX1doO9xtguOuzzOOqTZVrAv3V7yPrYk4cP54Q9/CP/+7/+OJnrSwJI9nMSAJSfySvikDrCCMZIOavD5ns/dQK4LHWzqATkbsEeD4AE3v/RBsUHrdiV8eMLpg/dMwD0fYPVWSa9YvRMD1ctwiicO3v1ZuKGviRg24aOTGnzwL/UgyoNcl5mdnPDRtobKMIm09XuQBARH3cuyPdsev//pQ+z/eHuR5cua8LkBX3gv+/2NS3A59rsLT7s2kqxC//d8MHOSD9PldiZ8dF0wO+dJm1c+wZOY98NT38WTOnH0uRfjvt6T8El2HHxKNLtHwREEqaVO9hr58MApv4RP6IDVhahDrDzGEi6954W5xAspZ/s0HygYX+UwkDJKSRT46u6VS5xUfVwssfNIwsfe7JKDJGawsiQHEmoA4whI5X5Iaffw1yVxTpUV349G6TPDIHJkPUa47NX8VLGJklnWtuQIpoPKrxKbLrvqi4GmnTAyyxdWf51sRPcY0Rs6Z6g/nhDYooSPQX+N6zNtb5W23JvpcB1JwCgZpZY5zTfLhIBMToT7cSlP1x47Sq7mjAUlI7xO+n6GX7ZnSKaA63c4UB25dNytwzz7Pb6cV6HrbNm620cp3Wbyq0pnQy2VGuD3LyqhattFYMInaqfMZt0bavehldgc3Ocr8k74mGzAMl8uZ8k2+98bqLYzc7Ypymu2IenbitC8xG0d0YHD5lLtJ7O8MAJ9j9f3ucvjrEOAbWXzpdtL3seWJ3w0WGInlOQgJB8ufpgb0wm4mDjnGwD9AJ4Sg8+n4Af6t589DyeY8Rfe+RS8EruW8RN27k374aFPvQQ/s895eKXKP3v8Hnj4/XJGxTOv4tf9oCYHm4PBm67XR5B6Mb7+GEyxuk198Hl41fz9VVbnN/G/s+r93adkUqH2g+g37JlZzsVRcrRkfPlRXt/74dA7WXBZOAHP/8z4m8SXgS7DYyJZ8bl4XRg/e/ZhuS7a1BNSF5RvPQn72XXvef/DbOBcgBNf+lnymh9chudffCXxXBTki0ZOOTnLiNhdyLUK7LnS1qZY+ZL1fOUT98PUuz8Cz/9A/faPz8BD3NGze9u2/oP6Q1Lmlu3Z9vjq52WC9LGvD66RvAJP8lldlqyi+rw9+cyffekEe2YBDtXkZ8Fj5549AVP/7SF46hu6Xr52rZ797ofhYWZTBdY+srbX0DbobZ82iO1IMtTlTewZP7HOMX/0MNdfrM3g95I+/UVvwufEV4zfGFinwZFvsJAvKzE6Z/lGnMnkSnjgpIKV+5GAMTTgUMEn+jWXm+zcVPLrHBiiDlbwpDdpnn+A19v6qgRSTvl2FRlos3IUuS7TEgW+unvlEsevD/XFl1g5+Vtt9jeJL3P0oC72RLCei5QFG6DJL4vthdJFa3B/qy2/JJPhHr66JM+pPRxYParX4s/snNNfgEofRI6sxwjknP66GDJY6qqvIWVuS66ET1D5VSJsTynxVR8+KOVT/s031UmZB9afJ7XEV4RK0IyVr8fuze3Fvj8OnhDw+JUQEvbN2sfRGdjNfIw9uNIJLPvrNHFUIg8ZdPG/FzpPHbhp38zKZW0aLAfyvJ0OZnaE+nEpT64XJnvLDvRXtKbNGQtKRtgXhKLPQE+ZA2xlZ8j9xbmD07BwqhnJIDzhE6gjrWNblnpmIpLwkXJjdbBsXbYJcyN3TZhf9ems/1wJpg8sQu2qz84Yvv5Qt2e7bqEJH4b+ot70iQZ09Qwo8zzzZdxm9p40vxLn8xWjJ3z6l5dhbgr5GhW3L57wMTcdZ7j15kMl5qamRf1iNiVspwDTU8z/YslWRx/uLkeovDACfY+jjBJ3eZL9giJhW6P60u0l72PbEj42WEInDXMQkidyoDvBBrSPwVO1Z+BilFQJTPgwfvCZQ8LIJ//kI/BU4zK88oOX4GL9b+A9IokyhQxuU1BJB14+3+AzdLD5L//yM7j8l/tlg/yNKXhP8TF4+N374A0FPmh+Ck7Y9d7ihI9Z7z2PXo6fQwa/l/+SL+kqwP6/+Bxc/s6r8Op3LsPn/vo9MPWmKZHYGirhYyQeJt74GFxOnFeJJnb+oc+/ap1D2OEJn3/52WWZ7Cvsg/f89efg4jd/AK98/Rl46oPSTgrvfSaW2Hqlpm39Mfjciy/BK9+8KGVeeAge+h9cLnHbS9ijThq96SF48ksvwQ9e/QG89CVme/9tkumNJ/zsBMfP4HnxlSem5/c/ydrq80bi5wfwOZ4cnJiE+z/8FDzz9VfgB7o87D4TbzL152vXA3+AJ6PchLZBf/u0GCrh8y/ws689Bvt4gPaWh+FJ2x8VDsFT3zGv1/eykjoCR8Ln20/KddpTC/AY8xvPtH6EdhgDWId8mE+BnoS5pTq0rnWhc6UBtROzwpaSn/n1dPZO1BIpZiezx6pQW20OArEhAo7u0zL4nDxYhtpaGzrdDWjxDYpFUmE60+wfUQc7eFLBs7A1O2DCynlFbkY4WErTgbYoh/psa1qiwFd3r1ziSH3MwuJSRUypjzi5CLNiX5cCzLCg39QjfzMs+ppdc1A8U4PaSkls8Dx9ZDH5XKQs6ABND/wKM1Bc4ZszMnms1aB0gOmd3TfvJV2CTRZ0C1kXYDcbIJVYneemmD2zgWbtiYyDyFH1GIGf04OlyYMVsbFo51oL6mcWYLqwCIsPInVy4RqoBZWfD5TkgJ77iOXzLdjY5O2nIjd4ZmUyP6OdlHl4/fusfLOiXU3C9OEiVI4vwAyzSz5IrZ2074+DJwQ8fkUPkNhALHUGBWbfysdMH61Ck/nEnvAxZVkPZlvtlP1MZPKTlUsvq9hsS52rgWOWhI/wzSJZy+5zogYNplvum6vH5GbCsYRMoB+X8mTt9PgcFPYVocr9KF8Ss1KEGVXHWKJGyWjmeBHmYu27CkW1MXziU9J6qeE+ZWfdgT/gdTLf7IcnfAJ1pH17YZaVndnduk6yqKSxsaSre60p5DbJdIXt4SO/SMaee7jCbK4BLWM5VZBfjfTLdHZK6pdvuC7shN8jlkBz0z2/IPYf0v6lK5Y2lSI5JBJuQyR8xAye02oT612srzlZhfp6E+qsfnoJVeEAK2/sWT5fMXrC5xf9tupvZqF8ntlvryfbx1HZ9vTyQo1Pbz7kLCr+bHvpnKoDP4fN6nL04e5yhMoLJ8j3eOMMd3mc/TRiW6P60u0k7yO3hI/+Ryz4N0hL+PjAkj0ccyCTL6/C8x/eH22i9vCzWWYCuAfTr375MTj029yxyvtxJt/2MDz1TWSGSCrqOaxBoTNMFKGDTc2r3/gcPPbe+2HfH+6DfX/yEPzNl3jSAKn3Vid8mE4+925e/v3w5LfM3xno4DeuQw4f5D7zHVWXoRI+TD51vp8QknQS6CTDFHzkaxl0u9MTPpyfvARPFeNynCi8AQ49ehGdxfRK/WHY/xvGtb/xHniS2Tlme9hvP/vmk/CeWFuZhPv/+jK8JMqHJDhY+Z78H2+QgcDEPss2XoWLjx6CN3DHbtxvf/EpeCk2w8WfJPmXf/wcvIefR5N8bkLbYJb2GTFkwofzs28+BQ+/TQZJmsn9D8PnOrbfdiR1Us796IsfgVnxdR4GFogkwDf5mz/TRqfv+wblTm5uQPXB3cpOjL0jhgw4kpvUchkWoWbN9HAh6oAET+0lPlsC2a/AUc7ecyxw0bLm8MHQhY6SUUqiwFd3r1ziyGch8I0aDy7C8jp2jz5srKrBnbieDxRbLDhEnouUxTlA67Vh+ZDWM6cAMyeb6H1zSfhw+MadbKC6cN8MzDAWTtVhgwXRIYPIkfQY4T7XOV+M33/XgpiV5KtvAs9ALXv5Jf1r9iaacsBob6KZLN9w9f9FbwPqS2zQK3Q0B4tnmmKmQNb6O3Xp8it6aUOWr1yhbc1uH5wC7D6UlBFOD5qn4hsB88RKY1PJKFPCh4P5ZqbbVWwWY3Y/HsmTDQibJ9VAXoHZwUBGHWZrpbhcds1B5SK+LAOzM76prn19SFsdEKaj/rUqLOg+w5z9EbMhyeTBZWizsvM6J+05rtv4DIUAv6ruldAZ+5vZ4zXYSMyMcoNu2n6I1QFLagyV8JF01yuwwBPq0XMYPAF0htUxcb3PV+SQ8OHcaEHlYLI8rvbh1psHZfvYHllyryvHvZx9uKscofJyEeB7nGXkuMvj9NuobY3qS7ePvI/cEz4cewDAGSXh4wJ7jgYb6ATzzz+DV18dJimD87NXX2X3Y9jLKXY86YPIHcvPlMxz0qNM+NiJhbsA0RakLNOXNIVcixO1FXPpng+uZ2e7Msrzz9j5FFTCZ98nksvDdiKYP8T4+Y9+BD/i/BQ/Pwq8H+D7caQnewxu8c/Sqr0hsPN5wPeeCAhk05B7juR7z3AMuSHT3Xc6wXaShZvbr5fwQeS49Tju9hVe/u1uP1kTPqnYfkUt1XFuMpyZgUwHm58GoPbaybKRvJcQ35zh2kTbSGuv9sAwekbGeun7jyoHlDAdCX+HtY/QMgoZuK8N86uGzkbwPVF7HvesibHqcwhCypOity1j3OXIy/fkRlg73QnkfYwl4WPDBwBbnfAhQvkRfL36JLyIDf7aj4mppXO1HybP3VV8B54UG6s+Cd9BzxN3It/5BJ8COwtPfhs/T7j36CGIO5rNOixfwAb1PagfZn2FayNbYsvoXa5CNbHPBkftN5F5tksAYtPg5P5jhCQ4GeqdCUAQBHHnkfexJQkfDiV8djjtx9S63UV4svUd+eb/Rz+EbzYei9bWP/NPyN/dDXzvRfhc43PwN4fl2tSHv/hz/DriDuKH8GL9GfjcX8v17IXil+Dn6HUE5u8J4s5H77/B9ytoiP03xNvDzZZjnxNi62mrDbv5Hiktsc8G11H3WkMtxYjv5ZIXfN+IUb+AdCdDCR+CIAg/eR9blvDhDzIPLIETCjb4IIbn5xtPwcNiAzweIA2YPPgYvHi3JnsYP6zx4IQnvWbg4do3aeB/N/C9p0SAyQcEM+9/Cr45hmVPtyuYfyeIuxO+B8ZcbL8C3Vfg+zgQW87NDaipBFxMR569X0als1aBygXsc/0EhxI+BEEQfvI+ti3hk/XAEj0abDBCjA7f2+OHGy/C1zvj2d+DIIidB+a3CYLIgNgPoQNt/pWaXsjeGcSWwfeU4F9oEV8S2yn7ShAEQRBEkryPHZ/w0QclfAiCIPIH89cEQRAEQRAEQWw9eR+3TcIHO7DnEARBEARBEARBEARB3G7kfVDChyAIgiAIgiAIgiAIYpvJ+6CED0EQBEEQBEEQBEEQxDaT90EJH4IgCIIgCIIgCIIgiG0m74MSPgRBEARBEARBEARBENtM3gclfAiCIAiCIAiCIAiCILaZvA9K+BAEQRAEQRAEQRAEQWwzeR+U8CEIgiAIgiAIgiAIgthm8j4o4UMQBEEQBEEQBEGMh1t96PV6jD5+niCIiLwPSvjcrtxowOKuCZg8XIcudn4L6VyoQGWpAR3kXL70oHF0EiZ2LUD9OnY+Se9SlZWtCq0efj4Ptq7+A7aiXjuGXguqSxWoXurh5w2kXLQuOtBgf1e50BHn+lfr7FwFalfSgo0etM6yv3t83DoNt+excEPKt7Lahj52PgMh9ijbSxY9SDbU9Vn0PxK3utA+z+pxfAFm7puBhePsmWsb0LuFXHuH4Ws3IUjd1qB9Ez8vUfdfaUEPPU9si39XfrZyppktptB+Y4S+r31mBgqFGahcznsAyHz4Ci+bTRXq623o9rG/CQHz3eHtJhk77JA+gTE+3TgI6OfzY9x2kh1M3gn72EFxfyidC0WYKUzAxASH2fcN/LosbEfMHdHvQmt1GUpH5licMAeLJ5ehdqk7dOy0owlpky83RNtpvIyci1Dtjfr+TOR9UMLnduXqMkxzx1koQxs7v4W0TnEHXoYWci5fNmB5ij+rAOXLxu8ep9RdnWPXz0Ft1ODJ48y2rv4DcqvX7cD1GswxW59bTXdWUi5aFy0o8zZyqiXP91iwxP9/vOnvnF+uwiy7bu9SGz+fG+H2PA46K7OsDKwchRI0hwxwQ+xRthcGC1pTO/1+E0oqSMyi/2HpXSzDbBSMWrDBV/XaFg16tglvuwlA63bvyZanjan7H6xt0aBlmCBzCwLTrei3QlB+dmJiL1SuIEtrLnwAAIwQSURBVOctIr8xdN/HkxsFcY/5p/Nu212oHZS2iMIG1sUhEpoDMN8d3m6SsYOjT9hyxqUbT7sK6OfzY9x2khVc3gn72EFxfxAqpiocKEN9vQnN9Y10v7rDYm4OjxN00mpyaka8GNqt/l/Yx8rjfdFxGxLSJi+VhRzKl5BzEaq9bVnfn4FtSTRnI++DEj63Mzd70NvCNxAuttT5iimh1uDL45RyC5w9zmw7Op9tGRBsF3klfH7Rh+ZxrquiN7EhBzLZBj0jE2jP+dOB6n4mk0IBCuyZixeG6/RC7FG2F84sVL1vg34B/eeK6trxyaPP2rYIoqcWoXalZyQq+tBdr8DcLi6feahtxv/uTsLfbrIz0G0Bis+5kmRbnfAZJsjcgsB0K/qtEFR5uP4KJ1KS4txv3CevHa3vQ/xfLrj117/ehPIBPrhO9z9eEr47j4QPA+sTtoVxlMPTrra039NsgZ1kJilv1D52SNwfhIqfSxeRcy52WMz9i+t1mC/IpFWza55jccJaScQQhQduv5lXXkLa5O2a8NkWv5ONvA9K+BAjsy3O12QrAmdK+GwfAQ45beDaXy+lJDbUQGZ/dXumC3O2sgO6UoG9Qh5NqOxh9c4y6wYhxB5le5H4Z1H1oH54cO1Y5NFnNsLrPcVsxvV2brMmAr1ttYkxk9ZusiJ1OwuzIom4CA102j4lfARb0W+FoMoj21vKkgvlN+S129j3O0nRn5pxMHM2z9kbOSV87mg8etnKfi9iO+wkO3eMfWRKBljssJi7c3aGPXPGmfxrL+1l5++wmDykTVLCJ3fyPijhY+ELtJJOxujgb7Sgcmi3GEzyt5u7DxShdjU5qIzucbMNy/r6mPH3YON8GRamJkXj4UxOLUD5/Ib1xs0VXPSgdWYRZu+V00MnJiZh+nAFWmjwxp61WkxcG89eD+hdrUHxgFHHQ/K+WZ1v6xR/TnJ2hX6LX0Dq0jjC7r2nEk1fjT9LOQ9RHgNDngN99mVd+dt6fk1hN8wvZZmur+RsY5R1UCYm+6X5aIrnxK5ZKK7aetOEyd7Gbadj1mlvA2rHZ406TsPCUhO69j4nkRPtQO/ysmHPkzB7pAptbG+KW11oLi3AtNaRll+AQ04fuLLfeNldiQ01kJldsQK8MdU71J4lo9mOiQxSZJuUAU1KwNJtQuXwNEyqsk3uZ37uWj9ogCrqfLAK1ROs/L5lZCrYnj1RcuvfKk/hXmkzWZNWvfML7O98s1Eksn4FKK0Prot0d1PahiwDb0PLag+buD8o3DsPy5etPmGYdsLgM48G17r7G/72Md6fqGutJWrp7SYbkUxUkgx/46nujwZ9WWxbzUrbU0om6fRyTLWkTNaL38fEb6dZ/yapg0VYvphlhty4+q0R/YKyxfKKane2D4zoQ1O13ZKYMantxoC1y+Ujhr9k5Z49spyIQwb1HPwW2dCtDtQNn8vbT3kt68A7bWDRghIvl2njvn4GGczEfTfH3W5C+lnvfTfr1n3K0HDMPBw1XsN0w+lfq0MZ8bn+GWEZ2pUh//41Vvb9RttSZbfvycnuCzGGsBNFd30ZFiP5SjksnsHbZhaZedtCdJ3HNyN9oas8IWV3gsVEp+qwYfpkpVNx3gQrf4Sqo+dvBnIJiLmzxnAO5DMXoRG4r1qYrDEfXoY62l8zW9nsQCOKPeJtOqhd5BB7j5zwCdIPk1PgOLl/ZRnmlVxlfbLG29tH3gclfCxcnRzH6XxPVKAyVYCZBytQW2tCfaWoDGuaGT82RXMBFo8U2GBpEUpL5nrmPjs/LYxu8mARqueb0FyrQfkgN+qCtZYac/w8QcKfOwlzp2rQ4Gtlz+slCfbb1i40jvJnxcstr51JlFsve+BBV3GlLspVeXAGClOs/Cd4ndIDCJnYKSQcgkwEsXsYiR2B2nPFnAUQ14FaD35iXgzS9z5QEmt+zfXhWp+lU/MwuW8BKqsNJpMqFIVMJ2D6dNpGtWojRha88utnj7B/8/8b67p1R1BmutN6a6wyZ7uP18vWGydM9hi4neal0zKUj5pyVlxn+uD7C7B7LSxx+6pD9fic6GwS65d1R3GcD1ZYB8ev1/dnvydnS3Sg9oC0XS7jQdkL7G/LmTud/vW2sT6cdQq8DVyLd3IyyYG/wUbf0oyx3qH2nIftDGA+hHeuek8jnWBxDfT0TBddLy0H5lsqp91+00bUmXWqHZVcWziPByGRnq7gQQfvwMW+O7vm4vbLruWbWqbPxlEJ5YJl5xjKFxWYv9C/6XZfPD7JdLEMddMuHqiyfzMff7Ac15G9PCC4nfShfWZW1jHhayZh4em47tqPa1vh5eP9CfN94trp2JLFLO0mC6Y9d1bnWTmZ/1u17Un1XYnAKsC2me1w3zX9uDlDrAt17kOMRJDc/LgE83wW15552d+mbIac/jesnz6t7CzSwbLqU7D62oyj38rBLyhbLF9SM+vs/lhzow4LvNysX477L4X2E5G/ZGU5syD3vmB9S9tI8GL9mNmudh+S8h20n/TkrMQ/kO9fLAnZx2Z6+gY3IyR8QmMn532PFqG4azfMH+cbCg98zUShCE0r8RnctyOgMYZqdwVmk8s8Pl1vQPWYbAvxtpgktV1p+Z8sw/yuZF9r206oL8QZwk4Y0rdJOYi2ycq5rHx2om1mlJm7LWSzMdG+kL6w8ADra41rg8ruYpP5AOVbIj2dHLTxKCZS+6P44uckY4i5Q2I4B70Li6I8c0+kJzc1YbLuML8r+5CED7fGktpWFo9MyzYu2pLexDq0XeQTe4+U8AnSzxDj5MOLsFjg9ZN9rdyvJ0u8vb3kfVDCxwLt5BRO58uYswdJN9tQEdPa42+w5T1YY+cZR/N6TrcBpQO7YSZxjjVI3khiARji+FmgxRvnrD39lP2+wLOf6wNlc+dVmNgLxTUrqL/J7ssbnjnQ8Cx76Jzj8uJ1MuXioN+EIrt2bywwYHJi9947xRyXNV0SSxAldcDwBGpSn8lOL5Jp1oy9x5k5dcpkWRLBTTxwDpK9A8xOw3SqNsPFdKo6qbicVTJxTzGxTEMHl7FEgdIJtveJHoSam1LK+iCDJS1Ddq9MnU4WVKIheT9pi/HZP+Otd6g952E7Grm8zZy1omZOoPdQHTVSLx0Uu/ymjaiz6PA9z1P2KfYSQeXBdMXre3A5/kaRoRMN5mwcHLV8zxHsx1HXGjNWdLu3/W3nrAxUC/bm4NeWYYb9HlseEGgvevCQDDpV0Bbrb+Tb6UQ5uK3sn4XFc9kD16zE7VklYBJ1Y8/ndbbkHmrbUj7M5tS9eX/B9Z5MCPgHdTiev9HJpkQ/3YPmSZ6kzLjnh6edh/ZbufgFVR7exyV9wwC5x5msY9J/9WHj7AJMs0GIy0+Yg2esH9PtKiFf/oUi3mdl2idD6e/+KmyIz0BrurCxJjdoTySFPfoYOuEzROzkvC/yAlHq3dpYObhvx0F1c5L9bSLBxAdgs2wwVYMN12zNCE+78vhCLatY4iXIF7oYwk76G1A9PA27E22TyUG0/3jbzCozd1tAbMG0Md0XOm3MaMeBZcdRMRGip19sVoUOE3uAZUoGWOQWcwfGcE50QobZxP5FWD7fgo0bnhgjUNayLXtiYMOHu/sHRmC7kPfKIfbOpGOs/QfqZ5hxMuI7I3x+f5vJ+6CEjwXmdDVO5+t4Q6z3CzEDUHmPjEkGA/l3Jb/jdw5kbVSjcyxrkYmWwaa1OvGCD6BYOXhwkSGAkA2bXXufEXy+XGWDIPasSzIZZHboYuYPmjCznpUaOCdnFXG4g828OW9q54PbTPtx27GHyd5F0k7z1KlKfJhyVjLGZ2OozZBNR6uuTy7TYwidm/pSg2nHoER3bvk5ZJVosINO1X6wt77jqXeoPedjO5LBsoxk54/cQ5UdD4xUuRxtwEbUWcleLqlKrouXv6tyYPJg7ZEPcPG6Kns60kDlNAALnl2oOho2I3WH+HLlK5KJB+R5wfbCA6N4AjlCXR/Zr56VlKl++ZCwZz1IZ3IbtG0lh1j7G8K2+22RzChwPauBdSK5JUjqLh3X36h2w/VuDa4EevZLymwHgbOdh/ZbQ8gOQ5VHPlP1AQl5xpPiqP9yoet7buBDkv2YvifmS5S/zfQ8JRNuZwizJxtsgG/9jUcfWP+frHuyffv7WXY9Ejs574vZr3qJlv2ZSN/uIKkbFb9lmRHpxNMWfb5Qz/Y22lWQL3QyhJ14iGQWJUKyy8zdFhBbMPWt9yVcQ/Td70J7vQmtl9PkgJXdAZOt2NfoiQ30vJyZa/WLnvjZSV4xt7KrzDGcF7mUSC8N4oglWkv1Ee1ExaT3LcOGdS2nd43PDGtHy5vk3+M+Paxd5Bh7K31lwmz/OepH2gUyTvbFgj6/v83kfVDCxwJzuhqn83UZE9pJ2fdwcLMH3WstMf2zdkZP6zPLlXT8UaZ/Qi3putLBd/NXQcLuY1V2f+5ILFaKsJud105BOtB5qKN7AYQEYXogN6iHkLfoDFXHGAWYeMCJys/TYH36DOqEUjsfvP6J5wfK3sWo900mokySOpVB5G4oriD3ZlSP7Y7fz+dE7XPIcpkYyMB3VMy31Po3LME41nozguw5J9sRuJIBxnIN83c5yLRmmxhk2v9HIeqsO3xVp/jzVPCjgxBEHvp5lTVEDsxnVngQnxokZAgGIpIDFWe7d/oKxGcH2YsKzg5WxDKZRL3XKuL6QX+j3pwxvYklXZc2oIslKXIEkwlvQ3xAMpgJpeRgBn1D2racNbIX5h9g7dm5SbRnkOnE9TdKB85ZJh2o3Z/xWR7dB/VbefkFVR59X9nG4ktf7Zk/vr6P9yO9XkcMOPlytJJaymDaP1ZP3z39zzNR+ruvpD4DPaCxWlZL3WZh+YoxSPa1RaRNJ8uSbN/DxE5Z7jvA9czsfbsLTDd6RpFcntSCjS6WVPLhaYs++SfqGeoLXQxhJyb9HvQ2+XJYvgympJbNxO0kq8yytYWkvmU7ddmYhwxlx9AJRVcs8AvmkxP3CYm1NTnF3MExXEb6NzaguVqBRb3XlG/pbJqslQ93xsAW7v4hsF3kGXsrfUVL8FDUkk6j/Y+kn6HGyRZev7O95H1QwsfC3ZBCO2L8vM9RcXoXK7HsMXesu1lHMccHQFkMWW18JdY8K8T0Q3MzSWXgg2fg6AaQVua08zHUs2XwGU/yCNnrNyGONzToszwN1qfPoE4op84nVPYuRr1vqE7l8+L3SpIsD1oP+5zv2iznh0HdM5qxwjrcaAmRcd1Y680Ismf1O16GAVnkJBOvEzDzYNnqkFmn+QZ2H+uNpLcdZThvIuocdfhsAGLPNFIzraI3Pk65peG2b4kO9vG3W3Hk8igzAe1sQ05fgfjsIHtRf5+GeX+xGaOxqSWDr/lPbm6YD7hM9BR2vW+Qqoc56BvatgdfcnNPy/cMMp24/sbR70YEPMuje297su1raNlZqPtE91Vv8gdyTe7tg+o7tom5Ytc0zB2aFe3alB1WT2e7SjkXJ0UPyDIJnz6wNp0sS9I20sqLnc9y3wH5PBPDZYN8M9hYjMo3Erc363Xi0YtP/ol6qv+ngcrMZAg7YcQ3lZZMTs3B/AHu55K+P4vMsrWFcH3bhJbdJrWvx/o/T/zsxPM3vjrb5ZP/H9QVx1OfLPBlRvxlu/XSIbOsvbafxK2DwHaR9tyQcmXScbK9DaOfkcfJJoGy30ryPijhY+FzZk7nm5j2rECytj5H1b+sNto7UIY6n53TG2SL5d8FGPKtvsh88s23ZMMw9jZQWd3px1vGmmUENQDL642RRL295zJT8omSOqLhyWmKUg/JZ6Ly8zRYnz6DOqGcOp9Q2bsY9b6hbx7lDA82YLuE3NMgagc+J2qfU2V3vo0bwwyfaPCiBvv6zbWt37HWmxFkzznZTtTpsqBz5r6ZJOLLB/ElAVIO7mUhQ8/w4cQSPEgCCJGH9kn160j9Ixxv2wzkfTLsuYIsl3W2e6evQHx2kL2oWY9H6tBF66tAB1598YaRb96o30gm9nrLAadM2MBJ7CUzxTdeVXIwbWBI2476zAKzGeyrXYKUQR2K62+0DlyzwgKe5dF9UL+Vl19Q5RnYrZXgSSSAMH2rfZuYz1xcacJGl/lG/ZUVdX/T/rF6Om0o5VycdD0kfJavLSJtOlmWZPseJnbKct8BrmeOPiPba4OMPp+9xTcHPqKSe45lIXE8evHJP1HPUXyhyTB2Upebkk8tQnV9gz1/0M9ImeFxIscns2xtIdTGLEYouyYtFkD7P0/87MTzNz4/YMsxOIYbFlZesbQuGs8EyDotBrbAbEUS2C7yjL0z6TjZ3kL1k+s4meP1O9tL3gclfCxkQ0ruKYF3lMqYXEsHbAfA8Dmq9mkeKOFfDxrKkDU3ZaMeBKnKKTjW/NvIBonvJxDdK0MAoZEdaBEaz3H5mHWS0xFnzrbkzB+kE0bl52mwbsfICOmEcup8QmXvYtT7ymmUzDbRdd9q00ezTvasizR8TjRxTpXdEXTp2Sh5O2Rp17ytq7aNteOx1jvUnvOxHT1wc3byOlFtznZCEh4D9CwLRzuzEHWO6VolgXnwiy0pQ+SRGnRmRctCfcYbvSYaxMZ9s7PdO30F4rOD7EXNiExdqpbGBizzad8j3yeJzxfqDRinT5VlfxSzgWFsm/2NSCKxemzK4BrXY0ASJsL1Nyl7cui2k6Vf9ug+rN/KyS+o8ph2ay7hwr5umNB3l+mB3cNeEipQ9zftH6unz4Z85+JkGMiLDW2NZ6vyxTZVV+j+0pRNsizJ9j1M7JTlvgOS54L7dgdeG7TYeILHdFn8sUcvnvaQrGdevjDcTrpPzzvrKmWGx4k2tsyytYVQG+PLKtmAWQ3wcym7NxZAEmSckFhbk1fMHRrDobRZnzkDMzyRgp5nXI6P98JkrXyByw5vxpMemK1IQtuF/7lBsXcmHSPtLVA/uY+TvX5ne8n7oISPjWq09u7vfWaUfGCAOl/mbJOfYtU7osen+PkclczUI4Z8kwWRopP2G3L3/CLM8K9j2E5ABaHmTCS5h8k0a5zJoKBzdg6mD5ehqd8QoRtvSvgzubyyBBARooEXYHqK1ddaTiFkMDUtBgZYA0TlpxussRmkxu0YGSGdkLq2dDF5LqjzYQTJ3sHo99WDpRI0Y/bWY/WRnzyM10kNypEvQYhzB6dh4VRz4MR9ThQ5J7+4s5fJ1yr7LVVO171GQQW/e48VPRutjrfeofach+3IgAxLamvULJvYW0OlB2wWBWvPvL0625mFqLMVYOhNmsVeLK5Pl5syVbpDv1LB/GVpKutXqPjXUuTXsObP6s+Sm7D2oD7Bbc+IcbZ7p19Bgo9Ae9EDcOzT3/3nSjB9YBFqV6Vt9C8vw9wUtq8As1ue8Mm0lI3RbUPzUidTMsHnCwey5tcwLBsItW35NTR2/WV5vRyYYX+vgsz7EVtx4v4bnw5kmbIMfBmedh7ab+XhF3R54narBwTzIqFmL3lN6Fu/MUYSPvrrdbGYBamnz4b89mWSMpDXM85iA6O23ETZ9vX6i6uWbJJlQdr3ELFTpvt6zylfnblvx0nops8Gvay/S34dh+s2rU/ReNqizxci9QzxhW7C7cT9skHFCqyckZ0EyCxbW0D0rZN4iI3x5J+ZhAgquxNPTKTHKnbyOSTW1uQWc3vKy8/ZMRxKDxpHeUyE+1h+H/lp80HsEipr6cORGFhfb9igr38IbRe5xd6ZdIy1tzD9jDpOTuDph7neO5ea0E7rO8dE3gclfBJoI+cbXZagulqD5eNzMFlYhEXxFhtxvg8WobhnEuaW6tC61oGNS3WoHJTLIuxG5w1Y1MBpMFWtA+3zy7DAEyCiTCmGnJhC2BVlKR9QjkoFxgL1hRO+0djCGV7uLnSuNKB2YlZ0EOLLJ/pahv6c5+TBitj8s8OXi51ZgGkulwc9dUJhMhZvtpBBNnMaMgjCgwdUfrrDK8xCcaUGtfWBzH2OMagTUjMBJqYWoMJsonFpkI0O63wYgbLHyOO+IokpHOQkGxAUoXJ8AWbuLYhBdI1/StSqUzSVMtp8UE5PLgn7KsRmsnmDN+ycDqxY2YsrDWhvDu49e2RxDEu6ODqxwevqXtYzznqH2vPotqPaXsr0exk0xN+66BkahX1FqK61oSOWCJWYDc3C4hHkrZ4DUWc7wNZ1ZvdPBIsOmfI3aLyMkwfLUOPl6W5AS/hLfh+9X0wWBp9b5fvbFM+w9q02AdTrxKePNhLycrZ7p1/BfHZgO2EBk5xtNNiYXyzd5b6Y35vPdtHXRrYyC+XzTD49FrwwW6keVQku81POTga+OmbnDny+UKDLxMuasIEA296UsonP6BkEju3Y8iXdzpkvOVaF2mrTa/sS398wHQh70X1+vJzJz7U7yLPfGtkvMJS92XYrByLs3sjgJalv/oaZy22wpKt7rSnKMcniGP4m17R/rJ4+G0q1rwg1sNgzD6XYHmUM1s9Ni809J5l/i7cBOfjhPm4BSlwnYiNQ5uOPyDfdpmySZcEHF6GxU9b7+s6F9u0YSd30oX1a+o7Z03XWR/fkMtGVRdkvZPpcvqdd+XwhWs8AX+hkCDvRSTwzzl7n8QCTNX+JyZ49sJPsMsvWFnB9676wcKAk+0LxjCLMiHIacggqu5v+5Yq4dxQLmH0v9rn2kFhbk2PMHRTDuWB9jhhfaXtTmwrzZdJyE2Y2ZlxqD3x/qKyjvnFa+fD4WNIso7d/CG0XecXemXSMJ1iD9DPqONnG0w/LpB07N/JMwuHI+6CED8aNlmpk3HikU6td428n+f9x59u/VoUF0TkoeONZTb5h9jkqTu+5Msza97nQUX+XbshikzC1A7yGD2Iq5qbNGmxzRb6Z3FILDQ4754vxsu1agCoql3TEF5Gw7DdrfGI2kuPts+tZQv5qcGb+rdcxBnZCMd1k2byV4Xx+oOxtcrtvbwPqS4swJ/ZvmYPFM03x6Ue3nJOb0KH2FTyQZfTasHxotwhc5L1ZB3qyyQIkXyA4IswGxPPSEiBjqneoPQtGsR3WWfJBl3uDWw3zL7wTtDrm3uVla/PJGSg912ODGl8AEkfU2R7sM+SSEeRzwh6ZJjfvYwOr/dJf29f66cHGKvNv1r2Ejtdxu3O2e6dfQXz2MO2Ev6m3NmLmwd3s8Vpy41SrLxPsYoEN0jfh6ODRemHgwOcLI1TAhtlANtvGZ89ydOA4bc8wYfetPqh9S5aZCAzv3yA64OU8087kvzW59luj+AWOsrfEfdUyS8xHovqOyU0yeXAZ2syH8vub9o/V02dDmexLoAYWRhkidk3DzGE2WLiKxERCr3MDGeo4DpF5sizuwUVI7BRyX++5wL7dBrdBSz4Ch+9x4WpXPl/orGeAL0QZzk5i7VbABtes7W8ImdltKJvMsrUFt76xjWxnjiXlEFZ2N1hMxPveOvZZ98BYW5NnzJ05hvPRbUIlFqMO7lNeS8ZUwbLGfPiuuUQZvf2DILBd5BF7Z9IxnvDhhOhn1HGyjasfjhJRmZLZ+ZP3QQkfH/xTet5OwzYmtV7WWGs5HMZ99IaHgfTV32fZuJRv8Jy93HnVcTzweg8rs6yIZyC/D0WQ7AMY8b6pQaFaU5zJvkLR984UsG0x46w3gteex2U7GdD+ZdxtLSuRv8vBZvK813gx9J+mhy2225EZl22n9ukIvr/JqZzedh7KNvqFGLebzcXgMsy73DtDL1kTPl5MGxvWbodpi05yKM8QRH1F6ocSGHnILANZyxRUdg9b0V8K/4j8PhR5+CVDl1nuEyxr3jZGLaMg0OZ2QuydWT/5tydhZ1voP9LI+6CEz0hkyx4SxE6kd7kKVXQ9slrGgb2BJwiCIAhix0J9O0EQxO1N3gclfEaCEj7E7QoL/NR64cWVltjfg2fKu9ca6JphgiAIgiB2OtS3EwRB3O7kfVDCZyQo4UPcxvD1wsfkF4iitbAcZM0wQRAEQRC3AdS3EwRB3NbkfVDChyDudvh6Yf6VBbHj/ahrhgmCIAiC2HaobycIgrgtyfughA9BEARBEARBEARBEMQ2k/dBCR+CIAiCIAiCIAiCIIhtJu+DEj4EQRAEQRAEQRAEQRDbTN4HJXwIgiAIgiAIgiAIgiC2mbwPSvgQBEEQBEEQBEEQBEFsM3kflPAhCIIgCIIgCIIgCILYZvI+KOFDEARBEARBEARBEASxzeR9UMKHIAiCIAiCIAiCIAhim8n7oIQPQRAEQRAEQRAEQRDENpP3QQkfgiAIgiAIYnhu9qDXY/SRcwRh0Od2wujfws8TBDEqfemPe33kHHE7kPdBCR+bXguqSxWoXurh5+8kkLq2z8xAoTADlcvb7yR6l6pQWapCq4efH5bOhQq7bwM6yLnR6UCDybRyoYOcQ7iyDDOFAswstaGPnR+VvOx5m9rFeHWVkRsNWNw1AZOH69DVv+3IttODxtFJmNi1APXr2PkxsAP8Ze9ag5WhCAv3zcDM4SJUVurQvk5Bzki83GDtjrW91Yx+ielAXL/Sgh52flTG7SfHxI7wX2OH9XnHmO+bmIAJDvOTY7EBIicCY5Q8udmG5YOsj1K2spe1Z/77uGK9cLahDw2iB62VHPwsFtPc5SRtcKfbgps+6y/nmH5lO9sLlSv4dVnYOW3z7iPvgxI+NtdrMMcaydxqPoLZ0STqyh1cQTiJ+adHqL8aLDReRs4F0F2dY2WZg1rOzrZ1ijvBMrSQc6PTgjJ3sqdayLkkvbVFGSg/MKaON8iePcHENrWL8eoqI1eXYZrrqFCGtv5tXG1nJDZgeYrLqwDly9j5Ydl5diG4uQHVw4PBQ5wCzJxsjn/g6fN1OyAZNjSXykqOLNi9gZyP0YfmCWn7EwdrY/FjY/eTY2JH+K8x01mZFe1t9nQdmutNaF67De39riIsRskP7SemYXGlwWylAe1NeW5csZ4Tp28eVx+aF12oHUT8bGhfg8U0dzlJG9zptuCg34RSgZV7ahGqa8wfr7XTXzh47GfL2yYRkfdBCR+b7RzAbDVoXfk0wBHfjqvBQvkSci6AuyHhw+HTm8c2tTnInh3BBGeb2sWOGTDx5QrmUoVxtZ1RuTWOMuw8u+BvqWsP8MHDJMwtNaFr6uZGG2pHp4UPmmbtcKwzQny+bttkkwNRwmcCZldSZgKwALOorh1XwoczVj85Ju6GhI+sY+mOruOdxXYlfFQ/cn8tMQDd8kGlzzePpQ/NC0dfPExfY8c0dzmoDe5oW3CgbeFcwAw+j/1Qwmf7yPughI/N7RykhzKuulLCZxuCKQdBOqaET2buJj+xA+1CzyqYX3UFNX1oHucJoQKU1scYsN0FCZ+JPRXvW+De+YXBtWNM+NyO3D0Jnzu7jncW25zwQXzEjkr47GhyTPgQMe6YxMYwtuD5G0r4bB95H5TwscEMP/qtA72LFZi/V01f3zULRbUOun+tBsX9ennBJMwer0PHehsZBUY3N6B2fBYmxbUF2H1oGdo3+TU9aC3Nw24+HY+dK9w7D8uX8Sma3fUKLEzp57F7HChC7apjOuetLjSXFmBar+nk5V7dgD5SV2fj7jZh+chsVLaJwm6YPbIMrdh0fxVI2NiBBbtX5fC0qj+vpyyPvfwiURY2COHT+hfO4/VsneJ6SV+CMAhQO9CI9CDlXT7P5IL8DdfNxmoRZrXumY6nD1eg2bWvM4KpGy2oHNod7W0wuZ/p6Jo1+PTpYLMH7TNxvS2utPFlKgE6xpDPVH8bYcjeuE/c1rn9Viw7MMioaxdD6SqTrSqYjmLX7pqGhSV76RISIIe0HdR2ylC3bYHT24D6KUOPvmsR8MFXHzbOl5P+IsM9x2UXQf4rQRsqe9jf7a/6pyrfqMMCv//x5sBOovJ2oHOB6UTJOZawQWx28YzDJmyEjaig3D5nB+lM17wfiNneqTpsiL5gRELaAIZI+MxBdaXE/JcvadaB6n52//0lKLkGc+vLsHhg4AdxeZr9YxuWtd/U93P5saztxdB77/KyYXusrz5ShXam/Qlcvt1tv3h7zC4TSYD/YIzWthQZ2gDuG9Jf9Az8JPNLvF5Rn8V9bxO6VtwUXb9p9gFxmWaRpz8+YLrlbcXefyhj/+W1XR9jtV/LbpgPmBd9G9KfOcgm+3R7k/KxGfQjA5sY/I0ka9ylSLXbdN/sarPZ/bUh3806FCO75H1iGRpqGdtw2AmfjH1NApcNsDHImcWEvDP3G1xfsViDxb1TC5642mLI/tk/fgj0M4YNjm4LjFH7YkGWPkDp1MZrC+n245Rj5E/se3IC2y2BkvdBCR8bLLDUvx3nxj4H5dUGNFZZJ7ePG/M0lFcqMFuYgYUzdWiu1aDyoNzAsHDCGGgwpPNYhOLxSZh5cBnq63WoHp8TjqvwQJX9uwCTB8tQW2tCfaWoNt2ahWpsf4g+tM/MivtPHixC9XzTKMskLDxtv/EeLH2YPVIx7l2AxaVyoq5ox7tZg3nurHgdl2rQWGf3OLMAM/y3qTK0o2mhajNA5tx4I+fPExt5GpsD8s3EZvnfMTkWV+Ly4hvImQO4ZFnUQA9zYHrd6pGGwwEN0Hoon5qGwr4FqDB9Ns9XoSg2EyzAzGl7GUgXGmKJCDv3oClDdh8mk/IlxOkeLUOZdXpzx6tMzw2oLSl5FebjG8Ah9qbrXeQB1v5FUb6Bjplcz46mYwy5MVsJ5rl898xDievN3KhNl/NkGeZ3aTsY2G/cDiQhunYRrKvMtsrgGxeq8vE2zfefqC+p9hizIyQ48ugtHrR2oC72mUFsh/sO03ZusueINePabnjbZvoTdmbZjQMsQGk/rm2X+xy+ppvJT/mutM388reLUP+FcKUCey3ZZ0aX98giTPOA5bj0UXoPnv6lsrQVw2aXIx9tLkXw+Tq159GJeVHOvQ+U5DlzDyQ2EFhQ/iOS2cmBnbZGSfqEtAEXKuFT21Q+17URr9LFwvm2NRCRdFbnZV+o2y6r57LyAdOn4xswS9tdgMUjrB9kfk/YmpYZ0t6C2ov+e9GHs0Egl4vhj1KThwLlB06wck3F27N8CWS1ZwbWHkNkEuQ/8mhbjKxtQPqGCizyhB+LUxa5vhhpe/dpP1leWoSC2TewtpRsZ4PrF4+wPuDeeSiK5ww2ws4sT4/f6K/zxOYELF4YJCpC+i+v7boYq/32WZmk3999KG4L06xsJf43AQkfXPbZ7U1uXu7uR/C+MyTuwu1Wy2dgU+m+GWuzYf5a+YmjRSju4n0M163RJxaK0Bzav9sJnwx9DYoqY8wGetBg9ivs8ZTsN5rnK0rei9BITVBomxvYA9dBWcVqmfY2TOufhxg/hPqZ1IQPZgtGvBOzhTz6YrQPKKl2ZvYBKiYJsoV0+9FyKZ2ah8lEDI71WWHtlnCT90EJHxsssFS/JRz1zSYUecNFkjKNo/x3dr3RoKXzSA7YO2floKFgvonmXFuGGfb7jHk9C1r4ZmtzT9gZczXoL5Riz5SNFVn6wIKNEu98+b28g9Y+bJxdgGnW2desNxP9iyXhJMwgSSAGC9ibPjZ44AHOweVEJlwGbfE3yZgDbj++N/EbRwZs2ZZvaD3YDn/QYcUHwr0LfMPQvVBcs+qpA7ZYsKU6UiT41/La+7j8MoUAsTdZb6x8Sn7WRnuhOnZjBxMGURtI2kHnnCxv3A7CdO0iTFdhtirLbbdd/ju7x+Gy8TYCCY6ceovbprQdj24M2+mulWD2XqRDZEEDf1bMbhwkAxT2HC4/27dw293PBg7nsrx5y9EuAv0XRv+5orhv2kwCFE95I5tFEi495l+4/ST2s3H6OgZiIxIVWGNl2KyKv7FfFmRnCH+NoRM+zJbl8rlkO+HPEpuwCp0hNtLnm2pPw26s7Z7kfpwNIoyZCbqto/suYe0tpL149K4Toukbc2rfzsph28FNZjs88WHZb6I9BsokxH/k0baGaQNJn+NH928Te1h5jLpyeud5fePPcPeHjCB5srpxmSX8mGnHxrUB/ZfXdh2M03558oPrKlkeZgu8nfJ7xQb7OF7ZB9ubux9x951Z4y633cp+yIo3nL4Zs+dQf+2OAWWdRvmwg0OGnvrgIDGNsrvEC0X2+wKfpbOecu9uA0oHdsOMy+ZSlgYLPHYe2iaH8zNxGwyxBd2/Du6ZT1/s7ANudaDK5Wq3s2BbYHj+xu0DtC/B+qys7ZbwkfdBCR8bzPDVb8kBl3K+9yUN2O084o1DoAYNxeesjh9xymJasstxvlwVCaKBE2EO4T53A9MN2awrVm4nWlb25mCuQRD7nTsCfFaBKqsxswItC6vjLLt3POjEAjY3Ug8zyACGoZaBDHStdOx4wy0Hn2adlM6wwbEOOM3ZI4i9yXqzAA4ZRHbOzlgyCdexG3dApstZwILEXgMWYzJjBOraRZiuPCC26koeJkGCI6feLN3wQeB9LEDRf2fQu9aE5no7MbU4iXr+yfQAPRGgKN2gestMfnYR5r9wvD6Kb0TZs0kG+pjNRG/512w/zOlB/TCTgV12l6/jIDYiUD5shg2UYr8r2kvJgX8uIG3AiaiXkrFqZ7EXDxzd/sSnlT02ghDp0AiEnf0jxyVLFKS9+OxU2V36vdV9C3hyQ9uP2Y8n2qOHpEzC/EcebWuYNhBSR46/T9J1HvRn8vrwTwtjNoYmL9Xs4NigPbD/8tpuMKPaL9+/jP29Kx4SdWPnM/QJPtmH25vbR0S6inx6WNzltdt+F9rrTWi9bJTF408S9hzsr5X+MF+oN7gfuj92yDDIP3JUGc1yeGbAjYqUaYaN3VU90JgusE0O52dMGwy1hR5s8FlRVzLIT+srtS/29wFaZ7F2FmwLDM/fSLng4xGe3In7h7B2S/jJ+6CEjw1m+M7GENKBIc5D4xw02E5ZObWDFTnd0matIsoZOUs92DuNOE9OIlDAyz2A71jfEZ0nn9JX0tOJ7c7LUR+drKisWeUWNKDCZWkEEHhZlAM0ExyqE5UDD/NaHKceBB2o3c9lrHSq7r37WBUpM2OlCLvZ+YHDRTrSCMReENvy6SBxbggdu3Hbs78TSdY5VNcugnQVI4OtqjeUfHowX9LV3uxZb6Y0iE6z6E3ZjlM3Hvq9LmxcYmVfGyynwOsZJykvPU2bT69dhvqlDehab8fSycsuAv2XAyln1yCE19/GkIenvNJm56HuWGcu3xRb7dLpuxmOZ8mgxzOj5GLJfc8gMvprDFEvXVc1gLTaa3zw7LERTr8Hvc220PNgOnq8jt627rWzDO3F9/cp9x6g7NmVqEYTnJ46pckkyH/k07aGaQN+H53E1345MhE/GED7+sMYGWxMD9jMF0bYICS0/wqVgU2+9qteLD1Qd7TF7EkHt+yHsbeAeDkw7kqz2wQeedq6DPfXvhjQdy4LDhlm9mEarBxqBo1e0nWlM/xXvG72oHutxXTVgNoZvQQ1Qxv21CO0TebhZ4JtwcmQfXFqHyBncMfuE2wLDM/feP2vHf8EtlvCT94HJXxsMMN3NoaADozhDAoyJ3zU/9PQ16c1fOQ82rhjm0wrdk3D3KFZkV1OOC1HfWT90xjIx+Vo5O+Dt3ShWWNRDuTToBJLp0pGeFkHDGTo69ARe8mqA9e5IXTsxm3P/vsk6xyqaxdBuuIE2mrvqrnRMIfvg+TYjNz82yx6C5I9pxfbtF1Q2A0zh+fEIAXViwXqY8SG3vH7+jcot8nLLgL9lwvlX5IzIhmxGT4bUOUJQVMenvKisjNA26XTdzMcz/K1b4HvnlkI9dcYogyDMiaXzKpBZfQmD7eRfmwjb8nk1BzMH+DBdryOXvmjsgxoLz479dqwic+34+exOmWWSeZycfJpW8O0gbS/sUmzf/t82vUhNhbZafTCCE9myjqlMahzqAwk47LfcDt14Zb9MPbm7kcSz1F1Qu9poOscLH+PPO17pdlf0l/75Jtd9jgOGQb5Co6jHGozYrPv4HtSLV/MNkCPfdRGUIDd+xZgjr+k9clQk6qXNLLrzT6PXR9sCzaj9sWpekX0GGwLDM/feOts2766z0AfOEFlu4vJ+6CEjw1m+M7GENCBMZydkjPAtxuzXhJUh25syYKFfoOPvHGMkWmGTxfqYkPgaVhcacJGtwd9vQRFN27baTnqozPq9etImSMGgzino4ktMVABmzE1Mw1/cGDNGlEynH68hZTVIHoT4uvQEXtBbMvnYBPnhtCxG7c9u9sAJ1nnUF27CNLVMLaqucnfsPLN91QAHtuvK1unGqybGH1on5Z7McyerovZRgmbwvRi4ZdXX7wB5xtrLqoBUnIDcIy87CLQf7lQck3f50a/hfa3N439xs8GbZdO381wPCs5DdrCd89URmgDJqIMZl3jCZ5kAgjzbXW5YeXUIlTXN5jObd8er6PXdhOyDGwvPjv1nYuh7mvvh6VB3sYm6hQikyD/kU/bGqYN+H1OEnkPxzJdhj1bA213mkAb4/TOLwyer3RmL1cM7b9CZTBe+9W24JiJpuqcxQ+4ZT+MvQXEy4FxV5rdJvDI09ZluL+2+z0T37ksOGSY2YdpUspxqy9m6Aw2pC/gL1gM+pfLYsZ04UAZ6nx2UKJ9ONqwiaceoW0yDz8TbAsxcuiLU/sARI/BtsDw/I3bBzBs2w9st4SfvA9K+Nhghu9sDAEdGMMZFDgDfLsx8+UZ7P8ZlsJIVKeMlI8jA594vRLl7rKAil2DLpdyOS1HfcKcpc/RKDnwJI9yMPE9ffxIPWCbkDLU/QZr5eMDncT1CXwdKTYoStqWz8Emz4Xr2I3bnn0dAlbnUF27CNLVMLaK0F/jZU9ZppdJb37d6NkocvDYxj8LLFDPd93HwOljEmzAMk+GZPIledlFqP9yoZepMVlbmyHGUOvbY8GSp7zSZvG16lFi2d6/xem7Ga5npeyVoKeuY+0/lZzagKxXvAyDJVzYXi5JG+k+Pc+ux32AbCtxuXltNyHLwPbis1PfuRjqvi77ZTIT+4gY09XtOoXJJMR/5NO2hmkD2X2ORNdz4Tw2a0DZkVGPpF8dEGpjAtVv8CSPrG+yfwntv0JlMF77Vbbg2GtK+58sfsAt+2Hszd2PJJ8TFnel2a0YZJrJJ488E7oM9td2v2fiO5cFhwwz+zBNQDluyvaStudi+zTvkxegjnzNS8oUb8MxPPUIbZN5+JlQW+iLhIZKOuXSF6f0AdgL3WBbYHj+xu0DGIn4J6zdEn7yPijhY4MZvrMxhHRgnqDAOWhIOmX9ZjWxYzs/91wJpg8sQu3qIMstv+CwF0oXrez8LdYwxXrdeL0S5VbBEea09NfFEk5L1ad00fiN01ebI2JffLjJzk3FvxrkczRSDnuheIwnNBwJAQdSD6xOJ5Nf1JB12st0MZCXHOgkv7ggzp2dS/+iUwRiL4ht+eqNnQvVsRtVPmwJlbcTQeocqGsXQboKstUu1I/OiK+72G1XLhE0lwwh9cuoN2k7iG747BM+zTkKONwdJS+PWO/t6vQNbB/Tv7wMc1PYpzDV7JdMM+Pys4tQ/+VEfVFkYqoEDWzfBv7FEG779udkfeX12aza+yNhWy5fx9HPSmzMqHSPfFEm+vLjsAHTMP4aQ9TL8kF6k+YH5hP7oGC+zR2gq/qze5h9nrN/5CT0FthefHr32rCJsmfUfjvqy0Rxe7PrFCqT7P4jp7Y1RBvw6g1B+kn2N4j9869L8VkC00aS1tcfhspT0pcfedhThCJPXGIfPAjsv0JlMG775XLh90j6HtbviWQ5u38GP+CTfbi9hcXLQXGX0he/t60vLc/YviFaZsimuUldhvprXwyInetB51IT2lg/lsAhQ099cJLl6J5fhBn+RSlb10y2YkaYa2ajQs7AQRI+WkYOO4rhs/PANpmHn3Hawp5S0ha0f9T3zKkvdrcDnoBHkmw+Gbrw2I/PB2Bj17DxEuEj74MSPjZYY3E2oLAOzBkUII1GgnUOeprgYGM1MfXyzILcgHbKeuOiP4VXmIHiCt+YtgPttRqUDhRg9ghz8Fa9kuXWb9MH0xK715pQOzELk1PT+Jsi5fgmphagssrKeGnQ0fI3crzznTxYhtpaGzrdDWidX4YFkZiIfw7d62h0B8+fEzg4Enq4rwilByZh5lhVyLBzpSHqxMtWsN9k9NuRDBfO1KF1reu53tfZI/aC2Jav3ui5QB27UYEwC+JmmVxqq81Bx+rtRPA6h+jaRZiuwmxVl2/6aBWaTKc9Ub4yzHK7YkFC255ib9Yvq9607bAySdvpiKVjlYN8SVUhFoTqz+tGU/w327Jds7KLto34GZuEj4lsdxbK55kOeiy4ZPKrHpXPyvZ52DztItB/eejxQQX/G7HvUgmq55tyQ8Qjes08EnR4y8t0eH5B/O3kwYrc4FosgStFNuEK8jBfF/koJvviSg1q64Ngqn+5AjM8eN1XhKrdNrBP0qo+Il1fQ/hrDPE82wdpO2D3SAT2iG+7wQJeITe93KYLG+vcL00ym+aDg1ESPoHtxaf3FJsYoOz5wSIU9zD7XUq2Z3vwm6hToExC/EdebSu0DXj1hiD9JOurjs8N7J8/Y6Uo2oT9DG8cECpPhUxW8HK7ZgCw5wb0X6Ey4IzXflUCktuCYafLh6dZn7koErdZ/IBX9sH2FhYvh8VdA30VDpSkvmI2ZZXF45sxXYb5azwecp2TSUv2W6bZUg4ZeuqDg8U09vJIucy9zOJI0ZdeTg7gY1yRH8EYLOlicaiQEbNn0WZcdmSQ4ovDxw+j+RncFmSyiPf5y+dbsLHJy1CRG1PHEv459cWsHVT4BvRRfC/1wtsy1udk788MPPbj9QHY2DWw3RJu8j4o4WODNRZnA9qOhA8H2eyPD3qO12DDHpBwem1YPrRbNDZ5bQFmTjZZgJGsF9q4b25A9UHz77nDXYb2Nfn3mNPqPccGzmJnfob1ZiC5sRu73/4i1K7FOxSvo2HIz2G6AzYXQg9cZzeZXETgrMvBg6MW7oywzdcKu2E+cb2vsx9TwocToGMvMV0b65+9nYi7zll17SJYV0G22oeNVdX5R9cXYPehSj6bNmvQjfvmoJLYCLEHzVPx63ig0thUz0f8jA3qY2601ABxcN+JXaxjX826aTMjV7sI9F8+uk2osMAnJlulQ/Qtkre8EnTjyUPMhhz7Q/h8Xf9aFRb0vazZVOhms6xt1JFlajzQ5eXI9HWQoDbgAE34MNSU9uT+SXhfGKu/gLXbM23W7nhbGS3hE9RefHrPYBOSgT2Lemmdc3gwjrQnrE4hMhFk9h+cfNpWSBvw6g0h8pObTH8n1ddqFDzJFPe9/v6QEyxPgZph41iCosnaf4XKQDJm+73Vgbq9Ae/hKrMDl19Okib7MHsLTPhwMNtH4y4JZrczx3Dbd/lmly6z+2uffJPnor1vXF9Vi+GWoas+OHgZRR3V1+00/AMPuK9JEusLOdwvXugomfrsSJHBF2dtk3n4mSBbQO6ZS1/MQfsAFsOdRxJ7mfuzOC778foA19g1sN0SOHkflPC5rVFrk/k6fr0ZmA/9BZvQQZUm+gJOtsE6h69pdQ0q5XrX4csjEz5FaI64AZguRyYZ3jJkjp3fbkbVsYZ/4nbUexiMqmtNZl0F2epAp2PdTI7LNEuZsl4XyhDtN0GudhHov3yY7XLUeykim81oE/x6n69zlStL25DT5QN9XR76zolQWQYxrvaSwB4kjdYXBMskqJ75tIdx6C0xgMipzworq0r4pCxT0WRpo0MzbvvdkvaRv/+NERh3hdgCvzakzGO1hRwIrQ9GVMehbGbMtqBI08O4/IxJZlvIqy/egrach/1E7PTx0g4n74MSPsTtiVoXnP6lHoIgiNsZtUEqTYXeZrLPjCDceN8YbxFyXxfzK3MEQdxJ7AQ/QxCjkPdBCR/itqJ/pQl1tT9N6GbNBEEQtx9t8UWf0KWrRN5QwicPtm8g1of2c/XBfkTYZs0EQdwRUMKHuN3J+6CED3Fb0Tql1pg69zAgCIK4g7jZhtpSNbk/ALHFUMInD7ZvIMb0p/aaQffbIAjijoESPsTtTt4HJXwIgiAIgiAIgiAIgiC2mbwPSvgQBEEQBEEQBEEQBEFsM3kflPAhCIIgCIIgCIIgCILYZvI+KOFDEARBEARBEARBEASxzeR9UMKHIAiCIAiCIAiCIAhim8n7oIQPQRAEQRAEQRAEQRDENpP3QQkfgiAIgiAIgiAIgiCIbSbvgxI+BEEQBEEQBEEQBEEQ20zeByV8CIIgCIIgCIIgCIIgtpm8D0r4EARBEARBEARBEARBbDN5H5TwIQiCIAiCIAiCIAiC2GbyPijhQ2wpnQsVqCw1oIOcu13YsXXotaC6xMvmpnqph/8t4eVOsFsXvUtVVrcqtHr4+dzpb0Cd2+NqG/rYeQNZtgo0Nvn/e9A4OgkTuxagfj157eiM8f4vN2QbzFBnwTV1/UoLeth5YmeifHDMz2K/ETuQDjR4m7vQQc5tI/0utFaXoXRkDmbum4PFk8tQu9TN5kecjNuXuuhBa4X3pTZVqK+3odvH/ubO4I6JIW71YGON9cvHF5g9zsDCcebb1jagdwu5dqwoW8rcR26XzY+JK8swUyjAzFLGmGIYeixWO+O32x6LbaonF2GO2cLckRKzhY5bHzoOQnHEoLek/ysenmH2tgDFMzVodZHrRuGGGjtljc+2iLwPSvjsNFSDaLyMnNtpgeMQ5WmdmoCJiTK0kHO3Czu2DtdrMDfBy+ZmbjWfBn+3cdvbraetdlfnWN3moLZlQRALvI5weRah6Q3wO1Ddz67bU4G2+P8GLE/xvytA+bJ9bR6M8f6Xyuy+/N4s2LyBnI/Rh+aJgrz+YA266DVEKFsy4FI+OOZnsd+IHUgLyrzNnWoh55JshT31LpbZoI77jQmYnOIDnhnYrf5f2Mf6o5v436WTk6/zxasoXagdlOVHKcxAcacl3HIilxhiu8cHmzVY2CV1VbhX2uO0+r9IpIgXM1uFsqXMfeS444etpbe2CAUu9wfqY4kRuutlmFW+BrfbPrROz8gyFHYLW5i5V8YthQMsZkN8k4w19T1tkBh0sx7Zm/R/0zAprp2GxRz9RGdlVpahUPLGpFudtM37oITPTkMNDMqXkHM7LXAcojyU8BkjNLAYG7e93XpsY+sTPr+A/nNF4eeKz/XR84KXqzDLrtn7eHvw260+9HqevxmVcd0/SvhMwOxKSqDSb0JRXUsJn/zYkjaMtTPyy7cJYQmfsdvT9TrMswFX4UAZmrE32n3orpVgmpW1MMpgLw9f54tXUdyD9P71JpQP8AHjLFQzJ5BuH3Kxl+0cH/TbUOYJk6lFqF2NJ5V6V6rCViemytDesllaoQkfxrjjhy2m3+tBP++ZVb0NqB6eZHZWgJljZSjeh9ttf70kkj3TzF+aM3oi33SimZgt0z7N23faiz6NeuFXmIeamUjk5XsgTz+hn1MQ9Vm84E6YbvU4IO+DEj47DUr47Hh2bB1oYDE2bnu79djGdiR8eFKjxAPEIw3n9N/O2RlWrjsk+DcSPoMZSzi98wuDaynhkxtb0oaxdkZ++TZhZyV8pP+bcfq/9tJedn6L/bZNjgkfgUryz5y982b55GIv2zg+SHtJo/ut7LYwKkMkfIhURDy4aw4qF3niQ8kYsVtpz1jypg/N49jfqN8LZW/8E6HsGfUFzE/M5GXrVyqwl91r8UITKntY+Q7XnTHpVo8D8j4o4YPSg43VIsyq6Wl8utr8Es9iIgFB5GQ70LnA/kZNP4s5vS4zpMN6KhqfCjkLi2fsdafq3jbiWbrRWSQcnVXuiUmYPlyG+rWQjDa7x/kyLEzxDK98zuTUApTPbxjZ2qzlSRLeYLLWyROseYNwt956V2tQPLBbTlmcKMDuQxVo3fDVAStrxXo7N7i2dWYxcS2/f/LajAR2+FE9brZh+ZCqp6lDvn731MJgyq5L9oYse5eXDduZhNkjVWg79obprleMa5l8DyzCsuhkkOuRNlRc3XA6ZpPWKS7jZMekA5hCwmbUkiNjYD7QOdPb0nw0rX5ilywHuu6Xya92fNa4dhoWlprQtd/IDOlDstU/va0OEj59ab9a35Hfs++p/i6hv2LizZ8PqRfXEqe27Hz3V2PTZ5Ntb9Du+1eWYV61p1gbsGQ3uZ+Vk9kwlujy3Z9PL477g7LaWygDIlCfg+oKfytWgNK6yyert037S1ByBLPd9WVYjMrh6k8MvW52oMHsUNZf1Y2Vh//9wnlcX37daDLIPrgNdKF/jfnd/QMfgvpQn6+zB0Xq/zaJgUlIG7vVheaS4Ru1H8DKZf+m/r93yZi5ZiD1thcqV5LnErAyLx8x5Mva7OyR5UQ/4mzjeelDEyLDIfoXp3/MameCgPgOYwR7wtqpC+mLFqERuLdaVv8wmq9T19qkyS51kN6Ckus+GW0rqhcW1xi2lDlWyWqnivHEjT55jzI+8LRliyC/FCNrDO+TEWY3xm+mrhmT+/FY0mfzQX3YqTpsJJYsefyH3QdEjDAOQO456PN70D4T758WV9qZfE/vSsuom7atpE7cunKdS2v7Fk6ZpZwLRCbO5fhAJtnjMaEgq8/PmbwPSvgk6DNjnWbK5I66CNXzTWissoHNvgJMnyrDIle02aC14R1ZhGkeOBzna/wGa2z7zFDEGuxdc1BcqUNzrQbLx+dEp1V4oGYMZtSGgSx444Y0e0TeR24gqDYnOzEvMpF7HyjJc7HNyjpQ19PwHqxAba0JdTa44OXm6x3Ll1wDDBNdd+YwD8q68/KWD8r7zj+tjSVLeXB8TiJJSJ0CnW0GvYlpiffOR3qrPDgDhSleb6wOXWgclXYzKGsR5rjDLcxYZeUJBV6HSZg7VYPGOpPz+Yq6lgV4WZw9RqATlLpYgEVWFt5Blkwd3mTyFOudWRmP8w0VeTsoqzLOxze90889zoNx1rEssTppebHf7UG7sDO19lfbmW5jws5YMDa4lumCdcRiLbHRhvS9Jw/XU9fTysROIeGc5aCW3deecdFriHZuDsikrBahzNoHWuaobSius3tw+THdL3B5rNehqtu9vfdCmi2OVP/0tqqDhNKpeZjctwCV1QazxyoURbufgOnT9kZ2fRZMzDr0NwkLT2d8O6verKBJB3XOtuWk/1Dt/vAiLBZ40C7rF+1jsFmTU821XWo9MBsun9SD4Az3P1qE4i6uG94WBrqcKLBAIcs+GiJg4IGYSmS53iJFMmmjwVFndV7IvaD1xMqyrGzB1pPW6+KRaenHuN6j9eeqHFjwlWH2lSRF9sO0gdMVcS/pcxpQW2LBsPI5sWndPl+ngrOovat9LxZ5Im2C20G8fXHC2lgHamJKOa+z6esL7N7MR9rlSpS1x/o1Xie7D+HY+1Z50LYdyZeVgwX5It6wllZoWygzeRbMOrJ4IxmLMEL1wQiS4TD9i8M/BtmZL75j93QmG0yy2FPmuM9N78Iiuz+r9xOOFwoIIf5hNF/ni1f1NRj+QV//Ykn4P3tZRYhteeMabUtZY5UQO2WML24cdXwQEp86UH3T3uNp/YJJ2LgkaZMaT8Ln/hKUmT/OEouExw9qDxnTt5wc+Ni4b1H3ypzwGXEcgNxT+/kif8HDbJ/7gIE8mN0Ez5xzJ3zkjC4kAcjaTAmNcZR8Tragf2MDWtwn8jZyxbUBvTtW6pzj9Rwm+WjDysR1eVwtP1MzDBPL7jP4/HGQ90EJHwtzXWLcCHWQxxSOJHywAEgYLO8sEo6BNRb2HO48E4ZlB6smqNOQ8OCAvzm2B8u/uMUCSN5gUzajEnQbUDqwG2awuvN72EGopzwu3A49SVidAp2tT299di/uaBC9SUeTrIMs614orlmDVx0wmIEEC9T5sxPOl2+Ix2dTrQ/ZKAP1IXWB2Tprr2slmL0XCQRU2WP7qnhk2X5cBhmxTfJY4MCDIrSNJfSq2tDB5cQbFRnc+mZLKNSeKLEyq85k7xQbDFvT5rEEkVNWunOLtQ3Vke8pJjptHRDG2r3PFvOoP8djGzJIwAYiSh/2W2alv+RARPnILL5GoDp0JPiXb12SM0ycARua1NZrs5HBqdJD5oQPcn/Z5ieSyT4M4dfls+QGgdhSNbVZs5AfEuD2+dr6adid0BMbxJ7k8orrya1XSftxfEmI7AOz2JVP9kO2AeZDS+uWD71RZ8E4O2f6UJ+vc/Sh7r4nrI1JuSL9kvYFdrmQssrBfDIJ7Qw4E/Rh4+wCTLOBZcK2kUGztoWJPcy2rFkMvfPSjkfSR6gMc+pfQu2M/8Zl4+x7+HOwGAIh1Z5C4j4UPViekDMWzrdg44anTQb6h1x8naOtudGD9Cps9HrQi+jCxprcJDaZYA2zLVkvPK7x2RIWqwTZ6bjjRo5P3h6fGPwclMGLOp7QKq82oL3psUeGfG72cYm7TXkSPqw8aHsWsUg8cRJi85FvwfzOZlXIOr5PjboX5j8w3Yw6DkDu6e7zVRvKupwqwp3wGch4FkqrTdhgY/yN9RobQzpkpl6kTuyalAlkg8I+5r8Tvn3gw6cPL0P9Sgd6m23xUkP8hrXvQJKxjooZHe3BbZ/jIe+DEj4x9BpDx4BFZbixhE98MCnRyaPFNcwpqrd8dhJlKIeujPQ+1iHGfleocvs2o0pDGnopbuieDsZF9gYTWqdAZ+vTmxrw4wMe9hwe7MbqoJwikonmyPsZ2WhV9hC5ZULVyXSkMayBtdSFNZhPRcn5ZLINJJdGMRJrbfUXiNhzraCI07/ehuZ6Czq6TKw98EAFz+QzG+EbyqXOROAdN7vuPsOJi3Kx+16SySCzbYiZP2ggkhwcc+TA2ZCjkge+XEb5GLPde2wxn/oz1DMwm9MDWMznyIFp/PlCPq4ZCErfWX2NTH7YcmXBCQ+ckXol/YeyR0wGqiz44Er536wJHyQpFW2ujNm9jfDr6llswLzA/i6xNl39LmeWYQGuG6lDdn8jaJK/uWyHgSYXzKSTcS2KR/ZDtgFXfaWdGIlZjz27+lBn3xPUxtT/HQGhDrixvib2m5pFZW9s6U4GBqCfd26gV7RcEbqvNeoUqo+8/JSnf0H9ozqXzc7U/122LerAzmdpzwyXPQ0V9znpiSX2eqkJRyzRWqrDRkC/jfmHXHydo625GQzSMWZPNpL1CrQtWS9HXKPsJVus4iNpp2OPGzk+eet2nyj/EM/xIJYMRks8GXyJ0/EqJJeGab+SfVzi9NFof6htCdtHhpHp/p4+TPWPM09sxH9XyJdSpp2pe2G2helm1HEAck/ZzvFYzrlcyYuWMaYTxvUWVA7x8ybzLK5G6tRrwfKROZg7sgyNa13ocZ3d7EJ7tShnTO1h48vEmID5v6eL6uXcgOljYf4PB491fHGT2z7HQ94HJXxiuAcaAqzTczpZ3cDmoe5YIyuz/lYDHMahq3IVTiMBkcCzLtrFzR50r7XY4LsBtTNq6qddVk/dXWRuMMF1CnS2nrLLAbxLbypoNOugyrr7GOv0+LRMm5Ui7GbnBx2PyrbrqZw8c506uMqAqlM0pdfGWnKXVRd9/vbtEquHMS091vH67CBxTgVoGb8uojupypolU0EDKrwzyhA8y+mnA/sVTl0sqVDJID2lU/uA6P8Sn6yiYFrdWwZQu6G4gpW5CdVju9n5ZIIIk19e9fc9wy5/jIQ/Uvo7WJFTkG3WKtIGscEZBpZ0EAE+Hjg7Azak3WPJKhMsAAq5v/+chZCjfhaS8GDEB/tYgGvQ74m3XVzmgyny8X7Dq1eBCsjN5IXyZa79ZeK46z9sG3DajR28+3yOow91teGgNqbeUjr7JWzQiJYVCzZV23IMzNzwL890oM3Le74KJb08xdCLL4jluJLWWfUxip8apX8JszPl2119DxbfefDbU2DclwG+DKK5ypcVqMG2bylOBv+Qi6/zxasoyq/dVxLLo0xdDZZKzcLylUG9Qm3LpReBz2/4zjHS7HT8cSPDJ29X+Yd5ThZuMp/Dl35He+dYS6jUc0PGJW7deRI+rj4y0/3T+rD4jK8YF0uWLjztB9XNiOMA5J6+Pj89HsBwJ3z47BueqOGzc6rrG9Dt9aBzpcF8Lu9/CjBzOvsMHC5rbkPxl096hiOTz1Id2pvMp3U3oKWXve1agDoyKygzui+39RV78Wb8zvD6ljGQ90EJnxi+Ds9x3tNJpBkH2gCHceieMkjS6jWgd7ESe5vEHd7ufQswxwcGdllTn5skc4MJrlOgs/XcP62MifPqXgOZ4cSepTaCM6c2ujaay0ygPvz17MU3J+YUdsPM4TkxQM8akCfPZbdFjixjGtntSQY18SSPaId6Pw01YLODH5+s7HYs/2+WD8NoSx755V1/7BneQCDhj5T+0sio3yigiJIO+FsXTVIPbnvy1stxPuT+QbYs5Dh4VnIqsRqMRoN9PJiNb6ArmZyag/kDfLAR7zfS6s+R1wxmlMggN+vb3jTZD8qIY5TN50Ow877rHX2oqw0HtbHQcrp+47ByxmxA+R7XRtoJbib7EP62fe7QrEjGmHoJbguB9Qz3U/n0L2F2ltZeA9ozw29Pyd81abrIBF9+L5ZoLMaWrIT4h2Q5h/B1vngVJWWQrpdFGknoUNvyyt9n1+i57HaapvfEefW8eD2SxMrjk7erbsM8JxQWz8pPZU8P+g6frAVJm3LLELMb9Zs5GzBGlvu7bT61nSZ04Wk/LlmMMg5A7ukr83B+R8k4oROVrEKWL/IYTu6TNg3LV+1zLpjseBszkvHO5YCcTbl/XeFollmjOPqrcjMPlq2X40WYewMrC7LHXlobz5u8D0r4xBjiDZDHqSXemlmgDXAYh643mXW+Vfc4IoP+ZblesnCgLNdL9gZvWaShW2VNdehJMjeY4DoFOtuh9Ya8qVFlnX68ZaxJR8Cy97f6YiYV30BPJtoKzs9ephKoD7cu+tA+Ldezz55WmfWo7ErOGQPy5DnVxjJN7x/oon7dkmWMLPJSsxl4kke14yipI8ooB7myTSZ177Nbux3LmSUs8LmElXVA9PYjgy2OXH/PM7yBQMIfaf0xH4mWRYEs13MhO161PITpRix1cfiqpB7c7V7qwf2Gbvtm+HDiCZ5kAggJcK/X5Sa9U4vqjdpA71KH8X7Dq1dNbHmZ8m3m0h4vabIPbwPOzzGrZEi0h4jHnl19qKsNB7WxtH4p8wwfjrIBlXiW9uhYnpCgC3U1wFpc4fsnMFnqL1Kp55l6kbbg/sR3YlZKoD7C/FR+/UuYnaX0PVh85yHNnoLivmFgdi6WjkX9WJh/yMXX+eJVlJSED8P2y6F9oEsvAp/fSJwLs1O/3nOKG33ydtVtlPg0BPX8yDcGx/A+3XkSPk5bGm2Gj/QtnpcfCV142o/P7jjDjAOQe/p8y3B+x5Hw0f2csWw4htqfyHk+ga1L3V6sbUQMWif95/2o5/Hk7X0zScTX+5KzzL2+ZQzkfVDCJ4Y2Mua0sf1F1NrsWIP2NORowIF2hupZdhZxGIeuAxmX48OCUIT2ae5o8E/xSkO3nEWaE0PI3mBC66ScrbUUB7+WMbTeVLlidVC/BU/Ft7gpO8isyZAEgfpw66Its+1ofZKBjve5iXNqdg2SPRfwqegs+NCDl9RONwA9oGo8xwNl05blUoqZsy1ZNsTmfHab6EjVcofMb+pTbTGH+nue4Q0EEv5I6S/LMrKsqMCQDy7TZpgk9eAJslS7x9fgqwSgVe+g+3vPWQg5xp81WMKF7euRDGa7T8+z63HZYAO6bAGesb+V0kO2DWU5nvoP2QZcPlTWxai7uh5LSEgbSvahrjYc1sb8/ZJ+axhrZ562JweJPMkj72vv6eOkywb37J7o0jstS0Mv2j5wfShbM+0vUB+hMsytfwmys5S+R90rU3tm+O0pMO5L0IZlPvDgiXX0PONyPOET6h9y8XW+eBUl6dds7CVvoX2gSy8Cny0lzoXZqV/vOcWNo4wPQp6ToAuNY3ww7On3tU+KEjx+X4nF5VJ3WNJbLXeN3UsP2h17cqn2nLx/RptH/t4k+cJI3SvrGMRF1nEAom9fn+8750bJ2G5PWjYpCR8z2de/XBMzaNAvW+lkeyQ75atTEz7uxLoXpg8+Q8+ZjFTlsftjr28ZA3kflPCxYYYsdgBnio41th5rzGK9JcN0Dr4ORL+txr6Sog3ODtiUQy9dNH7T6GchjUwOHrCd5nmA4U7kmMjgE7nuJjN+3vHZzsJTHhchDSasTvyNJ7t3Ynd4NpgSnz60dOTT2w3mcHl9Wedi662rvmhi18FdVt4xzMH04XK0qR2/xwz/uorteBNOLxBfnRDcunAHCHqtbazj9T0XOacTp0m70Xo1nLivDTG7LE3NwuK5jJ+tFZ1UAaanmJ1bsxiE7U9Ni7aP1cNnt8mOVCUT0Omu7NzBaVg41cwmv7zqr5+BtFVvIIAEmHo2CjbVtv9cCaYPLELtasrbqRhM73wZ154iFFM2NA0K2ETQwO7LfEL1Wrw8nXP86y78XvF6h93fd84CSfhEa8UfmBd9QTzRkhwYuQc+OnkV11PWAE/qcy8Uj/FkRciGwb76D9cGUB+qvyQzZdqFGozZ97/JfBciC47ULRJABrYx+TWfvayPtsp5iz1bxQixtuxr3yoOWDhWFL4J27cKRb89RxI+nbO8L2LlMPQibQGRFyP6EooZ/IbqI0iGOfYvgXbG2xDe97D4QcUJmdozYyh7csV9CZjvOsrLg8cUvG7yq7GD9hrqH3Lxdb54FSUl4aNty+wDAttnsl4GPltKnAu00zHHjYLcxweO5yBsnJGzndAlNqwPl8t44j4sdFwik5bJZ+gvCcbtRicjsDrr+4/Sv3t8C7M7MSaK2UbYGGTkcQBiy74+P2s8EMeR8FFtcmL/MmzomaUGG09wvVv9mfZ9/LPsxrWczgovG2Y7heSX5Tis/xPyj308oQedS01op9gxRybr3DNeo5jUSig5ff6YyPughE+CPgvoZqVzEbvPV9ggZAZ2F7gTqiU3P/YGI7xRL4j1mZMHK1C/tAHdzTY0Vkvi85OoI1GNYmJqASrseY1LhrHrRlaYheJKDWrrhpPjbwj5xnyFGXaOfy6RbzBXh+XDPidtoZJdgyVdfFO2ZVjgA2ERyFrOwlceB7LBsA46tmbSwNxYOLBOOpib2DUHxTOsPGqzwukji0kdpehNfu5zoLcOn255ZgGmC4uw+CCvg+0A2QCEy4iVdeFMHVrXumIDs9oJaUsFM1ufmHot61XmnzPkneNls3NUHVKWTY5T6mTjC4z0J0qjqcz6c4gqKZI5IEfP6eUI7P4n5GZ1g83erIEHgwcBUhdlqK21oSM2buN2yctvrBlPRQVw7F6JzD4LpITtODoBn6ywjjRaHrlvQX5Ot8va0pr6ZCWrd2yPoDQfkkf9PW3VGwioADM+eNb6G2w2KKYjc/vgMowNzLMRzZ5k+GaYhAVsjE0WhAs5FWD3gUUonWS+gE/XZb639kSy3mH3T3m2iZCjLWMdVPBn2ol2ZGCkBxSm3xCfQZ2USUx2n2ESPpFt8HIEvQX213+YNjBzvAhzkb/n11ehKDacTQ4apI+S9y9xmxYfF2D3PSJn2dgJHxlAMv9yuAK11Qa0jEAuqI3pQWmsnLJes6yvCZlNag7Gw2bNqWQmK5te0tW91hT9zSTz0fZMFWkLrLzH5+Qmm7yOPBZZUV9IsWORIfQRIsPc+hdGkJ1FiRK5EWjrWieKKQpMdzwBm6k9M7z2FBr3Yah9KiI/qzbZbaxW1CbMBZhZag8GT4H+IRdf54tXUZRf2zMPJTv2O870Lz4OMgkL5+O6DrEtX1/ttSXkXJCdMsYaN3JGGB8EPQdD+z1udw8uDzbd5hs3H5T7RvFP6pvyCB6XaBvmNn98mbUrtQk9s+nF+9nvWMLnwRKUWJ9ut2fRNq04LzR+6F+uDDYmtu0O+fR42BgkZByAgNirr8/PHA/EcCR8GLpNcr9bYf5P2ALzu2W1iXfCFli8I5OCqn0I+2G6Uh8ZwGxH2hu3hWpkb/UzizArNnefgYohI5nwZr+n9qNqHOD40qZGx6Tm7FGfzx8HeR+U8HHQu1qHypE5uZ7v4CIsr3NBIc4hJRjhoBshH1qGtsNYes+VpUHza60sb/9aFRb0vey9FtBNHJnzP+8eQNnEns3hjvpCRznKpLPwlgdB3seD1YGG1akPG/oTf+Ja3gm0WCeN6CiD3jrni3FZ7FoQMwWcAQVW1sJumOdlsK4VmyuKoG1wbeHeeajYm7WpabLuLx0YZKiTiTcwYgOK5ql4XXin19hUbSBrQO48h22GyGxtFZ+tkmxDrIPYX4SaNXMjDfFJcewtKAuWxFsVhw37ZOXqSLENNFEdD+VDwuvvaqveQABN+HAQ/bH2Nnu8BhtZBjMJmF2Je/lnmCT14A/YBLe64kskC2p99sIp+UlPrN5h98/wbA2a8GGoqdHJpTxIwocR06GA+bgzbeb3eF2GTPgw5CdmA5ZgCdLrH94GOqwPKhk+nMGC58T1Am6D6ms5HO0/PDZr+jR7j4SgNtZrw3L0dRoOGwSdbA7V1+hlYOmzPixYf1N90CwDD6RZXMFkzp9n6iWyBTZobZ5UX/GK/qYCLXtW71D6CJFhTv2LIrOdcW51oG5vlHq4yvxWQHsWhNqTP+5D6TahErMzCa9beS0ZA4X4h7x8nS9eTaIHkAi7pmHmcBnqV0ezLW9c47Ml9FyAnSrGGTdych0feJ6DgmwyLGAx+eJKG79PUAyv2rIRG0v/1EH6Q2VL3CY3G/F42hFLDhM/YL6F2x3+haiAMQjDrivH6bdsEHv19fkh8cAAd8KH07tcjX+in8N1y2SP21RPyCc+xvTYIPPVjVN2nMn9aCUxIy1K/Ke9HFcxV/rSdWYb/Lkxm/P7/LzJ+6CETxDpzsFHP3BzNH69q/MU55CpdAK1D0q2zWwx+Gde5T2cz7DwlicPAuvkk10YhizQ8wi3sv9NZBOueinnNG7H4mRkW0ohQFacSF5DJRW2iZv5yTCP+ufbVsN9xU5huABoZxDZwagbbSpkwifrhsFDkNYG7OA18gtZ2gy/NqBtiXu7rw9qY7peI7TH2KblyPlUMviXhK2nlXskfQTIMO/+JcTX5vXsrPY0Stsy+sks5c3bP2SBPzOfmMtPUPvMi2BbGehrHHEjxydvcc7VHwc+ByfMHgWhMuRtOdB+tW2Mww5D7c6nH5vo3pnta4cxhC8N1VV0/U6IM1N8fl7kfVDCJ0EP2itVfMqtGnzbWVqCGBdymuKQG5MRxN3OZh2WL2BvctRGyambp94FqP0IMm8YPA58b97vaNSSrpTp5aMSnNy8a/VBEARBENtP3gclfGzUPjZiXeUlvo8Nzyp2YWOtAnN8GlqBDb7t6c8EMSbEl9OC9tUgCEKi9znhezA0xD4nwp9vtpz7Rd1N9K80oR7tdRKyWfMYuNsSDNdbTPaDvSzGPYOTEj4EQRAEcfuQ90EJHwRsXSUHXetOEGOjD+1zFaimboRIEASOtc+LxrNf1N2C3M+KycKzJ8uWcZclGGQCZuvskBI+BEEQBHH7kPdBCR8PfM0g//qM2J1/K9cLEwRBEPkh1lx3oM2/mMPXgWPXEARBEARBEMQ2k/dBCR+CIAiCIAiCIAiCIIhtJu+DEj4EQRAEQRAEQRAEQRDbTN4HJXwIgiAIgiAIgiAIgiC2mbwPSvgQBEEQBEEQBEEQBEFsM3kflPAhCIIgCIIgCIIgCILYZvI+KOFDEARBEARBEARBEASxzeR9UMKHIAiCIAiCIAiCIAhim8n7oIQPQRAEQRAEQRAEQRDENpP3QQkfgiAIgiAIgiAIgiCIbSbvgxI+xPZzswe9HqOPnCOIPOhLG+tj5wiCIAiCIAhiXIg4tI+fIwiLvA9K+ATRgcZSBSoXOsi5O5Fx15fd/9gMFCYmYIJzuA499LoM9FpQZWWtXurh5+8Q2meYvAozULm8lZ1GuB1sTzlddKH+QAH2nmrtgIRPdlnuLBmGgNTxyjLMFAows9S+LZNuvUtVqCxVodXTv/WgcXQSJnYtQP16/NqdRLLcBDGgc4G106UGdJBztzd5xy496KzXYPn4AszcNwMLx5ehdimf4JnYSeRlN3nbXzj96y2onSnB4sEZmDm4CKUzNWheyyk+Do63A+Sh7l2xWF5tQIuV/7Z+aXejAYssDio+N3pMd/vGh0RW8j4o4YPgDoJaUOaJCTZwjP9+pzLe+nZWZmFiogCzp+vQXG9m6Ix60FphullpJRND12swx8o6t3onB2F8kFkQybH5p7eynqF2sF3lxOmvl6AwMQvVl/HzW0tWWe4sGYaRrGNvbVEmdh+oQzd27fYQOtDtrs4xXcxBLUrubMDyFKsP81/ly/FrtxxP8J0sd77cuQmDLeblhhjUNMbhozz3bp3iNlyGlvX77U+OsctNdq990hcX7mWD5/tmYHoXl9sETLP75z8A9cQ5oYzTru5IwuxmZ44VetA8abxItZg8XIWNm9jfBRAcbwfIQ90bK7tg1xxULt6+L3bbS3thYqoCbeRcdm7n+JDISt4HJXwQ3EHQdjrx7WC89ZVyLgUEm12oHWR/c7CWHDTeFQkfTn8bpoQOYwfbUU6MHtQPs7KPMnssV0JkuVNkGApexz5fUnfLvG77CB3ooomTWztEPx7fN+6Ez52bMNhiLpWZHCegfAk5Nyqee1PCJw02sDrCBlaFGSitme2rxwZu8oXV4oW8B5+eOCeUcdrVHUmY3ey8sUKflWla2OXMsRq0bxjn+l1onp4ViaDCA7XRkvRbkPBJ3Lvfg86lKhRF8rUA86vbN3tqJF6uwiwrf2l91Njhdo0PiazkfVDCB2HnOfHtYrz1DQ82KeGzPdzGdi86151kF3eDD9n5dQz1PeNOnIwEJXxufyjhkzM5+aBuHebZffYutZHzLSjxZxxv5jzLhxI+20eY3ey0sYKczeyfedY5yxOVEzC7MkLCZDsSPppbHag9wJM+O2XWdigdqN7HZHGksUNeQhI7lbwPSviYqM7RZtBZGk7rRgsqh3ZH0yYn9xehds2Rbe02oXJ4GibVtYV7Z6G4uhHW2HsbUDs+C7sLqly7pmFhqQld+4155Cw70Lu8DAtTk6oekzB7pAptdC+HHmysFmH2XjlFcKKwG+aX+HTi8E6ru14xnlmA3QcWYdmafikHIfx8HF9Qgv+NMZAxOon+tRoU9xtlOFSBlvmmwyBZXqbHqxnf2JmyvliBeS2/XUy/aq1yvCxMB8fr0MFmOTAbWT5i6JfpYPbIcqLc2AAuCjpYR1g3bKRw7zyU10I69RA7GPzWv7Ic1V130olysrbF28rCeVy2rVP87xegbtY3q817kOWYwQMDdv/6qYVoej7Xz/ThMtTtdrxNbWqsus5ady8BdUSCuKh+mx1osLpI/2gFz0G+0yoPVqdUH4/j1UV0nVHvzToUD+j+gfugMjQ2B39rMrwPUgNDVYcIY6A4KHdfykbrO9KVfU9GFl80pBwlGfRkkKVPEddF9tSD9hnDtpk/XlxpJ+o6VFsK9EnpZVc2YxPzDz1onVm05OXu0wak33tgw+wZS/NGvWQ7sweN0fU327Cs459YYiK7bpPtR+NOePSusv401q6kHLxtMSRWC6INlT3snpmXqabrUdqwLOeAuN/J5rvTdG/IJ/obBeKrIwL8cf9aHcrItZmTYzu5f96RYwU1m7lQgqb3AyjKbu+rJmf5YP7tVD25BMxpIyPKleOzP426poDdL2sdjDLF4nRe5jNqr8GY7ri/Wc5mT6ptN7vYtb+A9ml+XTFFT36wmIQzcrvLOBYhxk/eByV8TNR658X93NBZkMj+HV//rBzE0TKUWacyd7wK9fUG1JYWYIY3jsJ8YgNPPhie5ed2zUFxpQ7NtRpUHpTraycPs8G/ca2T6w1Y5PtFFGZYcFmDxnodqsfnRIMu7OPBl3mtcpbHmfPhgS6/3njmxH7byQ+mgO4+VITq+SY0VlmQuq8A06zu4g1WFifN73Na1eugvs8yFA9yJxqffik3EvXJOYn8mxLM845qzzyUxN8Ym5Hqep8sw/yupJwmpsrQjjnXPhsUyOmtg/LKenNnvfB0hsFzTNZzUF5tGPeYhvJKhemeleVMXO+FE9Ybwc0azAv70eVuQp0NWIRNWeV2DzwXoXh8MtJhfaUIcyJQyrpBXKgdqLZweBEWCzywKgkd6n1EkuVUQQb21rLfhBKvq/nGI8TmnfSheZzdA1s2yPdlEHuw6HbM61uWMrPb8Ta1qbHpOqTuTgLrqGWIJHwWj0yLQXZRtOnBfghhvpMN2g9LXzPzYAVqa6ZcWFu8pOSS6uNx3LpABplHi1DcxYJGIVvDBxVYgBez21F9kNrr48Q87GX32PuAbIPm3h+63KVT8zC5bwEqzEc1z1eVX56A6dPWJtpZfdGQcsysJ0H2PoWj61rkycP9i6KuA3lOwOzZ+PXBbSnIJ2Fl12Uxy642M2UBtijjESnHweamakkRb6unpD6a5yuqrS5CwxuEp917IIMya8toOa39IeT1C6zNFoSMRT8c2Rum25K6l61brP1o8IRPnw2yp7msua8w/QGzy/JR+17hsVowavZotpkS2fSYGudk9t1pulfywfohxFdzgvzxlYrUFfM5y8ymmkz2VfWBjunHsdlSFju9f96JY4Ue80/sWjQJkoXNOiwI+Rr+7eTA96PjjJiN5BP3uOwvjk5usT7J/D2kDrH+ml3P4/Sob2S+7yz7N4ttZXs1+vGEPXWhcVTWO9GnsXLYfo/Tu7Aorh9l/z8sJhm53QWMRYjxk/dBCR8EdyCiHAQSvPQvlmTQHWtUbJDLO62Dy4nscmd1njXCLOs4VaCwp5gI7nQAFAs4lLPkHUrNeqPcflw6JdPJ8HvwciengHYGb4+zOGnlaJL34RvI7WX3SU6/dMvZRfqSLqzenXPcMU7E19qr8s49YWe+1XTR1LckjOiZ1kDuZpN1FLxudp37rGPgv5uZ/T5snF2AaRbE2uXWNmWW2z3wRGQvvgjAzmV4AxluB+62wMHK2X6c24HVQTHkNGSzLQTavBM1dRaxl+5aCWbvRTpj1uFxncba8Ta1qXHpOqjuDoLriARxsn4sOEH3EwjznTyA4r8l1vWzgUOJD56sIC3U97h1Yd7D3SZk+awNFvPwQRxEthq3jLWe2GAzemMZ5os4oXIM0lNgn+Kuq7Ila3AQ1pYCfZKz7Erutm7ZPXhZEjOkVJu0k1X894WpBSivZwjgXPdmOGWg9bEnvrmo83qGU7e3mB9G6uy2HaSf1y8FEoM27Q/se4XGatng+5D1eh1ony+LwXnhAJOPVR6UID2645xg3+3UvZIP02P8dwbqT8L8ceskuxZJcLdOzcLskRpspPi126F/5rhteBvGCi9XYYbd158ocaH8GyLDX2xWhXxjLyoRG8lNrp7+zETGk/NQj2bRBNYh0pEdpzN/JRJ5yaT/xhMz7Pf4jHHp9/ZCcS3eN0ZJy0SCiHFZzngfZcNlNCYZqd2F9//EeMn7oIQPQqoTxxIO3GHzAMmcpcAcIHcElSvmdZqM6ziV88OXwqgZDGZQpq5Hs/yJDkH9vWtgIcrPzqc6aXafE8zR8sEDFvzcqMMCu48djLjl7CI94YNP8ZRvPszniyVEVjAboeSU6tzUMxNBli4nMmUWc9JOdMd3bhAkugee2D31DJc0GQ9jB6otOOwXrSf6RlTZjvnsUJt3osoYtMeC+puTRl23pU2NS9c+kLqjDFFHbctGECfr5/CPQb5TBWj3sWA5cS0LyK7xN13t2LKbUN/j1oV5D0//wAasRUsmufggDiJbjSw3G/Agg30eqDrlb4P4Ik6YHEP0pPxCQJ/iq2vnLA/UR2hLQT7JX/b+9TarZws65tIAZu/owPxKRQTaaYMfL657M9wy0AMqMyGor4//JvHrVtfDtGe37ST7+f5zRXata9CrYq+sbRGL1TKh7qmYYQOoTMkeTpAePXGOE4fvdupeXY/1Q5g/CfLHfODN/l/I6hdCQOqpyrvV/TPHbcOB9hckXweeds43PO6JZGWcKC5SsdnMExvJv2WIL0uZ7T5hIznKFbM/hESfHFoHrSNErlKv5otZRULGqq06Pgoi/RaiV11Hqz8NIRmTjLHd5VBeIpy8D0r4IKQ6cdRpJTtpHWRW1ngga9OACr8+ZeAqHcZuKK5g92hC9dhudt7niA0S51TH45oVgAxScFSH5Jxd0IHa/XHZcNxyduEJhLydhK03Vd6DFTm12matIu6VTORYOJ/pLic2cBzAd93vQJuX4XwVSnpKsiF/7O99cswm42HswNcWXPVUgwLzjYe6txmsBdu8E38ZNf1eFzYusXuv1WBZT9s1dbctbWpcuo6TWneUIeqIyNDXFoJ8p7ah0ynt1SBUVtl04bO3Mfkgjsc+fTL2DhAy+CJOkByD9BTep/jqOmpbCvNJaWVHcOqCtTX+llgvBbrSgR42oPLh0bNPBkEyS9Wt2uDYsB/3s5P9J5Z8GoAk6Lxt0RNHeOnBhtJ3tOzNsWQjSYges5Uvk+926t4jH8SfhMayekajXFrSgo1uFhnh7NT+meO24TD7y2OsoHWNvSSQbZmX1WTQtnVC1bnE6GJJ/E1kR+OUq0+fBnLm/gh18OjIqVe7Pal67T5WRfTGWCnCbnY+oRNVx6x2hoH55/zaXbb+nxgveR+U8EHIy4nL+6SBPWcA7qhtjEYf1Pn56pPlvCbtOjyAccvZhScQ8nYSdvnU/9NIq7fzme5yYk76FzflJnMikNHsmoa5Q7PiraBZjqAgPOXcgGHswP83aD0ZvfML7PfBFFrZScffgATbvBNfGa1NSjmF3TBzeE68JYrpblva1Lh0zQmoO8oQdURk6LIRjqxLGqqu3raPk11Wkmy68MnFPqf+n0YGO/HV3ydjdDAY4Is4QXIM0lOajSV9rK+uo7Yl+ff8Nx/6/mllR/AkZfQmpKZO+P452MbVKJ57+2QQJLNU3SZl4n62K5bCy4mf9+nAE0eE4Fiq5iSzHn3lC/TdTt175IPoUso3jbh++Ibl0YcsOHwzXHTzXIyd3z9z3HYZZn/DyDeBquuMvWyQY83waZzg9xu0bW8/wbHtaJxy9enTQCxfMvZnDK6Dp0xOvTrkMNARTqIu6u9CXlLZuOo7UrsL7P+J8ZL3QQkfhLycuH4rVb8ed7Zx/BlYOe1+GiqXsL8d4FtbG5E4p7LyrqmimbPyKfdxBDBuObvwBELeTsLWmy5vHbqILCPSHKTzme5yJp10F+riE5PTsLjShI0u06VeeqLub8o/KAhPOTdgGDvwd+DOzlctxZBBib0UQhJs805UGRNvnfrQPi3X9s+erkN7k90vCtjV35i625Y2NS5dB9YdZYg6IjJ02ggjyHciSzbTCPU92XThaxP2uZx8EMdjnz4ZJwPgMF/ECZJjkJ5SbAzxsb66jtqWwnxSWtkRnANzg1t96F5rAd8QVAb0GTdp99zbJ4MgmaXqNtk23M92xVLmfh0mWzXDJ4m0C8+sAoxUPbrKN4TvdureIx/En4wSy/b5TAG+AfERNZDE9jSJcXv0zxy3DYfZXx5jhahuGeRrtxdpx57lvY5Ex1jk6tNnBJMvTwYaS6mC6+DRkVOv9j2U35t+vIXoy8BOCGeqox9v384Ib3fh/T8xXvI+KOGDkJcTT3VAWVBrv12fs07gcySJcylrPtWz0xt5yn2Us7fXWLvl7MITqHkdqK03Vd60KbJpOJ/pLmfCSXfrMM/usXcJCZQRJzvqwAVnGDvwtQVfZ6SCDb6/keosExswh9q8E6WHxF5K7USwMEDVa+iAMq82NS5dB9YdZYg6IjL0BSxhvlMFm65y35RBl5kgDPU92XThaxNj8kEcj336ZJwIXgN9ESdMjiF6SrExpE/x1XXkthTkk9LKrupp7CnlHpg7uCl9Z6akkufePhmEySxFt4m9VPS9kH0youWOg3vJmaATsLiGDHqZLYgNnWPl8rVFTxxhob8o6vr63FAJHxNUj67yDeG7nbpX12P72yG6yiWWZchNb9Puc3v0zxx3ewizv7zkKzep3guli57kkHrpFrM5VW9UhozEHmjjlKtPnwq5nKsQXyoVWgePjpx6TbQn5fdQW/Wg7pNpjz4H3r7dIlO7G6L/J8ZL3gclfBBkY0c+5RzoxHUggn6F5iY7NzULi+fsL7TYsOCH73uCfJ1CnDs4DQunmkN2frKj4Wszk5txdaHOd7zP2Mj1l5YSX+hgdM7yT4MmnY27s3ShZHw/Ik9vJ5HUm6+8/edKMH1gEWpXU96oOJ/pDigTTlq/GUWcrJRbvNwjD1wchNuBry34OyMp+71QPBZf3jUg0OY9yGm/9qDC3UnzgYVYqzx0QJlfmxqPrgPr7iC4joicvAFLoO/srEgfkwx0lS1ZiRUpK8zH42TTha9NjMkHcbRskQ0VvTK2g9dAX8QJlWOInkL7FF9dR29LYT5Jlh3TCU948/Zh7UejdFG6aPzG6J5fhBn+1RS7TirhlWlDese9OT4ZhMpM6hb7aqOu8wLUjS+cdZ+eZ78l9dtjdU76Iea3+B44UyVoxr6S1mNl4oNcu1yBsZoLNYjcezL5VbKQJV1henTFOUP4bqfu+dt8Xnb7izy9yH/H+rsQf9xvwzJrDzNM9rbM5KA7/pWjJLdH/8xx+79A+8tlrMDQX9djeq1eRZIJvQ2oqlkc8Xjc49/0V2dNfYxTrj59MvvcWFkQM1aSsgqsg0dHTj9n95kMt9/j9j4H04fL0LRmJkrfmtYO/CT886jtboj+nxgveR+U8EGQDXiCNdQK1FYb0IoCs/Agggc13AlOHixDba0Nne4GtM4vw4LYwM92ujj9y/Kzr4ONuORUvdIB7kStLHdg58edpPgEMN9McKkOrWsd2LhUh+XD01A4sijfBGRq5MypH5407tOFzpUG1E7Mivpjn3H1BY84LHAUXz8pwOyxKtNNc+DwvZ0Epjce8Kh6q00UxTTrMwtC1hNTGd68O58ZkPARb0Z4OQbTKLvXmkJuk1PTibciow9cXITaga8t4OUcwP6Wd77875GgjhNk8x54EIIlG/XnWqMp45ttqXsmc6H/EQLKvNrUuHQdVHcngXVE5OS3EXY+xHeyYEd8ApX9vnBmUJ7KQe6Tkvbi9vE42XThaxNj8kEcNViYKMxCcaUGtfVBwO2VcSJ4DfNFnFA5hukprE/x1TWPthTmk7RuWTs7IXXLy149Jje/nLaXPqkvzExMLUBllV1/Sd3reh3muW6nFqG6vgFdsYFtHcrimWyQcTlDQtB1b4ZPBqEy47qtqI2MiysN5ltkWblP4HJIJO705++5fo8vM/tRm4Oyui4iH3noX6mIT6Hz66cPF6FyfAFm7i2IgV9NJPaztsWAhA+LOXRCafJgEarn5WasjdWyWo41CQvnsZjDIkiP7jgn2Hd7dK8H6BO75qB4hslwpSQ2op5m/hvr77L7Y2RJFitnY2VRtp8Mm5nfDv0zZ6eNFTj9a1Vpa0x+uw8VYZmVS254rfdEcrxkuFyBGXa+sI/Zuf18OzE4Trmqe+99oCRm10Ww9j69i5efyehwFd2TJqgOHh05/RyS8In6NOb3ZJ8W76cKiVmYapY7OhtKlSlDG0n651HbXXj/T4yXvA9K+KD0oHlKrXtkDNZXDxdE9C5am2gxJvcXoXYtQ7Cm6F+rQXE/D34H9yjcOw8Ve8O/4M6PcasDdXszQeFQffXFwDfamz/TRgf13uDRxc0NqD64WwYqZsbaV2+n3pDyss5q9ngt2wZnzmeGJHwYsTpJJg8uQ5vpnN/fLHceAxcnQXbgtw20ngbyE5n+JRKZbd6HemuR3Mgw3sbFvVmQ0NhU9RopoGTk0KbGp+uAuvsIqSMipzQb4QT5TnTDwTmHvbh8PE42Xfh06zo3og9S8CB/QcvJWMLolTEWvAb4IkmYHAWBesrap/jqmldbCvNJWNlnoLiKv6nvPVeGWTWgMWd8iGfyRIq+ByPUD7ru7ZNBqMwEqG5noXg+OcDk2HWbPFiB1g02cHQlZHobUF9ahLn7ZmDmvjlYPNMUn/EPa4shCR9ODzZWiwP56bJyP4TNonAQpMdYOzTfzIf7bpfu+QCR14sPkOW9+EC9xQaJ7v4uuz/mtq++qBUR4td2fv8scfm/4ewvj7GCQG0QHu9X0m0W82/8b+qxRAljnHJV9zbLINg1DTOHy1C7hOjZIHMdPDpy+jmsz+Rgfo/3U7w9mdcJ2HOZXgonkNmZallVls2c8f5uxHYX3P8T4yTvgxI+Pm7xT9MFOloPfb2BV0Agn0Dtc5BnuSKinfxHvLeQm7wXFtjmAi/rKHKMYZTX3FdhqxmnbkPIyw7yYiS5qLeliX18FOOu606TpUleZduCOgb5zpDy5OzjhycfH8TllIsPC21zw8gxWE9KPtj5rSZEPoFlFzp0/J75mQ5c986dUJ/A5ZmyLMpHaiIqJ4L8kIMgPXI5Ys8awuf6dB9qF5nlYNr+MH5piHoGkdf9c+5H8rAzyUD+Ie1r5OePW28ZyE+GAWTx9WKZqGM/HbWENNOLEx+jtrudMha5y8n7oIQPQRB3NmJae8qGdQRBEMSOpXe5ClVkn4xoz5fMM3YIgiC2A7mfWXKZl0RuQWDt70bcteR9UMKHIIg7HLW2mQ0I0Fk+BEEQxA5Gbdos9pdoQUe9ve5eazj36SIIgthRXKnANPqhEkn7dMG5ryVx95H3QQkfgiAIgiAIYufC98lQm14P9qdgOPd/IgiCuF3oQ/tcBarGxurE3U3eByV8CIIgCIIgiJ0P3x+Ef33nSof2mCAIgiDuSPI+KOFDEARBEARBEARBEASxzeR9UMKHIAiCIAiCIAiCIAhim8n7oIQPQRAEQRAEQRAEQRDENpP3QQkfgiAIgiAIgiAIgiCIbSbvgxI+BEEQBEEQBEEQBEEQ20zeByV8CIIgCIIgCIIgCIIgtpm8D0r4EARBEARBEARBEARBbDN5H5TwIQiCIAiCIAiCIAiC2GbyPijhQxAEQRAEQRAEQRAEsc3kfVDChyAIYidxqw+9Xo/Rx88TBEEQBEEQBHFHkvdBCZ9hudGAxV0TMHm4Dl3s/BbSuVCBylIDOsi5u4JeC6pLFahe6uHnCeI2oXOhCDOFCZiY4CxA/QZ+nUDZfeVMM5sPuqGuv5t9BUEQBEEQBEHsYPI+KOEzLFeXYZoPygplaGPnt5DWKT44LEMLOXdXcL0Gc0wXc6v5GPNO4K5J4uWYrLvtZfZyFWaZHRcOlKG+3oTm+gb0sOs0yu4nJvZC5Qpy3qKzMqsSSXexryAIgiAIgiCIHUzeByV8RuFmD3p95PcthhI+d17C567RaY66u+1ldqksEjKli8g5jCjhMwGFE03oY9dEdKB6n7yWEj4EQRAEQRAEsTPJ+6CEzx0AJXwo4XPbQgmfASrhU76EnMMwEj6py7+uVGBvdC0lfAiCIAiCIAhiJ5L3QQkfjBstWD4yC7v1Xhq7pmFhqWUtr2hBmZ871Rr8Fg1eO9C7WIH5ewvq72eheKEjrulfq0Fx/6QaeE3C7PE6dG457svKUTm0Gwri2gLsPlCE2tXk0hfnQLe3AbXjdj2a0I09L5RB+WJ1KeyG+TNtOcvALvehZWj37PtwerCxWoRZLScmj+nDZahfc2xWe6sLzaUFmN7Fr2Vwua5uQN+TNOiuV2BhSss7gwxvtmFZl/1gTe6NYur18rJxP6a/I1VH3TD6sHG+nCyPWV816LdJJAG6TagcnoZJdb5w7ywsnrFtlNV/dY6dn4PaZg/aZ+KyW1xpO5YMYXqpQLOLXesgtQ11oXZQnTPRMtewesbuw+xs9sgytMzkRprMfEklT5Ilq+14yaKnWOLGwPQtGOrvyityKdjsivQxSfrQPMF0WShB6Ti/N+IrssiZMbCnDjSYb5H1UvdjsuTtZuE8LqPWKW5PKYkpgiAIgiAIgriLyfughI8N34yZD3p2zUF5tQHN9SbUl+bEwKZwpGEM1DwJn+NssKz+vrHKBo37+EBnmg3MKjBbmIGFM3VortWg8uCMGCDFl2Oo+56oQGWqADMPVqC2xsqwUlQJJHafS/GECJrwuc7qMcV+589bqkFjvQ7V46oe+3hiw7g2CFW+o0Uo7lJ1OV+F4kE+MC7A/Fn278IkzJ2KP3Nif9XaW6UD9cPybwZ1LA1kZdWRX197gJ/jSZaBTOZ2FWBxqYwM6PvQPjMr5Dt5sAjV801DF5Ow8HR8cCxluACLRwowuX8RSnxz2xU1MI/plQ3YuTwN/SXrhtN+fFrVd1nu0bLGZKXqG+3B8nIDKuzZi/t5efiz+L40FWi8PLhPnw2sxca+zMaKK9KWlrVuH6jFyqIH6EU+OGf1qsRscgJmz9pJgi40jupymnJmz2O2lNQLQqY21IMWaw+VE/Ni5sneB0qinpHMOZs1mOf3iWyY3efMgqz7VBnaejllmsyCEz5htuMis5705stH5B473L6FLFSS2IlO+FzqsbbEnrOngu8ndqMOC+y6vUtt3FdklTND29PikWko3DsPRSFrvW9SGyp72N/YSTtOvwklfr+YDyUIgiAIgiAIwiTvgxI+Fp1zfEAzC1VjgC1/XxCzTwazHNwJn4lCEZpmQuVmE4p8sJO4b58Nrvnv7PpoUKXuy5iz39jfZAMqPqgtlIzrsYRPDxpH2OB0TxEa1tt0Pgjlm027ZwOkoctn16UDVTHgLkDxuXhSYOOJGfb7TOz63oVFNqAuwPyqVY5b7D585odVRznQRK6/2YISH2RyeZkD+isVUc+5JzasvU1U4giV4QRM85lLsesZkV7nobYZP6eTOOXL8d+TsHKyexSOW3utsPKX98/C4rl4OdGBuYDZAE/ksYG4nbTrrZdE8sTUrZRbMhEU3cfadFzqZS8U16xZGryc/PoMya3sbYjhTMb0YeMsu/7epMz7F2U9Fy/Ey+iUWWjCJ9B2cML0JECTTx6ihA+TCbsnb0+l9WRCTm7WLPWRlFGYnN32JGk/vpedn4Pa9fjvvvIRBEEQBEEQBCHJ+6CEj4VrwJLEnfDZ+3jbuI6jlq/clxws6zfmg+ep+7KBeHKwrwdOE7GkSmIQp8qBL63oQ5Mv63DNBkhFlQ95Uy/LYSavFImBrEoO3bcMG+Z1GrXfyGCgqTacdSQb9CDUHNCL5SOuOr5chZnY/XXZF6GBLc9S8ixgS2zUvdBkgkmvAYuueyC4khda/4tr2MA5OdNDJ8qwJELnLE/Emban7PRwHZ2F0X+uyK5P/yJU9jbE8CVjXOi/ORdPmLhk5n0GkmQJtR2MUD0JRkj4RLNr7ISi/l3p1CkjDETO0p48NqC+NBZPZg2WlKUnygiCIAiCIAji7iXvgxI+Nurtvl6O0t7sJWd8CNwJn+TAUg2kkaUOzoSPa+mDShyYSSV7ECcH5ruhuMI/7Zykemw3O+9IbqSC1FvhHEzaA9l+E4rs/4XTdmJMI2fDRM/QyRLX9Ymki0oQHayI5SkJGaxVhJ58MozhSxj4zsVQs64m1JKuSxvQ9Syrc5VHJmnmoe7YT0fOrhnYU9K+BiTOKb3sPlZNyoyzUoTd7HxasiN7G2Jkkl8fer0OtHkZzlehpJfSWTbo1KHvGVgyMtB2MEL1JBgp4aOfGd8jx55Z47XzDHL22ZNEJXPN5KyyK76kLHk9QRAEQRAEQRCavA9K+CD0rpobK3P4vjH2BqZjTvggCRXXeXsQJ+/Jf/PhG7T5yCHhkzrIt56Rdn3ivPr7NDwyjOF7fmpdDMSm0/ODjXEZfB+U8nl76ZC7PP4Be9KefAP0xDlVl5iMELLUNVsbYvjkd1NuOi72gNLsmoa5Q7NiBphtg07Z+J6RSLKE2w5GqJ4EIyZ8krNrkjOJ0HIFyNlnTxp5zWBJX9aZYQRBEARBEARxt5P3QQkfHze7sHGJbzysBumxvXmQxIdzYDlEwiexNEOBzI6xB3F8HxaxEfClHvR6bpyzLrwg9VY4B7n2QBaZpRTHekba9YkZPmoZy5E6dJF6RxgzbLwDdF/CwHfOSR96m22xEfCiSorYmye7yiOXS7lnZ9n25BugJ84pOU8/3sLlpQlZluNtQwxPm6mLTbqnYXGlCRtdZq/663Lqb2wbdOrQp6NEkiXcdjBC9SQYNeFjJ3iQ5VVJGYXJ2WdPEWqT6Blh02oJKbKclSAIgiAIgiCIOHkflPDJSH+NJ1HM5SxjTvi49hBhg0KxN0hi/xljEKf2wHF9Hnk0ckj46EE19jUfjiuB47i+d35B3H9wPV8+xa53yRDBWXaOL2HgO5eJDVjmS4issrrKI5N5+J480eDa2P8pKOFj7fdiXz8qyTbEcMmvW4d59ju6DAhJRHCcOlTXywRE/JycfRJPmoTaDkaongQjJ3ziS7jaSzzpFF/ilZBRoJwzJXy0DHmSRyURExtUEwRBEARBEASRIO+DEj4xulA/OgO7H0gmFvTAcLBZ8pgTPmzQlvgi1S/0V4IWY1/fSg501T4ayBeCxLmD07BwqoknW1LJI+HzC/XlIOzz63wwzGccxAeq8mtYe6F00br+Vlt+DcmSux74JmXIdVmC6QOLULs6uJez7BynXlPOGfQvL8PcFPZZc7VnjDUDQpanlCyP+rw1+pUkNaPDHLyHJXx8euF7xMwlv7KVIKQNMbT8rA2Yo1ldSCKic1Z+vhxP+CAy+0Ubynx2kd0e9Ffv2L2wpElW20EJ1JMgh4TPIDk6Lz61XjgRnymYsPNAOWdL+GgZ7oXiMZ6MTX6xjdN7uQXNK/l0QARBEARBEARxJ5D3QQkfi+7T82IGzfTRKjSvdaHX3YDW+TLMqgFjO1rOMuaEz4NFKO6ZhLmlOrSudcSymMpBvvwnORDFkhX9y/Lz64V9C7B8vgUb3Q6012pQOiA3DrY33pXlyPZ58TwSPr/os4HpPp68moHiCt/YVy79WT4sP3OOfX5dfBY8un5Qn9kji8iXsvRSFSbDUzVoXOlA91oL6mcW5IbCU9lm1AhySPjw+sryz0L5fBs6vR50rjSgelTV9+n438vEC7PDwxWorTagZSwN6p5fEPutTB6syM2fxfKwUmSjZlIjNOEzKOcMLJzhttcV5aydmBXtouDaTNwgextiqMQIl0txpQa1da13vcn1YKlR91pTlGNyahrdw8cnM5kwlO2hxJ9zpghzu1g7OCJnh8WTJmG24yJET4JcEj4DOWD75iTtPEzOWRM+kV7532MzxlSiifb2IQiCIAiCIIgBeR+U8EnQh43VIszowYqgALsPVbZ80+b+tSos7NJlYPBkx2r2DX771+yNc9mA9955qFxMLvVqn+aDPvdXhQbklPDhoJvFsoH/+eTMCkGvDcuHdotkgry+ADMnm9DbdMm9By1rk2SxefDxGmxYg+2xJ3w4N1oqaWeUh9cX0Skve/PUQDaxWTGM3sUKzN/LdabvxW10GdrWnjHBCR8OppfCbphfamVc6pW1DUmEneu6mDOdWDmqD5r65skTVkdm11zmSRv0yYzbwtygTrotOZMs2W3HR1Y9CXJK+Og9dGJfylKgdh4g58wJH4ZcUuZYWholFpnPyXAvgiAIgiAIgrgbyPughI8T/olitUFryCa1I2MnVAblGG6TZcZNVY+eaxmKsecGen7M9NPKZ6Hrk3ngbchQb0i7naTqw+AWL7v7ur6q11hsVDxbyQ07n0pYG+J1QfUTIi+OV2Z+eSYxZDCC7YxVT3kRKucUZMKnCM2dXGeCIAiCIAiC2EHkfVDCZ8fhnkEzPuS+H+6vZhEEQQRwswnFQnIPIYIgCIIgCIIg3OR9UMJnx7ENCR/xpR7aS4MgiNHoX2lCPdorDN+smSAIgiAIgiAInLwPSvjsOLYh4bPZgMpSAzawcwRBEBlpnVL7Fe2aQ/cKIwiCIAiCIAjCTd4HJXwIgiAIgiAIgiAIgiC2mbwPSvgQBEEQBEEQBEEQBEFsM3kflPAhCIIgCIIgCIIgCILYZvI+KOFDEARBEARBEARBEASxzeR9UMKHIAiCIAiCIAiCIAhim8n7oIQPQRAEQRAEQRAEQRDENpP3QQkfgiAIgiAIgiAIgiCIbSbvgxI+BEEQBEEQBEEQBEEQ20zeByV8CIIgCIIgCIIgCIIgtpm8D0r4EARBEARBEARBEARBbDN5H5TwIQiCIAiCIAiCIAiC2GbyPijhQxAEQRAEQRAEQRAEsc3kfVDChyAIgiAIgiAIgiAIYpvJ+6CED0EQBEEQBEEQBEEQxDaT90EJH4IgCIIgCIIgCIIgiG0m74MSPgRBEARBEARBEARBENtM3gclfAiCIAiCIAiCIAiCILaZvA9K+Pz/27ujlea1AAij7/+iKoKCiArHmh6mNOBFoK3dsWb+9cGilym9HHZ3AAAAAG5sdAYfAAAAgBsbncEHAAAA4MZGZ/ABAAAAuLHRGXwAAAAAbmx0Bh8AAACAGxvdpgefz8/PxWcBAAAAbEX2jdFtevDZ7XaLzwIAAADYiuwbo9v04PP19bX4LAAAAICtyL4xuk0PPmnpWQAAAABbsUabH3yc8gEAAAC2ao3TPWnzg09yeTMAAACwNWtc1jxXMfhM07T4TAAAAIC/KnvGWlUMPsnoAwAAAGzFmmNPqhl8Un4sf+8CAAAA/qrsFmuPPalq8JlzkTMAAADw16x1QfNSlYPPXH7I3W7n1A8AAADw67JHZJf4zaFnrnrwkSRJkiRJ+hcz+EiSJEmSJJVl8JEkSZIkSSrL4CNJkiRJklSWwUeSJEmSJKksg48kSZIkSVJZBh9JkiRJkqSyDD6SJEmSJEllGXwkSZIkSZLK+rXB5/7+fj9N0/GxkiRJkiRJWqPsL9lhlvaZS50cfB4fHw+fkiRJkiRJWq/vO8y1Tg4+z8/P+7e3t+OjJUmSJEmStEbZX7LDLO0zlzo5+Ly+vu6fnp6Oj5YkSZIkSdIaZX/JDrO0z1zq5OATDw8Ph09JkiRJkiSN7/v+MsJZg8/Ly8vhSJEkSZIkSZLGl90l+8vSLvMTZw0+kUuD3OUjSZIkSZI0tuwtoy5rnp09+Ly/vx9eDfbx8XH8OpIkSZIkSbqm7CzZW7K7LO0xP3X24BNZnPIlnPSRJEmSJEm6ru87y9IOc42LBp/I4pRjRvNrwiRJkiRJknR+2VOyq2RfGX2yZ3bx4DPLRUK5PTqvDJuXqGmajl9dkiRJkiRJKXtJdpPsJ9lRsqeMvKB5yY8Hn1neDz+vUjmGdHd3BwAAAMBR9pL531LZUZb2ldGuHnwAAAAA+FsMPgAAAABlDD4AAAAAZQw+AAAAAGUMPgAAAABlDD4AAAAAZQw+AAAAAGUMPgAAAABlDD4AAAAAZQw+AAAAAGUMPgAAAABlDD4AAAAAZQw+AAAAAGUMPgAAAABlDD4AAAAAZQw+AAAAAGUMPgAAAABlDD4AAAAAZQw+AAAAAGUMPgAAAABlDD4AAAAAZQw+AAAAAGUMPgAAAABlDD4AAAAAZQw+AAAAAGUMPgAAAABlDD4AAAAAZQw+AAAAAGUMPgAAAABlDD4AAAAAZQw+AAAAAGUMPgAAAABlDD4AAAAAZQw+AAAAAGUMPgAAAABlDD4AAAAAZQw+AAAAAGUMPgAAAABlDD4AAAAAZQw+AAAAAGUMPgAAAABlDD4AAAAAZQw+AAAAAGUMPgAAAABlDD4AAAAAZQw+AAAAAGUMPgAAAABlDD4AAAAAZQw+AAAAAGUMPgAAAABlDD4AAAAAZQw+AAAAAGUMPgAAAABlDD4AAAAAZQw+AAAAAGUMPgAAAABlDD4AAAAAZQw+AAAAAGUMPgAAAABlDD4AAAAAZQw+AAAAAGUMPgAAAABlDD4AAAAAZQw+AAAAAGUMPgAAAABlDD4AAAAAZQw+AAAAAGUMPgAAAABlDD4AAAAAZQw+AAAAAGUMPgAAAABlDD4AAAAAZQw+AAAAAFX+2/8P4uyXtLO7Lf4AAAAASUVORK5CYII=" + } + }, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![text-descriptives.PNG](attachment:text-descriptives.PNG)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Conclusions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this tutorial, we have explored how to add text descriptives as metadata to records and datasets using the `TextDescriptivesExtractor` integrated on Argilla, what it is really useful for annotation projects." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "argilla-markdown", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.18" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/_source/practical_guides/annotation_workflows/annotation_workflows.md b/docs/_source/practical_guides/annotation_workflows/annotation_workflows.md index 6cf834aee9..cb5d6dede2 100644 --- a/docs/_source/practical_guides/annotation_workflows/annotation_workflows.md +++ b/docs/_source/practical_guides/annotation_workflows/annotation_workflows.md @@ -24,6 +24,11 @@ Find similar and dissimilar records in your dataset by using semantic vectors. Schedule jobs to run at a specific time or periodically. ``` +```{grid-item-card} 📇 Text Descriptives as Metadata +:link: add_text_descriptives_as_metadata.html + +Add text descriptives to your metadata to simplify the data annotation and filtering process. +``` ```` ```{toctree} @@ -33,4 +38,5 @@ active_learning weak_supervision semantic_search job_scheduling +text_descriptives_as_metadata ``` diff --git a/docs/_source/tutorials_and_integrations/integrations/add_text_descriptives_as_metadata.ipynb b/docs/_source/tutorials_and_integrations/integrations/add_text_descriptives_as_metadata.ipynb index e62abc852e..28aeb0fe48 100644 --- a/docs/_source/tutorials_and_integrations/integrations/add_text_descriptives_as_metadata.ipynb +++ b/docs/_source/tutorials_and_integrations/integrations/add_text_descriptives_as_metadata.ipynb @@ -1,184 +1,557 @@ -# `LangChain`: Monitoring LLMs in apps, chains, and agents and tools - -This guide explains how to use the `ArgillaCallbackHandler` to integrate Argilla with LangChain apps. With this integration, Argilla can be used to evaluate and fine-tune LLMs. It works by collecting the interactions with LLMs and pushing them into a `FeedbackDataset` for continuous monitoring and human feedback. You just need to create a Langchain-compatible `FeedbackDataset` in Argilla and then instantiate the `ArgillaCallbackHandler` to be provided to `LangChain` LLMs, Chains, and/or Agents. - -:::{warning} -As of Argilla 1.14.0 the `FeedbackDataset` has been refactored to improve its usage, so if you're using Argilla 1.14.0 or higher, you won't be able to use the `ArgillaCallbackHandler` as it's not been updated in `LangChain` yet. -::: - -## `LangChain`-compatible `FeedbackDataset` - -Due to the way `LangChain` callbacks and `FeedbackDataset`s work, we need to create a `FeedbackDataset` in Argilla with a certain structure for the fields, while the questions and the guidelines remain open and can be defined by the user. - -The `FeedbackDataset` needs to have the following fields: `prompt` and `response`; the `prompt` field is the one that will be used to provide the input to the LLMs, while the `response` field is the one that will be used to collect the output of the LLMs. - -Then, regarding the questions and the guidelines, the user is free to define them as they wish, as they will not be used by the `ArgillaCallbackHandler` to collect the data generated by the LLMs, but they will be used to annotate the `FeedbackDataset`. - -Here's an example of how to create a `FeedbackDataset` in Argilla that can be used with `ArgillaCallbackHandler`: - -```python -import argilla as rg - -rg.init( - api_url="...", - api_key="..." -) - -dataset = rg.FeedbackDataset( - fields=[ - rg.TextField(name="prompt", required=True), - rg.TextField(name="response", required=True) - ], - questions=[ - rg.RatingQuestion( - name="response-rating", - description="How would you rate the quality of the response?", - values=[1, 2, 3, 4, 5], - required=True, - ), - rg.TextQuestion( - name="response-correction", - description="If you think the response is not accurate, please, correct it.", - required=False, - ), - ], - guidelines="Please, read the questions carefully and try to answer it as accurately as possible.", -) -``` - -Then you'll need to push that `FeedbackDataset` to Argilla as follows, otherwise, the `ArgillaCallbackHandler` won't work. - -```python -dataset.push_to_argilla("langchain-dataset") -``` - -For more information on how to create a `FeedbackDataset`, please refer to the [Create a Feedback Dataset](/practical_guides/create_update_dataset/create_dataset) guide. - -## Monitoring - -All the `LangChain` callbacks are instantiated and provided to the `LangChain` LLMs, Chains, and/or Agents, and then there's no need to worry about them anymore, as those will automatically keep track of everything taking place in the `LangChain` pipeline. In this case, we're keeping track of both the input and the final response provided by the LLMs, Chains, and/or Agents. - -```python -from langchain.callbacks import ArgillaCallbackHandler - -argilla_callback = ArgillaCallbackHandler( - dataset_name="langchain-dataset", - api_url="...", - api_key="...", -) -``` - -### An LLM - -First, let's just run a single LLM a few times and capture the resulting prompt-response pairs in Argilla. - -```python -from langchain.callbacks import ArgillaCallbackHandler, StdOutCallbackHandler -from langchain.llms import OpenAI - -argilla_callback = ArgillaCallbackHandler( - dataset_name="langchain-dataset", - api_url="...", - api_key="...", -) - -llm = OpenAI(temperature=0.9, callbacks=[argilla_callback]) -llm.generate(["Tell me a joke", "Tell me a poem"] * 3) -``` - -![Argilla UI with LangChain LLM input-response](/_static/images/llms/langchain-integration/llm.png) - -### An LLM in a chain - -Then we can create a chain using a prompt template, and then track the initial prompt and the final response in Argilla. - -```python -from langchain.callbacks import ArgillaCallbackHandler, StdOutCallbackHandler -from langchain.llms import OpenAI -from langchain.chains import LLMChain -from langchain.prompts import PromptTemplate - -argilla_callback = ArgillaCallbackHandler( - dataset_name="langchain-dataset", - api_url="...", - api_key="...", -) - -llm = OpenAI(temperature=0.9, callbacks=[argilla_callback]) - -template = """You are a playwright. Given the title of play, it is your job to write a synopsis for that title. -Title: {title} -Playwright: This is a synopsis for the above play:""" -prompt_template = PromptTemplate(input_variables=["title"], template=template) -synopsis_chain = LLMChain(llm=llm, prompt=prompt_template, callbacks=[argilla_callback]) - -test_prompts = [{"title": "Documentary about Bigfoot in Paris"}] -synopsis_chain.apply(test_prompts) -``` - -![Argilla UI with LangChain Chain input-response](/_static/images/llms/langchain-integration/chain.png) - -### Agents with Tools - -Finally, as a more advanced workflow, you can create an agent that uses some tools. So that `ArgillaCallbackHandler` will keep track of the input and the output, but not about the intermediate steps/thoughts, so that given a prompt we log the original prompt and the final response to that given prompt. - -> Note that for this scenario we'll be using Google Search API (Serp API) so you will need to both install `google-search-results` as `pip install google-search-results`, and to set the Serp API Key as `os.environ["SERPAPI_API_KEY"] = "..."` (you can find it at https://serpapi.com/dashboard), otherwise the example below won't work. - -```python -from langchain.agents import AgentType, initialize_agent, load_tools -from langchain.callbacks import ArgillaCallbackHandler, StdOutCallbackHandler -from langchain.llms import OpenAI - -argilla_callback = ArgillaCallbackHandler( - dataset_name="langchain-dataset", - api_url="...", - api_key="...", -) - -llm = OpenAI(temperature=0.9, callbacks=[argilla_callback]) -tools = load_tools(["serpapi"], llm=llm, callbacks=[argilla_callback]) -agent = initialize_agent( - tools, - llm, - agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, - callbacks=[argilla_callback], -) -agent.run("Who was the first president of the United States of America?") -``` - -![Argilla UI with LangChain Agent input-response](/_static/images/llms/langchain-integration/agent.png) - -## Synthetic data - -If you want to create synthetic data with LangChain, you can use the `ArgillaCallbackHandler` to keep track of the input and the output of the LLMs, Chains, and/or Agents, and then store that data in Argilla. This means you would monitor the data in a similar scenario as described above, but instead of providing a direct functional prompt tailored to data generation in order to set up your LLMs to come up with some synthetic data for a `TextField. If you want a more tailored approach to data generation and computational feedback, you can take a look at [this integration with LangChain](/tutorials_and_integrations/integrations/use_argilla_callback_in_langchain) or [this tutorial on SetFit for suggestions](/tutorials_and_integrations/tutorials/feedback/labelling-feedback-setfit). - -```{warning} -Do keep in mind that LLMs have licenses and not every LLM can be used for creating synthetic data in every operational setting. Please check the license of the LLM you are using before using it for creating synthetic data. -``` - -```python -import random - -from langchain.callbacks import ArgillaCallbackHandler, StdOutCallbackHandler -from langchain.llms import OpenAI - -argilla_callback = ArgillaCallbackHandler( - dataset_name="langchain-dataset", - api_url="...", - api_key="...", -) - -topics = ["opening a new account", "applying for a loan", "applying for a credit card"] -sentiment = ["positive", "neutral", "negative"] - -def get_prompt(): - prompt = ( - "Write a customer review for a bank. " - f"Do that for topic of {random.choice(topics)}. " - f"Do that with one a {random.choice(sentiment)} sentiment." - ) - return template - -llm = OpenAI(temperature=0.9, callbacks=[argilla_callback]) -llm.generate([get_prompt() for _ in range(3)]) -``` +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Add `Text Descriptives` as Metadata\n", + "\n", + "In this tutorial, we will add text descriptives as metadata easily using the `TextDescriptivesExtractor` integrated on Argilla.\n", + "\n", + "We will cover the following topics:\n", + "\n", + "- 📂 Load an example dataset\n", + "- 📃 Add text descriptives to records\n", + "- 🗒️ Add text descriptives to a FeedbackDataset\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Introduction\n", + "\n", + "Text descriptives are methods for analyzing and describing features of a text. They range from simple metrics like word count to more complex ones such as sentiment analysis or topic modeling, converting unstructured text into structured data easier to understand. For annotation projects, they provide information not captured by annotators, and added as metadata, they help in filtering and creating dataset subsets.\n", + "\n", + "To get the text descriptives, we will use the `TextDescriptivesExtractor` based on the [TextDescriptives](https://github.com/HLasse/TextDescriptives) library. The basic metrics added by this extractor as the default are the following:\n", + "\n", + "* *n_tokens*: Number of tokens in the text.\n", + "* *n_unique_tokens*: Number of unique tokens in the text.\n", + "* *n_sentences*: Number of sentences in the text.\n", + "* *perplexity*: Measures the text complexity, vocabulary diversity and unpredictability. Lower scores suggest that the model finds the text more predictable, while a higher perplexity score means the model finds the text less predictable.\n", + "* *entropy*: Indicates text randomness or uncertainty. Higher scores denote varied, unpredictable language use.\n", + "* *flesch_reading_ease*: A readability test designed to indicate how easy an English text is to understand, based on sentence length and syllable count per word. Higher scores mean that is easier to read, while lower scores indicate complexity." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Running Argilla\n", + "\n", + "For this tutorial, you will need to have an Argilla server running. There are two main options for deploying and running Argilla:\n", + "\n", + "\n", + "**Deploy Argilla on Hugging Face Spaces**: If you want to run tutorials with external notebooks (e.g., Google Colab) and you have an account on Hugging Face, you can deploy Argilla on Spaces with a few clicks:\n", + "\n", + "[![deploy on spaces](https://huggingface.co/datasets/huggingface/badges/raw/main/deploy-to-spaces-lg.svg)](https://huggingface.co/login?next=%2Fnew-space%3Ftemplate%3Dargilla%2Fargilla-template-space)\n", + "\n", + "For details about configuring your deployment, check the [official Hugging Face Hub guide](https://huggingface.co/docs/hub/spaces-sdks-docker-argilla).\n", + "\n", + "\n", + "**Launch Argilla using Argilla's quickstart Docker image**: This is the recommended option if you want [Argilla running on your local machine](../../getting_started/quickstart.html). Note that this option will only let you run the tutorial locally and not with an external notebook service.\n", + "\n", + "For more information on deployment options, please check the Deployment section of the documentation.\n", + "\n", + "

    \n", + "\n", + "Tip\n", + " \n", + "This tutorial is a Jupyter Notebook. There are two options to run it:\n", + "\n", + "- Use the Open in Colab button at the top of this page. This option allows you to run the notebook directly on Google Colab. Don't forget to change the runtime type to GPU for faster model training and inference.\n", + "- Download the .ipynb file by clicking on the View source link at the top of the page. This option allows you to download the notebook and run it on your local machine or on a Jupyter Notebook tool of your choice.\n", + "
    " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Set up the Environment\n", + "\n", + "To complete this tutorial, you will need to install the Argilla client and a few third-party libraries using `pip`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# %pip install --upgrade pip\n", + "%pip install argilla -qqq\n", + "%pip install datasets", + "%pip install textdescriptives" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's make the needed imports:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "import argilla as rg\n", + "from argilla.client.feedback.integrations.textdescriptives import TextDescriptivesExtractor\n", + "\n", + "from datasets import load_dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you are running Argilla using the Docker quickstart image or a public Hugging Face Spaces, you need to init the Argilla client with the `URL` and `API_KEY`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Replace api_url with the url to your HF Spaces URL if using Spaces\n", + "# Replace api_key if you configured a custom API key\n", + "# Replace workspace with the name of your workspace\n", + "rg.init(\n", + " api_url=\"http://localhost:6900\", \n", + " api_key=\"owner.apikey\",\n", + " workspace=\"admin\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you're running a private Hugging Face Space, you will also need to set the [HF_TOKEN](https://huggingface.co/settings/tokens) as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# # Set the HF_TOKEN environment variable\n", + "# import os\n", + "# os.environ['HF_TOKEN'] = \"your-hf-token\"\n", + "\n", + "# # Replace api_url with the url to your HF Spaces URL\n", + "# # Replace api_key if you configured a custom API key\n", + "# rg.init(\n", + "# api_url=\"https://[your-owner-name]-[your_space_name].hf.space\", \n", + "# api_key=\"admin.apikey\",\n", + "# extra_headers={\"Authorization\": f\"Bearer {os.environ['HF_TOKEN']}\"},\n", + "# )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Enable Telemetry\n", + "\n", + "We gain valuable insights from how you interact with our tutorials. To improve ourselves in offering you the most suitable content, using the following lines of code will help us understand that this tutorial is serving you effectively. Though this is entirely anonymous, you can choose to skip this step if you prefer. For more info, please check out the [Telemetry](../../reference/telemetry.md) page." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " from argilla.utils.telemetry import tutorial_running\n", + " tutorial_running()\n", + "except ImportError:\n", + " print(\"Telemetry is introduced in Argilla 1.20.0 and not found in the current installation. Skipping telemetry.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Load the Dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For this example, we will use the [squad](https://huggingface.co/datasets/squad) dataset from Hugging Face, which is a reading comprehension dataset composed of questions on a collection of Wikipedia articles, the given context and the answers." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# Load the dataset and select the first 100 examples\n", + "hf_dataset = load_dataset(\"squad\", split=\"train\").select(range(100))" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Dataset({\n", + " features: ['id', 'title', 'context', 'question', 'answers'],\n", + " num_rows: 100\n", + "})" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hf_dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Create the FeedbackDataset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To create a FeedbackDataset, we choose a `TaskTemplate` for question answering with the default configuration, so no metadata is added." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "FeedbackDataset(\n", + " fields=[TextField(name='question', title='Question', required=True, type='text', use_markdown=True), TextField(name='context', title='Context', required=True, type='text', use_markdown=True)]\n", + " questions=[TextQuestion(name='answer', title='Answer', description='Answer the question. Note that the answer must exactly be in the context.', required=True, type='text', use_markdown=True)]\n", + " guidelines=This is a question answering dataset that contains questions and contexts. Please answer the question by using the context.)\n", + " metadata_properties=[])\n", + ")" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Create a FeedbackDataset\n", + "dataset = rg.FeedbackDataset.for_question_answering(\n", + " use_markdown=True,\n", + " guidelines=None,\n", + " metadata_properties=None,\n", + " vectors_settings=None,\n", + ")\n", + "dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We will also define our initial list of records, matching the featured of the dataset with those of the task template. But for the purpose of this tutorial, we will not yet add them to our dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "# Create our list of records\n", + "records = [\n", + " rg.FeedbackRecord(\n", + " fields={\"question\": record[\"question\"], \"context\": record[\"context\"]},\n", + " )\n", + " for record in hf_dataset\n", + "]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Add Text Descriptives" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Our dataset currently lacks metadata. To address this, we will add the text descriptives as metadata using the `TextDescriptivesExtractor`, which has the following arguments:\n", + "\n", + "* *model*: the language of the model.\n", + "* *metrics*: the metrics to be extracted.\n", + "* *fields*: the field names to extract metrics from.\n", + "* *visible_for_annotators*: whether the metadata is visible for annotators.\n", + "* *show_progress*: whether to show the progress bar.\n", + "\n", + "For more information about the `TextDescriptivesExtractor`, please check the [practical guide](./practical_guides/create_update_dataset/metadata.md).\n", + "We can add metadata to local or remote [records](#to-records) or [datasets](#to-a-dataset). Let's see how to do both." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### To Records" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First, we will add the text descriptives as metadata to the records we have defined above. To do so, we will initialize the `TextDescriptivesExtractor` where we will compute the default metrics only for the `question` field. Note that as this happens at a record level, the metadata won't be visible for annotators in the UI." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "# Initialize the TextDescriptivesExtractor\n", + "tde = TextDescriptivesExtractor(\n", + " model = \"en\",\n", + " metrics = None,\n", + " fields = [\"question\"],\n", + " visible_for_annotators = False,\n", + " show_progress = True,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Update the records\n", + "updated_records = tde.update_records(records)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As we can see below, the default metrics for the indicated field have been added to the records as metadata." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'question_n_tokens': 13,\n", + " 'question_n_unique_tokens': 12,\n", + " 'question_n_sentences': 1,\n", + " 'question_perplexity': 1.27,\n", + " 'question_entropy': 0.24,\n", + " 'question_flesch_reading_ease': 89.52}" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "updated_records[0].metadata" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Thus, now we can add the updated records with the metadata to our dataset. And we will push it to Argilla." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "# Add the updated records to the dataset\n", + "dataset.add_records(updated_records)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Push the dataset to Argilla\n", + "remote_dataset = dataset.push_to_argilla(name=\"squad_tutorial\", workspace=\"argilla\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### To a Dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, we will update our dataset with the text descriptives for the context. In this case, we will initialize the `TextDescriptivesExtractor` indicating that we want to extract the metrics related to `descriptive_stats` and `coherence`. We will also set the `visible_for_annotators` argument to `True` so that the metadata is visible for annotators in the UI." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "# Initialize the TextDescriptivesExtractor\n", + "tde = TextDescriptivesExtractor(\n", + " model = \"en\",\n", + " metrics = [\"descriptive_stats\", \"readability\"],\n", + " fields = [\"context\"],\n", + " visible_for_annotators = True,\n", + " show_progress = True,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Update the dataset\n", + "tde.update_dataset(remote_dataset)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this case, it is a remote dataset so it will be updated directly on Argilla. As we can see below, the metrics have been added to the dataset as metadata and they are visible to the annotators." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'question_n_tokens': 13,\n", + " 'question_n_unique_tokens': 12,\n", + " 'question_n_sentences': 1,\n", + " 'question_perplexity': 1.27,\n", + " 'question_entropy': 0.24,\n", + " 'question_flesch_reading_ease': 89.52,\n", + " 'context_flesch_reading_ease': 76.96,\n", + " 'context_flesch_kincaid_grade': 6.93,\n", + " 'context_smog': 8.84,\n", + " 'context_gunning_fog': 9.34,\n", + " 'context_automated_readability_index': 8.43,\n", + " 'context_coleman_liau_index': 8.75,\n", + " 'context_lix': 34.65,\n", + " 'context_rix': 3.0,\n", + " 'context_token_length_mean': 4.46,\n", + " 'context_token_length_median': 4.0,\n", + " 'context_token_length_std': 2.55,\n", + " 'context_sentence_length_mean': 17.71,\n", + " 'context_sentence_length_median': 14.0,\n", + " 'context_sentence_length_std': 7.46,\n", + " 'context_syllables_per_token_mean': 1.32,\n", + " 'context_syllables_per_token_median': 1.0,\n", + " 'context_syllables_per_token_std': 0.69,\n", + " 'context_n_tokens': 124,\n", + " 'context_n_unique_tokens': 68,\n", + " 'context_proportion_unique_tokens': 0.55,\n", + " 'context_n_characters': 572,\n", + " 'context_n_sentences': 7}" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "remote_dataset.records[0].metadata" + ] + }, + { + "attachments": { + "text-descriptives.PNG": { + "image/png": "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LbLi0adz2bYddOvWrd7a0Vj4wEd9QF5405//xOPzMbnwkv8IZ9dZBT7GFWs0SNGVA4wNSMua3OE2xesOqyr1SV/55ZqU9NGnpSXneKcJfNS/eEJa3vImxJ0YkxvvtMgT3vw4hQOemQOfZfeY7V7MrbTJDzrk6f2281ikJQ+Dc3XqLTg3jPK5qbs3X5CHAj1v8upXw7VnzDLvz+A18sDWbkkHLkTnWDO9Z7LH+8COCzLmhybK5zfk7NZD3jaD9bJCnjB1FVw3d5tFBj5me08fNZ+VzPayvYzyjnPsgjRqGOj9mXOcOeU35fWGTWqvoMZ3cs+3rU6LCnz8MrtLXSYG5NBL4Tog8EFERERExMVlpMBHraqq8pozxfPRRx/JN77xDet2w/785z+Xq1f9x+pMT7yBzyy957vyQ+1F8rffDQ3vitcV9//Q6Unzw/tnG/R4Ok9cCjSaS3KFfPdvtQfNQ/LAX9heL0WvAf3uC5bXitOvs4d+EJizZpa6w+BCc86U4l884JZthuvmWz/Q+n3Ae4T79OGCrjvzefiWPKD7zWxzOr11C9RfJiQpSDgYcrdnK6Oeq2jXtnvN2dct5tgiOk+fb0RERERExLiMHPioP/jBD+R3v/ud14iLxnvvvSf/6T/9J+v2Cvlv/s2/keeff96Z82c6FlXgU6Y6w56cSYLtr8+131p7SNIfdMuvQo3/b2294PSqKLXn0dy6wp1X6UaHPGR9fa5dXL1JntAeME7vrEJekEbL+xAREREREXHuLCrwUf/dv/t3smfPHq+5OT06ROtf/at/Zd1OFL/1rW85j2N/++23vS3mQuBT/uqj053+ITqXzZuHpGV/h/PIa2cITXCyYAzI8CFERERERESc3qIDH99//+//vTz55JPS2trqzM9z5coV5/9fe+01Z1iW9tKxva9U//zP/1zWrl0rv/nNb6S5uVna29sJfCrEFX/3gnRcGsv2CBnT8OdleSK24WGV5q/kkE4ebDzkPXkKERERERERMWjJgQ8iIiIiIiIiIi5OCXwQEREREREREStMAh9ERERERERExAqTwAcRERERERERscIk8EFERERERERErDAJfBARERERERERK0wCH0RERERERETECpPABxERERERERGxwiTwQURERERERESsMAl8EBERERERERErTAIfRERERERERMQKk8AHEREREREREbHCJPBBRERERERERKwwCXwQEREREREREStMAh9ERERERERExAqTwAcRERERERERscIk8EFERERERERErDAJfBARERERERERK0wCH0RERERERETECpPABxERERERERGxwiTwQURERERERESsMAl8EBERERERERErTAIfRERERERERMQKk8AHEREREREREbHCJPBBRERERERERKwwvzQ1NSWIiIiIiIiIiFg5EvggIiIiIiIiIlaYBD6IiIiIiIiIiBUmgQ8iIiIiIiIiYoVJ4IOIiIiIiIiIWGES+CAiIiIiIiIiVpgEPoiIiIiIiIiIFSaBDyIiIiIiIiJihUngg4iIiIiIiIhYYRL4ICIiIiIiIiJWmAQ+iIiIiIiIiIgVJoEPIiIiIiIiImKFSeCDiIiIiIiIiFhhEvggIiIiIiIiIlaYBD6IiIiIiIiIiBUmgQ8iIiIiIiIiYoVJ4IOIiIiIiIiIWGES+CAiIiIiIiIiVpgEPoiIiIiIiIiIFSaBDyIiIiIiIiJihUngg4iIiIiIiIhYYRL4ICIiIiIiIiJWmEs28Em/uU3ObvlLOb7m/5IjD/8fWEHqOdVzq+fYdu4RERERERERK90lF/jcunpGejaukLd/c59cP/+KfDY+IlBZ6DnVc6vnWM+1nnPbtYCIiIiIiIhYqS6pwEcb/tr748M3G7xoACodPdd6zgl9EBERERERcSm5pAIf7e1B2LP00HOu5952TSAiIiIiIiJWoksm8NH5XHSIDyxN9Nwzpw8iIiIiIiIuFZdM4KOT+Oq8LrA00XOv14Dt2kBERERERESsNJdM4KPzuDBB89JFz71eA7ZrAxEREREREbHSXDKBjz6uG5Y2eg3Yrg1ERERERETESpPAB5YMBD6IiIiIiIi4VCTwgSUDgQ8iIiIiIiIuFQl8YMlA4IOIiIiIiIhLxXkJfD788EO5cOGC461bt6zrzLUEPrBkA5/JYUm2H5TugTH76xjJzz77TD7++GPra7N3QsbGxlzHba8jIiIiIiIW55wGPn19fbJhwwb5q7/6q4x/8zd/I9u2bZOPPvrI+p65ksAnOte7t8jZ9nPyyV1vQYVQcYHPjW55LvDZyvP5bhk16431bnP//ulBGfTe29+i6zwn3TcC2xt3A4cJ/2/McceOHfKrX/1KJicnra+X5pj0t22VNQ8HzpvxsY0vS3d6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IiIiIiJWoksq8Ll19Ywzjwuhz9JBz7Wecz33tmsCERERERERsRJdUoGPqg1/7e2hQ3x0Xhcmcq489JzqudVzrOeasAcRERERERGXmksu8PHV+Vx0El/t/aGP68bKUc+pnlvm7EFERERERMSl6pINfBARERERERERK1UCH0RERERERETECpPABxERERERERGxwiTwQURERERERESsMAl8EBERERERERErTAIfRERERERERMQKk8AHEREREREREbHCJPBBRERERERERKwwCXwQEREREREREStMAh9ERERERERExAqTwAcRERERERERscIk8EFERERERERErDCLCnw+/fRT+fjjj+X27dvyu9/9DitQPbd6jvVc264BRERERERERFz8Rg58NASwBQRYueo5t10LiIiIiIiIiLi4jRT4EPYsXQl9EBEREREREcvPGQMfHdpjCwJw6cjwLkRERERERMTycsbAh949SC8fRERERERExPJyxsCHCZpRrwHbtYGIiIiIiIiIi9MZAx9bAIBLT9u1gYiIiIiIiIiLUwIfjKTt2kBERERERETExSmBD0bSdm0gIiIiIiIi4uKUwAcjabs2EBEREREREXFxSuCDkbRdG4iIiIiIiIi4OCXwwUjarg1EREREREREXJwS+GAkbdcGIiIiIiIiIi5OCXwwkrZrAxEREREREREXp7EGPu8fq5WVK1fKylU7pXfEvo7rJTn0jFlP1939tuX1mR0ZGpKhoRHra7Px7d1uufacs78+rR9pmYZkxPZamWu7NhARERERERFxcTo3gY+x/sSQdR3HC/vlZ956pQU+b8sefe/Go/K+9fXSnU3g4763Vo5esb9eztquDURERERERERcnM5Z4LNy/SG5ZFnnd78bkd5/WZVdj8CnLLRdG4iIiIiIiIi4OJ2TwGfd2nXm/38m+y9Y1vvwhNSbdVaZdZxePuHAZ6hfTuyul6cf11DoYVn3/B45+k62t9D7XTvk6WfWycP63lWPmX8/LU9v7w4EP0PSf2KP1D/1mKzS/Tz+tNTvPiH9Q/7rWd/v3e+tt0oee3aHHL04UiDwmWGb73fLDlOOdQl9r9nWU6ZMz+yQ7vcD23i/V177Ta23jjmujQ3yWu/72dcXubZrAxEREREREREXp3MS+Ow54YU6/9KbN5/NpdfXm3V+JvtPHJVas05O4DNo3ucFIut/s1P2NzXIei9EqTt2yVln+sDnfTnxnNt76OH1DbJz337ZueVpJ6RZuXaPpALzCl06VucuN9vQfe143mxzVZ3UbdT9BQOfCNucIfAZeWePrPP2Vde4X/bv2yG1/nF1xhP6jIyMOEZdXqy2awMRERERERERF6dzE/icG5ITz2ugUS8nPgyuk5L9PzPLnzkkl66EA58h6f6NBivrzPsDAcXNS3LICWGC2yowpGvwhDRsfFrWm20Gg6bUvp855dpx2tuu18to5c92Sm+g58/IOS+YMWYCn6jbNNqHdI1I6tV6eXptvRwdDCz3y6B1kVm3dHft2iU1NTUyOjqaWaZBzz/+4z/Kyy+/nLNuKdquDURERERERERcnM5R4PM7Gend6fSCWf+62zNHzVkWDnyGuqVB/27M7xU0cnqHs92GLn9oV3Fz+Fw64par9pjbm+b9zjrn72DZXP2gKtjDx254m2pxc/i8LTv1GFbukbetrxenhjtPPfWUPPPMM07oo+q/dRk9fBARERERERGXlnMW+GR68/xsv6Sc10O9fsKBzzt73GFSiXXuMK2gax8OhSvTBD43h6S/96i8tm+H1AfeG3x/qtkdomULdS4dfDr/tQjbVKcLfEaupOTE6/tl529qnWN6bJX7/rgCH1WDHQ151q9f76j/jiPsUW3XBiIiIiIiIiIuTucw8PHn61klO3tH5HcDh2S9eS0zr0848Dm3xw1A1jfI/n06z02+h87N0MPnw17ZsdYNUh5e+7TU63w5r5+Q7n25vXHsEzO7+j13Mq9F3KZaKPDpN/Xgzxf09EadB+g1Odp7yO3RFGPgo/o9feLq2eNruzYQERERERERcXE6p4FPZp6a5zUg0TlvAk/uCgc+g97fTf6cPtNpD3xSr+g+VknDiexTvVS/XH4444c69aH1nEfGN2oIkz2GqNtUrYGPP1Rt/X5JfRRY7h9DzIGPqkFPnGGPars2EBEREREREXFxOreBjwYo/+IOn3JcH5igOG/S5ktyaL35e1Vd7uTGxkude2THvteke8APMbywJDThsRu4PC2HBrLL3BDHLUMmnLmw330kfLA8qh86Gf1jiLzNQuv6xxmem8jr8TQXgc9caLs2EBEREREREXFxOseBj9EPV4w5PWryAp/fZZ+SlaiVPce6pbe3V07sq3UfwZ7zWHUvHFq5StZv3y/7j6WcMGXozQZn6NSqZ/fICfPe3t5uObRd59xZ5+w/G85kA5tVT9XLayd6pfvYHqlNrJO653Ln8Im+TX8Im667Q/bvO+qVt19ec4aErZOG191j6j2xX2ofXyfrdI4jAh9EREREREREjNm5D3wyPXd2Sm8msDFaAh916J3XpO7xQK+glavksbr98nbO492Ng92y4ylvvczE0PoIdC8g8t+75ahc6nXnBwqGM/q49xON67PrrnpadnRdkkuWXkqRt2mOtXv70+58PcHha8Gyqon1sv9svxx1HjdP4IOIiIiIiIiI8Rpr4BOnI0NDMqQGQyKbHw3JyM3Qspsj7ntz5swpoLdu3jbCFrtNy3r+MYUfO18O2q4NRERERERERFycLtrABxeXtmsDERERERERERenBD4YSdu1gYiIiIiIiIiLUwIfjKTt2kBERERERETExSmBD0bSdm0gIiIiIiIi4uKUwAcjabs2EBEREREREXFxSuCDkbRdG4iIiIiIiIi4OJ3zwOfWrVtYAX722WeIuIi03a8RERERERF95yzwsYUGWL7aGpyIuPDa7tuIiIiIiIhFBz62MAArX1tDExHLR9v9HRERERERK9dIgY8tAMClo14DtgYkIpavtvs9IiIiIiJWjtMGPtooGBsbs4YAuHTUayDcWETE8td230dERERExMowJ/CxNQgmJiasIcBcefPmTVxkfvLJJzI5OYmIc6jt/jvfBr8PEBERERGxvM0EPrYf/76l9vKxhQdYXt6+fdvaOEXEhdd2v56twS8IREREREQsX79k+8Fvs1DoYwsJsDIk7EEsX2338WK0fWEgIiIiImL5GDnwUXV4l4YABD2Vq55bPccM40KsLG339Jm0fWkgIiIiImJ5GDnwsTUgZvLOnTuIiDhH2u67M2m7v0+n7YsDEREREREXv9MGPrbGgk1bQwQRERdG233apu2+b9P25YGIiIiIiItba+BjaxgEtTUwCvnpp58iImLM2u63hbTdx4Pavgds2r5EEBERERFxcZoT+NgaAr62RkRQW4Mkqjo3ECIiutruk1G13Z+D2u7vvsHvg5m0faEgIiIiIuLiMRP42H78q7YGg6+tseFra8QgImK82u6/vrb7tq/tfq8GQ52ZtH2pICIiIiLi4vBLth/8qq2BYGtQqLZGSFB94hMiIs5e2z02rO0+rdru67b7v2oLeGza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" + } + }, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![text-descriptives.PNG](attachment:text-descriptives.PNG)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Conclusions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this tutorial, we have explored how to add text descriptives as metadata to records and datasets using the `TextDescriptivesExtractor` integrated on Argilla, what it is really useful for annotation projects." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "argilla-markdown", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.18" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/_source/tutorials_and_integrations/integrations/integrations.md b/docs/_source/tutorials_and_integrations/integrations/integrations.md index ca20df6f8b..6e94838edc 100644 --- a/docs/_source/tutorials_and_integrations/integrations/integrations.md +++ b/docs/_source/tutorials_and_integrations/integrations/integrations.md @@ -20,6 +20,11 @@ Learn how to use Argilla to process large scale documents for LLMs with Unstruct Learn how to use Argilla to monitor NLP models with FastAPI and ArgillaLogHTTPMiddleware. ``` +```{grid-item-card} Text Descriptives as Metadata +:link: add_text_descriptives_as_metadata.html + +Add text descriptives to your metadata to simplify the data annotation and filtering process. +``` ```` ```{toctree} @@ -28,4 +33,5 @@ Learn how to use Argilla to monitor NLP models with FastAPI and ArgillaLogHTTPMi use_argilla_callback_in_langchain process_documents_with_unstructured monitor_endpoints with_fastapi +add_text_descriptives_as_metadata ``` \ No newline at end of file From e7e56910d6083b6a7bd54fe225142c2ef3bf2ab1 Mon Sep 17 00:00:00 2001 From: davidberenstein1957 Date: Wed, 20 Dec 2023 17:54:06 +0100 Subject: [PATCH 09/14] chore: user require_dependencies for textdescriptives import in __init__ --- src/argilla/client/feedback/integrations/textdescriptives.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/argilla/client/feedback/integrations/textdescriptives.py b/src/argilla/client/feedback/integrations/textdescriptives.py index 1757cee558..a39ae12e99 100644 --- a/src/argilla/client/feedback/integrations/textdescriptives.py +++ b/src/argilla/client/feedback/integrations/textdescriptives.py @@ -18,7 +18,6 @@ import numpy as np import pandas as pd -import textdescriptives as td from rich.progress import Progress from argilla.client.feedback.dataset.local.dataset import FeedbackDataset @@ -30,6 +29,7 @@ ) from argilla.client.feedback.schemas.records import FeedbackRecord from argilla.client.feedback.schemas.remote.records import RemoteFeedbackRecord +from argilla.utils.dependency import require_dependencies _LOGGER = logging.getLogger(__name__) _LOGGER.setLevel(logging.INFO) @@ -67,6 +67,7 @@ def __init__( >>> updated_ds = tde.update_dataset(ds) >>> updated_records = tde.update_records(ds.records) """ + require_dependencies("textdescriptives") self.model = model self.metrics = metrics self.fields = fields @@ -99,6 +100,8 @@ def _extract_metrics_for_single_field( Returns: Optional[pd.DataFrame]: A dataframe containing the text descriptives metrics for the field, or None if the field is empty. """ + import textdescriptives as td + # If the field is empty, skip it field_text = [record.fields[field] for record in records if record.fields[field]] if not field_text: From 0123d1dfd8b72294120c6cad78c1ba2c63f8c549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dami=C3=A1n=20Pumar?= Date: Thu, 21 Dec 2023 10:24:11 +0100 Subject: [PATCH 10/14] =?UTF-8?q?=F0=9F=93=9D=20Update=20changelog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cf9aae2d8..783395d965 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,8 +16,11 @@ These are the section headers that we use: ## [Unreleased]() +## [1.21.0](https://github.com/argilla-io/argilla/compare/v1.20.0...v1.21.0) + ### Added +- Added new draft queue for annotation view ([#4334](https://github.com/argilla-io/argilla/pull/4334)) - Added annotation metrics module for the `FeedbackDataset` (`argilla.client.feedback.metrics`). ([#4175](https://github.com/argilla-io/argilla/pull/4175)). - Added strategy to handle and translate errors from the server for `401` HTTP status code` ([#4362](https://github.com/argilla-io/argilla/pull/4362)) - Added integration for `textdescriptives` using `TextDescriptivesExtractor` to configure `metadata_properties` in `FeedbackDataset` and `FeedbackRecord`. ([#4400](https://github.com/argilla-io/argilla/pull/4400)). Contributed by @m-newhauser @@ -28,6 +31,7 @@ These are the section headers that we use: ### Changed +- More productive and simpler shortcuts system ([#4215](https://github.com/argilla-io/argilla/pull/4215)) - Move `ArgillaSingleton`, `init` and `active_client` to a new module `singleton`. ([#4347](https://github.com/argilla-io/argilla/pull/4347)) - Updated `argilla.load` functions to also work with `FeedbackDataset`s. ([#4347](https://github.com/argilla-io/argilla/pull/4347)) - [breaking] Updated `argilla.delete` functions to also work with `FeedbackDataset`s. It now raises an error if the dataset does not exist. ([#4347](https://github.com/argilla-io/argilla/pull/4347)) @@ -38,6 +42,10 @@ These are the section headers that we use: - Fixed error in `TextClassificationSettings.from_dict` method in which the `label_schema` created was a list of `dict` instead of a list of `str`. ([#4347](https://github.com/argilla-io/argilla/pull/4347)) - Fixed total records on pagination component ([#4424](https://github.com/argilla-io/argilla/pull/4424)) +### Removed + +- Removed `draft` auto save for annotation view ([#4334](https://github.com/argilla-io/argilla/pull/4334)) + ## [1.20.0](https://github.com/argilla-io/argilla/compare/v1.19.0...v1.20.0) ### Added From 26fd180a03dbf3eee2d2172fcf9d7b3a3034477a Mon Sep 17 00:00:00 2001 From: Paco Aranda Date: Thu, 21 Dec 2023 12:17:59 +0100 Subject: [PATCH 11/14] tests: Fixing some integration tests --- tests/integration/client/test_dataset.py | 102 +++++++++++------------ 1 file changed, 50 insertions(+), 52 deletions(-) diff --git a/tests/integration/client/test_dataset.py b/tests/integration/client/test_dataset.py index 83ad23cae6..ab7699c46e 100644 --- a/tests/integration/client/test_dataset.py +++ b/tests/integration/client/test_dataset.py @@ -472,6 +472,7 @@ def test_from_dataset_with_non_argilla_format_multilabel(self): "prediction_agent", "annotation", "annotation_agent", + "vectors", "multi_label", "explanation", "id", @@ -504,6 +505,7 @@ def test_from_dataset_with_non_argilla_format(self): "prediction_agent", "annotation", "annotation_agent", + "vectors", "multi_label", "explanation", "id", @@ -654,21 +656,20 @@ def test_prepare_for_training_with_spacy(self): assert isinstance(train, spacy.tokens.DocBin) assert len(train) == 100 - train, test = rb_dataset.prepare_for_training( - framework="spacy", lang=spacy.blank("en"), train_size=0.8, seed=42 - ) + nlp = spacy.blank("en") + train, test = rb_dataset.prepare_for_training(framework="spacy", lang=nlp, train_size=0.8, seed=42) assert isinstance(train, spacy.tokens.DocBin) assert isinstance(test, spacy.tokens.DocBin) assert len(train) == 80 assert len(test) == 20 - assert "id" in train[0].user_data - assert "id" in test[0].user_data - @pytest.mark.skipif( - _HF_HUB_ACCESS_TOKEN is None, - reason="You need a HF Hub access token to test the push_to_hub feature", - ) - def test_prepare_for_training_with_openai(self, request, records): + train_doc = next(train.get_docs(nlp.vocab)) + test_doc = next(test.get_docs(nlp.vocab)) + + assert "id" in train_doc.user_data + assert "id" in test_doc.user_data + + def test_prepare_for_training_with_openai(self): ner_dataset = datasets.load_dataset( # TODO(@frascuchon): Move dataset to the new org "rubrix/gutenberg_spacy-ner", @@ -725,44 +726,43 @@ def test_prepare_for_training(self): train = rb_dataset.prepare_for_training() assert (set(train.column_names)) == set(["id", "tokens", "ner_tags"]) - assert isinstance(train, datasets.DatasetD.Dataset) or isinstance(train, datasets.Dataset) + assert isinstance(train, datasets.Dataset) or isinstance(train, datasets.Dataset) assert "ner_tags" in train.column_names assert len(train) == 100 - assert train.features["ner_tags"] == [ - datasets.ClassLabel( - names=[ - "O", - "B-CARDINAL", - "I-CARDINAL", - "B-DATE", - "I-DATE", - "B-FAC", - "I-FAC", - "B-GPE", - "I-GPE", - "B-LANGUAGE", - "I-LANGUAGE", - "B-LOC", - "I-LOC", - "B-NORP", - "I-NORP", - "B-ORDINAL", - "I-ORDINAL", - "B-ORG", - "I-ORG", - "B-PERSON", - "I-PERSON", - "B-PRODUCT", - "I-PRODUCT", - "B-QUANTITY", - "I-QUANTITY", - "B-TIME", - "I-TIME", - "B-WORK_OF_ART", - "I-WORK_OF_ART", - ] - ) - ] + + assert train.features["ner_tags"].feature == datasets.ClassLabel( + names=[ + "O", + "B-CARDINAL", + "I-CARDINAL", + "B-DATE", + "I-DATE", + "B-FAC", + "I-FAC", + "B-GPE", + "I-GPE", + "B-LANGUAGE", + "I-LANGUAGE", + "B-LOC", + "I-LOC", + "B-NORP", + "I-NORP", + "B-ORDINAL", + "I-ORDINAL", + "B-ORG", + "I-ORG", + "B-PERSON", + "I-PERSON", + "B-PRODUCT", + "I-PRODUCT", + "B-QUANTITY", + "I-QUANTITY", + "B-TIME", + "I-TIME", + "B-WORK_OF_ART", + "I-WORK_OF_ART", + ] + ) _push_to_hub_with_retries( train, @@ -792,6 +792,7 @@ def test_from_dataset_with_non_argilla_format(self): "prediction_agent", "annotation", "annotation_agent", + "vectors", "id", "metadata", "status", @@ -904,11 +905,7 @@ def test_prepare_for_training_with_spacy(self): with pytest.raises(NotImplementedError): ds.prepare_for_training("spacy", lang=spacy.blank("en"), train_size=1) - @pytest.mark.skipif( - _HF_HUB_ACCESS_TOKEN is None, - reason="You need a HF Hub access token to test the push_to_hub feature", - ) - def test_prepare_for_training_with_openai(self, request, records): + def test_prepare_for_training_with_openai(self): ds = DatasetForText2Text( [Text2TextRecord(text="Michael is a professor at Harvard but", annotation=" he used to work at MIT")] ) @@ -918,7 +915,7 @@ def test_prepare_for_training_with_openai(self, request, records): assert isinstance(jsonl, list) assert isinstance(jsonl[0], dict) assert "prompt" in jsonl[0] and "completion" in jsonl[0] and "id" in jsonl[0] - assert jsonl[0]["prompt"] == "Michael is a professor at Harvard but" + assert jsonl[0]["prompt"] == "Michael is a professor at Harvard but\n\n###\n\n" def test_prepare_for_training_with_spark_nlp(self): ds = DatasetForText2Text([Text2TextRecord(text="mock", annotation="mock"), Text2TextRecord(text="mock")] * 10) @@ -966,6 +963,7 @@ def test_from_dataset_with_non_argilla_format(self): "prediction_agent", "annotation", "annotation_agent", + "vectors", "id", "metadata", "status", From 512fe5ecd93780a2026d4d89a4add421a0b897f1 Mon Sep 17 00:00:00 2001 From: davidberenstein1957 Date: Thu, 21 Dec 2023 14:11:56 +0100 Subject: [PATCH 12/14] tests: add random seed to allow for reproducability tests unification --- tests/integration/client/conftest.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/integration/client/conftest.py b/tests/integration/client/conftest.py index aa0e2dd426..0bed7378d4 100644 --- a/tests/integration/client/conftest.py +++ b/tests/integration/client/conftest.py @@ -13,6 +13,7 @@ # limitations under the License. import datetime +import random from typing import TYPE_CHECKING, Generator, List import pytest @@ -66,6 +67,8 @@ AllowedQuestionTypes, ) +random.seed(42) + @pytest.fixture def gutenberg_spacy_ner(argilla_user: User) -> Generator[str, None, None]: From 370d241cbe8459cedc997c1fa1be7f55ac8daa88 Mon Sep 17 00:00:00 2001 From: davidberenstein1957 Date: Thu, 21 Dec 2023 14:18:57 +0100 Subject: [PATCH 13/14] tests: resolved test_get_unified_responses_and_suggestions pdb issue --- tests/integration/client/feedback/metrics/test_utils.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/integration/client/feedback/metrics/test_utils.py b/tests/integration/client/feedback/metrics/test_utils.py index 59d66f7665..cfbb982177 100644 --- a/tests/integration/client/feedback/metrics/test_utils.py +++ b/tests/integration/client/feedback/metrics/test_utils.py @@ -123,8 +123,5 @@ def test_get_unified_responses_and_suggestions( else: unified_dataset = dataset.compute_unified_responses(question, strategy) unified_responses, suggestions = get_unified_responses_and_suggestions(unified_dataset, question) - import pdb - - pdb.set_trace() assert len(unified_responses) == len(suggestions) == len(expected_unified_responses) assert all([isinstance(response, value_type) for response in unified_responses]) From b1cb46cc45e485c62994fc444689c5af0827e1d8 Mon Sep 17 00:00:00 2001 From: Paco Aranda Date: Thu, 21 Dec 2023 14:31:38 +0100 Subject: [PATCH 14/14] tests: Skipping test with sqlite --- .../client/feedback/dataset/remote/test_dataset.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/integration/client/feedback/dataset/remote/test_dataset.py b/tests/integration/client/feedback/dataset/remote/test_dataset.py index 70825ff52e..df06a84afa 100644 --- a/tests/integration/client/feedback/dataset/remote/test_dataset.py +++ b/tests/integration/client/feedback/dataset/remote/test_dataset.py @@ -51,6 +51,7 @@ from argilla.server.models import User as ServerUser from sqlalchemy.ext.asyncio import AsyncSession +from argilla.server.settings import settings from tests.factories import ( DatasetFactory, RecordFactory, @@ -179,13 +180,19 @@ async def test_update_records_with_suggestions( assert suggestion.question_name == "question" assert suggestion.value == f"Hello world! for {record.fields['text']}" + @pytest.mark.skipif( + reason="For some reason this tests is failing using sqlite db. Skipping until we find the reason", + condition=settings.database_url.startswith("sqlite"), + ) async def test_update_records_with_empty_list_of_suggestions( self, owner: "User", test_dataset_with_metadata_properties: FeedbackDataset ): argilla.client.singleton.init(api_key=owner.api_key) ws = rg.Workspace.create(name="test-workspace") - test_dataset_with_metadata_properties.add_records( + remote = test_dataset_with_metadata_properties.push_to_argilla(name="test_dataset", workspace=ws) + + remote.add_records( [ FeedbackRecord( fields={"text": "Hello world!"}, suggestions=[{"question_name": "question", "value": "test"}] @@ -196,8 +203,6 @@ async def test_update_records_with_empty_list_of_suggestions( ] ) - remote = test_dataset_with_metadata_properties.push_to_argilla(name="test_dataset", workspace=ws) - records = [] for record in remote: record.suggestions = [] @@ -205,8 +210,9 @@ async def test_update_records_with_empty_list_of_suggestions( remote.update_records(records) + assert len(remote.records) == 2 for record in remote: - assert len(record.suggestions) == 0 + assert len(record.suggestions) == 0, record.suggestions @pytest.mark.parametrize( "metadata", [("terms-metadata", "wrong-label"), ("integer-metadata", "wrong-integer"), ("float-metadata", 11.5)]