From c62f5bd2dc283b24b1d4377392c7ad0551bd461a Mon Sep 17 00:00:00 2001
From: lxr-tech <1838593642@qq.com>
Date: Mon, 30 May 2022 22:48:28 +0800
Subject: [PATCH] update example-12 lxr 220530
---
tutorials/fastnlp_tutorial_1.ipynb | 2 +-
tutorials/fastnlp_tutorial_3.ipynb | 2 +-
tutorials/fastnlp_tutorial_e1.ipynb | 990 +++++++++++--------------
tutorials/fastnlp_tutorial_e2.ipynb | 300 +++++---
tutorials/figures/E1-fig-glue-benchmark.png | Bin 0 -> 158817 bytes
tutorials/figures/E2-fig-p-tuning-v2-model.png | Bin 0 -> 50517 bytes
6 files changed, 599 insertions(+), 695 deletions(-)
create mode 100644 tutorials/figures/E1-fig-glue-benchmark.png
create mode 100644 tutorials/figures/E2-fig-p-tuning-v2-model.png
diff --git a/tutorials/fastnlp_tutorial_1.ipynb b/tutorials/fastnlp_tutorial_1.ipynb
index 09e8821d..db77e6c3 100644
--- a/tutorials/fastnlp_tutorial_1.ipynb
+++ b/tutorials/fastnlp_tutorial_1.ipynb
@@ -1325,7 +1325,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.4"
+ "version": "3.7.13"
}
},
"nbformat": 4,
diff --git a/tutorials/fastnlp_tutorial_3.ipynb b/tutorials/fastnlp_tutorial_3.ipynb
index 8c3c935e..353e4645 100644
--- a/tutorials/fastnlp_tutorial_3.ipynb
+++ b/tutorials/fastnlp_tutorial_3.ipynb
@@ -288,7 +288,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.4"
+ "version": "3.7.13"
},
"pycharm": {
"stem_cell": {
diff --git a/tutorials/fastnlp_tutorial_e1.ipynb b/tutorials/fastnlp_tutorial_e1.ipynb
index 628dd7ae..6ec04cb4 100644
--- a/tutorials/fastnlp_tutorial_e1.ipynb
+++ b/tutorials/fastnlp_tutorial_e1.ipynb
@@ -4,7 +4,22 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "# E1. 使用 DistilBert 完成 SST2 分类"
+ " 从这篇开始,我们将开启**`fastNLP v0.8 tutorial`的`example`系列**,在接下来的\n",
+ "\n",
+ " 每篇`tutorial`里,我们将会介绍`fastNLP v0.8`在一些自然语言处理任务上的应用"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# E1. 使用 Bert + fine-tuning 完成 SST2 分类\n",
+ "\n",
+ " 1 基础介绍:`GLUE`通用语言理解评估、`SST2`文本情感二分类数据集 \n",
+ "\n",
+ " 2 准备工作:加载`tokenizer`、预处理`dataset`、`dataloader`使用\n",
+ "\n",
+ " 3 模型训练:加载`distilbert-base`、`fastNLP`参数匹配、`fine-tuning`"
]
},
{
@@ -48,22 +63,64 @@
"\n",
"import fastNLP\n",
"from fastNLP import Trainer\n",
- "from fastNLP.core.utils.utils import dataclass_to_dict\n",
"from fastNLP.core.metrics import Accuracy\n",
"\n",
"print(transformers.__version__)"
]
},
{
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 1. 基础介绍:GLUE 通用语言理解评估、SST2 文本情感二分类数据集\n",
+ "\n",
+ " 本示例使用`GLUE`评估基准中的`SST2`数据集,通过`fine-tuning`方式\n",
+ "\n",
+ " 调整`distilbert-bert`分类模型,以下首先简单介绍下`GLUE`和`SST2`\n",
+ "\n",
+ "**`GLUE`**,**全称`General Language Understanding Evaluation`**,**通用语言理解评估**,\n",
+ "\n",
+ " 包含9个数据集,各语料的语言均为英语,涉及多个自然语言理解`NLU`任务,包括\n",
+ "\n",
+ " **`CoLA`**,文本分类任务,预测单句语法正误分类;**`SST2`**,文本分类任务,预测单句情感二分类\n",
+ "\n",
+ " **`MRPC`**,句对分类任务,预测句对语义一致性;**`STSB`**,相似度打分任务,预测句对语义相似度回归\n",
+ "\n",
+ " **`QQP`**,句对分类任务,预测问题对语义一致性;**`MNLI`**,文本推理任务,预测句对蕴含/矛盾/中立预测\n",
+ "\n",
+ " **`QNLI`/`RTE`/`WNLI`**,文本推理,预测是否蕴含二分类(其中,`QNLI`从`SQuAD`转化而来\n",
+ "\n",
+ " 诸如`BERT`、`T5`等经典模型都会在此基准上验证效果,更多参考[GLUE论文](https://arxiv.org/pdf/1804.07461v3.pdf)\n",
+ "\n",
+ " 此处,我们使用`SST2`来训练`bert`,实现文本分类,其他任务描述见下图"
+ ]
+ },
+ {
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
- "GLUE_TASKS = [\"cola\", \"mnli\", \"mnli-mm\", \"mrpc\", \"qnli\", \"qqp\", \"rte\", \"sst2\", \"stsb\", \"wnli\"]\n",
+ "GLUE_TASKS = ['cola', 'mnli', 'mrpc', 'qnli', 'qqp', 'rte', 'sst2', 'stsb', 'wnli']\n",
+ "\n",
+ "task = 'sst2'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "\n",
+ "**`SST`**,**全称`Stanford Sentiment Treebank`**,**斯坦福情感树库**,**单句情感分类**数据集\n",
+ "\n",
+ " 包含电影评论语句和对应的情感极性,1 对应`positive` 正面情感,0 对应`negative` 负面情感\n",
+ "\n",
+ " 数据集包括三部分:训练集 67350 条,开发集 873 条,测试集 1821 条,更多参考[下载链接](https://gluebenchmark.com/tasks)\n",
"\n",
- "task = \"sst2\"\n",
- "model_checkpoint = \"distilbert-base-uncased\""
+ "对应到代码上,此处使用`datasets`模块中的`load_dataset`函数,指定`SST2`数据集,自动加载\n",
+ "\n",
+ " 首次下载后会保存至`~/.cache/huggingface/modules/datasets_modules/datasets/glue/`目录下"
]
},
{
@@ -84,7 +141,7 @@
{
"data": {
"application/vnd.jupyter.widget-view+json": {
- "model_id": "253d79d7a67e4dc88338448b5bcb3fb9",
+ "model_id": "adc9449171454f658285f220b70126e1",
"version_major": 2,
"version_minor": 0
},
@@ -97,9 +154,16 @@
}
],
"source": [
- "from datasets import load_dataset, load_metric\n",
+ "from datasets import load_dataset\n",
"\n",
- "dataset = load_dataset(\"glue\", \"mnli\" if task == \"mnli-mm\" else task)"
+ "dataset = load_dataset('glue', task)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ " 加载之后,根据`GLUE`中`SST2`数据集的格式,尝试打印部分数据,检查加载结果"
]
},
{
@@ -111,62 +175,89 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "{'input_ids': [101, 7592, 1010, 2023, 2028, 6251, 999, 102, 1998, 2023, 6251, 3632, 2007, 2009, 1012, 102], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]}\n"
+ "Sentence: hide new secretions from the parental units \n"
]
}
],
"source": [
- "tokenizer = AutoTokenizer.from_pretrained(model_checkpoint, use_fast=True)\n",
+ "task_to_keys = {\n",
+ " 'cola': ('sentence', None),\n",
+ " 'mnli': ('premise', 'hypothesis'),\n",
+ " 'mnli': ('premise', 'hypothesis'),\n",
+ " 'mrpc': ('sentence1', 'sentence2'),\n",
+ " 'qnli': ('question', 'sentence'),\n",
+ " 'qqp': ('question1', 'question2'),\n",
+ " 'rte': ('sentence1', 'sentence2'),\n",
+ " 'sst2': ('sentence', None),\n",
+ " 'stsb': ('sentence1', 'sentence2'),\n",
+ " 'wnli': ('sentence1', 'sentence2'),\n",
+ "}\n",
"\n",
- "print(tokenizer(\"Hello, this one sentence!\", \"And this sentence goes with it.\"))"
+ "sentence1_key, sentence2_key = task_to_keys[task]\n",
+ "\n",
+ "if sentence2_key is None:\n",
+ " print(f\"Sentence: {dataset['train'][0][sentence1_key]}\")\n",
+ "else:\n",
+ " print(f\"Sentence 1: {dataset['train'][0][sentence1_key]}\")\n",
+ " print(f\"Sentence 2: {dataset['train'][0][sentence2_key]}\")"
]
},
{
- "cell_type": "code",
- "execution_count": 5,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
- "task_to_keys = {\n",
- " \"cola\": (\"sentence\", None),\n",
- " \"mnli\": (\"premise\", \"hypothesis\"),\n",
- " \"mnli-mm\": (\"premise\", \"hypothesis\"),\n",
- " \"mrpc\": (\"sentence1\", \"sentence2\"),\n",
- " \"qnli\": (\"question\", \"sentence\"),\n",
- " \"qqp\": (\"question1\", \"question2\"),\n",
- " \"rte\": (\"sentence1\", \"sentence2\"),\n",
- " \"sst2\": (\"sentence\", None),\n",
- " \"stsb\": (\"sentence1\", \"sentence2\"),\n",
- " \"wnli\": (\"sentence1\", \"sentence2\"),\n",
- "}\n",
+ "### 2. 准备工作:加载 tokenizer、预处理 dataset、dataloader 使用\n",
+ "\n",
+ " 接下来进入模型训练的准备工作,分别需要使用`tokenizer`模块对数据集进行分词与标注\n",
+ "\n",
+ " 定义`SeqClsDataset`对应`dataloader`模块用来实现数据集在训练/测试时的加载\n",
+ "\n",
+ "此处的`tokenizer`和`SequenceClassificationModel`都是基于**`distilbert-base-uncased`模型**\n",
"\n",
- "sentence1_key, sentence2_key = task_to_keys[task]"
+ " 即使用较小的、不区分大小写的数据集,**对`bert-base`进行知识蒸馏后的版本**,结构上\n",
+ "\n",
+ " 模型包含1个编码层、6个自注意力层,详解见本篇末尾,更多细节请参考[DistilBert论文](https://arxiv.org/pdf/1910.01108.pdf)\n",
+ "\n",
+ "首先,通过从`transformers`库中导入`AutoTokenizer`模块,使用`from_pretrained`函数初始化\n",
+ "\n",
+ " 此处的`use_fast`表示是否使用`tokenizer`的快速版本;尝试序列化示例数据,检查加载结果\n",
+ "\n",
+ " 需要注意的是,处理后返回的两个键值,`'input_ids'`表示原始文本对应的词素编号序列\n",
+ "\n",
+ " `'attention_mask'`表示自注意力运算时的掩模(标上`0`的部分对应`padding`的内容"
]
},
{
"cell_type": "code",
- "execution_count": 6,
+ "execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "Sentence: hide new secretions from the parental units \n"
+ "{'input_ids': [101, 7592, 1010, 2023, 2028, 6251, 999, 102, 1998, 2023, 6251, 3632, 2007, 2009, 1012, 102], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]}\n"
]
}
],
"source": [
- "if sentence2_key is None:\n",
- " print(f\"Sentence: {dataset['train'][0][sentence1_key]}\")\n",
- "else:\n",
- " print(f\"Sentence 1: {dataset['train'][0][sentence1_key]}\")\n",
- " print(f\"Sentence 2: {dataset['train'][0][sentence2_key]}\")"
+ "model_checkpoint = 'distilbert-base-uncased'\n",
+ "\n",
+ "tokenizer = AutoTokenizer.from_pretrained(model_checkpoint, use_fast=True)\n",
+ "\n",
+ "print(tokenizer(\"Hello, this one sentence!\", \"And this sentence goes with it.\"))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "接着,定义预处理函数,**通过`dataset.map`方法**,**将数据集中的文本**,**替换为词素编号序列**"
]
},
{
"cell_type": "code",
- "execution_count": 7,
+ "execution_count": 6,
"metadata": {},
"outputs": [
{
@@ -189,66 +280,27 @@
]
},
{
- "cell_type": "code",
- "execution_count": 8,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
- "class ClassModel(nn.Module):\n",
- " def __init__(self, num_labels, model_checkpoint):\n",
- " nn.Module.__init__(self)\n",
- " self.num_labels = num_labels\n",
- " self.back_bone = AutoModelForSequenceClassification.from_pretrained(model_checkpoint, \n",
- " num_labels=num_labels)\n",
- " self.loss_fn = nn.CrossEntropyLoss()\n",
- "\n",
- " def forward(self, input_ids, attention_mask):\n",
- " return self.back_bone(input_ids, attention_mask)\n",
+ "然后,通过继承`torch`中的`Dataset`类,定义`SeqClsDataset`类,需要注意的是\n",
"\n",
- " def train_step(self, input_ids, attention_mask, labels):\n",
- " pred = self(input_ids, attention_mask).logits\n",
- " return {\"loss\": self.loss_fn(pred, labels)}\n",
- "\n",
- " def evaluate_step(self, input_ids, attention_mask, labels):\n",
- " pred = self(input_ids, attention_mask).logits\n",
- " pred = torch.max(pred, dim=-1)[1]\n",
- " return {\"pred\": pred, \"target\": labels}"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {},
- "outputs": [
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "Some weights of the model checkpoint at distilbert-base-uncased were not used when initializing DistilBertForSequenceClassification: ['vocab_projector.weight', 'vocab_layer_norm.bias', 'vocab_transform.bias', 'vocab_projector.bias', 'vocab_layer_norm.weight', 'vocab_transform.weight']\n",
- "- This IS expected if you are initializing DistilBertForSequenceClassification from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n",
- "- This IS NOT expected if you are initializing DistilBertForSequenceClassification from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n",
- "Some weights of DistilBertForSequenceClassification were not initialized from the model checkpoint at distilbert-base-uncased and are newly initialized: ['pre_classifier.weight', 'classifier.weight', 'classifier.bias', 'pre_classifier.bias']\n",
- "You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n"
- ]
- }
- ],
- "source": [
- "num_labels = 3 if task.startswith(\"mnli\") else 1 if task == \"stsb\" else 2\n",
+ " 其中,**`__getitem__`函数各返回值引用的键值**,**必须和原始数据集中的属性对应**\n",
"\n",
- "model = ClassModel(num_labels=num_labels, model_checkpoint=model_checkpoint)\n",
+ " 例如,`'label'`是`SST2`数据集中原有的内容(包括`'sentence'`和`'label'`\n",
"\n",
- "optimizers = AdamW(params=model.parameters(), lr=5e-5)"
+ " `'input_ids'`和`'attention_mask'`则是`tokenizer`处理后添加的字段"
]
},
{
"cell_type": "code",
- "execution_count": 10,
+ "execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
- "class TestDistilBertDataset(Dataset):\n",
+ "class SeqClsDataset(Dataset):\n",
" def __init__(self, dataset):\n",
- " super(TestDistilBertDataset, self).__init__()\n",
+ " Dataset.__init__(self)\n",
" self.dataset = dataset\n",
"\n",
" def __len__(self):\n",
@@ -256,16 +308,27 @@
"\n",
" def __getitem__(self, item):\n",
" item = self.dataset[item]\n",
- " return item[\"input_ids\"], item[\"attention_mask\"], [item[\"label\"]] "
+ " return item['input_ids'], item['attention_mask'], [item['label']] "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "再然后,**定义校对函数`collate_fn`对齐同个`batch`内的每笔数据**,需要注意的是该函数的\n",
+ "\n",
+ " **返回值必须是字典**,**键值必须同待训练模型的`train_step`和`evaluate_step`函数的参数**\n",
+ "\n",
+ " **相对应**;这也就是在`tutorial-0`中便被强调的,`fastNLP v0.8`的第一条**参数匹配**机制"
]
},
{
"cell_type": "code",
- "execution_count": 11,
+ "execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
- "def test_bert_collate_fn(batch):\n",
+ "def collate_fn(batch):\n",
" input_ids, atten_mask, labels = [], [], []\n",
" max_length = [0] * 3\n",
" for each_item in batch:\n",
@@ -280,35 +343,136 @@
" each = (input_ids, atten_mask, labels)[i]\n",
" for item in each:\n",
" item.extend([0] * (max_length[i] - len(item)))\n",
- " return {\"input_ids\": torch.cat([torch.tensor([item]) for item in input_ids], dim=0),\n",
- " \"attention_mask\": torch.cat([torch.tensor([item]) for item in atten_mask], dim=0),\n",
- " \"labels\": torch.cat([torch.tensor(item) for item in labels], dim=0)}"
+ " return {'input_ids': torch.cat([torch.tensor([item]) for item in input_ids], dim=0),\n",
+ " 'attention_mask': torch.cat([torch.tensor([item]) for item in atten_mask], dim=0),\n",
+ " 'labels': torch.cat([torch.tensor(item) for item in labels], dim=0)}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "最后,分别对`tokenizer`处理过的训练集数据、验证集数据,进行预处理和批量划分"
]
},
{
"cell_type": "code",
- "execution_count": 12,
+ "execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
- "dataset_train = TestDistilBertDataset(encoded_dataset[\"train\"])\n",
+ "dataset_train = SeqClsDataset(encoded_dataset['train'])\n",
"dataloader_train = DataLoader(dataset=dataset_train, \n",
- " batch_size=32, shuffle=True, collate_fn=test_bert_collate_fn)\n",
- "dataset_valid = TestDistilBertDataset(encoded_dataset[\"validation\"])\n",
+ " batch_size=32, shuffle=True, collate_fn=collate_fn)\n",
+ "dataset_valid = SeqClsDataset(encoded_dataset['validation'])\n",
"dataloader_valid = DataLoader(dataset=dataset_valid, \n",
- " batch_size=32, shuffle=False, collate_fn=test_bert_collate_fn)"
+ " batch_size=32, shuffle=False, collate_fn=collate_fn)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 3. 模型训练:加载 distilbert-base、fastNLP 参数匹配、fine-tuning\n",
+ "\n",
+ " 最后就是模型训练的,分别需要使用`distilbert-base-uncased`搭建分类模型\n",
+ "\n",
+ " 初始化优化器`optimizer`、训练模块`trainer`,通过`run`函数完成训练\n",
+ "\n",
+ "此处使用的`nn.Module`模块搭建模型,与`tokenizer`类似,通过从`transformers`库中\n",
+ "\n",
+ " 导入`AutoModelForSequenceClassification`模块,基于`distilbert-base-uncased`模型初始\n",
+ "\n",
+ "需要注意的是**`AutoModelForSequenceClassification`模块的输入参数和输出结构**\n",
+ "\n",
+ " 一方面,可以**通过输入标签值`labels`**,**使用模块内的损失函数计算损失`loss`**\n",
+ "\n",
+ " 并且可以选择输入是词素编号序列`input_ids`,还是词素嵌入序列`inputs_embeds`\n",
+ "\n",
+ " 另方面,该模块不会直接输出预测结果,而是会**输出各预测分类上的几率`logits`**\n",
+ "\n",
+ " 基于上述描述,此处完成了中`train_step`和`evaluate_step`函数的定义\n",
+ "\n",
+ " 同样需要注意,函数的返回值体现了`fastNLP v0.8`的第二条**参数匹配**机制"
]
},
{
"cell_type": "code",
- "execution_count": 13,
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class SeqClsModel(nn.Module):\n",
+ " def __init__(self, num_labels, model_checkpoint):\n",
+ " nn.Module.__init__(self)\n",
+ " self.num_labels = num_labels\n",
+ " self.back_bone = AutoModelForSequenceClassification.from_pretrained(model_checkpoint, \n",
+ " num_labels=num_labels)\n",
+ "\n",
+ " def forward(self, input_ids, attention_mask, labels=None):\n",
+ " output = self.back_bone(input_ids=input_ids, \n",
+ " attention_mask=attention_mask, labels=labels)\n",
+ " return output\n",
+ "\n",
+ " def train_step(self, input_ids, attention_mask, labels):\n",
+ " loss = self(input_ids, attention_mask, labels).loss\n",
+ " return {'loss': loss}\n",
+ "\n",
+ " def evaluate_step(self, input_ids, attention_mask, labels):\n",
+ " pred = self(input_ids, attention_mask, labels).logits\n",
+ " pred = torch.max(pred, dim=-1)[1]\n",
+ " return {'pred': pred, 'target': labels}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "接着,通过确定分类数量初始化模型实例,同时调用`torch.optim.AdamW`模块初始化优化器"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "Some weights of the model checkpoint at distilbert-base-uncased were not used when initializing DistilBertForSequenceClassification: ['vocab_layer_norm.weight', 'vocab_layer_norm.bias', 'vocab_projector.weight', 'vocab_transform.bias', 'vocab_projector.bias', 'vocab_transform.weight']\n",
+ "- This IS expected if you are initializing DistilBertForSequenceClassification from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n",
+ "- This IS NOT expected if you are initializing DistilBertForSequenceClassification from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n",
+ "Some weights of DistilBertForSequenceClassification were not initialized from the model checkpoint at distilbert-base-uncased and are newly initialized: ['pre_classifier.bias', 'classifier.weight', 'classifier.bias', 'pre_classifier.weight']\n",
+ "You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n"
+ ]
+ }
+ ],
+ "source": [
+ "num_labels = 3 if task == 'mnli' else 1 if task == 'stsb' else 2\n",
+ "\n",
+ "model = SeqClsModel(num_labels=num_labels, model_checkpoint=model_checkpoint)\n",
+ "\n",
+ "optimizers = AdamW(params=model.parameters(), lr=5e-5)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "然后,使用之前完成的`dataloader_train`和`dataloader_valid`,定义训练模块`trainer`"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"trainer = Trainer(\n",
" model=model,\n",
" driver='torch',\n",
- " device='cuda',\n",
+ " device=1, # 'cuda'\n",
" n_epochs=10,\n",
" optimizers=optimizers,\n",
" train_dataloader=dataloader_train,\n",
@@ -318,42 +482,35 @@
]
},
{
- "cell_type": "code",
- "execution_count": 14,
+ "cell_type": "markdown",
"metadata": {},
- "outputs": [],
"source": [
- "# help(model.back_bone.forward)"
+ "最后,使用`trainer.run`方法,训练模型,`n_epochs`参数中已经指定需要迭代`10`轮\n",
+ "\n",
+ " `num_eval_batch_per_dl`参数则指定每次只对验证集中的`10`个`batch`进行评估"
]
},
{
"cell_type": "code",
- "execution_count": 15,
+ "execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
- "
[21:00:11] INFO Running evaluator sanity check for 2 batches. trainer.py:592\n", - "\n" + "\n" ], - "text/plain": [ - "\u001b[2;36m[21:00:11]\u001b[0m\u001b[2;36m \u001b[0m\u001b[34mINFO \u001b[0m Running evaluator sanity check for \u001b[1;36m2\u001b[0m batches. \u001b]8;id=22992;file://../fastNLP/core/controllers/trainer.py\u001b\\\u001b[2mtrainer.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=669026;file://../fastNLP/core/controllers/trainer.py#592\u001b\\\u001b[2m592\u001b[0m\u001b]8;;\u001b\\\n" - ] + "text/plain": [] }, "metadata": {}, "output_type": "display_data" }, { "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Output()" - ] + "text/html": [ + "\n" + ], + "text/plain": [] }, "metadata": {}, "output_type": "display_data" @@ -370,16 +527,23 @@ }, "metadata": {}, "output_type": "display_data" - }, + } + ], + "source": [ + "trainer.run(num_eval_batch_per_dl=10)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ { "data": { "text/html": [ - "
----------------------------- Eval. results on Epoch:1, Batch:0 -----------------------------\n", - "\n" + "\n" ], - "text/plain": [ - "----------------------------- Eval. results on Epoch:\u001b[1;36m1\u001b[0m, Batch:\u001b[1;36m0\u001b[0m -----------------------------\n" - ] + "text/plain": [] }, "metadata": {}, "output_type": "display_data" @@ -387,473 +551,155 @@ { "data": { "text/html": [ - "
{\n", - " \"acc#acc\": 0.871875,\n", - " \"total#acc\": 320.0,\n", - " \"correct#acc\": 279.0\n", - "}\n", - "\n" + "\n" ], - "text/plain": [ - "\u001b[1m{\u001b[0m\n", - " \u001b[1;34m\"acc#acc\"\u001b[0m: \u001b[1;36m0.871875\u001b[0m,\n", - " \u001b[1;34m\"total#acc\"\u001b[0m: \u001b[1;36m320.0\u001b[0m,\n", - " \u001b[1;34m\"correct#acc\"\u001b[0m: \u001b[1;36m279.0\u001b[0m\n", - "\u001b[1m}\u001b[0m\n" - ] + "text/plain": [] }, "metadata": {}, "output_type": "display_data" }, { "data": { - "text/html": [ - "
\n", - "\n" - ], - "text/plain": [ - "\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
----------------------------- Eval. results on Epoch:2, Batch:0 -----------------------------\n", - "\n" - ], - "text/plain": [ - "----------------------------- Eval. results on Epoch:\u001b[1;36m2\u001b[0m, Batch:\u001b[1;36m0\u001b[0m -----------------------------\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
{\n", - " \"acc#acc\": 0.878125,\n", - " \"total#acc\": 320.0,\n", - " \"correct#acc\": 281.0\n", - "}\n", - "\n" - ], - "text/plain": [ - "\u001b[1m{\u001b[0m\n", - " \u001b[1;34m\"acc#acc\"\u001b[0m: \u001b[1;36m0.878125\u001b[0m,\n", - " \u001b[1;34m\"total#acc\"\u001b[0m: \u001b[1;36m320.0\u001b[0m,\n", - " \u001b[1;34m\"correct#acc\"\u001b[0m: \u001b[1;36m281.0\u001b[0m\n", - "\u001b[1m}\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "\n" - ], - "text/plain": [ - "\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
----------------------------- Eval. results on Epoch:3, Batch:0 -----------------------------\n", - "\n" - ], - "text/plain": [ - "----------------------------- Eval. results on Epoch:\u001b[1;36m3\u001b[0m, Batch:\u001b[1;36m0\u001b[0m -----------------------------\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
{\n", - " \"acc#acc\": 0.871875,\n", - " \"total#acc\": 320.0,\n", - " \"correct#acc\": 279.0\n", - "}\n", - "\n" - ], - "text/plain": [ - "\u001b[1m{\u001b[0m\n", - " \u001b[1;34m\"acc#acc\"\u001b[0m: \u001b[1;36m0.871875\u001b[0m,\n", - " \u001b[1;34m\"total#acc\"\u001b[0m: \u001b[1;36m320.0\u001b[0m,\n", - " \u001b[1;34m\"correct#acc\"\u001b[0m: \u001b[1;36m279.0\u001b[0m\n", - "\u001b[1m}\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "\n" - ], - "text/plain": [ - "\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
----------------------------- Eval. results on Epoch:4, Batch:0 -----------------------------\n", - "\n" - ], - "text/plain": [ - "----------------------------- Eval. results on Epoch:\u001b[1;36m4\u001b[0m, Batch:\u001b[1;36m0\u001b[0m -----------------------------\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
{\n", - " \"acc#acc\": 0.903125,\n", - " \"total#acc\": 320.0,\n", - " \"correct#acc\": 289.0\n", - "}\n", - "\n" - ], "text/plain": [ - "\u001b[1m{\u001b[0m\n", - " \u001b[1;34m\"acc#acc\"\u001b[0m: \u001b[1;36m0.903125\u001b[0m,\n", - " \u001b[1;34m\"total#acc\"\u001b[0m: \u001b[1;36m320.0\u001b[0m,\n", - " \u001b[1;34m\"correct#acc\"\u001b[0m: \u001b[1;36m289.0\u001b[0m\n", - "\u001b[1m}\u001b[0m\n" + "{'acc#acc': 0.87156, 'total#acc': 872.0, 'correct#acc': 760.0}" ] }, + "execution_count": 14, "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "\n" - ], - "text/plain": [ - "\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
----------------------------- Eval. results on Epoch:5, Batch:0 -----------------------------\n", - "\n" - ], - "text/plain": [ - "----------------------------- Eval. results on Epoch:\u001b[1;36m5\u001b[0m, Batch:\u001b[1;36m0\u001b[0m -----------------------------\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
{\n", - " \"acc#acc\": 0.871875,\n", - " \"total#acc\": 320.0,\n", - " \"correct#acc\": 279.0\n", - "}\n", - "\n" - ], - "text/plain": [ - "\u001b[1m{\u001b[0m\n", - " \u001b[1;34m\"acc#acc\"\u001b[0m: \u001b[1;36m0.871875\u001b[0m,\n", - " \u001b[1;34m\"total#acc\"\u001b[0m: \u001b[1;36m320.0\u001b[0m,\n", - " \u001b[1;34m\"correct#acc\"\u001b[0m: \u001b[1;36m279.0\u001b[0m\n", - "\u001b[1m}\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "\n" - ], - "text/plain": [ - "\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
----------------------------- Eval. results on Epoch:6, Batch:0 -----------------------------\n", - "\n" - ], - "text/plain": [ - "----------------------------- Eval. results on Epoch:\u001b[1;36m6\u001b[0m, Batch:\u001b[1;36m0\u001b[0m -----------------------------\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
{\n", - " \"acc#acc\": 0.890625,\n", - " \"total#acc\": 320.0,\n", - " \"correct#acc\": 285.0\n", - "}\n", - "\n" - ], - "text/plain": [ - "\u001b[1m{\u001b[0m\n", - " \u001b[1;34m\"acc#acc\"\u001b[0m: \u001b[1;36m0.890625\u001b[0m,\n", - " \u001b[1;34m\"total#acc\"\u001b[0m: \u001b[1;36m320.0\u001b[0m,\n", - " \u001b[1;34m\"correct#acc\"\u001b[0m: \u001b[1;36m285.0\u001b[0m\n", - "\u001b[1m}\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "\n" - ], - "text/plain": [ - "\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
----------------------------- Eval. results on Epoch:7, Batch:0 -----------------------------\n", - "\n" - ], - "text/plain": [ - "----------------------------- Eval. results on Epoch:\u001b[1;36m7\u001b[0m, Batch:\u001b[1;36m0\u001b[0m -----------------------------\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
{\n", - " \"acc#acc\": 0.875,\n", - " \"total#acc\": 320.0,\n", - " \"correct#acc\": 280.0\n", - "}\n", - "\n" - ], - "text/plain": [ - "\u001b[1m{\u001b[0m\n", - " \u001b[1;34m\"acc#acc\"\u001b[0m: \u001b[1;36m0.875\u001b[0m,\n", - " \u001b[1;34m\"total#acc\"\u001b[0m: \u001b[1;36m320.0\u001b[0m,\n", - " \u001b[1;34m\"correct#acc\"\u001b[0m: \u001b[1;36m280.0\u001b[0m\n", - "\u001b[1m}\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "\n" - ], - "text/plain": [ - "\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
----------------------------- Eval. results on Epoch:8, Batch:0 -----------------------------\n", - "\n" - ], - "text/plain": [ - "----------------------------- Eval. results on Epoch:\u001b[1;36m8\u001b[0m, Batch:\u001b[1;36m0\u001b[0m -----------------------------\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
{\n", - " \"acc#acc\": 0.8875,\n", - " \"total#acc\": 320.0,\n", - " \"correct#acc\": 284.0\n", - "}\n", - "\n" - ], - "text/plain": [ - "\u001b[1m{\u001b[0m\n", - " \u001b[1;34m\"acc#acc\"\u001b[0m: \u001b[1;36m0.8875\u001b[0m,\n", - " \u001b[1;34m\"total#acc\"\u001b[0m: \u001b[1;36m320.0\u001b[0m,\n", - " \u001b[1;34m\"correct#acc\"\u001b[0m: \u001b[1;36m284.0\u001b[0m\n", - "\u001b[1m}\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "\n" - ], - "text/plain": [ - "\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
----------------------------- Eval. results on Epoch:9, Batch:0 -----------------------------\n", - "\n" - ], - "text/plain": [ - "----------------------------- Eval. results on Epoch:\u001b[1;36m9\u001b[0m, Batch:\u001b[1;36m0\u001b[0m -----------------------------\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
{\n", - " \"acc#acc\": 0.8875,\n", - " \"total#acc\": 320.0,\n", - " \"correct#acc\": 284.0\n", - "}\n", - "\n" - ], - "text/plain": [ - "\u001b[1m{\u001b[0m\n", - " \u001b[1;34m\"acc#acc\"\u001b[0m: \u001b[1;36m0.8875\u001b[0m,\n", - " \u001b[1;34m\"total#acc\"\u001b[0m: \u001b[1;36m320.0\u001b[0m,\n", - " \u001b[1;34m\"correct#acc\"\u001b[0m: \u001b[1;36m284.0\u001b[0m\n", - "\u001b[1m}\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "\n" - ], - "text/plain": [ - "\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
---------------------------- Eval. results on Epoch:10, Batch:0 -----------------------------\n", - "\n" - ], - "text/plain": [ - "---------------------------- Eval. results on Epoch:\u001b[1;36m10\u001b[0m, Batch:\u001b[1;36m0\u001b[0m -----------------------------\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
{\n", - " \"acc#acc\": 0.890625,\n", - " \"total#acc\": 320.0,\n", - " \"correct#acc\": 285.0\n", - "}\n", - "\n" - ], - "text/plain": [ - "\u001b[1m{\u001b[0m\n", - " \u001b[1;34m\"acc#acc\"\u001b[0m: \u001b[1;36m0.890625\u001b[0m,\n", - " \u001b[1;34m\"total#acc\"\u001b[0m: \u001b[1;36m320.0\u001b[0m,\n", - " \u001b[1;34m\"correct#acc\"\u001b[0m: \u001b[1;36m285.0\u001b[0m\n", - "\u001b[1m}\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "\n" - ], - "text/plain": [] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "\n" - ], - "text/plain": [ - "\n" - ] - }, - "metadata": {}, - "output_type": "display_data" + "output_type": "execute_result" } ], "source": [ - "trainer.run(num_eval_batch_per_dl=10)" + "trainer.evaluator.run()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### 附:`DistilBertForSequenceClassification`模块结构\n", + "\n", + "```\n", + "
\n", - "\n" - ], - "text/plain": [ - "\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "trainer.run(num_eval_batch_per_dl=10)" ] }, { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { "cell_type": "code", - "execution_count": 20, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "\n" - ], - "text/plain": [] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "\n" - ], - "text/plain": [] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "{'acc#acc': 0.644495, 'total#acc': 872.0, 'correct#acc': 562.0}" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "trainer.evaluator.run()" ] diff --git a/tutorials/figures/E1-fig-glue-benchmark.png b/tutorials/figures/E1-fig-glue-benchmark.png new file mode 100644 index 0000000000000000000000000000000000000000..515db700127d0d2f4dc7b139a29bf1749419f2c3 GIT binary patch literal 158817 zcmV+W{{#SuP)
hucZt%o`A*HnGu|@=T^>2?$L(gFB^;U44dk5<1oED!mUFb1BC$MwR zY1|8lfnKS~XxV2lx>OO<_atBJ9NQHmHhV&wj8wv)WmC|?%nVgJdD#X%o+{D*L_TJe@|BFf5sd$DeyWD?mLbfKA}i9E`v&qd&0SMWu3tu z^K<^o7|W8v6|-tK#3FZ1Dg?b6b8zeSj~O2oRC-}i!|IrIOS_E3)CpUB4`85J3{{Kq zyM&WxuHv?vKjMwcqRv-+(6gycT6sYE^a6HWdX_e5wM5SbP5U-Q;fMRNS;)VABIPiz z-5tXRH-)9Xuv?)wuw%nsTn|#AMCCRZFzhSXr5D9C6yf)9WZw~7a}Pj7vJA$h%A;2E zuIS#{4(Y|)jN66G3QhX8MA?X2IP^xnq#Z1Sj$_OAlW>nL2 OmOPmkU8&k-op4Bz~7uN(#TQfZAIckgRw+`rmmsy0!mXs%I>vLKS+@ zZ?~hhiz$NX>Y8Cx)leKrO=eA_w``@sb_LP~lag7Fp_3u*bZ~AvGS>`PCw%nk(THzb zQ3Z$kRNuacc2G2_F;yMCp90i_v?xR9`d FemdW~n&>T%V{tWY7+mui=n=vh+F%}=!F zA=M`^h+ PwUV_DKuNd^3n*R s0KJ?n0? z^vE^3dVDQ?+uWS=q^8twul5R7_${NxdgA#>9Yj(2lH6U^($LC!LR>6K^@jaKf1mfI z7|kyj`)|bU>P=<(?x8rvV_M#{pqL&@s6eA7 whewD*OU}`_@g7~X_nK8 z&uL=l-YO?5XDFj$tryUVTbF3#(2AsQP?mbG^CWdjlj6RC#yi!fQmJ{XtRy*h=|#Qz zPNu(ok|_A<8X8vBP}~<8S&!aATL;@wfz&ik$UnxMeDap`KUFl{Ue%lIjOA3a?T>Wi z<`r7mwG_!DdSu@ISGunrQ#Rk{zl^agrQD{ORrJJbUnt|%m_zs87{oP!jtwuJUaycA zr-28>++Mi$FDz(JmIg8^-tK>N-t89c|F#|(OAAqj9=}m)bwL?&lQvJNM+GAl(s*5WZ82wJ$avjX&HM@dDnoDa*yWJNKXvZmGz%=OD{Q); 2VRZPV z_I#zd&Z4TC3YbuVt2LMIr)Cr>ZZyv^HQr<_Kt`_riVQUKK~fV3>h<$w3QI{MpA{|0 zSWJ}ls?4O@x|z;r+ko(z&Q7w|G Wg8nPc426(8hfopLPR6ZsK4H-JVrV2v-@E8GVY fPUbQRNemj%lE><8D#-Jvy4rP0OKJ8k|L4Dqu`K1N z0U?DR%&D24R;86!QpjsnTWwk=cc3ZP#N0RbAPp=o)FC8tD${Q#rB)XdPkv6zza{b_ zRBOZrdJwIoM7M=hzks$ZA(0iK^6kE%y*@%p8tFo#ZROf+N=>Q9_S6jjB7I$6JzqvP z!w!?SXrWH)s%rO9qEEHv-WTR*rKX#j^@t4!8j L=SbBOo=T0`A)e5 zdQRvHYjqc?0yWU9OJ!~MM%4=(U$+C=fva_v&p-ybT&N~z^6K6l_@oe&&ktbn;`0d6 z4E}C}dXwg0T%FQ7-A6wMISF$G!o#)C#3}<%;)vMwOj^7cifDq5kmBtIckM~ccz!aG zr)^9K{R;gtcXDHtkn5vdvqouSXD9+5!!OIx!agsm=ew|c^CL}nIGK$d9E99TeLgHK zN(ga`5OZZa_INA4u=w~kxru|XaPN+f5Uxr*Kd=~!&xZ)V78upTq&eeIC&RPi872E} z!|*;EQSP_nSlz2!`T$Uj 2 h@|BtD zId(1Ij7Qpp26ozn2677v&0xqBa|PS?cx$hbY^0~(DkK(h!v9wPfW>=VuzBVvjGVa^ z3)LC)+tUBo(^x$BFrF(3GTX*zra2}V%2aYdF=2U$fd#BA h8`p{HFzNX;6dL1npikatxap7{o8(>z5u!Zia&DG_+`2((2DDT)^> zqFt6!;O*uv)={+E%`y#=3+bNtr%<>>|GAUVxP%<~<(i>Udf^t2zl6dq%Qx~azmye* zSz+DHCn7OX{4*!wFXHgYFzqw%5@Qr8k~-$O3`L8I&s_?Mf}hI~+|PVXN%UY>xw57J zU$9DlEL fzG?LNRw~{a<^}Q9^c9%c+X?-r{EY8fs?TFGUOWrNYjwM-K>HfQ zT#}|I#1eh|^j99f&$>QA3D?fydaM|L@xC~-a~rm8+lIprBZUAF<|%w|?|$N!8y_>s zO&lx)bBm&y%v|;2Jm$Blglb*C$NH;bDAH{XzG;+Ih!Il!wqj`e5!jtj2b~(~K4;EY z9xBuVqX$)ko+e*YBJ|K&><$$EzkfY$))@w;%&$bsEa4i?U5^!FxLQ1X&+OEsGusY7 zM5H#MfzQ4BLhha$66o1gMrCbgf&L5EVC}*_sQ6|EohI?h0UQg`UZK)rB~Vf p^EZ9%9m6hV4*p(u)qgQJkb z4=zXUzxg&AVI4B5m<|f%Sbcp>Vkr>Hm}$wQ40=K(Kb1=T<}9^>Ut#)+iRkUrA5(t* z4%(u{3p@*cEvA_y6iIViBGnnhR-zC6^un#?g NTzmdV*(ftX3-2FLQtBQscrf64y(Kk$921 z{ah%}ZVKjfD6MVXBqgG~{=v_qnxS&T@!0d^b8bC+dzcrjHUz)!K7h5OOXAAHA!uEz zHk=k*KzQzhmuaZ*GAIObV)&^-ec C+~oy^Z1vj*l7TJPsB> zyD4+fp|tjH11Y27_0P{3)vPiaj^BeUgG_N3KXo06oqkD3d9((jzB>f5EW=nHlcU o*+1rcD9(YD8-oY39D2>SYi@(i0l^xVmf78IMN=(c+tFm_B_PCbzfA zQ4a)NQk@3i?5Pm>*cUIv!ZWE -t1B3Lt1)3JZv=l)o7?9YyCwwK>hyI5; zN}apc5~(2y6?~5s%N;O!?`7=!p$qIyWZHyFD9A+J`4hv3u7uZ@*n$}Eh3yj?qk=;# zOxTlx{#&l%@|r# |r9)raPpJ!ks^dW9UjR&2$FRlIS`2JJeNI(kKN_wv5O4?fz+L31fLlYTXZ`I;$UT z5>PVk{x%$|s4s>(=HPz3T(_bxhkcJ9Go>?&v0zlAkN##B-FHc;o(CY{ZM6VS?-=`h z3H1nfuP`xhRrh4-h|bmILUkcMEt9AlC$-tO2Bjq)63F%SKX|1m-fKH1Hm(4NRuix% zr9ZY@y^J+|%I0O|i?I(P596>D7X2onueI3ihmvsn%vnTer`|$^h=+IJn<)I+tO_b? z1}6|u0^;J*OAyHkiQ=nJB#_xxMdh?EKL1WiR1{*w&B&}A;E>Z-&8JZz+Vu#|h1j6$ z{KN2gd;?pjcR)E!b0{J1#tEGJ(r&w}La=vqI}G{bDnb-R(QU!+7*_QiukvI$AVpEz za++s<)l@IM;4XIW&4}ZpMsDJuDjKdw&LPC6E9M{ez~dWRF}*`M?Hvt@!;KT?w9Dy| zmO+(XbFqGIQ|+tdl+SVSn^E}F`@`PrVC?;vq0^XtC@*HGs@E#i9qNqo={<7aN?Ozw z<W&%oxLwa=~83~Cg+8d4%8SJZ8PJi6>15kzR#prfJ@W<5 N{_HT2hJNW8b5r({vntoDAsKL%&`Xwa;# pN_aKQ8n)K}j+cyeA={*$QEg8vak5eV#_ANJPU*RrC*{`~ywz^SMq7MV@ z1Ej)glaZorxe%zJQl@W3+sa7@KeH6y{&idPoRzG2Z8$GJiYuG?z(T!`GGSfvygem3 zrc^+o6Nu_(zH}zDkad`=Ui^dg`+~IVk`zXX65 9(@%Z4GBX2InY;!I+Z5F#x~hmXm^ET4?r|h% z|236L$k&oHTmuCfj>KR`1I- ?~!qwj`X{9YD#F50JQ=Q(W!MQP25-auDP{! zHLj*7G)VH 4A=AwCNpE9%94dy$IIb zW~?W!-Fu((jxm-$WGX|xkx0bg%JNk>r*0ik53D|FJ$~=69T-)=UO)7zsHaKaRIgtm zy}A&B5V6rvV$=kEd)aE!vJXOq7yqosz94N`O;Wf7(u)?H@v~1~9_N-)ly+K)1Aq31 zgZkyC@wc(4XCEv%8<;jKsao>$D_AqWC7OKgjUIpR#pJ5$n!7&gPFaD8RSQBw3AlUZ zx{!PbRN=UC%?Ao$-9?(u!7roAX%hrh3}R9Tl@kg&FGEthIlY6z3tJZcj;q13h`9M1 z7VilVej^bISY0vu+r~nbK%nn{TGa|`15y=*OFOsV!u7NGdsc7EIjt_HXud$;eOCl) zCKd2nI~~jK#A&|K+&!_ehKWQ1<3?YhWf5_9K9}s{i)U$-RzixuFP>z+>63#nmrC`Z zFSZOr>5qMj=40);g%~{IPk5&;tO|wq=__!L5)yi4EMn7|j|p+HG1|r=;oq3pxbzGr zURX|?P{zcjw>T>kp*zOTrzsgezR!>z{3-DFeUkCnJ=0E@F})> L@24TVt-dO7E{?BZ&! z KxyZMwzSe?p08PyF%P;}&kG#$Pp=1BQ%j0~4tn75a@ty9{^967VcQ9I#kTxJG;* zr6!`L=rm)xkWOe48l-sqhF+b=WBH~{ST%Pz+IHUvvyLrMT=-gQR2;<$ijD2chuF95 z7c3Yy4qHQwgxp+6zlC&KdzA|7c^QJRtTO1grT<24FzcrtD5DwNK#AbJqtUkaEUeyl z8s`qL#`vC7aVkzPXXzbdEdP1+1W&@#69rX72%d|{kSfL-2Y={>zAHVUC}4xHemsVA zzjs1OO~G8CUl&tWOhi>7{h@@rxN=>a>#D-9;F^z67?2i0^Etm@R5@+)flA1~Q+pe$ zdpO6Yj`KqCh)+oULf!K@wICtn@rkMXrXI*KK}eURkm~6py=buyi{`Jzx`l%=;!p3i zval-D8>g?hLtDJnDXUT(Vq>)1Rq2*t5s%pP!mUnvsLVKkBx4_&`dyBBPf3yYw$Y4k z)u@=hgdj1Xk`)_L>-H_Fj$H{dGOa-ECjLPegWr6|sq*<5`pKyxnHt(s`^j@@_K3z* zx p+ zNTzugwVX^U_MjElGrsLU(fu##UbX-UMHkKQB!)KB?uY%f!pVr_1xt`^wffYeV^12o z{2ci#Zb&5^>Qhbo8q|L1H#B3~C~9kKOs3U`)Aq;t;T^6Cbimo1I_bXXTZiK5KAO?D z9n~nMpLV>OkHnBH96M0InfvIe`W4(c4)a*Lwy+r$my2O8ocl7ZX3# 2XtLLJmEDq3eNieY3MgIXxgZ@WNU0H#JBCF4l>2FJv6qK zZk#h9%chfPr*8yZSkjd$8cWmmMWSaxPV0R&`^(tJ6nA+!Ik&Gx=IZyDOJrnPp(!~{ z+Cm A!rg<3oRO4l{7Q&&>Nm7l3c zMH6j4sfqJ4GI8ihzg&+LzD*TJx0bY`(t6r;N%Trk OCk)&I{kyB+;lBT6)dQ`g6ETO#ffYuJEMy5LHPr>q?>9<> (KB`?n2Sx8ZB&AoaExVBnD-u)r^DWeR~l#5bGI|0_Di**P9e}V+D1!FRW8; z&?AecbLsTmZ8W-0NzzL@b}5+&>D5+GAKEdp2AS#Xk72nsH1|ZfcqEK{ToUy^LgdRg z6j6_G %lX;k`_Y+$GWIo@@-v-vk|`w zy-N1SK6MM9o+Ey|djqq~eR1u^Gsw! C+17LPzTS3GzY3*)lYP^*d+j6Yo8sed662M5|>V+#))8TPS< zzDlJ)g!gsadZL0wbw^aTFw*uKiSfnd3*Jzbt&MtBN@eky%?}{)AO_lQL<^52_|Fda zoj!c{t74zw&J8yNB@}{rB}de;6<>2I$W44y#rWgFJwL=2E{_`3gzr;VB1k+q5Vjjz zz~jiUk3L}9$C1-G7>h@@p1`zrH57{vgq!OFJc~6(*=n^=#me{tc7J5-6Qhd7vpYB7 z7L lszgQG5Ujx;IYvzqb{;lM2c-**+M-hNo*&3)@!vbQ@ z;ZGp{WsH4XaR|J3OStx5L0YT=9P3ub`&JinL=ge+>q5GtvOsmm%CIofCNwd=mvO;c zg|fBlp^9!Lfw50SzH30pb6GSX%&GG3#*5EOUNj)&p)a5T;lJ?dHy|*^7-Nhv# OLGc7<|8Yfh+UQY?F~%5U zj4{U8mx&t?bSOM_VCl9fI64o-(4oR-NMBTbz8 !aj4{R-V~nv6Nl#A?Mn*>BRNnem z4G8Vqx5vqoC&eko7-Nhv#u#IaeMsthw~LF5IF+~lRRhA)r%w?R6C+MB#u#IaF~%5U z>_aMCxG-#NY{aR&^{*Nb7-Nhv#u#IaG4^HPK@=Hdj4{R-V~jDDEp9+yj4{R-V~jDz zShlzUficDyV~jDz7-QMu1_Z_!V~jDz7-NiOiyIIaV~jDz7-NhvmMv~TV2m-w7-Nhv z##pwv0f8~b7-Nhv#u#JS;syl97-Rn-1zrU|!PC%K;V)5QI6|Y8;zX(>1m8M|%f7MV z^nWBV1a1$WB2k?DIC2^WEdPtg^9OG5PZB2?V~nxSoP-48-;6QF*vFxY!ktY&;M*U! z!A(^f7KLK)Qc(u=+qOlO=;OFiYd21QUmh}XamBs4s8eqN+=>mwKTkHGV*&Atk3kuT zv#VEO+lliyf8{
oydGBipTf9Od{2fk#@OeNb34WuV;`9Uo-2Ez?U>z=k2wk70B^Ya z1jGOQeAEuwj^(@E5b!(%y465wiAtzhzBsIER)>{bocXcHEYN)1A}n^cMN({9pMVc6 z_hG=Yp^8TEV?RU`Ymccbx8ab>DV#a$f^Y4g!}qZ-g6$^5qWdO)dN$%tB;~$&SPNJ z4nG_^hc!+m@iHVNy*s}^YS9N9J;U(y*d$c_*bN9mRw$FnQLKss%s =Vs8}y$)8kM3# z5up<2 vTsqo19hRr52sht**W}^&v %uF~- !Ogn_&OgF#f4$hd|)dI>lM&wCGxVr}e zGgWp~asIgD9s*5WJ2?X0S5D%rt3P7iTKQHYCeRHRkGtUV!(ha`=gs%zsHeDd{y0wG z^hIQ{I5UUdKMZoRT@~W|@4!7o8vw}>-neq|EL{C#v|%KQ_u7cz&P#DOE*Y^ef)E%O zj2Cg40xdZTPw$+^@zXcq`<8>uefSHDMf%~^>7#JD`~ZQmZ(A@KQBUvS{PEMc=^H6b zWuw4XSh-~kEOT0tk(R>n&3|bBR>k4D`*oc8=Mauwa6{-@M$J@2KZWbL<2ZKi4*Z3P zlbHDK;WZU8VF(Tk6v9^g1cxJ5=UnG@eT*^2SboC|2#hhtJ}%?7=r2@`Wt0T(4SmtL z>wKK>*Qp-L%A)7E9!R~B93OB6+kP5@PBkoHRcAWRXzti6@#OeYeBG-tDp*uN`=wXl zec)R(DQAruEjpleT?f=0um!%E8wXz9#;U$`(em4Uc=+@#wv4KY;>GRI@T)!;I$|V7 z&)f;0>>3neJ+O0XAM_r-1ltZB!16)0P|CbEMsN2)jyuHXG7L1b+ph8njxYThy&6}5 zMTPcQdc_+DzHNeX)-}+gLu=G^sEGkve6`Q$E8MaB&!c#fpaLm_aBBH #o06 ze=CTE$Ihwf(|bIYY&(Pl%Lk!WDRa~wy&XQ 8MRoCotWCEH^wj7hC>IIV^FP9Ft0rt+qE12fK-UQx*WY6n_$BJ$4HPS z!t<}*sA%5_EAHra4M~R2-y_kg$0A&d7K%!tCorv<70L}gfOv6 e?1+3~&uNk}P!OJ8{3BN#79Se{| zR)$6#kJT=$5kdb9w m_QU-4(r(#l)7}lZr_fo{U*~@tt2y(Pt1t}5(Xw6q9B#{ zt2(`C#_k97GAV_^FH9xJ0uqvy8IgAEr0}C =aKnQxChQ zv^ag^6ujAqib*A8SZ6-vB8UZXw6muXNrdn%)P1`!l}X$Q8f_&by{a?mcItYggncS3 zC6mq@C`3H($}nMGk 2TAo2EWs_(Qh?N}(qm1b2H!c)H${UQD^!E+-GY}SPq zo(mB|HC>L@#~5RbeFC@#0%MG^k4{>&C4M@66}zUihP5G}iuS +)~#`-_g4HKiU*H1pKq z?}Tr@Zi@2d?O<)B9zY-t=a$aHDO+a@P2V`oyL5y0Q1@b=dgC8T(rd&dNuY0JgtRw< z8kR;$6A9wHAL6mNDOG?s9>+w(>%rr+UM2=LhhR`$8IrPm0)r5F^?z`lavx4x_G4Cy zQW;*ynfvuI#u#Jy6E`3*#u)o=6mmeXrKjP3cOPbVEC)lV@XCD~CbVgXA1+2^cwS#U zprJI297-b0lnV7uiM~Gc)4rF$%&f5RFCjiYK5ae9s4%=pAmCLLBGbkeC;-PgRiO_h z;$kw65%8uY+&JihAeAT9^=pGxty+nnp4b##5sr@4VVW3 2S3=Lw5bt{LfhzhD zd_xGPZRW#8ciU&r HV`@)*%O46Bi z$NyH3KxKn)Tw6U7jjA-kxD9s@mDTA~`C|9t4Y+;pBz9kYAvOYKlAHB0#u#Jy7&jm= z#u)pE5^?{C3;Z$
lJTAMj4IT7j1M;kPlUUf= (~_s_99fXHp7QR#q85mL*^$S7)=i z4TGF+_rplgZVZ9`ud=TEz6VuYJQ9hJ5FCim%*U-OApuFkRwDxa@gE$jAQ?~ge~l(} z$KZOk@38XMIT+sD5(VCP9it3ZJ=WmLl}(t~%pB6NYxr|yQ`8@_4St!QsF2!W?CRNQ zWax{HBZpy)cUF&F ;Bi^{bM z!7lr7Axa9%9*ePK&mQd2{cN9u_Ga28a}@?T-mW Oj@iA%nM1OxOW7Ho@UX4kmQ3Kx5I=(Ee-9V9>tNYHx8G57w|z2Rgiq=C-ffp z2ZGuzz>==kZ|xnS3V(q6{z{lsAB>e3JaA{{6f`TDjK|x*!`#Ce$E#2?s6GXomUTq& zmoE6;c@C~*S|qpYV~jDz@+)pYV2m;L5$T~^Svd~O{R!u@ss$<(P^#ZapjQdCs_XO! zpwtfKl(B318`Em)iszei)vq25*f$2dg@#4Nk~+W4IwJj zusP}!B)mE?A5(VwXB-qV`X-ire*}v3jpZ!-3pPdjW+srtU%}FetMO3xU4^O;9G|ls z*9^?m8%Ogx1wpS)VE&Zd$atvHxVh|m98ox=4Z@f`f Gj_il7FZv6Tu z&c^DY{E*og?VvMmz48@asYkREDtE7S`nFSS95i)bMG8{1vjl|5$XCL@gyh7;^h#s= zQOp^?0T)iL#++FTu;iDOSiNo&{@!^2CvH4KM6$S?NcB1fvFc4G#6w-5RA-Ua&Qma? zf*i_#T^Q1+B}U9$iM8u~$HEDn&~&UPx=ihbQrYIDxebHdY?lxlm!LUjMM`>zfFcq3 zN_{ZNNK6z%L|PP<=0=bz{BZfK58`9Ju>bEfkXk!q%8&|>D+91=NF%fyF&8V>uEXyO zC!kZ)vGD9N6}`lR%yIZjOQO%LZ&25m5O#7hn$~KEA>Yl%l0`Exq-8t&Qg{SLSJM}k zvxf7OA*di%B4F1LG-^2lb62j#y5ARKLZ_w}>)8cUdzH$0VMQ5?ppX>JzNCZ(Avi #!?KpcRk(rP~x7l>aH&OF_PUJ2Ow6fi PSOYU8Qi%_o))eYx{U= zWNx5Yhk7}gi34?;eTZ@%#INc0)_L?zzuHt>racCUtSHs$HHqe|yG&uJ+l#(Pa~qc+ zxv)PHxiQ(am`Gb61knD0=E6P-`&X(ZEx7oSQf|+tM%Lx2dYe(S;IHkpYttec+M+oP zTX&ygh2sjpzJ=zGZblaBxFs>7Dt+hC>Z2YM^ZYukakeM9CJYNw*%rfS&VKJ~*2frQ zjD0#J>VJfcmNCW{`#9qL;1*B})zsbKl`(jB|1P|OUO`^U4ppnzp;Vz9-(Suh<#V|F zI13l5e1jpjF$f8JiO86EBqk*yAvy$J=XT&&iJ#%NwtWsBX;2{QsRtgtkfM~GJ*-XN z^=^SIymuJ<%k5?%G29y-ff86%u}ASt2d;>E3XexGAQkq@-rDq?Z&1iFRUAURy%3}- z2|EW%n7sLxLoG!Vo_ajO3u!6X*<0fS9BS57u?Y6^Lu?5L*rx_SQW(5EBVcM@38f1P z(~5Y+qaX@OVi4@(ji4kml&e(!jqu8`54}Fd7-KAta03EkjIqy93@%J-hCxrh!@a!& zP*j|o!K >W_GK}QT2 z&;b=qxf32^j4{SoZg2wvV~jDz7-Nhv# #xPBy!Edd5Ex^OF~%5Uj4}3Q;4u&wV~jDz7-NhvmMv~TV2m-w7-Nhv##pwv0f8~b z7-Nhv#u#JS;syl97-Nhv#u#IaWs4gS7-Nhv#u#IaF_tZEKwyk9#u#IaF~(T7xB-DN z#u#IaF~%5U*~*s<2#Qz1@beACi`Zmwj#?tZL!+P+r}9*M1R~ #7t^zVWO eW$Q_|%?o5U<5=S^lJa z=7MFD`k+Nc3zVrf277|DPw*7?7NJw)4rt!=YaG)w6etmRb`_=$71B_flCY{X35UXT zt{%lbEb7!49hx`A*V-Sjd@9Lp7k>P9Fxu2i%?lf1>X{rAqhnm~ZT-~WR78VrV=;UC zgO8X0Mc>AzX@k(VdKs8k>VtLO3h@iZ82cAEzhdmep}dypO*_ZdCNr6wN;I5E8*hYA zlK2$~e}&!NLX#R6Cz%AK*WiETqY%H!l_=WhT#BT^;Yv-r(xwolIR8Dw9H)_1GLo4O zqytgnJoX-x6ttr+S(-MW1+EF=JVDGAnpVS*inRTm9_Q#>E9mL(ZK;T{85Jo~B>hv= zlDhu&MEF)sNy?O9+U!(Jhy(i6>W>g%4pURSd_epEH-cK)8k1DG#?(myphwkzxJxPG zGFl4qSXhVjG~a^^I&KiIDweM)6_gmVj|N-H2zvEtaSjqsWza4fTCoJ#eDxc-i4j1l zP*6(94(d}%O3-ggzdX~qdXz!5YiLC(Ve=LJmihyhk15H2RZA+Uxz;6Q(qRn+yfZOW zQox!HWFn>wGV4)vB3AtBqft^) =P4f!H^u zGY0>47u0w@j-6bIq4i2ZU;G-vUnOb}!f&Tsu&}8qkPwdqbulK_j9_2E0y5z*X{idZ zFY>;JtwPv=b=V)Igfiq1{@mfuce3A?!V?>o??-6W9vD!gfH+fNR3D=T)I!YpZP@FZ zgDxUTxAEtucuct-j);he^v}xx92#9#bI#QrZKU!Ns9dp>IL)+_@Vj;$v5sG3!~S__ zVJs2nXRUyNf%q5uZzaR)uen(FD3@KPq%s&3uZZ#`bDrKwE&5=iXBeIyn}n)zah^;D zd9f;RFwf%Zky@b7Mo)x2J%&lCKS=YQ`^`QgDN2?v2Qz(r=t&5#&-{TkcXb~X%tY}J z8`j=Maob|r{L{(~b~?#*ZX|pCg}Lh L3%@cs zWbb>_%E#Ec!2^xDH$)-C;=-DBcvsnry@N6U0U;{TbAR|}>Y=Jqs!wt DC7P9n%YQR!u~wViKY3wFQ5j$}~zJ%Ncn& z*jmY;e7X(uS3N-5(`d@aICRVn@@k#XDZ54ir4Kf)Jd8(&N5Qs88PsSq2s74RL|{(i zfFcHA!GQ=2O#K8SJoXE{TPl@7{vI_0<+F>}v1KzhZ%+Me#i2V9`J!izD(*Slub;s` zhmPWcTWES5%R+LbA8wsKDx@I~5E%RRz0cWl$%uM-2j`EU#!cTyVJa)}2z=-b?NA?y z2y?%Hlb1XYmT4#By*6UF^HSW6OGfOAAWa;7q1%e8io p1hzAsoHnhS2OD>r_U; z_u2^@JNE$3-`X0RjBs}k1m>bfmmKa651k)mz26GNM+EC!Z-EGY9+`GtRB;gqPMseS zFV1Bjg?@F69@`X!Bua!FScNTF4G5$P!QQ`)!D_@9)Y22@Wl4p&=kB Rx!s(FoN^>x08y39(#(nD9*TF+5gWcQT?wwciDY zMCpw8pg{CfSDZV34Cn6nBSx8s#O$s)UCEK~yLB2zT`uE6VC-AdNmXnB?wvo1Lx){( z**)y-<4q)m!oxdESdR+PJ{NK9 29Wi^oplKDXwvPg!yp+}xgt z*MpF6dR)SBmuv8QrF$hMBf|R%PM&o|z+3X@%=uL+Mf6j+o;xn&k9Xi7qZF>~tS|1I zmKPZNoaI>qf_i|j6ZexrkfTbIW+ pSuvu=|}F#>@aHfaM { zLD=Mxg9=FE3p_e82W@O^QNQ1OY&&%wKH(p+1?n>-F)oH8$(Qi+s9_j2{V!a8E`_P7 zL0)!k5=zUFxVpR-9GgtQ{>KTBCVJwp-W6frX$9_NdJ}=_#SQ#9stwwW{0%N1&)~YU zHymtgVZa}*&}|yb>2k3iJ2ACSZ;W5E4Tlaa$DmrJU|xGPw)<$~RlMIBterCqZ7N!# zLZ@GG-Ssc@u`dpbYAw*dSyfn=*TU#se%iaS3U}=O^C+Gqs6fgfoLW8|)27YDy6fuU zD^-ZRx*WY6n_$BJ$4HPS!t<}*sA%5_D>5IXD8XY522?8xhsGVysZ~u_mb8b{FE1fagHAOpVO3{3&Sdx2hWG$n+4d90bgBVMt2&sT`h&ObcQjty`x{@kC=Y8Z zYuL7%i37KuBDL(Nc VaaZu?@7LaFU{iQ) z#M*n!Fm8zLTOa*WMP9}7UXExoVLu)xNRjCI7kXE;N2e8cpp6X*xbOZGN1r4>MM?yn zT8`<{reWr~>xgvUjX#e*L4t~)3_6A7)2CtD%yrO2ZygkQ1&ca5z^-9u^y<+C&8k*J zn`L)0?r*FIc24bs-s6{G+o1zkKByK-nb*eX?LL~yhAPGfJI6FYSt~2pRPT=!mjV#4 zN X&=5W6?Z(!{ zqAV^H9@7_3ZZcZsF{P;MkXjqzh z?|7a*AYO_e{oTi! wKh@O?04J|Joo>Fz*eS}OHPo?ePudo)-CBy< zk5pS L_TY}Q2CmpX^&t280;+W#AvdT=~300x6_UxYO50!7Se^z8`1~RR7RYo zDYeY0@w^)pojNakD><1-GhEj>T~6_2Gxe}*N{ihRHPgaU6ujAqib*A8SZ6-n6BbK& z?n&2okD O6onrxYb=}>y*jkOLno5P zouJWHGSaI$lWwOUBsmq9l1b+clr~PR!h;sn5q>W#PUTw+qMr}lAh)|`Xv?%#R7xhK zUos2ow@aOdDk<{uExLAWCN)rBLk68_W9q@Gr97k`9fkGi45Fxvd`!2l9iy2I3`qh5 z>ZJRFx9zteV&4ENEgYww =Je`RQ#fxtY~II{czO)kHd;(R3L#DWp*rbF zK+ Mg8b7Ki=z{xtw^R 6w?kps wns%pc5#pkXC$zbT9W`C-ChV)0GI%pN6%(#c!#Xryec=fdKFg?y5VxhJ zhtZL=!hw?f|7=HvB{C{I{#@GcHKKP?AN&5aN3$Izg$Zd$k qrG4|Mcf <|q5rZOXl?Ww*O$(~h9@7Y zsa*ljO&jsJ?Fe)eyGqC_55@47#=^eb$EMY1#k(ymcl2RDcr*6Qs0~A;;N|mRJUsLp z)|~d&-hs_2nF-1^>Vs)(&%^ENDs;DzL-BMwX8wE)ZyTb5BCq0q&QoyTX$$txYJpPx z`2TxIhVQoF=(DjLemlMzy~<_NW#m7S^g0Gf5`7pMrH}SzSh^%kBysS5_*jfv$#7l% z4Ss*o5woV$gNbGyAT92MZ@zAd^5yMdZKQsrBd5#7;oQ=BIA!aMp_Nkyo0P)5OE)ww z1SH(wi+v9iC{Vg0>UU}adwmHcM$Ir~^=$O4V*?BOCK&enLHydeC?s*WvF6VUZ`={i zAn7$?k|fYKGD6x>8kR;$6A9wHAL4Q53rJEF-4?FJcU|kDYRy&{^uL4HGr2P4s^{3Z zY%M$#Qka#mg9fcyqgD YVdz*yJ*7f`_hZCFd*Q+3)Lv=^s5xX1>dKPDo&i})c3qBdet&_Ev! }U$v2Bf|lP-L8m4?Fnjk^TwC28xfoNG zv3x-;*GFLoJCqUPuC!D~Or6&kHHxcqi9)E}vm+|Yl?Z!X3p0N14cmh1Ulc^eZtaEn z3i#f-sd-fJUHO$Nz#ETaqT%)6aoXcT1~rFZP+b|4G#7VH%K?miIr1zk(Kjpr&101m zi&ru4%6{LZcyJK^c&9*STM-pAl{BRmozba|Ff;Zl4j&JFXU3jmiC3_8dkT7wY$YUD zBB>?%j_it3QY8ZS{E5AxA7o2km^iqt8HgdrYGc#cZ5Us-2qe+Bv9ga)!PzEu m|7@pXsq@O;cxtZ=dxiqS0BC 9e;q=M2L%t(R86e@-6Jz=Lt!ThNz!*QL7oD$=vgJ z pf{rn@m? zG#oP!b%a?8_e;2_eo$IXBJ5kPPNhUxK9}cSzZOPMXbTh7GwfWs2O;97lWy$6sS>@> z&yHL3{wq>-BYYoA$S4u6Yga*AuU6vmmbJLsWE=*m O2n)P`8QMsQ@Oy`F z{K0Dw#UbX6NcbKk1yI;TCzF>JMe!nfkf_%mt6qN+ZX9$$kjfM5`n5^F?ppQ4rtpez zbgT~3L~;BRX(>1j?t~H)f+O3GB2qn1pi(M9!u6;OI*e_n#g53TVC>!g3~dHC6RIL= za@f|W3bUk}__0k(jNfn@5kkC`l^cucgG#)kAeD?;yY|6TQUeV$CLl0sG8g9`d0@wo za>A4X4-WnV?-UtqD^|?ZfF^}S=T6WTYYyXhYFR@n6TXq4u#K%y4$;m^C)ErVEh%gR z+kBKZGxokDLO!Dz2U|Umt9F)#)XWrx)%mHu0St7ay2Qi;>K9AV%P^!D&$G_2 4Q*0y?9Q`0gQb?@+@bOTiHPKRxqL967(!6SI@g9Kk?O! z+)qlyo0UwVfKk!DG^C_J;Imxb$$pE7!)vhHKLiKHHAemV_0vA>7hl&DOjOY)@z+K# zer7jUl<-|Y03E)2hMwzwMV}S}@cYGU*gmQTj1_*^HEsxg@p!+pXVhp2`c#%f74ZTu z-#95M`0iee4Y%*% YewO_L+XLN@(US^ixkGIJ3Fvo z(ilwn>n=>I+6#pYoeIq>4|w=1p=W6L?z`PNTuv4J2)-c%(>C+ra`r6FrhU%i>do75 zy?qHkcYNDxX3`a`QW@%U2*pGrI-3BM85cp}S9h>u!6b~C@)z!!R)xJ#lFDhdO=ejO z)r86gsUq>>KfMs7u {cI$NKKTdMxvQ`9L|ol-0cHF4h835?{zFD+HDM$i4J1$o z?#9X;flwt~$C@qC=smtO%*3VMN@k2Ag f`29za_BI{$ zHdN7g>?`rQ*$ePg=Ah?A6cxC<5^;^J|M3`=W{IL1scaq5g&x$w V_5Ca%jk$r1n{R37=a$?C))qnx zFe;2UdzDFHH+D5 JC3|A-cXc)4ODRvVW&C(-PaUi%uO&ah z{;!*$?wISS_T5VSI%hbVTNcPr%uvO}BasLR!GQ>U$8P6zIV2=N-H|yW&_8WZ!1o~$ z(kw$^M e18X-3Cs!$DWpXBf06$< zD=5?glSbD^f#myGzw#tv;x1y(wTkHLR4VT>r7x7c(g;lKSOiklE1X`v1{e0P#^KT< zFj{@fI~&PQ_T%d&bus38HGH@7SIimS9G1C?g#T6o5|V`LG$IiGIl1r(G{um9l^}n8 z4%_z%75kV|SaqNT#{aSa10D4cb#ey|g(ksu%OM!|9e@hD*PzS_J=R>ol}!`T%v_4F zYky*7)A|_luHJI0IK(FsC?Obu?@U0{#Trei2BE>hZ>~>f43k3YYt;}13-T4h*oP&9 zRgX2ea%B@HHZzAb>>B vspYUuG^eC=v524l1b`s?{u?>+b2Q;H?-x{dn4#2Fdr atq#vHVP(iO)X_VA0q)AJ|Y0>~VsGn&I z3>IhRk}?7>qX>no) 3W9cb>P#B;(Fcy)p2QAhcbu1YNDOt^`Som4UfXAXnTwf be#c(isnt(>-kg0@LP1#gFTJOV zv?O|d?JVS}gp`M?;XLIAnv5TavhQrb5+vWjPrV1?kD#_#u%s*Aa;be>(xND>z3_1K zNLG(5su1k54;RudJUMC(8h| b8+}>S%|b4%9s~|RB;bS4n57H zmtqopZrm3BZ-B~`)#Vih{O;Vwo5h+KyoysnYE}(3)rB9%*axEue}Mb`N|;n1jFlHW zaA)TfG%J~m$J@WdT;bx*X*qzgFG(IYWgDQu)TJ0)P7jE`f~AxHf;c8pmQ>L<@!RNG z_$N{*dfN9w-v)(*-(AC@!@(H~4ay+gb$bqpZ66G6o8D!ZR5_^Ht3q*j_A&&OR}bo^ z7KLLgw<50V5VTJnH6;^SIcE%OV*;rv6374Cj7K?d2l=8&3ztSIV+m4%p9zIBai&O8 ztSHPRGL*Kq6z2$%Lg9YP3omj|K~?%6!wJu_7&xXQjKz7G$;%JNn$?3~lYAFH51fd- zU;4W^RY^!pPOVZ16^WFT>>I-js$lY(g=kY$os6guab^}qEWM|F-Z-x)9^u$AHzY_5 zVO%)9piOb{h!+k|C8PtIqb%4I?VFiEqE34zuExU*?`jIc@j1(J&A=?@%N1&f=5-2! zUY)@FDZ4d~Sfz``&1K)?h{7Rl@TIIN;eY80eBprpLz-#_gp=q&PfvYES2#Zjspm(5 zN5_uAEkOc9 ^2X#QZ7PovC|&^vz}X{)hq&Iv1X-f-?p+ zH$sZ*Dhytc2 @e00 zG>0VWDg0i%E#ohUrtO=-L=ulHODAG=#smZ*$LC=AHT9J)gMF{Q&=zYB;c%w>NEvh& zZqFsK?K2cQ#Trqj0LtWzZ*!5CGWO}nNuHH;ex(XHfK@wv#0EYSRPM73ht~{*xg-it z{a)m}93V;xb;m8Q7u>s*eb(}@0YTV!i7r@q_)qk+Gr)_J-=b~D>Dc8KnX#2#5rK#M z=Au`}*$8!-gih)^R5E*v{be?qn#SPFFF)Z#+Vi_Agq>K5^$+bae$`wwOB?JY1;Ihu z-U0L~9O0Q?nV^b8@a?@=*rz21?Jz^rmPIuIlX&YFEZ7@|I?d{(k93}yv^d(guOk$K z2njcS$C4uvn(eS`6l#l+Ln=agZwpqQ)(tZSy!ZVBH=#l{Y*1}+UM!AHsEeBQtE1t! z=d`mF53sz0J?!dq$E>}c!V+4lsGIm@!g@%jZN|dpx})8wker;N$vjj- V6Rdi*Mc`uU}WsTSeYtMnJNlTZ;z5KfxhehmJY z*9xYZHiXx>ws08cUCF~YB1?^7D%55q>TxuduEr%#4_w`|1mCW86=D`4^06D-J+9$i zP%-qG^$qG86T(hTM$=mDFyy=WSh8pahO}&lUkZ=F=xRc(T3RxP%UQ#D$`DkLD-p13 z2pY8 k-sl76G&sV*?g9~?~#VH`!ZwF?syoU-Ce#Nx<;&}4X zqOdeKf>b!3%V&KMAM1tvf1iV~X<^7E3i$0>iq)4q;c;~jmVCPwu0n~0A|J!e-2>O| z1*lh`8L$!b8c~tzcrQxy#+Dz};dzH;SUuiO^GKM6@)cgG2fw2@yw>d!E}UD zZKOz}6yk+?U`nCl`1)1)b)4mXpAo-IrH`5uropKMA+dA^3~2n;RF-`(O8B{ZBl6ip zJWvl5mP%%aArqZYQf7=6V@9L4Zf^qN+y@40pY>PiJT@pbHbr5%1PZ@hSi1TWJUy;r z&ysJkRvQWN*v%at*X|)eDMe9Ba~Mg5_ gu zv0nS}_nA;_e2Y`Zw^&4Hh=(zVew{27{^JoBtKJbQtevM|NCi2R0lP4yQA>=NyAo^H z{f>nbI-%)UPjs2uE8`%8QVaAN+yO=DCD3FdhKOB#q$RpyV5j0x#hat^kkr8wbtvv( z>+17J?`&X*qGpAlZ`Tw}-#T!I6eau2!Z&q|L18B`xoIu58}c3IFIj{cLt3KUFNHB; zbT!SAGW)UkWp-1T#+<<~Kb=S)pGOsj6HC|Qq5XKQn%gXGAZKMlB7~4YeDrIfgp-Lf z0g1^%@Wn^tb%rmq$V(af1W=sV=&4A_5Z~1eqKU$M)oUTH>lWb>glj;(d`hx#ouqn8 zekGI+?rmL-^Ey3=42znfkiH$7HnkEi;~bU))MAfeLfx9EU%er|J(s7qQyBZW#Me3I zt!Q`JGp7&Lvoa>RRG&;N9H>r Pk(T4WMNg(%<#|`t}x@J-R*F719jrBzz>KXH<+VEG@}W_>{3IL1u;YNTMBk zjPz>Ep?fJQ^kC}*YH6uY5=g07oq;rS!&QpN{QhLcGdi(yCJnAvT>U;w&AMeJ>e0|; zrzx1 _p?Rm&@1{@nnhp=OAelsuY{s0@ z&Qd(4wcRW=Xkdh|0HgrV&GXQ#YrfH1E)3N`ITL`dz)(Y0Lc4)XYLIoKFcERq0Ff zRv#sgnCEnTjWgNHwdby2*%mZx&VKP%UjhaB|Fe+B4e3seZ3?LeA{Va_kVIxm4sH6; z$XPqdD@8%i_Kc-kX43TU4J@hYq@Cm&aeMDh2 zxPEiGTq1dGA5V?U4aBe%(gPC*>Nfij`Rcx4K5i%VG?I|CXhZ7WtqHa0Ig-W=ccLaW z>rjvRrzj{>(8ZjeO4W^p^h?-xW4pez-b+|n^hKK6xC9CHH !s#4)t)fAd})%sKcP~G-<3eb!pa&y8m!cXTKEm XH zQ=dzTA=!7IO~0RbL_yd8rk_SOpb|3md_A)1@IC!{(2b&=oTart4Wl}mcp)J}`|dRR z_Y>qRCe>N)_ZwnpdygXIIQIeZTeLr}*SEIN52L>#`@+Kc63$79I{oOo6(`6)HNGX? zpqcGP(BZV#qbtbo$TIqVa1*jnw~(vjw6ri)>N1|@?sy=?_Gr2|w=tDaKZmG3$2Ki! z;?@Tguzw(#%Opa)EJZCBTqMn<7IU7aRyP)}NjvJh-b?tsS`3|^TAhr=cx=~qJ$Zz> z(zYMEQH28PZAz&~o#FJq!=4oP@)rF)+llN9wew9KhtTpf{+hKUdTpoijm*hFbA77Q zFB5X;Hk%G*xbSquo}w}Kb?L`U?{1GfGma`XTT0$ J17P4he3QDGrmH2WtnN{&NT(bbFzh#;C=*NvhkRwbx>i$OGH z&I}sWwFxyFu#7H*h|4O2>B8#oshfi~&6X5&Ag7tX(Vp8cg_KH3!53E3_uU+*kUA_R z1 uL*h6nbkD2b6HJ!Poi&2ooB2fm&b_|aB)4&?p8s2-Ajv8 z!?DZh(8DMSxw?^Nb+e-ansmjI+D% <<$Bx$mba24ec*b_3nB3`6tS+3+BK~pR7f*Q z3W3|bNR+Bw870zAun=F|Zs4w81X2nVMHz>hs9Dw|{cbAzVyR+-;pG>L5)Sq#F5b*f z3iE ;5+-j;KytdAA_`AE9^r+w6zuG+VVXJpir3YK~;wmY45SmEHT_09)S{ARY^Y{;UE$0<%if34zN!@NO*Zh zz|_7HN@u!-n)u=ge4m9QPQL`oS1gC3?@U)ej4bzCSJG8XX)qh+drsrq@_glfdW8Hp z*b9EKCE#FRT#Ua-2=nqpgsD9$=|;lDaCmzJLSj`VJ-%urBHY^pff6fJNn6MJP$255 z2OhnUqLiIItlzlsbg2hC4M71*i}Y7BtEs}_A6y95mKo-1DB|ORf(0Ro35Jh%5R%Nw zp;GzW?5_%OA>Qx`QlX@s11#T~fCz+Z`? %fO_q5nV_z^uJmQ5? zK|x6jf_=OZlw^i-mC9$GXXT(AkQCyEdy%D3yK)JxkFd{HK5IZ=jIqz0ZUe%jKhgUA zYfPWK=z~MhbXQxP-Ps?Sm+~;i7-NjFe2t52j4}3~5(jdkoDBN(nNft|=Kk$CaM1$+ zKKn3r-UGCq(489(7-Nhv#y$~TY-5bE|B&i+6rxjv|Hr&QxbA4qUw}lH8R**E89ka; zfpx|HxY%ehW;Nl#6B%QSF~&X>vUvjWZ^js7AB_?LXIA0Ixm$2EMg}8;2zcKQKv z%GM|glP{n-S6bW@N#1S c%X_%fa( zi}T(?{PPEJ^G_0|K2NIHSBQDvc&Us-=+kEii+k5q6p!Z*-0+zPUr_!drH_Qj{3#eXix!9Kn`Z$MB*-NA QX8$l09h}!g`x6Bo{@f82?d)Ke z(~m >2cI?O?92{Z19K4TG!MXMHZ!AN#5pu{#o~@GDq5Z7|w9TB4*?U3_~iN{B_DA63*t z?3vdGjt&E`GeVr1L-F2wF{4{kwCp_@OIEMIw4Sx$P=649z4}57D;2`8ti`m!?cr!y z5>|D;#jz-k^Esoyy+xhSxI=R^{rVUZ#kq_z#u)p=DehrWr^e{eyeYmu_6g_kpUZKu z|3uz4ASgVsvS(Y2*)7MIlfDS>c85=}KhDpujiBwzvD+;G&qFdcASgUnqG#JN*exG} zlfD7)cK1QB|9Q->9fa-6cf&2 {nVA%cE|;%&YXqIxAu7M`xw5# zc9`sP7H3Ym;LwiMnAyz&ubw JA7P9K1lRZ2qp)_5fzZ~F^-y~7bN(Sw13 zL57bkJ_LULCa`xjfwV+JjQD9W`dKF;{_Ust(-rHDV}JgL@%?*X#DYDz|1x(GQ5C!s z!`qI+#o8-yeE&MESoRw>yIjVG<_|Hmi!=W8RR@|BB^r*vPmBA(Ix+sg>E-j;5W=of zO_VQgg__l^AQ$H{#u#Jl6DKW!N;S))xK&M5xB7&0_|N4y*ndO)8pphncxD_~%Vbn$ z)Co!y=cQ6S-9p_h%&5=qIB||zBApp$O)^;-8g=50U(nMn-O0kN4{3jrNT V-+(Xl2C-4~>@zC?O@U^G=OAR$Og z(~!e4;(Ye6#JSP$oh?XDZb44#yx)i$8Y#DEcKh!1$Nksh3|%pFct~j~(BOaMop!QP z==RJiqz8R!^+yOP#krq_gxB;sN&Ww$G_16gV9<`%gloUbUJ5<@(UJ7@n$XWrbgvX4 z%KI&&MtXWwf01{3oKLw;Gppzk^jpy%A)i$!ei%uf^yi$l O0Y=m!eKx%=`-ReSLLE?;zl>Ucssnienxrc?$`V3&>v>0=`7KK#x z68Btv!JQEPB~=7mzVC%^kA}l~#CmKPVK0R8n^Z4fqsFwg7*pL?oTDS|JPw_VLiNsF zQC{cdOC%EUug^>YV;HL+>yQ}2&>*+Rs6;?O2vqbO{{ER_uS%)8_L7s6g{vu*L OU~X8h4b z+d=*fdi61RR#y}eC({*1wQA)wjS71D`a<=J{TuOD7vtM?kDxGVhv~E1!c3f>nY2_Z zbZ=fX%Lp8I?jTM^Rz>Ho72e%=q=-RSa3BH$Q$N87kNwg*=gU#f)(SG^Q*4{R3J=mA zdsE`^p<{59S3{>x_V4U=uZRgla9S7&pWtxheK0Bz{nQobjvvFhJN}4KCL%G*NBWWx z_4E$TA3u$ozLCPzn^bZBxZ@t8ZOBZHfcKS?IO{5`Gs6%N@$lL>9L`JcA}%==FM`zJ z@B(qV%Or=x-6Qag3VCvv8{D3WBT^=Y!Q;|#xLotYs|>$Q-EIJ4Gi+B^_QxU|4E*o7 zBSd>Ji15CGlV@GwpW$F~a4hVTriyzG_v>fy&!MBZ;1>FhaXFF^>30jKkGkOUgTQyj zy__w_7-R1#ISg)Y&!EjM6A|Wd3CCTo!7pQGnU=2wyfq }=aU@5F>TD|* zVd?-A%GhaPCC*PPV<$%=RP$XhLZWoTKc{6kmgmHt2zevoAPp`fQ`h!MVqilZe{`W| z>aN)D5OHuYm65626eMI|(~*93c}Cg%B!jm;!tmab=-LzqlFMvp+}XE0{=jl5f_C&O zN>WJ4wC7gxxv_(mOdUs~#(YgbEZ;@9LQ}-$-X)ff3^OMK#~ x;@)(V^Hz0{b2!}TF>0C$> zJ$Lzz>Y7)fAzL#&LXzT2v#Mr%gdxW760IL%PkMUh)M&&aTEF)a0zo45Li736np2~FzR zh{~DhQL%x0$YcLE)YzgB8OS9BNddC$yO|z~-TPA>ZlsA$j#N@eo212RQs=(?sNbNU z=yX&Z`CtB<7LM*n)k_#qfleDKTKxk>5S?B1Jvq0kM8)(CsMRVDy1HsGIa-vU5~ljX zxzH!;K3nNYf*&26Cj8dcn#_gyQenGxgLl!hOpg`3`&iVEqa33plX_C4aweo#Y#{CN z*iYXywxB`=a*_yXX#v~5v{^iuw;zk PBd@JKXhd066#}bN>&|z zA=g-O84V?cUtdilTQ{ff6X(&2Rr9H*T|p{Zy+8eaTlcZCoG!;0WA8&jL1$Od_s*@T zQZaopXtjzwuCAiNjuup+gemDuB&2WMhqgXRAismtsAF|ovNjjyOND&RZZPf2lv$<( z9Hlv(YLH{sZ)wTzztWtMEvc+=ajNC~8(odimT8n>w`jv?M=C58>Kf9L)Mn0Ma*q}t z*^Kj{ _NtQIo@`i*ctN-}k1j{YKN|NyDj~ zoiXVZb)-?--tu^^DDK&1`g_qh>Rh8F8I~GK`|timPS!%%K*+BRn=L0_^ x|$ zk6RQeUwhDj+3m IA4p2M;m%OCQ#0YM$< z&d#I;MWn(-AzY{uq2gG%7cDsAOK*)X%9nI@W& {2QAnl=*(d2e8xYizyf;(7@&=mBNw1hW zm8m|Iws~f#QD|k+fRNz1kp?#FLJQA@WEgKGZwQ&@RwpV_dMF)95N8S5+wIx)$w? D5_49;pGLjHJi6uF)|e%`z0y9fQ;c1R*}ABt_Ek zQDsR685OV5n`Z62N8#$$&{$WRTUWh)3##3sBTd{briXD4>3>a)NFuL9({83Wo95(L zgoB~PfHm}$iG(Bttf)?}8MOPsOG-)!rwdaZsenXAWkwvQ)T7DrSlA~o?!;)alIc;^ znYYu@$0*@?DJ(Ui&Kpw4hEY<)*(p@ZyfMwYk$TN2DO_02Oo&tZj`Qe&=Epf*jxomG zUrI_6MIIkTWn~~)@fy^7)=s*o%`C}vZe30OWl_5Yb)2}Hv{ivPdicMlLRnu {_aE8aw(ZKTtK%{>jjB7X=XJ&p^ow9_+}}0f1uiR z=4kR(W#nVJb?q3 !+aYf+bpOX$d@eKfy!LuxQ~KZPdeY~6`BXHqpi2}w&2p^S%-LU(_t zO?Bqn)8qyJ{x}rmv#g0w&XH2-VMo&Ia7usrvt41KJX4m&pG*C H;YYN!fMgbG5;=Gz}zctRplGmQ^skOB_qHB92NKLAc^Xl7T z;r#7BbOS=%rEkc=s2Y8LiSM@jTPgSFPz|BdBGGfCIrnrEnmD?*xG9+mg;IIBv2^Mk zV^zi;8AcZRHH6=1PM9+`ASfcP(Bd9n(WC><(hAa_ynoSvpq6xZ9@Q~SZJ0BoI$s~8 zXKw_fZUcgXBCaf^9$!tO18*#tSH!_G4eQgwv r^TOlRk{<; zjVBvnJ?RYyY6^Pta}&*as?KuNiHG9AmXMR(cUN?JSgE&LPZNh*tPINau?Po46Uzzl zL%$9EnSL;&@Lojq)%#s@PFf= }O} ~_(63K**O7~LD}!i#CsUG2i&LLn=|`a+h lRO*)AzdMdURZkC z9e4N5M#pl7fa(?8w@pBsc0b@^mhYM`1czQraoYVZ_Ra1H?N8jXZ9*Hg`{5$~v)9un zyW{sC4#R5UHY{k8#bACcSCo+vh!)PBv_wrbsHi)1VnNiHJO}-)rBFQEi!G-z51^SU z4i^rc#0!T`=vL+JPwlHhj^gVM-{JX~tytk?&9BuH#JR1(z#+#_d(&BL8($YiB+MBQwBRS-+J$|d64Yo|U&v{77&XD%^N--MV+hI#(KV;bF~-=xrOrb&12_wL zWrp0+%v6(qN%Re1pp(f;OiW;`&evXsAyl2ODIVb9Ki)`@*`i`a^{CT2)PtyZssnw* z;_Bh!+U13u*P_oD^e~s;+qf8z^RuiC3`gv<(LKw#<+>ONIT*CLME(MQrHHzrF7Syi**af)@Mu)m`h&b_~yMxKa6cS zZ8H$PGmI1W?~X%S$_axzl>mht!M3B3+Mf$06{V7dH7gNxSlDKaI5&7Nwm)lw!OaS4 zXXK>p#`2YXbWV)m(0xAsalIf^>TIAdQ6c))5)A+0oN!6Mg^>fg&;JLm7laCojXor* zXxv&d96y|U>o49xi6 7VRh@BIwX8EQ7mOB!| zf+(QQ2nrg*NS#sWDo`KI>k5@8)hqaT`ezz&Qxtar2T#0!U8k<7`quia3ckA+W5exx zIJx^OvOKk47^Uxe4CwG3p7mUZU;DJcfZs3T+V)YXVXT1Pu5lRhi^rQI@vD5Xd+`R` zzIPJ4ufE7pCHlW&*8P82J)$xU!mnZV$VRBrWE?i!iAvi?RrDkHh7e5K%!kX_vpAdf zIghJ1Z^QNWCH&mcNL)HA|3R{uIsS{ZZxSIWl2bIt^Y4!(Uu6R9qm#k7NMXFXvjYnz zjlq<^?!vUHy-)yGHyvob^6-GaQV)iPnF`sN mo4D Ren1 +|dkXSs$!Ce={?{Y%|J(E(fva-tfu`B^2dFEhB#-ojs@N?4l*gn4*gw3)K zKyGCX3%LZU*U?$@BGE(1QpGea&!p6OGl!123kPxHMFn)~TJ0UZ3Z$?byBf0_8Nzqt zNDN!!{e{1#E(xcX&co3cwb0wS3ba*$0 vdvF>t`ic3qgEjz&_Cmw4n9Sn z)3LCRPbft1|GEk4j=7F%->t;2bB3e2WdWTcxC(Lci6BZqaA0WG4Ye9MU5+uv@&l>5 z^MX)-3`KD8o8=Xmu?Y$ZWqI|Dtb%D}dVM$!ABy?~lHvOI7Puxw;^ KlZ^KP 4NW_=OBLsqYB{!#Ycg6e}; z;)2H=?3~gJC6gax`*(A3SlCBt(b6bxDAc8H9>o!DU&V}6!Eo7k0a4;)e)c&X3;WoT zap$Mr82CpJ+AdgvuGU$llTs8bV=fe=6}Wfg5T0hyGLYnR1GmGF!{s Akj1lckq4L>@4SR%2ad-y|uA75iSJI*= zt<9Wq^oaP#VFoG$yX?b-_sN`dy>1!GIgdblQx$x6ZV>7 NxC%U?jDRF9{qhf1}oi}T(@!g@6q zfMx(m&8%EdA@uk(d~>Q1mapgz%M49tz^m&^uyEJ&Ebor99FSK--#(5gptz3{#{)Aa zRH_I(^^XLZG()G>CgMyT@fUF5L^#TR)eW_BkSNN_567C-gJ6?<7e5c2h`qsYCX1h< zY~x;1DBN#(;YDhLfV415mokPVB^b~AGt~kl#Zc5t0$FKmSZ3L!Lyq#p*I@M^8zkTT z83QNoMcxOa3IPXJVW*~>p1`Cs`Ybz)H3Q8diFyjZ7sC8P4bi+#LC~ucm_KEAdUr*Q zXxv=(J&q_Gpp9ihI-&W$F7-jaIai{m2R-$z+@wS#WcoTSt;ubolil*UI+m sZ()O7RHCj=3R0VhH2H=@&G`#Uoxg$10Ud6AubDZI5Oq62xCwiixWqB3`?ghAQMZ z<}AMk12bukm(xl@sQWE=r6oL!G4>&4l~?PK*`rtA1}G$?RM!q2&Qw-WBIvFgo=a@e zXK34uy$hrz(S1aBSRe?yCND*zlQSy5Q65U)c6uIL2o0OpK|y+j6Z5BFcgD;a(Knaj z`y&cCWKo>WO2%W${ko+kyJJLm3m|A0CNE8dlXJx^Zp3|v4|es>LZ2X{h-d$TzyyRm#F?-~$fR M|=^SV`RRoAUrHxe}J4+hp{OU-9SJSm>1>g4v@T#JQO% z5gZhP6yd_4lu!h{ahLmDREWGV8{GzOhp+EejP2MC?b@Y(+O%*)wVu~u-=%!UE;%e0 zWT-T55yn+g;_|NxaWp(Vg-`a{itX17P=Cr?49L)GsTHq&d?CCnI(DlqcEit1h2-QE zP2#9ZNfG{)qICBa*f^;w V;Y9(BqQfoL*8G;IOB?5L0L8F!< zFn8rztowZ-CUk0wv7TKpwO1)k;z+S^+6uHH1?t1qB1A^M(qy&CiHT_$uC%Bn%#EZ_ z_+7?XpLoQ2?Z@9|LN(thU%f(%I^BtTt@Ee>Ax@~gX;+`VcFlZs_i!PMUq`>rkj^C} zXt$S=EIw0`i(?TE265Vh(VjaIMMmP4df$>0k*NM!@UCNFpD3v@Obg2)QTSok($%=+ z>4B?zmf+j9u0o6u;`C!TxO-f~gQT9A^-WzEQy5N8Zi-s%hTyyTOR#9h5VUOf3kr`I zjcP)AAT8M^N6V?j9-Dx=HS42#!*6l!-J^yv#@HJuE>6?XnK3U`&yUySTk1z&UuVoL zwfR|!P-fB0kimZJFPPob6ftLh!A~dB$8J)Eoxsxd4`Dxk73Ma}FeI#oQR`7~t|&vC zq&}Q844#-uiC3@GgU||j_v=^UdZn;-o`NA2 i4DL`A^b%+? zaR_WRl_DuhbRCFJ#Z`zm?~Ea7gD2|9uI$G07x8Tjc_4{yOs_>1TJ)nnt!q)0+U=;% zhzT@dcz0@GX-Gz8+tAegembu(6C~ZB>9s0Qi++8mb*(B?yImg|F<}A?@7{nc4UMR5 zo2j(lFVpK;M6YjerFq} ~m zubI_QhF_;G^G8!N3%PLaC1g~kFU?zhlssad)AcpZWG`2rKM55q+k%G8*)RU;6O#0p zPH$L2GrHPQ0rfLH5<_z6F`Ir{e}G(H>IT?rIy}^ZWD-5H8FNaUr } z<0M-2$2$7sr!my3QWfel@09qyWF>`P-$L_8HzNzVMA(KARp~pARv-1CnCI7Njk7(; z)z^WfAeC(~jOOh3)~xxzmqNE^HzMou)v3*>1@zbUU9@S@P-@Y97_Gan`?9%2^4dP0 z8krkt(*xoB%S;@o+w4Q+tBFGj3OKil#x*XkepL-gjjK_=AGgu1myvXL`*iAHt*7~& ztaJ;Syyh~6Xu>Fl&QGmQ#_HF^%8kjc?|SlzdqQW|{zSv-m}%A{F|?=dvwx=(K9A_k z>Y3EJta?2X(zoqQGgi6K 8az5tTeTlyoN4o z4kn9Z$>Q2#pO)y0bE$C&Z8|77wxJdix6*@v{WQ>A$QLDYD%ElUT~wzT$`Cp|zcbkt zPR%dnRMc?@t-2bX@pW1`T<-PZp=2SGke ikPuoEnZ@PKO>wQOMPeG^?AoJS34@QoE@u>1b*O7VS#w zMl~U;66LAIpeZzG#whCAq!|rZc0tU?v!bBKKR2g}Lb)l!D{BRcCueExPs6B=nYw-; zAw&D_H2e1x NSEt%D<0F&%`4KFQ<;zA!ykwG)sOadf20Q)-cuPzXU0{cW=p-rS7qwT zY1xhC7ZUY9LS)Dj@qTUyC{|6=^+g%;4EOJPA?TGHrR=Jrid`wqfF>Em!_O@M#j2^h z ^|;#1>u#v6zr;2fnBLWS+oqYPm=;sk8a_nXDE`5EK$9_Bdm?yc6O5x;`snU za$7js73DbaE=eJ9yBCR4wJW1U=KgERk??W71+S2J$ck8_dhMF9GXC^JQxP8z6f6iy zOfY=BgOFra4wcFm&-zU%iYPqwc!U>HAq}y&hG`BSe|fh=gnN4+P-2BD_HWtd$MQKH z3;SqPvBB{2i$w_sdlVN>YEqaNJR?kDU#T>7?+dBoLf|drv8s}GaIiFa>)^E6FDInY zZubybsx~UWk?=6a*ndkE`yAfh0f^Nvg$k8yV3u976dM5lgfg(s_O%6B5z4bqJ@Dv- zRH)zB!}{N6&N_0p?$`i-B$Tnv{_P7{%Eh)+5eN(^085LEJvvn3{s=B)4a+w>dgQL` z#`0XAHXty@7-Nhv#u#Jl)4(@t8Doqw#u#IaF_tZEKwyk9#u#IaF~(T7xB-DN#u#Ia zF~%5U+2RHS#u#IaF~%5UjAe@(5Ex^OF~%5Uj4_riZa`p+F~%5Uj4{SowzvU-F~%5U zj4{R-W7*;c1jZO+j4{R-V~k~s8xR;{j4{R-V~jDDEeQ$4zjMvi)fF#Zybz}tV~jDz z7-Nhv_8}E7UL3V+*A}Pp*1u{%=+> PBF$9V~jDz7-Q^1YS*qE4jw!xPUWqC z)qublV~jDz7-NjFF9VN(z!+nUF~%5UjInHS0|H}=F~%5Uj4{Tt#SI9IF~%5Uj4{R- z%N92vFvb{Tj4{R-V=P JKqA6Jqo5S0w2}~f>m)Aw#){J)SW+- !?4TcszgL2LB{+k}<{@dk@@z zz!+nnEES^eY{2+tRw!yy4_|d|2KzF$sMl>QX8$l09h}$WsZI-m;@%>3YTN #DFYuY!K-eT1WZ8wEV~%*B{)4N%G64t93J$KD?H>QChwsN19+dX4!3zyITbd|w}r zzKu=O2BB^BGBB^y2kX4we$<}~#Y6nuv7+|8 {rJ5*T+zK_TTS2>e87!KQ!=lB`uuY16N9Fh5$ZETv z37G|&kNp87tEmu~NaD=5k)u-YAMw|Y`DmK-1dks-L0rS7xaH-EyXW^~)s#*s<+B;z zcB_u^b;n`v(|lYhH+95->7!5s5lG1Hus;>5Xaqm@LqxImn7VQs4!NAdnX@kV*8Vws zANwNMZZcfXp1~=XL)fu;Cc0U?!js2A;*b9g;reJe0zWP82kXT6Og)kqV~nxf;|2uA z82cn8;o={^;^M1P=rQ#>)GsO~=LS}2_roEaTjPY1FGCQbQ%{sy^ub2YFg!gr2~|Hr z1A;}LjqnV6ier s=&cqCeHcLvfgg0WUs$4cip2;wBjU%Oy3lh?8-nQ z&dHWRIoOs_#{~%rnHGVeRE9z&Y*DZ4M65b-96vTRMAV%%7&-D!_~qL`kwV|BGVClq zqaq3=AxYjG^R}(Pi1u|+$<_u|mZf1@Kzn8-2Bs)&X$2eGN~qm>2>!QqA=(xAK+hUT zrI44XjEbc`)-}i&W9&=94G4@e_9;?D!RJv3loXH1h&XX(Dj970&&TLWQ3#F558ag| zkmk4s`~k&W#r%OYa3bte?gF37Y+53N>}@|WsDh#W>qB3n!t2Wia5R{^vj1l&8S*;a z(ak1jm3XN|XLPHpFHU_35=ipm8f1(y_SxYE1jZQq6iExAh*?2MR8csxU>XhuXS$s% zcYyQcuCP#M?gO5TaCZ*`zEP)6Mwpu$o{2X-6T>`k>9`B7`Mt_8a9?tSH?EvK3)g_y ztOxW<4u`u(V6GllP{lom`}H&U=g?7Ha0|_P9D}4#czA~i+f*Ui=OT`sypIg+0-3iP z?}d%Sow4+89Fk*SASf^p!7pME8xf2^;qTN>@bk#D?Wy7-5S%(cB3_*PE-J)5cgOWJ z|KQNk3vdg~dISbloImckhiJnqIRf5SPU5VqKVsfme^((KVbraRr`XIoXe#lA>_Y zA}|pCB8o>`d}`dt*>Pld4svuJ5s$!!-gu!o_C$oaU%<&r9teBOCetM$)a^Wu2 -7Lgl}ZBIP)C}e1(-;#=tVC zr7meH4Bz~R=Fbnh-#QdAVQKMQ^9c?|Y+AtPetnGnpS|;di{kj=`0olJ2vSs32t`rA zUa%`Dc4Nh^QDco=6UCTlY^X76{M)fbjmF-M1$zrtL htx;qTCLE{ zr4yQ0tBnDFJw@u1!}zIl9aM8@jV@i=p`o1vhV2Pf59E>&;<+6QMt4RX3rjTk=`8ZD ziO3g4G_EY|4F|{ZIPfS1;?w}F>r)kWotME|89*|ES-1 `mXfD(|qHV!Eeu69D9-i1<4V5dMSRIHVxA^Ud59;e_@hyWms8S!KVFm9K87y zaq5A*>~#P<=6sFHwFhGLt=ENadr(AQ!P4Fia2$UCk5a@)4OoXhRqfDunYTI(ay&V) z7?XN8M-}rb=&;xmfd?nUv62<)ICn&whW4oY%~m|l6^R0omwrQ!It?&z)df6$dKK%( z_Q0sUPuZRKfgDdCK2h2gkd&*9dX^Fm-lM282!g!Ga%7 1*&vjj%&Bp zp|4$8n6q#?v=EF$*2AdXL9FE7grYt1YbSfyHtmAmJ-ebs&8ld-#9KX(en06`lKudH zjckpczg|Tw>m9v3ISnl=D`DutoRc_-@Za?#`t})zMcWVK;L^dUSKbWuNA19a_kFs- zdrkM7q`rEHle5~w#-=g)yKTqmYYz}9yu(|D$Kh}|oG%38a5x;!r-}SEotxf-OvEDL zQ! X zcHN1jG wNg*bqdR=M4qN8+qzZ>;w+JwgJ3#11H#w(+sCCy0-TGU{kKfB5%YX3J>K@8Gz zm_xpq5i3cf8$Z>ix-31yYZXiqog8IJ60Mpv-772Qw9Tayi4D8Zrf}6e%4De&vi@r_ z6p5&WWdrK{^B(efokkgv7wJcb5+ss*MI%lmD*Xzi+*&}*jLoU%pHHgJchAWg8IGisQz5;>6;cz}x#HG<{!5LiH?TR*5x)3bA z`|q2DZVrtwXzeY;sl5q{&5SEIK$F&OP_JAbwJ()IyOcd_1;cyd^62#A9Q3PG7Q*i9 zlC^uGQ*|li5%H+^^Y7?mqo;gCuWEO6VD7TVH*rH)i!L@s fw=cb z^&MRz(SfP04N8j%3cF!gJ--iXmDYx|Y;z3jSqD;i1iY_5R(*AsKf%Ty7viS*U`+d_ zVzx0zNoDlxTK6qZmrx+q_Xsv_*nqVwe#KA2JD_&s?{Flc3VJO%g549Uu`o4wkD|(; zM>%8^RgUbsM1`to+}RO!+HAP2-vX}gGtsMoHO%cCF>LiAEbmg9#p#9hYcJ+rqAx0i z)^oSPt(zT;rD7O08i@gow4g}zgLgpsdrO~?Um<~ykPv$xK94e&66m1rkilpuNmH!= zNji_kb56r%;80W-_JIh9&Cs=bbCg61?(W+UA5~!Qg(B;R(nW)GzX2io%5NCxdKWHR z4`7CKd1PB=c$@Gz91e%`CBY{UI2;b=lcpr>doRWrzuVY9vlA)_^9XT%*gn23+W&GX z_nH49v0#w>O$2wPg*6Eq^s1bch>c-Xin+5vjE+u@2StW3G+^%Z8WAtEBM`B`i_@qW z_uhs_5zle)=NifZvMllr4xjKrJQ0!-64)?Djab6M6`_>14a(;eL2=`Sc0G(5-wuX~ zXV|rJFT&NYkam49PMh{Yf7_xA-5I0zwnvC~ei1*{P^KJ_cMju(Pdtc{kdTm-a#h-t zS*Q)|tVD`nY+Q={OGr*mRu0PKkM>~4Kglqx+c3`w8DdDawA6nW4@L4TJh|_Wz-Q6G zSI(HU_B8yS`eC1IbCgx4Mf3M4tPC_RM;3xIFLZQKGUq#rEzoOVdzgsj2=}^*$2kY# zq7o=&m}5FeTpDFfv>+1Fm-s%@CxuO&nlMhgfw^s4VcaG!2=+%MmB!$w!KO<2O1pjt z9-#^ZZ0wJ=ty`nD`qyhqB&s?%pteye3Njz@HoUKN=SG3>W0TNv$_tF%whS&-ISWFd zxZ!a)91iD8gSQ174u|tOpl^>JZhzy}MK&m~)`mzCi<^sv =Tao#&IZ{ldUuDcDRnsz9q+>!BbB8J6)@6q2{gxFJSu+dK#v8Lk6 z-ixT%uMaFWF_bScfJv!1c<-EtiKAVy?zRzX+LdAhTlM`yd9&$FyL%e}G7$_6jS9%~ z;v$Ag;}KZ6Yz0;<`yIc{orW>}TEX5V&&$2jdt~zN<)~MqIx4ZGQ9>+Yix4SM=5YcV z2d`QBjC=eB_Yu{h6L}TxBb%Xy<5+C+j!}PgS0MJ`V}z3t+PZn*+_`hvzYAAz!|N8j zF8_{B`by95iT9E2JSZMx&%#acx^oJ9uDr_EPqD(|a5x;!7YCm};BYvcPmt8RN8$0t zLH=SGH=KmsCzha%iI|P=pW@K&OX_ }@8vz-Lph4{^q|Wg5E+^v?;OWFqiOmimgu?O6P{ZppoN(j5m(n@ zq*G%I*{(9~n39sl2H;V69-RARl7Ef&l gj!kW-_n761K-bd0)mMx2t zVg+LT?%?5nHA;Ig@9}=hQ4#qHQ6xe6dW~T7UMCPVO`ighK6eo;H$?4kSK^|-H+H$U zK)Lis*fC``j>L&kx b=^1jlo_Lc*k+OF8Lq!Y@% z_Q2GEv*4M_Pw~U!a5x;!mj$0d;BYvcPY|t26(K!18w)PvH(-%LCMQ6og=+O`tKArs z`E8qu{gpfG(L%@$UIWjvhd6%R7bzlL7?jH1hfPVzNM?^K6pDP(n18tW$18%CmX`8s zZCYwd{!f cGW%D-(w;N6 z{PJ2Dedcy8(WE7s7>FSA-Gz;p6V$E(3drTEl_sSAf1!|^mA&duArA|qc<+%(9=EqD z$Gpf9eAyF^VUGbroS?E#nEg~ xV0UMg40-($MpM2gD8 z*Ta3VHPUbYjzJUlAuLN&-X=T_hr{7~N$?2-4u`|}1d&+Uz*6Lg6$8J)BF_jm1j{D& z`f{v2mk6!OLojocz1o!_k3*bb7>|+=AD3fr$*z-xQ3)FoWn`$FlrZ~ow+5)# #BDC~aW|eX$Hd|D3~v EFvJE%Y{1P1`$4<0*!?mB{I6i|qRH5Biw!9$8jpPa z;D7ZFSkgg>Nk|c1S(cF_?d<)cg{=yN^wd=LFAK9mKm7Pz4d}=pVeNqKn0+(=uhM0R zzPlasx87lSB1h<@?fCP+H3S#7LO~vmm#>5n6^IH8Lyj{h-k><|QD7MqqZ~6)y!OV$ z+p+2^AU$X&rmwt%D&v=9T4VL`27=5a35A-7*qp+oNMN$aQbNf{O3coP_mV!@9c**I zkmK|cU6eL132j>^I9W;{wi*c6AypuihhX=RW@t5HHdb!fh}HAQqqEZ(1a$ooz4Np3 zSrGCtgobCZR1h*=h9WFy6?>2AR-#BpdWK3alyzKO0X>#>ARZ1n(G$Zm!4iA~SwX z!sBo_9L|@E>NH0Vhr{7~Qlwp{pXya1=l*@EO}!dazkOdCF@8J^@6m)TboHrX+aKvb zP@3AajGmm^KnsR7AY(BbeTa0)uE$JTeeyA-J~>P7)4NbbEfI6qCYvrl(<+Zglp1o0 z{+QXFs^|z7+r(71=@?pi_#VZCU!l!2x|3}Q!M~I&+W$x^kDMWo4GU;k17qQAU=r!t zQIDCc>Ez=ydU@?HS}?K+nM#CswaB{TR9b$>m*QEfVlU06=B85RIrvfoYjU2jjeJ55 z&>%AjiCEa>ou|@@#je!dz9jR*!qu}Um+32Luh(nU8Thg%bS~d`)u-ksT%aFo8>qv# zrG9_hr_AH~vq_*GJx$1Awoft6@Q ;ZPl |;7s4I|A&-E^QS$b1Fp_wsVi2d zS)!#!U$vb_M;5gu{nAyb&Cr>&;dCIys*dr`Lk0!xn@i)nS0^K(jD`HrwxE{7rqRNK z_X~J84c=q!0XnjJYQbe7gi=&FwwgzmUNhfGyQr7Gh{UCvQjhMA 6>)X{e7e_ zjl5 VP4vCR^pLUFEMrO)-A;`3Z4DGwq%)_iM zsl60UJb7)KLqGJdPi0v?E6Yk!n(FnQNV7KnLlNqPzn^r=De~G@avRlx%$4P=Pc{0@ zA@^hcl9#g ;zqil|{<4q8#+c&%Rn>1XyYqhDL!)v`-i_Yn2H=p%D4cw- z4Ap~t5hk%lO?y*yy#F4Wq)(Qd4Cv`Wln{mofuTq gOM-qJKdV_zQf~iI2_LB1)o6Ra5x;!7fbA+A#mE=4(E0aK-o`W#^qBc=LEvT zwP?dfq#O>1!{K~#@a__a!{Kng9AqzX CayyTCN2mA5k93QkB--Ayc{3oal1?Dvv z$l-7}91iD`fp?iW91e%`#gOXpGrIK|h@LHLz^dv1TxzxuGaNNDp2&GO6!9^L&0zme zc!kJhwIhea;cz&dPYTH#hWc*~hr{7;K5N8fjgS`Ti!hNTnh#%&zZP|Y88 z;cz${4u`|xa5x+er%3n&0*Axla5x+ehr{7;IGiGR zr(+z44 Q_yhun!{Kl^91e%W;cz&dBH QMsx8}~_voL|;K@>~`0FGt`oBVk`i(>)5&KsPJwp?iT2(`Rr%o6!uoJ2qOH>|| z$Plo19#-x=jm!6=AQPTt&k0XLstZGta U zJd9AlxO{0yW1rz!f)VT+I-+@9Lj>##K!>v@Fu|P9*<_Q3r^gp#%1 }JmtG54^4#e6LMBMD>f9K&~I)i(-7f$;4^(SF`F6r7DtaJ#et&H;a5>BfUNd;SW1 zA{$|4@M(-K&*k~wAl7RO7OvWblV?0}-*6l*`7S{dZS_qK=d&XLhXz$aU(YUBKHU^| z4xh$9kK>>yKqqA(sf6LXj$v9IsoLRf^&p4C;cz${jS(ggJ{a;~TGdLA4I4oa8B)jf zAtYDd$`^SEt?y{4@(&4FeRq-))wh(SUZU@-NZCCx8Fkr6p&3N7l-Cq={V*;2t}&IB zh)7zxF-<-3^38aXPSYqWi85TXA%`heE~CVV$8`PhN*dqNoU}v|GI1C~d!MjCvq+`D zU1O-ev4o_iO=-gB>lB`*zQst3@S?2~n=0dlRulT|flPge!%BvpZtF*N8V#oTe;%c? zXAaZG>0QW1S41MMDm4CVu9D3M83itGM!H%?WNurHYSgHa>t}DSPhy>BwD^&b=W>!~ zgwdZaWk?KdYP}}Ba5?+bNVreO+~?5f4pqoN%<6_tds-i 2oKw8{Pm$Zz`$+lYV zJgiZ}p3L>dq| @PT3R5fohx0