From 60129a0bf46a271571c6bee21bfcb30978530354 Mon Sep 17 00:00:00 2001 From: ues1df <140159870+ues1df@users.noreply.github.com> Date: Wed, 16 Aug 2023 17:27:55 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E7=9A=84=E9=87=8F=E5=8C=96=E9=80=9A?= =?UTF-8?q?=E8=AE=AF=E7=AE=97=E6=B3=95=E5=92=8C=E7=A4=BA=E4=BE=8B=20(#813)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 增加浮点数量化方案 * doc: 在拆分学习中使用通讯压缩算法 * doc: 优化示例排版 * doc: 优化排版 * feat: 增加quantizedkmeans和fp8-E5M2 * bugfix: fp8实现修改 * bugfix: 修改qfp代码,添加qfp测试 * doc: 加入压缩模型损失和时间对比 * update: 修改测试代码格式 * update: sort imports --- .../SL_Training_with_compressor.ipynb | 964 ++++++++++++++++++ secretflow/utils/compressor.py | 115 +++ tests/utils/test_compressor.py | 45 + 3 files changed, 1124 insertions(+) create mode 100644 docs/tutorial/SL_Training_with_compressor.ipynb create mode 100644 tests/utils/test_compressor.py diff --git a/docs/tutorial/SL_Training_with_compressor.ipynb b/docs/tutorial/SL_Training_with_compressor.ipynb new file mode 100644 index 000000000..a941f7f1a --- /dev/null +++ b/docs/tutorial/SL_Training_with_compressor.ipynb @@ -0,0 +1,964 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 在拆分学习中使用通讯压缩方法\n", + "\n", + "> 以下代码仅作为示例,请勿在生产环境直接使用。\n", + "\n", + "> 本示例基于基于“拆分学习:银行营销”教程制作,建议先观看那个教程。\n", + "\n", + "在拆分学习中,由于模型被拆分在多个设备当中,进行训练的时候,各方需要对特征和梯度进行多次传输,带来很高的网络通讯消耗。为了减少通讯过程中的数据量,可以进行一些压缩处理。\n", + "\n", + "SecretFlow提供了Compressor对拆分学习中的数据进行压缩。同时也提供了多种基类,可以在此基础上实现自己的压缩算法。\n", + "\n", + "下面我们来试试一些算法的可用性,首先,我们在secretflow环境中创造2个实体alice和bob。" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2023-08-16 01:43:59,294\tWARNING services.py:1732 -- WARNING: The object store is using /tmp instead of /dev/shm because /dev/shm has only 67108864 bytes available. This will harm performance! You may be able to free up space by deleting files in /dev/shm. If you are inside a Docker container, you can increase /dev/shm size by passing '--shm-size=3.92gb' to 'docker run' (or add it to the run_options list in a Ray cluster config). Make sure to set this to more than 30% of available RAM.\n", + "2023-08-16 01:43:59,444\tINFO worker.py:1538 -- Started a local Ray instance.\n" + ] + } + ], + "source": [ + "import secretflow as sf\n", + "\n", + "sf.shutdown()\n", + "sf.init(['alice', 'bob'], address='local')\n", + "alice, bob = sf.PYU('alice'), sf.PYU('bob')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "接下来我们准备要学习的数据。\n", + "\n", + "我们使用“拆分学习:银行营销”中的数据准备和处理方法,下载银行营销数据集并进行处理。alice和bob的角色和之前的教程完全相同:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[2m\u001b[36m(_run pid=27337)\u001b[0m /usr/local/lib/python3.8/site-packages/sklearn/base.py:443: UserWarning: X has feature names, but MinMaxScaler was fitted without feature names\n", + "\u001b[2m\u001b[36m(_run pid=27337)\u001b[0m warnings.warn(\n", + "\u001b[2m\u001b[36m(_run pid=27337)\u001b[0m /usr/local/lib/python3.8/site-packages/sklearn/base.py:443: UserWarning: X has feature names, but MinMaxScaler was fitted without feature names\n", + "\u001b[2m\u001b[36m(_run pid=27337)\u001b[0m warnings.warn(\n" + ] + } + ], + "source": [ + "from secretflow.utils.simulation.datasets import load_bank_marketing\n", + "from secretflow.preprocessing.scaler import MinMaxScaler\n", + "from secretflow.preprocessing.encoder import LabelEncoder\n", + "from secretflow.data.split import train_test_split\n", + "\n", + "random_state = 1234\n", + "\n", + "data = load_bank_marketing(parts={alice: (0, 4), bob: (4, 16)}, axis=1)\n", + "label = load_bank_marketing(parts={alice: (16, 17)}, axis=1)\n", + "\n", + "encoder = LabelEncoder()\n", + "data['job'] = encoder.fit_transform(data['job'])\n", + "data['marital'] = encoder.fit_transform(data['marital'])\n", + "data['education'] = encoder.fit_transform(data['education'])\n", + "data['default'] = encoder.fit_transform(data['default'])\n", + "data['housing'] = encoder.fit_transform(data['housing'])\n", + "data['loan'] = encoder.fit_transform(data['loan'])\n", + "data['contact'] = encoder.fit_transform(data['contact'])\n", + "data['poutcome'] = encoder.fit_transform(data['poutcome'])\n", + "data['month'] = encoder.fit_transform(data['month'])\n", + "label = encoder.fit_transform(label)\n", + "\n", + "scaler = MinMaxScaler()\n", + "data = scaler.fit_transform(data)\n", + "\n", + "train_data,test_data = train_test_split(data, train_size=0.8, random_state=random_state)\n", + "train_label,test_label = train_test_split(label, train_size=0.8, random_state=random_state)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "接下来我们创建联邦模型,同样地,我们使用“拆分学习:银行营销”中的建模,构建出base_model和fuse_model,然后就可以定义SLModel用于训练:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2023-08-16 01:44:03.512175: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcudart.so.11.0'; dlerror: libcudart.so.11.0: cannot open shared object file: No such file or directory\n", + "2023-08-16 01:44:04.209189: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer.so.7'; dlerror: libnvinfer.so.7: cannot open shared object file: No such file or directory\n", + "2023-08-16 01:44:04.209381: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer_plugin.so.7'; dlerror: libnvinfer_plugin.so.7: cannot open shared object file: No such file or directory\n", + "2023-08-16 01:44:04.209397: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Cannot dlopen some TensorRT libraries. If you would like to use Nvidia GPU with TensorRT, please make sure the missing libraries mentioned above are installed properly.\n", + "INFO:root:Create proxy actor with party alice.\n", + "INFO:root:Create proxy actor with party bob.\n" + ] + } + ], + "source": [ + "def create_base_model(input_dim, output_dim, name='base_model'):\n", + " # Create model\n", + " def create_model():\n", + " from tensorflow import keras\n", + " from tensorflow.keras import layers\n", + " import tensorflow as tf\n", + " model = keras.Sequential(\n", + " [\n", + " keras.Input(shape=input_dim),\n", + " layers.Dense(100,activation =\"relu\" ),\n", + " layers.Dense(output_dim, activation=\"relu\"),\n", + " ]\n", + " )\n", + " # Compile model\n", + " model.summary()\n", + " model.compile(loss='binary_crossentropy',\n", + " optimizer='adam',\n", + " metrics=[\"accuracy\",tf.keras.metrics.AUC()])\n", + " return model\n", + " return create_model\n", + "\n", + "# prepare model\n", + "hidden_size = 64\n", + "\n", + "model_base_alice = create_base_model(4, hidden_size)\n", + "model_base_bob = create_base_model(12, hidden_size)\n", + "\n", + "def create_fuse_model(input_dim, output_dim, party_nums, name='fuse_model'):\n", + " def create_model():\n", + " from tensorflow import keras\n", + " from tensorflow.keras import layers\n", + " import tensorflow as tf\n", + " # input\n", + " input_layers = []\n", + " for i in range(party_nums):\n", + " input_layers.append(keras.Input(input_dim,))\n", + "\n", + " merged_layer = layers.concatenate(input_layers)\n", + " fuse_layer = layers.Dense(64, activation='relu')(merged_layer)\n", + " output = layers.Dense(output_dim, activation='sigmoid')(fuse_layer)\n", + "\n", + " model = keras.Model(inputs=input_layers, outputs=output)\n", + " model.summary()\n", + "\n", + " model.compile(loss='binary_crossentropy',\n", + " optimizer='adam',\n", + " metrics=[\"accuracy\",tf.keras.metrics.AUC()])\n", + " return model\n", + " return create_model\n", + "\n", + "model_fuse = create_fuse_model(\n", + " input_dim=hidden_size, party_nums=2, output_dim=1)\n", + "\n", + "base_model_dict = {\n", + " alice: model_base_alice,\n", + " bob: model_base_bob\n", + "}\n", + "\n", + "\n", + "\n", + "from secretflow.ml.nn import SLModel\n", + "sl_model_origin = SLModel(\n", + " base_model_dict=base_model_dict,\n", + " device_y=alice,\n", + " model_fuse=model_fuse,\n", + " )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 使用通讯压缩算法\n", + "\n", + "SecretFlow提供了Compressor,里面实现了各种基础的通讯压缩算法,可以直接使用。\n", + "\n", + "只要导入想使用的压缩算法并实例化,定义SLModel时将实例化的方法作为参数传入就可以在训练中实现通讯压缩。\n", + "\n", + "我们以QuantizedFP为例,该算法会将浮点数量化到8位以降低传输消耗。" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:root:Create proxy actor with party alice.\n", + "INFO:root:Create proxy actor with party bob.\n" + ] + } + ], + "source": [ + "from secretflow.utils.compressor import QuantizedFP\n", + "\n", + "qfp = QuantizedFP()\n", + "\n", + "sl_model_compress = SLModel(\n", + " base_model_dict=base_model_dict,\n", + " device_y=alice,\n", + " model_fuse=model_fuse,\n", + " compressor = qfp #在这里传入实例化的compressor算法\n", + " )\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "我们分别对没有使用通讯压缩的模型和使用了量化压缩的模型进行训练,并把训练轮次拉高到40轮,看看效果如何。" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:root:SL Train Params: {'x': VDataFrame(partitions={PYURuntime(alice): Partition(data=), PYURuntime(bob): Partition(data=)}, aligned=True), 'y': VDataFrame(partitions={PYURuntime(alice): Partition(data=)}, aligned=True), 'batch_size': 128, 'epochs': 40, 'verbose': 1, 'callbacks': None, 'validation_data': (VDataFrame(partitions={PYURuntime(alice): Partition(data=), PYURuntime(bob): Partition(data=)}, aligned=True), VDataFrame(partitions={PYURuntime(alice): Partition(data=)}, aligned=True)), 'shuffle': True, 'sample_weight': None, 'validation_freq': 1, 'dp_spent_step_freq': None, 'dataset_builder': None, 'audit_log_params': {}, 'random_seed': 11819, 'audit_log_dir': None, 'self': }\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[2m\u001b[36m(pid=28114)\u001b[0m 2023-08-16 01:44:08.296739: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcudart.so.11.0'; dlerror: libcudart.so.11.0: cannot open shared object file: No such file or directory\n", + "\u001b[2m\u001b[36m(pid=28127)\u001b[0m 2023-08-16 01:44:08.551930: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcudart.so.11.0'; dlerror: libcudart.so.11.0: cannot open shared object file: No such file or directory\n", + "\u001b[2m\u001b[36m(pid=28181)\u001b[0m 2023-08-16 01:44:08.767248: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcudart.so.11.0'; dlerror: libcudart.so.11.0: cannot open shared object file: No such file or directory\n", + "\u001b[2m\u001b[36m(pid=28235)\u001b[0m 2023-08-16 01:44:09.014466: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcudart.so.11.0'; dlerror: libcudart.so.11.0: cannot open shared object file: No such file or directory\n", + "\u001b[2m\u001b[36m(pid=28114)\u001b[0m 2023-08-16 01:44:09.160525: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer.so.7'; dlerror: libnvinfer.so.7: cannot open shared object file: No such file or directory\n", + "\u001b[2m\u001b[36m(pid=28114)\u001b[0m 2023-08-16 01:44:09.160694: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer_plugin.so.7'; dlerror: libnvinfer_plugin.so.7: cannot open shared object file: No such file or directory\n", + "\u001b[2m\u001b[36m(pid=28114)\u001b[0m 2023-08-16 01:44:09.160713: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Cannot dlopen some TensorRT libraries. If you would like to use Nvidia GPU with TensorRT, please make sure the missing libraries mentioned above are installed properly.\n", + "\u001b[2m\u001b[36m(pid=28127)\u001b[0m 2023-08-16 01:44:09.418021: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer.so.7'; dlerror: libnvinfer.so.7: cannot open shared object file: No such file or directory\n", + "\u001b[2m\u001b[36m(pid=28127)\u001b[0m 2023-08-16 01:44:09.418136: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer_plugin.so.7'; dlerror: libnvinfer_plugin.so.7: cannot open shared object file: No such file or directory\n", + "\u001b[2m\u001b[36m(pid=28127)\u001b[0m 2023-08-16 01:44:09.418152: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Cannot dlopen some TensorRT libraries. If you would like to use Nvidia GPU with TensorRT, please make sure the missing libraries mentioned above are installed properly.\n", + "\u001b[2m\u001b[36m(pid=28181)\u001b[0m 2023-08-16 01:44:09.654066: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer.so.7'; dlerror: libnvinfer.so.7: cannot open shared object file: No such file or directory\n", + "\u001b[2m\u001b[36m(pid=28181)\u001b[0m 2023-08-16 01:44:09.654235: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer_plugin.so.7'; dlerror: libnvinfer_plugin.so.7: cannot open shared object file: No such file or directory\n", + "\u001b[2m\u001b[36m(pid=28181)\u001b[0m 2023-08-16 01:44:09.654257: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Cannot dlopen some TensorRT libraries. If you would like to use Nvidia GPU with TensorRT, please make sure the missing libraries mentioned above are installed properly.\n", + "\u001b[2m\u001b[36m(pid=28235)\u001b[0m 2023-08-16 01:44:09.871219: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer.so.7'; dlerror: libnvinfer.so.7: cannot open shared object file: No such file or directory\n", + "\u001b[2m\u001b[36m(pid=28235)\u001b[0m 2023-08-16 01:44:09.871317: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer_plugin.so.7'; dlerror: libnvinfer_plugin.so.7: cannot open shared object file: No such file or directory\n", + "\u001b[2m\u001b[36m(pid=28235)\u001b[0m 2023-08-16 01:44:09.871333: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Cannot dlopen some TensorRT libraries. If you would like to use Nvidia GPU with TensorRT, please make sure the missing libraries mentioned above are installed properly.\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m 2023-08-16 01:44:11.224977: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcuda.so.1'; dlerror: libcuda.so.1: cannot open shared object file: No such file or directory\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m 2023-08-16 01:44:11.225041: W tensorflow/compiler/xla/stream_executor/cuda/cuda_driver.cc:265] failed call to cuInit: UNKNOWN ERROR (303)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m Model: \"sequential\"\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m _________________________________________________________________\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m Layer (type) Output Shape Param # \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m =================================================================\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m dense (Dense) (None, 100) 500 \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m dense_1 (Dense) (None, 64) 6464 \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m =================================================================\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m Total params: 6,964\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m Trainable params: 6,964\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m Non-trainable params: 0\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m _________________________________________________________________\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m Model: \"model\"\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m __________________________________________________________________________________________________\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m Layer (type) Output Shape Param # Connected to \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m ==================================================================================================\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m input_2 (InputLayer) [(None, 64)] 0 [] \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m input_3 (InputLayer) [(None, 64)] 0 [] \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m concatenate (Concatenate) (None, 128) 0 ['input_2[0][0]', \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m 'input_3[0][0]'] \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m dense_2 (Dense) (None, 64) 8256 ['concatenate[0][0]'] \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m dense_3 (Dense) (None, 1) 65 ['dense_2[0][0]'] \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m ==================================================================================================\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m Total params: 8,321\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m Trainable params: 8,321\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m Non-trainable params: 0\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28114)\u001b[0m __________________________________________________________________________________________________\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28127)\u001b[0m 2023-08-16 01:44:11.487105: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcuda.so.1'; dlerror: libcuda.so.1: cannot open shared object file: No such file or directory\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28127)\u001b[0m 2023-08-16 01:44:11.487150: W tensorflow/compiler/xla/stream_executor/cuda/cuda_driver.cc:265] failed call to cuInit: UNKNOWN ERROR (303)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28127)\u001b[0m Model: \"sequential\"\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28127)\u001b[0m _________________________________________________________________\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28127)\u001b[0m Layer (type) Output Shape Param # \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28127)\u001b[0m =================================================================\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28127)\u001b[0m dense (Dense) (None, 100) 1300 \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28127)\u001b[0m \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28127)\u001b[0m dense_1 (Dense) (None, 64) 6464 \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28127)\u001b[0m \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28127)\u001b[0m =================================================================\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28127)\u001b[0m Total params: 7,764\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28127)\u001b[0m Trainable params: 7,764\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28127)\u001b[0m Non-trainable params: 0\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=28127)\u001b[0m _________________________________________________________________\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + " 0%| | 0/29 [00:00), PYURuntime(bob): Partition(data=)}, aligned=True), 'y': VDataFrame(partitions={PYURuntime(alice): Partition(data=)}, aligned=True), 'batch_size': 128, 'epochs': 40, 'verbose': 1, 'callbacks': None, 'validation_data': (VDataFrame(partitions={PYURuntime(alice): Partition(data=), PYURuntime(bob): Partition(data=)}, aligned=True), VDataFrame(partitions={PYURuntime(alice): Partition(data=)}, aligned=True)), 'shuffle': True, 'sample_weight': None, 'validation_freq': 1, 'dp_spent_step_freq': None, 'dataset_builder': None, 'audit_log_params': {}, 'random_seed': 50480, 'audit_log_dir': None, 'self': }\n", + "100%|██████████| 29/29 [00:03<00:00, 7.41it/s, epoch: 1/40 - train_loss:0.4217776954174042 train_accuracy:0.8659462332725525 train_auc_1:0.5435447692871094 val_loss:0.40626364946365356 val_accuracy:0.8729282021522522 val_auc_1:0.5905393362045288 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.59it/s, epoch: 2/40 - train_loss:0.3423333764076233 train_accuracy:0.8874446749687195 train_auc_1:0.6285374164581299 val_loss:0.3637339770793915 val_accuracy:0.8729282021522522 val_auc_1:0.670577883720398 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 16.02it/s, epoch: 3/40 - train_loss:0.31453219056129456 train_accuracy:0.8949353694915771 train_auc_1:0.6967648267745972 val_loss:0.35318124294281006 val_accuracy:0.8729282021522522 val_auc_1:0.7181453108787537 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.70it/s, epoch: 4/40 - train_loss:0.2924026548862457 train_accuracy:0.8968473672866821 train_auc_1:0.771354079246521 val_loss:0.3476685583591461 val_accuracy:0.8729282021522522 val_auc_1:0.7567088603973389 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.96it/s, epoch: 5/40 - train_loss:0.3236430585384369 train_accuracy:0.8690732717514038 train_auc_1:0.8049758076667786 val_loss:0.32425957918167114 val_accuracy:0.8729282021522522 val_auc_1:0.8028783798217773 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.77it/s, epoch: 6/40 - train_loss:0.2683410346508026 train_accuracy:0.8920454382896423 train_auc_1:0.8347899317741394 val_loss:0.3059132695198059 val_accuracy:0.8696132302284241 val_auc_1:0.8184260129928589 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.99it/s, epoch: 7/40 - train_loss:0.24226166307926178 train_accuracy:0.9022727012634277 train_auc_1:0.850990891456604 val_loss:0.30843329429626465 val_accuracy:0.8729282021522522 val_auc_1:0.832201361656189 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.66it/s, epoch: 8/40 - train_loss:0.23420202732086182 train_accuracy:0.9053977131843567 train_auc_1:0.8667846322059631 val_loss:0.2918694317340851 val_accuracy:0.8795580267906189 val_auc_1:0.8382883071899414 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.87it/s, epoch: 9/40 - train_loss:0.24281850457191467 train_accuracy:0.8993362784385681 train_auc_1:0.8600778579711914 val_loss:0.28592929244041443 val_accuracy:0.8773480653762817 val_auc_1:0.8522564172744751 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 16.07it/s, epoch: 10/40 - train_loss:0.25411662459373474 train_accuracy:0.8985795378684998 train_auc_1:0.8763052225112915 val_loss:0.27862876653671265 val_accuracy:0.8795580267906189 val_auc_1:0.8518161773681641 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.97it/s, epoch: 11/40 - train_loss:0.2467927783727646 train_accuracy:0.9008620977401733 train_auc_1:0.8637750148773193 val_loss:0.27538853883743286 val_accuracy:0.8850829005241394 val_auc_1:0.8585635423660278 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.81it/s, epoch: 12/40 - train_loss:0.24046260118484497 train_accuracy:0.9030172228813171 train_auc_1:0.8943703174591064 val_loss:0.2793208956718445 val_accuracy:0.8872928023338318 val_auc_1:0.8582884073257446 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.74it/s, epoch: 13/40 - train_loss:0.2232421338558197 train_accuracy:0.9109513163566589 train_auc_1:0.9031308889389038 val_loss:0.27965837717056274 val_accuracy:0.8773480653762817 val_auc_1:0.857512354850769 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.98it/s, epoch: 14/40 - train_loss:0.2226562350988388 train_accuracy:0.9120911359786987 train_auc_1:0.8835855722427368 val_loss:0.28520363569259644 val_accuracy:0.8806629776954651 val_auc_1:0.854595422744751 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.90it/s, epoch: 15/40 - train_loss:0.23515889048576355 train_accuracy:0.904902994632721 train_auc_1:0.8961691856384277 val_loss:0.28021782636642456 val_accuracy:0.8850829005241394 val_auc_1:0.8563291430473328 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.79it/s, epoch: 16/40 - train_loss:0.23402053117752075 train_accuracy:0.9024784564971924 train_auc_1:0.8906980752944946 val_loss:0.27909329533576965 val_accuracy:0.8850829005241394 val_auc_1:0.859708309173584 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.93it/s, epoch: 17/40 - train_loss:0.2111150622367859 train_accuracy:0.9189712405204773 train_auc_1:0.8960785865783691 val_loss:0.27899590134620667 val_accuracy:0.8817679286003113 val_auc_1:0.8576114177703857 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.81it/s, epoch: 18/40 - train_loss:0.20241659879684448 train_accuracy:0.915678858757019 train_auc_1:0.9157640933990479 val_loss:0.28282174468040466 val_accuracy:0.8784530162811279 val_auc_1:0.8583985567092896 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.87it/s, epoch: 19/40 - train_loss:0.23259153962135315 train_accuracy:0.9071022868156433 train_auc_1:0.8956990242004395 val_loss:0.2828892171382904 val_accuracy:0.8817679286003113 val_auc_1:0.8546835780143738 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.82it/s, epoch: 20/40 - train_loss:0.22440506517887115 train_accuracy:0.9034845232963562 train_auc_1:0.8989371657371521 val_loss:0.28058287501335144 val_accuracy:0.8784530162811279 val_auc_1:0.8598239421844482 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.94it/s, epoch: 21/40 - train_loss:0.23205137252807617 train_accuracy:0.9051724076271057 train_auc_1:0.8899630308151245 val_loss:0.2741439938545227 val_accuracy:0.8795580267906189 val_auc_1:0.8636598587036133 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.72it/s, epoch: 22/40 - train_loss:0.22656284272670746 train_accuracy:0.9030172228813171 train_auc_1:0.907919704914093 val_loss:0.2767719030380249 val_accuracy:0.8839778900146484 val_auc_1:0.8592514991760254 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.98it/s, epoch: 23/40 - train_loss:0.22055070102214813 train_accuracy:0.9109228849411011 train_auc_1:0.913796067237854 val_loss:0.2815714180469513 val_accuracy:0.8928176760673523 val_auc_1:0.855701744556427 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.60it/s, epoch: 24/40 - train_loss:0.23475250601768494 train_accuracy:0.9043141603469849 train_auc_1:0.9076265692710876 val_loss:0.2773815095424652 val_accuracy:0.8839778900146484 val_auc_1:0.8560759425163269 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 16.07it/s, epoch: 25/40 - train_loss:0.2359710931777954 train_accuracy:0.9005681872367859 train_auc_1:0.9041743278503418 val_loss:0.28951746225357056 val_accuracy:0.8806629776954651 val_auc_1:0.8590589165687561 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.76it/s, epoch: 26/40 - train_loss:0.21646590530872345 train_accuracy:0.9094827771186829 train_auc_1:0.9059643745422363 val_loss:0.27530720829963684 val_accuracy:0.8806629776954651 val_auc_1:0.8600990772247314 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.82it/s, epoch: 27/40 - train_loss:0.21936063468456268 train_accuracy:0.9137930870056152 train_auc_1:0.9077043533325195 val_loss:0.2782182991504669 val_accuracy:0.8861878514289856 val_auc_1:0.8611392974853516 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 16.03it/s, epoch: 28/40 - train_loss:0.21766482293605804 train_accuracy:0.9098451137542725 train_auc_1:0.9155865907669067 val_loss:0.2878170311450958 val_accuracy:0.8806629776954651 val_auc_1:0.8582608103752136 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.72it/s, epoch: 29/40 - train_loss:0.2088153064250946 train_accuracy:0.9126105904579163 train_auc_1:0.9115303754806519 val_loss:0.28278136253356934 val_accuracy:0.8817679286003113 val_auc_1:0.8566923141479492 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.74it/s, epoch: 30/40 - train_loss:0.2089204490184784 train_accuracy:0.9156526327133179 train_auc_1:0.9117385149002075 val_loss:0.2774920165538788 val_accuracy:0.8861878514289856 val_auc_1:0.8575839996337891 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.52it/s, epoch: 31/40 - train_loss:0.20840761065483093 train_accuracy:0.9170354008674622 train_auc_1:0.909948468208313 val_loss:0.29270535707473755 val_accuracy:0.8817679286003113 val_auc_1:0.8584149479866028 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.67it/s, epoch: 32/40 - train_loss:0.21289651095867157 train_accuracy:0.9139933586120605 train_auc_1:0.9137017130851746 val_loss:0.2861045300960541 val_accuracy:0.8872928023338318 val_auc_1:0.8621078729629517 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 16.06it/s, epoch: 33/40 - train_loss:0.20959915220737457 train_accuracy:0.9116379022598267 train_auc_1:0.9085273146629333 val_loss:0.2869407832622528 val_accuracy:0.8828729391098022 val_auc_1:0.8602972030639648 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.91it/s, epoch: 34/40 - train_loss:0.20927441120147705 train_accuracy:0.91731196641922 train_auc_1:0.9242825508117676 val_loss:0.2853357195854187 val_accuracy:0.8872928023338318 val_auc_1:0.8595817685127258 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.88it/s, epoch: 35/40 - train_loss:0.21317821741104126 train_accuracy:0.9164719581604004 train_auc_1:0.9171379208564758 val_loss:0.2900511920452118 val_accuracy:0.8795580267906189 val_auc_1:0.8606053590774536 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.76it/s, epoch: 36/40 - train_loss:0.22284917533397675 train_accuracy:0.909375011920929 train_auc_1:0.903253436088562 val_loss:0.2755865752696991 val_accuracy:0.8839778900146484 val_auc_1:0.8629168272018433 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.70it/s, epoch: 37/40 - train_loss:0.19849534332752228 train_accuracy:0.9175646305084229 train_auc_1:0.923616886138916 val_loss:0.289157897233963 val_accuracy:0.8784530162811279 val_auc_1:0.8603851795196533 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.88it/s, epoch: 38/40 - train_loss:0.20322787761688232 train_accuracy:0.9178650379180908 train_auc_1:0.9194050431251526 val_loss:0.2820649743080139 val_accuracy:0.8850829005241394 val_auc_1:0.8655640482902527 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.71it/s, epoch: 39/40 - train_loss:0.1862594485282898 train_accuracy:0.9291487336158752 train_auc_1:0.9243948459625244 val_loss:0.2956363558769226 val_accuracy:0.8872928023338318 val_auc_1:0.8605613708496094 ]\n", + "100%|██████████| 29/29 [00:01<00:00, 15.88it/s, epoch: 40/40 - train_loss:0.20305216312408447 train_accuracy:0.915409505367279 train_auc_1:0.912611722946167 val_loss:0.2843382656574249 val_accuracy:0.8806629776954651 val_auc_1:0.8598018288612366 ]\n" + ] + } + ], + "source": [ + "histories = []\n", + "for sl_model in [sl_model_origin, sl_model_compress]:\n", + "\n", + " history = sl_model.fit(train_data,\n", + " train_label,\n", + " validation_data=(test_data,test_label),\n", + " epochs=40,\n", + " batch_size=128,\n", + " shuffle=True,\n", + " verbose=1,\n", + " validation_freq=1,\n", + " )\n", + " \n", + " histories.append(history)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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/v2xWq3MmDGjyPZPPvkEjUZjH0l24f7yUWTTp08v8lyn0zF06FCWLFnCoUOHir1eUlKSw9cyf/58unbtyvDhw7nnnnuK3F544QUAfvrppyuW4UhcF5Is5ZJpALZv387WrVvLFG///v2JjY1l2bJlRbabTCa+++47AgICaNWqVZnKurxci8XCV199Zd9mtVr5/PPPixwXEBBAjx49+Prrrzl//nyxcsrzO7jgnnvuISIignfeeafEn0dWVhYvv/yy/Xm9evXYtm0bBQUF9m2///47MTExDr1uo0aNiIiIYNGiRSxatIjq1avTrVs3+/7KeN8JURGkBkiIKlBaU8ClBgwYQM+ePXn55Zc5c+YMzZs35++//2b58uU888wz9j4/LVq0YOTIkXz55ZdkZGTQqVMn1q5dS2RkZLEy33vvPdatW0f79u2ZMGECjRs3JjU1lT179rBmzRpSU1PLfA3bt28nMjKSiRMnlrg/ODiYVq1aMX/+fP7v//7vimWVNa677rqLpUuXMmTIEO68805Onz7NzJkzady4MdnZ2VeN+eGHH2b27NkMGzaMBx54gJYtW5KSksKiRYs4dOgQc+fOtXeydsSAAQPo3LkzL730EmfOnKFx48YsXbqUjIyMYsd+8cUXdOnShYiICCZMmEDdunVJSEhg69atxMbGsn//fodfH9RapaVLl9K7d2+6devGvffeS+fOnTEYDBw+fJgFCxbg4+NjnwvooYceYvHixfTr1497772XqKgo5s2bV6wvWVkMHz6cV199FWdnZx588EG02qLfrSvyfSdEhamy8WdC3CIuHQZ/JSUN0c7KylKeffZZpUaNGorBYFDCwsKUDz/8ULHZbEWOy8vLU5566inF19dXcXNzUwYMGKDExMQUGwavKOqw9SeeeEIJCQlRDAaDEhQUpNx2223KN998Yz+mLMPgn3zySQVQoqKiSj3m9ddfVwBl//79iqKow+CfeOKJEo8tS1w2m0159913ldq1aytOTk5Ky5Ytld9//10ZO3asUrt27VLjuFRaWpry7LPPKnXq1FEMBoPi6emp9OzZU1m5cmWxY8eOHau4ubkV2/7aa68pl//7TElJUUaPHq14enoqXl5eyujRo5W9e/eW+HOMiopSxowZowQFBSkGg0EJDg5W7rrrLmXx4sX2Y8r6vinp+l599VUlIiJCcXV1VZydnZWmTZsqkydPVs6fP1/k2I8//lgJDg5WnJyclM6dOyu7du0qdRj8L7/8Uuprnjx5UgEUQNm0aVOJx5Tl9yvE9aRRlMumlBVCCCGE+I+TPkBCCCGEuOVIAiSEEEKIW44kQEIIIYS45UgCJIQQQohbjiRAQgghhLjlSAIkhBBCiFuOTIRYApvNxrlz5/Dw8Ch1vRwhhBBC3FgURSErK4saNWoUm5DzcpIAleDcuXNlWkxQCCGEEDeemJgYatasecVjJAEqwYWFJ2NiYvD09KziaIQQQghRFpmZmYSEhBRZQLo0kgCV4EKzl6enpyRAQgghxE2mLN1XpBO0EEIIIW45kgAJIYQQ4pYjCZAQQgghbjmSAAkhhBDiliMJkBBCCCFuOZIACSGEEOKWIwmQEEIIIW45kgAJIYQQ4pYjCZAQQgghbjmSAAkhhBDiliMJkBBCCCFuOZIACSGEEOKWIwmQEEIIcQNQFIW0nAJsNqWqQ7klyGrwQgghxHWWkWfmREIWx+KzOB6fyfH4LI7HZ5GZb6G2ryuj2tViWJsQqrkZqzrU/yyNoiiSal4mMzMTLy8vMjIy8PT0rOpwhBBC3CDWH09k5oYobAp4OhvwdNGr9856PF0MeDob8LjksaeLnhyTleMJmYXJThYn4rM4l5F/1dcy6rT0jwji/g61aV3bB41Gcx2u8ObmyOe31AAJIYQQV2Gx2vhkzQm+WBflwFk2nIKWoTUmUZDaDWt2Q+BiElPdy5nwIA/CgzxoGORBeKAnNbyd+etwPPO2RXMwLoNl+86xbN85GgZ5cF+H2gxuUQMPZ0O5ruFIyhGc9c7U9apbrvNLEpkWyW+nfuOvM39Rw70GH3b7EF8X3yueE5uWy2vLD/Pu3REEejpXWCyOkhqgEkgNkBBCOC7HZEGn1eBs0FV1KBUqITOfp37ay/bTqQDc36EWHer6kpVvITPPTGa+mcw8C1n5ZjIv2ZZs/Bmz+7/2cqrpGtCnxnj61O1Eg0APvFyunMgciE1n3razrNh/jnyzDQA3o45BLYO5v31tGtco++fT2ui1PLPuGQBCPUPpGdKTnrV60syvGTqtY7+vlLwUVp5eyYqoFRxNPVpkX7B7MF/2/rLUJCu3wMLQr7Zy9HwmvRoGMHtcW4de+2oc+fyWBKgEkgAJIYRj9kSnMfKbbZgsNoI8nQmp5kJINVdqVXMlxMeVWr7qfYCHE1rtzdOUszkymacX7iU5uwB3Jz3vDY3grmY1rnrevCPzeH/n+wD0r9Off6L/Id+qNnt1qtGJp1o+RRO/JmWKISPXzJI9sczffpaopBz79la1vBnXuQ4DmlW/YvNYWn4ag5cPJjU/tdi+as7V6F6zOz1DetKxRkec9SXXyORb8lkfu57fon5jc9xmrIoVAL1GT9eaXekZ0pNvD35LTFYMHkYPPu35KW2DiiY3iqLwxII9/HkwHl83Iyue7EKwt0uZfgZlJQnQNZIESAhRVQosNiITs2lU3eOm6fNhsli567NNnEzMvuqxRr2Wmj4u1KrmSuPqnjzWo165m3Qqk9WmMOOfSKavPYGiQMMgD768rxV1/d2veu7a6LU8u+5ZFBQmtZ7E+KbjScxN5JsD37DkxBIsigWA3rV6M7HlROp51ytTTIqisO1UKvO2n+WvQ/FYCkeLPdK9Li/1a1jq++XFDS+y8sxK6nvXZ1bfWeyI38G66HVsjN1IljnLfpyzzpmONTrSM6Qn3UO64+3kzd7EvfwWpTZxZZsv/n4j/CIYUG8A/UL74ePsA0BqfipP/fMU+5P2o9fqebPTmwyoN8B+zmdrTzJt9QkMOg0LJnSgbWi1Ml23IyQBukaSAAkhqsKWqGReXX6YyMRsHutRj//r17CqQyqTT9ec5JM1J/BzN7L40U6k55mJTs0l5sItLZfo1FzOpedjvWyId9tQH354oB2uxhunS2pytolnF+1j48lkAEa0DeH1gU3K1LR3KPkQ41eNJ9+az7AGw5jSYUqRxCQmK4aZ+2fyW9RvKChoNVruqnsXjzV/jJoeNcscY2JWPj9sOWPvk/RUr/pM6hNe7LjVZ1czaf0kdBod8/vPL1LrZLaZ2Z2wm3XR61gXs47zOeft+7QaLT5OPqTkp9i31XCrwZ1172RAvQHU8apTYlz5lnxe3vQyf5/9G4DHmj/GY80f46/DCTw6bzcA790dwYh2tcp8rY6QBOgaSQIkhLieEjPzefuPo6zYf86+zaDT8Ncz3cpU41CVIhOz6f/pRgqsNj4b2ZKBzUtvHrJYbZzPyCcmNZdTyTl8sOoYmfkWOtXzZfa4tjdE36Edp1N58qc9JGSacDHoeHtwU4a2LltiEpsVy31/3kdqfiqdgzszo9cM9NqSE7vItEi+2PcFa6LXAKDX6hkaNpRHmj2Cv6t/meOds/k0r/92BIDnbm/Ak7eF2fel5qcyZPkQUvNTmRAxgadaPVVqOYqicDztuD0ZutC3x83gRp/afRhQbwCtA1uj1Vx9+kCbYuPTPZ8y+9BsALrV6Me6jd3JLdAwrlMorw8sW9NfeUgCdI0kARJCXA8Wq40ftp7lk9UnyDZZ0Grg/g61OZ2cw8aTyZXSSbQi2WwKI77Zxo4zqfQM92f2uLYONdvtjU5j9KwdZJss9Aj35+vRrXHSV00SZLMpfP3vKT76+zhWm0L9AHe+vK8VDQI9ynR+himDMSvHcCrjFOE+4fxwxw+4Gdyuet6h5EN8vvdztpzbAqjNUM+1eY4RDUeUOfZv/z3FO3+qCctLdzTk0e5qk9pz65/j77N/U9+7PovuWoRRV/Y5hf6NOsHqyMM83qEX1b28ynzepRafWMzb297Gqlix5NSlmeEp5j3QE4Ou8uZglgToGkkCJMT18VvUb0RnRfNos0cdHolys9t1JpVXlh3iWLzaB6N5iDfvDG5K02AvTiVl0+eTf7HYFL4f35ae4QFVHG3JFmyP5n+/HsTVqOPXia3Yn7YeTydPOtXohKexbP87d5xOZezsHeSZrdzeOJAv72tVqR+QJUnLKeC5X/bzz7FEAIa0DObtwU1xcypbs5zZaubRNY+yI34HAa4BLOi/gEC3QIdi2Bm/k0/3fMr+pP0ATG43mVGNRpX5/C/WRfLhX8cBmHJXY4JrHueFDS+g0+hYcOcCGvs2LlM5FquNr/89xSerT2CxKQR6OvHRsOZ0DSt7rdQFZquNIbNnc0Y3E43ORC2P2sy8/StCPEIcLqusJAG6RpIACVH5dsbv5MG/HkRB4ZlWz/BgxINVHdJ1kZxtYuqfx1iyJxYAb1cD/9evIcPbhBQZHfXOH0f4duNp6vq7serpbhj1N9bKRYmZ+dw2bQNZ+QUM6XaOPVkL7KOM9Bo9rQJb0a1mN7rX7E6oV+gVy9ocmcz4OTspsNi4s1l1Ph3eAv11SIIURWH5vnNMXXmUhEwTRr2WNwc2YXjbkDLXZCmKwiubX2FF1Apc9a7MvWMu4dWK98Upa1mf7f2M7w5+B8CUDlO4N/zeMp//yeoTfLr2JBpdNv6NPiPPmskjzR5hYsuJZTr/bEoOzy7ax57odAC8XAxk5JkBeKBzHV7sF+5QM+WUZYf4cdtZ3NwT8A+bT0p+ItWcq/FZr89o7t+8zOU4QhKgayQJkBCVK7sgm6ErhnIuR+3zYtAaWHjXQhr4NKjiyCpWan4qS04sYVnkMpz1ztQx9OGvHcFk5akfriPbhfBi34b4lLDcQWa+mV4frSc5u4BX7mzEQ10rbvK6ivD4/N38FbkN75A/KNDFAFDLoxZ6rZ5TGaeKHFvbs7Y9GWoV2AqDtvior3XHE3l47i7MVoW7Wwbz0bDmlTpcfseZBF75azlnc/aic4vCSefE/U2H8FDLoXg5lb3J56v9X/Hlvi/RaXTMuG0GXYK7XFNciqLwye5P+P7w9wC83vF1hjYYWuZz3191jDmRb2LwPESAUx1WDVuCQXflUXaKorBwZwxv/X6E3AIrHk563hjUhDuaVufdP4/y47azAIQHejB9RAsaVb/65+L87Wd5+ddDaDTwzeg2NA/VMHHtRI6mHsVJ58S7Xd6lT2ifMl2XIyQBukaSAAlRuV7Z9ArLo5YT7B5MqFcom+M207BaQxb0X3DVf9Y3g2Opx5h/dD5/nvqTAltBkX02izs+lh68e9vDdKtf8kiaCxbtjOb/lhzEw1nPuud74OfuVJlhl9nPew/w6sb3MXgdAMDd4M5jzR9jZMORGHQGYjJj+DfuX9bHrGdXwi4sNov9XHeDO52DO9O9ZnfaBLZBp9VhU2xYbBbWn4jn9d8OYlNs9GsawGM9QrEqVmyKDatixdPoSbB7MO5GxzuGK4rCibQTrD79L4uP/EOy5RgaraXYcQatgZ4hPRkSNoSO1TtesWl2RdQKXt70MgCvdnyVYQ2GORxXabF+sPMD5h2dhwYNb3Z+k8H1B5fp3D9P/cn/bfw/FEVL3pmJfDDgjit24k7MymfykoOsLWz+61C3Gh8Na05NH1f7MeuOJfLC4v0kZxdg1Gl5sV84D3SuU2qCuv1UCvd9tx2LTeGFvuE80bM+ALnmXF749wX+jVUnh3yu9XOMazquTNdVVpIAXSNJgISoPGvPruWZ9c+gQcOcfnOo5VmLwcsHk2HKcKi6/kZjsVlYF7OO+Ufnsztht317oFN9os+0RtFm4+y7GfTpgNrZdVD9QYxpPIZaniUPCbbaFAZ9sYlDcZmMbFeLqXdHXI9LKVWeJY+Z+75j9sHvQWsGNNzTYCgTW0wsdfmD7IJstp7fyoaYDWyM21jiZHyO8nbypqZ7TYI9govc1/SoSZBbkL2GKSUvhW3nt7Hl3BY2x20hJT+5SDlOmmr0rNWF22p3JTkvmWWRyziedty+P8A1gIH1BjKo3qBizXg743fy8OqHsdgsPND0AZ5t/ew1X9elFEVh6o6p/HTsJzRoeKfLO0Xm1ClJcl6y/W+pofNQdu5ti1YD00eUPDpv1aF4/vfrQVJzrp7YJGebeGnJAdYcVROlTvV8+fje5lT3KjqRYUxqLoO+2ExqTgF3NavO5yNbFmlOtNgsvL/jfRYeX8jTrZ7moYiHyvPjKZUkQNdIEiAhKkdyXjJ3L7+bNFNakQ+NVWdW2Ttszus/j6Z+Tas40rLLMGWw5OQSFh5baJ9HRa/R06tWbzLi27NmnzOg4a5m1XnlznD2pv7L94e+tw8z1qChV61ejGsyjhYBLYqVv+tMKvfM3IpGA79N7ELT4PKNyLkWiqKw6swqpu2eRnxOPAD6gnrMGfgOzQPLPqTZptg4lHyIDbEb+Df2X46lHkOn0ak3rQ69Ro9Oq8Ns1ZCVZwVFi6ezE77uzug0OtLy00gzpV3xNbQaLdXdquOidyEyPbLoddgMWHPrEGhozgvdBtA/vEWxvj5HU46yLHIZf5z+gwxThn17y4CWDKk/hD6hfUjISeD+lfeTVZBF39C+fNDtgzIND3eUoii8ve1tfj7xM1qNlqldptK/bv9Sj31m3TP8E/MPDas1ZN4d83l9xTF+2hGDTqthxsiW3BFRHYCsfDNv/HaExbvVfmiNqnsyfXgLwoOuPOJNURR+2qE2leWZrXg663n37oszY+eYLAz9agvH4rNoUsOTxY92wsVYvAZNURS2nt9Kx+odK3yyT0mArpEkQEJUPEVRePKfJ9kQu4Fwn3AW3LmgyLDcFza8wKozq6jrVZdFdy0qNiV/UpaJ91Yeo1F1D+7vULvK54w5kXaCBUcX8MepP+xLHPg4+XBPg3u4LXgwLy+OYV9MOloN/K9/Ix7sUsf+z15RFHYl7GLO4Tn25gCA5v7NGddkHD1DehZpennqp72s2H+OdqHVWPRIh2v60IhJzSXIy7nMI62OpBzh/R3vsydxDwA2szemhDuZc++DdCnHyKBLKYpS6rVcGGEG8PRtYTx7u9o/LMecQ2xWLLHZscRmxRKXHWe/j8uOw2Q1FSnH2VaTzLS6WLIb4KtvwOQ7IhjUPPiq/YsKrAWsj1nPsshlbD63GZuirsXlonfBRe9Can4qLfxb8F3f73DSVV7TpE2x8ebWN1lycgk6jY73u71P39C+xY7749QfvLTxJfQaPQvvWkh4tXBsNoUXFh9gyZ5Y9FoNM+9vjYeznkk/7ycuPQ+tBh7pXo9neoc5NP3AqaRsnl20j/2xaoJ4d8tgXh/UhP9bfICVh+LxczeyYmIXalTwMhdlIQnQNZIESIiKt/jEYt7Y+gYGrYFFdy0izCesyP70/HSGrBhCcl4yYxuP5fm2z9v3KYrC2O938u+JJACCPJ15uncYw1rXvC6jhS43Y+8Mvj7wtf15w2oNua/RfdxR5w6OxOXyyI+7Scwy4eViYMaollccQnwq/RRzj8xlRdQKzDZ1xE1N95r0qtWLdkHtaBXYiqxcPb0+Xk++2caMUS3LtBbV5SxWG6//dph526Kp7evKC33DuTNCXUPKYrOQmJtoTybO5ZwjLiuO2OxY9iXuQ0HBWeeMIas356LbM7RlHT6+t3JG8Vzq+82neaNwkr8X+4XzeI/6xY6x2hSy8y1k5ptJyzURm5HA2cxYdsXGsn6/M1aLB0a9lke61eXR7vXKPLT9Uom5ifwW9RvLIpdxJvMMoHb4ntd/nn0ZiMpkU2y8uvlVlkctR6fR8XH3j7mt9m32/Um5SQxePpjMgkyeaPEEjzZ/1L7PalOY9PM+lu87h0GnwWJTUBSoVc2Vafc2p005l6MwW218vvYkM9ZFYlPAw0lPlsmCQafhpwkdyl3utZIE6BpJAiT+y7JNFnacTqFTPb/rVosSkxnD0N+GkmfJ4/k2zzO2ydgSj9sQs4GJ/0xEg4bv+31P68DWAPy49QxTlh/GSa/F183IuQy1xqWOnxuTbm/AnRHVr9sCm/9E/8PT654G4Pbat3Nfo/toFdAKjUbDz7tieOXXQxRYbTQIdOfbMW2o7Xv1yfBAbR786dhPLDq+qEjTi1ajpVG1RuhMYWw74kOAIZx/JvUrsWmhNJl5BTzy0wa2x5xEa0hFa0hFY0zD0z0Ld/dM0k1J9vWpSnJn3TvxzhvEzH9S8XUzsmZS9xJHrlWGmRuieG/lMQC6hvlhttrIzLOQUbjierbJwpU+xfpHBDH5jkaEVHMt/aAyUhSF/Un72X5+OwPrDaS6e/VrLrOsrDYrUzZP4bdTv6HX6JnWYxo9a/VEURSeWvcU62PW06haI+bfOb/YKDuL1cZTC/fy50G1+XJE2xBeuasx7uVIBi+360wqz/68j5jUPAA+GNqMe9tW3jw/VyMJ0DWSBEj8V6XmFHDfd9s5ej6TiGAvvrq/VZHRHpXBarMybtU49iXto01gG2b1nXXF/hKvbn6VXyN/paZ7TZYMXEJ8uo3+n20k32zjtQGNGdmuFvO3R/PFukhSc9QRVo2re/JC33B6hPtX6gKiMVkxDP9tOFnmLEY3Hs2LbV8E1G/D7/xxlDlbzgDQp3Eg04a3KNcHTK45lw2xG9h+fju7EnZxNvNskf2KoiXAWI+B4d1oF9SOFgEtcDW42puGLm0Sis2O5UxGDDGZsSga8xVfV68xEOxRg2D3YILdg6nhXoOa7jXVqQnMAfT7dCMFFhufjmjBoBbBDl/Xtbiw1tiVuBh0eLro8XIx4OlswN/DibGdQulQt+TO2Tcjq83K5E2TWXl6JXqtnk97fkqGKYP/bfofeq2eRXctKnUqCbPVxo9bz1I/wJ1uDa6t6fJyWflmvt5wiiAvZ+7vULtCy3bUTZUAffHFF3z44YfEx8fTvHlzPv/8c9q1a1fisWazmalTp/LDDz8QFxdHeHg477//Pv369St3mSWRBEj8F6Vkm7jvu+32mYcBfFwNfD6yFV3C/Crtdb87+B2f7vkUN4MbSwcupYb7lZtvsguyuXvF3ZzPOc+wBveye08v9sek07m+Lz8+0N5e05NtsjBr42m+3XiKbJNae9E21IcX+zWslFWmTVYTo/8czdHUozTzb8acvnMw6Ayk5hTwxPw9bD2lLhr5TO8wnuoVVmE1UvE58eyM38nO+J1siN5KakF8kf16jR4Po8dVOwejaPBzCaSOdwg13WviZQhg/2kdW08oWEzeaKwe3NO6FpNuDyfI62L/K0VRl7vYfjqVbg38+WG8Y8tdVARFUdh4Mpno1Fw8XQyFSY7e/tjDWV9lS2hcbxabhf/79//4++zfGLVGjDoj2eZsnmz5JA83e7iqw6tyN00CtGjRIsaMGcPMmTNp374906dP55dffuH48eMEBBSf+v3//u//mDdvHt9++y0NGzbkr7/+YtKkSWzZsoWWLVuWq8ySSAIkqlJafhof7fqIA0kH+KDbBzTybXTNZSZnm7jv2+0cT8giwMOJj+9tzgerjnMwLgOtBl7s15BHutWt8A+2oylHGfXnKCw2C293fptB9QeV6bxt57cx4e8JAORGP4CrtTF/PdOtxE6VqTkFzNwQxQ9bzmCyqB1Ve4T780LfcJrUqLgRU29ve5tFxxfh7eTNLwN+IcgtiKPnM5kwdxexaXm4GXVMG96Cvk2CKuw1L6coCnd/8wcHU/cQGnwerWtUkRW8vZy81GHh7sGY831YfdBMQb4PdbxCmH1/H2pVKz7KJyopmw9WHeOvwwkAOBu0PNC5Do/2qIens8E+F5GLQcffz3arkKYkcW3MNjMvbHiBtdFrAWjs25j5/eeXuvDqreSmSYDat29P27ZtmTFjBgA2m42QkBCefPJJXnrppWLH16hRg5dffpknnnjCvm3o0KG4uLgwb968cpVZEkmARFVQFIU/Tv/BBzs+sH+br+NVh0V3LcJFX/7RFIlZ+dz37XZOJmYT6OnETxM6UNffnXyzlSnLDvFL4VDY/hFBfHBP8wrpFwBqjcnw34YTlRHFbbVu45MenziUYD239jX+jl2KzezFKy1mMbLNlZcXiM/I59O1J/l5VwxWm/pvrX9EEENa1qRr2LX1d7owuRzAl7d9SdeaXfnjwHme/2U/eWYrtX1d+XZMmzIvnHktDp/LYMDnm7ApsOjhDgT755FtzibYPRgPoweKovDNv6eYWthvpke4PzNGtbrq73X32VSm/nmMXWfV956Pq4GHu9Xjq/WRZOZbbsjZqG9lZquZ17a8xt7Evcy4bQb1vOtVdUg3BEc+v6tscZmCggJ2795N7969Lwaj1dK7d2+2bt1a4jkmkwln56JDY11cXNi0aVO5yxTiRnAu+xyPr32cyRsnk2ZKo753ffxd/DmdcZpPdn9S7nITM/MZ+c02TiZmE+TpzMKHO1LXX51F19mg44N7mvH24KYYdBr+PBjPkC82cyopu0Ku6bM9nxGVEYWvsy+vdnzVoeQn32xl775O2Ap80RoyOJj3w1XPCfJyZurdEayd1N0+6dufB+OZMHcXrd9azcQFe/j9wDlyTKV39i3JqYxTvL71dQAG1h7Dwcjq3P/ddp5YsIc8s5WuYX4sf6LzdUl+AJrU8GJEO3XixDd/P0J1t2AaVmuIh9EDs9XG/349aE9+xnSszXdj2pQpqW1duxq/PNqRr0e3pq6/G2m5Zt5fdYzMfAsRwV6M6xRamZclHGTQGXi367v8efefkvyUU5UlQMnJyVitVgIDi66YGxgYSHx8fInn9O3bl2nTpnHy5ElsNhurV69m6dKlnD9/vtxlgppYZWZmFrkJcT1YbVbmH53P4OWD2RS3CYPWwJMtn+Tnu37m7c5vA/DTsZ/YHLfZ4bLjM/IZ8c02opJyqOHlzKJHOlDHr+iIJI1Gw/0darPw4Y4EejpxMjGbQTM2s/pIwjVd147zO/jxyI8AvNHpDao5O9Yn572VxziVZME5/T60aPnt1G/26v6rCfVz4717wvngfhf6tU2jupeRnAIrvx84z8QFe2n11momzN3F0j2xZOSW3jFYURQOxCXxwB9PkmfJg7x6zF8VzgerjrMpUp1R+KEudfh+XFu8Xa/PiKgLnru9AZ7Oeg6fy+SXXeo6XBl5ZsZ/v5OfdsSg0cBrAxrz5qCmDk0ToNFo6NskiL+f6cY7Q5ri7+GEm1HH1LsjqmS6AXF117s/1n/JTdVg+OmnnzJhwgQaNmyIRqOhXr16jB8/ntmzZ19TuVOnTuWNN96ooCiFKJvItEhe2/oaB5LU9ZRaBbTitU6vUddLbWboFNyJUQ1HseDYAqZsnsLSgUvxdvYuU9nnM/IY+c02zqTkEuztwk8TOlDLt/S+G61r+/Dbk12YOH8vO86kMmHuLp7qVZ+nezdA52Bn3qyCLF7e/DIKCkPDhtI9pLtD5288mWQfTfXxwEHsyc7i+0Pf8+bWN2kZ0LLEZCotP409iXvYk6DejqYexapYAYhoFsEr9V7gwGlnVh2K52xKLquPJLD6SAJ6rYZO9f3o1ySIPk0CyTFZ2BKVUnhLIsdjPgbvaGwWd3JjRuDp7ESHur50qudLlzB/6gc4viZVRfB1d+KZ3g148/cjfPjXcSJqevHMwn2cTMzG1ajjsxEt6d048OoFlUKv03Jf+9oMax1CntmKl8vNvz6bEJersj5ABQUFuLq6snjxYgYPHmzfPnbsWNLT01m+fHmp5+bn55OSkkKNGjV46aWX+P333zl8+HC5yzSZTJhMF2cPzczMJCQkRPoAiStauCOaXWfTeKxHPer5l/2DsMBawHcHv+Pbg99isVlwM7jxbKtnGRY+rNjw8DxLHsN/H87pjNPcXvt2Pu7+8VW/8Z1Lz2Pkt9s4m5JLTR81+bm846rVZi1xkcfLh3P3CPfn0+Et8XIt+wfgy5teZkXUCvswdldD2TvNZuSa6Tv9X+Iz87m/Qy3eHhyByWpixO8jiEyPtP8MzuWcY0/CHnYn7GZP4h5OZ5wuVlagayDZ5mxyzDkYtAYeb/E4YxuPJTIxj5WH4vnrUDzHE7JKiEKl99qJS40loGgYHPQmw5r2oEkNL4cTwspittq449ONRCZmo9GAokCgpxOzxratkuUyhLgR3FSdoNu1a8fnn38OqB2Wa9WqxcSJE8vUYdlsNtOoUSPuvfde3n333QopE6QTtLi6xbtjef6X/QAYdBoe6VaPJ3rWv+rkdPsS9/H6lteJyogCoEfNHrzc4WWC3EofOXQk5Qj3/XEfFsXCO13eYWC9gaUeG5uWy8hvtxGTmkdINTX5uXSeH5ti48OdH/LTsZ/oUKMDw8KG0S2kW7GJ037dG8vkpQfJN9uoVc2Vmfe3pnGN0v8WFEVhZ/xOFp9YzMozK9FqtMzpN4eWAS2v+PO43NML97J83znq+Lnxx1NdcDXqi/0MfJ19SclPKXZuPa96tAxsSauAVrQObE0N9xrE58Tz5tY32Ri3EYBG1RrxVue3CK+mdqg+lZStJkOH4zkQm4Feq6FlLW/Ca2XzZ8pkzLYCnmr5FBOaTXDoOq6XDSeSGDt7B6DOhTR7XNsiQ9iFuNXcNAnQokWLGDt2LF9//TXt2rVj+vTp/Pzzzxw7dozAwEDGjBlDcHAwU6dOBWD79u3ExcXRokUL4uLieP311zl9+jR79uzB29u7TGWWhSRA4ko2RyYzdvYOLDaFun5unErOASDY24XXBzbh9hKaHqw2Kx/t+oj5R+ejoFDNuRqT20+mb+2+ZWrD//bAt3y29zPcDG4sGbiEYPfiE9HFpOYy4pttxKXnUdvXlZ8mdCgybNxsNfPy5pdZeXplkfP8XfwZEjaEoWFDi8zRc/hcBo/8uJvYtDx0Wg13twzmyV5hRZrSkvOSWR65nKUnlxKdFW3ffvl0/GXx2/5zPPnTXnRaDYsf7UjLWkWXGPhq/1d8ue9LQJ37prFvY1oGtKRVYCtaBrQsdUkCRVH4/dTvvLfjPTILMtFr9ExoNoEJERMw6C4mfinZJpwNOhRNPiP+GMHZzLN0Ce7CF7d9USkLXVaUmRuiSM4y8eztDcq1zIMQ/yU3TQIEMGPGDPukhS1atOCzzz6jffv2APTo0YPQ0FDmzJkDwIYNG3jsscc4deoU7u7u9O/fn/fee48aNWqUucyykARIlOZEQhZDv9pCVr6FAc1r8OnwFvx9JIE3fztsX57htoYBvD6wSZFmp6Unl/LaltcAGFRvEC+0fQEvp7I3U1hsFsavGs++pH20DmzNrD6zijRhRaeoNT9x6XnU8XPjpwkditQE5FnymLR+EpviNqHX6Pm/dv9HfE48v0b+Smp+KqCuSt45uDP3NLiH7jW7o9fqSc8t4MXFB/i7sFO0Xqvh7pY16BiRwr/nf2d9zHr7Egquelf61+3PPWH30MSv7CuEg9phu+/0f8nIM/NUr/pM6lN8yLtNsbH67Gq8nbyJ8ItwqGkN1PWS3t72Nv/E/ANAmE8Yb3V+iya+F2NVFIXnNzzP32f/JsgtiF/u+qXM/a6EEFXvpkqAbkSSAImSJGbmM+TLLcSl59E21IcfH2xvn1smt8DC5/9E8t3GU5itCk56LRN71ufh7nUx6rQMWT6EqIyoa2pOicmK4Z4V95BryeXpls/Qvto97DyTyq6zqWw6mUxmvoW6/mryE+h5MfnJMGUwce1E9iXtw1nnzCc9P6FLcBdArRX6J+YfFp9YzLbz2+znBLgEMDhssL1WaE90Gh+s2c6e1L8weO9Ca0i3H9vMrxlDGwylX2g/h5MSKLrQaUSwF0sf71TmlcrL81p/nfmLd7e/S5opDZ1Gx/im43m0+aM46ZxYcHQBU3dMRa/R832/72kR0KJS4hBCVA5JgK6RJEDicjkmC8O/2cqhuEzq+rmx5LFOJS4GGZmYxZRlh+3LItTxc+Perjl8eewlXPWurBm2Bg+j4/PF5BVY2RuTxrxDi9mY/gWKoiP39BPYTBdrP8MDPfjxwXYEXJL8JOUm8fDqh4lMj8TD6MGXt31Z6od6dGY0i08uZnnk8iK1Ql2Cu6DRaNgUtwmbos60rFhdMGe0RMlsx9CIdkzsVZ/gEmZpLotLFzr946ku1A+o/Pl0UvNTmbp9KqvOrALUCSdHNx7Nu9vfxWKz8GLbFxndeHSlxyGEqFiSAF0jSYDEpSxWGxPm7mLd8SR83YwsfbzTFVf4VhSFFfvP8fYfR0nKMuESMgu9+0mG1BvBm11eLvH4AquNvAIrOQVW8gos5BZYOZeez+6zqew8k8ahuAwsNgVQcK75IwaPI1AQSCv9G7QLDaRtaDVahHhj1F+sOYnJjGHC6gnEZcfh7+LPzNtnlrpQ4qXMVjNrY9ay+PhitsdvL7KvTWAbhjYYSjWlNV+ui7bPh2PQabi3TQhP9Kxf4nIVpTmVlF1kodPxneuU+dyKsPbsWt7e/jbJecn2bb1r9WZaj2kyv4oQNyFJgK6RJEDiAkVReGXZIeZvj8ZJr+WnhzvQqlbJnW0vl5lv5vWVa1id+SKKosEW/RJNAkLJMVnJM1vJLUx0cgus9qUbriTI05m2darRpKaWebFPkVGQWmRF8ksdTz3OI6sfISU/hRCPEL6+/WtCPEIcvv6zmWdZEbUCgAF1BxDqFVpk/47TqUxfc4ItUWqNl1Gn5Z42NQn2diEr30K2yUyOyVrkcbbJQla+hRyThTyzOlfP5QudXk8Zpgw+2PkBK6JWUMujFgvvWliuWjohRNWTBOgaSQIkLvh6QxRTVx5Do4Gv7mtNv6aOLXQ5ZfMUlkUuw93SivMn773q8Ua9FlejDleDDi9XIy1CvGlXx4c2tatR08fFXivxb+y/PLFWXRPv2z7f0qF6B3sZexL2MHHtRLLMWYT7hDPz9pn4uVTeau8A20+l8MmaE2w7lerwuXX93Jj3UHuHao4qw6mMUwS4BOBurJrJDYUQ104SoGskCZAA+P3AOSYu2AvAlLsa82AXx5pnkvOS6bO4D2abmR/7/UhWZjBZ+RY1wTHqC+8LHzupSY8jyw28ufVNfjnxC4GugSwZuAQvJy/+jf2XSesnYbKaaBXQis9v+xxP4/V7D2+NSuHXvbFo0ODurMfNSY+Hk77IYzcnPe4Xbs56vFwMN8zkgkKIm5sjn98yaYQQJdh1JpVJP6sTHY7rFOpw8gOw8NhCzDYzzf2b0yKwBZR/ZYISPd/meXbE7+Bs5lne2f4OXYO7MmXzFKyKle41u/Nh9w+vaRX58uhYz5eO9Xyv62sKIUR53LizewlRRU4lZfPQ3F0UWGz0aRzIlLsaO1xGniWPRccXATCm8ZiKDhEAV4MrU7tMRafRsfL0Sv636X9YFSt31b2LT3p+ct2THyGEuJlIAiTEJVKyTYyfs5P0XDPNQ7z5dETLcjXP/Bb1G+mmdILdg+lVq1clRKqK8I/gkWaP2J/f3+h+3unyTrGlLYQQQhQlTWDillJgsZFtspCdb1HvTeropGyTlex8C4t2xXA2JZeQai7MGtvmqmt7lcSm2PjxyI+AmpDotZX7Zzah2QQUFILcghhSf4gM3xZCiDKQBEj8ZymKwnurjrHyYLw96Smw2q56npeLge/HtcPP3alcr7spbhNnMs/gbnBnSNiQcpXhCL1Wz+MtHq/01xFCiP8SSYDEf9aP287y9YZTJe5zMehwd75khJJRvfdxNTC2Uyj1A8o/FHru4bkA3NPgHtwMpU+YKIQQoupIAiT+kyITs3jnj6MAPNu7Af2aBuHurA69djM6NtzcEcdSj7E9fjs6jY5RDUdVymsIIYS4dpIAif+cAouNpxfuw2Sx0TXMjyd71S/zDMPns88T6BaIVlO+BOlC7U+f2n2o7l69XGUIIYSofDIKTPznfLLmBIfPZeLtauCjYc3LnPzMPjSbPkv6MH7VeDILMh1+3YScBFaeXgnAmCaVM/RdCCFExZAESPynbD+VwswNUQC8d3cEgZesjH4la86u4ZPdnwCwJ3EP41aNIyk3yaHXXnh8IRbFQquAVjT1a+pY4EIIIa4rSYDEf0ZmvplJP+9HUWBY65r0a1q2JqgjKUf436b/AdAvtB9+Ln6cTDvJ6JWjic6MLlMZueZcfj7+MyC1P0IIcTOQBEj8Z7y2/DBx6XnUqubKawOblOmcxNxEnlz7JHmWPDrX6MzUrlOZe8dcQjxCiMuOY8zKMRxLPXbVclZErSCzIJMQjxB61OxxjVcihBCiskkCJCpcRp6ZdccSsZRhzp2KsmL/OX7dG4dWA58Mb4G709X79+dZ8njynydJzEukrlddPuz+IXqtnhCPEObeMZdwn3BS8lMYv2o8u+J3lVrO5RMf6rSOT54ohBDi+pIESFS4yUsPMH7OTib9vB+bTan01zuXnscrvx4EYGKvMFrX9rnqOTbFxsubXuZIyhG8nbyZcdsMPIwe9v1+Ln7M7jebVgGtyDZn8+iaR1kXva7EstbHrCc6KxoPoweD6w+uiEsSQghRySQBEhUqLj2PVYfiAbVW5vXfDqMolZcE2WwKz/28n8x8C81DvHmyV/0ynffFvi9YfXY1eq2e6T2nE+IRUuwYT6MnX9/+NT1CemCymnh2/bMsi1xW7Li5R9Sh78MaDMPV4HpN1yOEEOL6kARIVKj5285iUyDY2wWNBuZuPcsna05W6Guk5qfak6pZm06z9VQKLgYd04e3wFCGCQ5/i/qNbw58A8DrHV+ndWDrUo911jvzSY9PGFhvIFbFypTNU5hzaI59/+Hkw+xO2I1eo5eJD4UQ4iYiCZCoMPlmKwt3xgAw5a7GvFnYEfmztSf5fvPpCnmNT/d8SvdF3Rn1xyh+3L+SD/9SOyi/OqAxdfyuvuzEvsR9vLblNQAebPogg+oPuuo5eq2etzq/xdjGYwH4ePfHTNs9DUVR7LU//er0I9AtsLyXJYQQNxTFZsOWk1PVYVQqmQlaVJg/D54nNaeAGl7O9G4UgF6nJS3XzLTVJ3jjtyN4uxoY0rJmucv/+fjPfHfwOwAOpRziUMqL6GvVoKnz3dzbpt9Vz4/LjuPpdU9jtpm5rdZtPNXqqTK/tlaj5fm2z1PNpRqf7P6E7w99z7nsc6w9uxaA0Y1Hl++ihBDiBmKOiyN92TIyfl2GOTYWlxYt8Bo8CM9+/dB5e1d1eBVKo1RmB42bVGZmJl5eXmRkZODp6VnV4dw0Bn2xmf0x6bzQN5wneqp9cRRF4Y3fjjBnyxl0Wg3fjmlNr4aO15RsjN3IxH8mYlNsPND0ATZHJnMsdxUabQEAYT5hPNLsEW6vfXuJy1hkF2QzeuVoItMjaVStEXP6zSl3f52lJ5fyxtY3sCnqKLe2QW2Z3Xd2ucoSVU9RFEzHj5O7Yyd6fz/cunZD5y6L2IoKkp8J1gJw86vqSEply88na/Vq0pcuJXfbdighLdAYDLj37InX4EG4d+mCxmisgkivzpHPb0mASiAJkOP2x6Qz6IvNGHVatkzuhZ+7k32fzabw3C/7+XVvHE56LT8+2J52daqVuezjqccZs3IMuZZcBtUbRJ+ApxgzeycaXQ539zjF5qRl5JjVqtp6XvV4uNnD9A3tax+ObrVZeWrdU/wb+y/+Lv4suHMBQW5B13S9a8+u5cV/X6TAVsCMXjPoHtL9msoT15c1PZ2cLVvI3riJnE2bsCRdnPVbYzTi1rkzHn364NGrJzovryqMVFwPlqQkbHl5GKpXR2MwXHuBWfFw7A849juc/hdsFqjZFhoPhsaDwLv4oIurURQFa3IyppMnMUVGYjoZiSkyEmtmJk716+PcqCFODRvi3LAh+sBANJorLwGkKAr5+/eTvvRXMv/8E1t2tn2fa4cOeN89BJeWLclas5aM5csxHbs4H5rOxwfP/v3xGjwI56ZNr/pa15MkQNdIEiDHPffzfpbsiWVIy2A+Gd6i2H6z1cajP+5m7bFEPJz0LHykA01qXP2DJT4nnvv+vI/E3ETaB7XnlbbTGD5zBwmZJkZ3qM1bg5uSYcpg/tH5zDsyjyxzFgChnqE83Oxh7qhzB9N2T+PHIz/irHNmTr85NPEr2ySJV3Ms9RinM07TL7TfDfUPQBSnWK3kHzqkJjwbN5J38CDYLs5TpXFxwbVNG8zR0RScPXvxRL0et/bt8bj9djx634be78b9Fl8RlIICLOnpWNPSsaanY01LA60GlyZN0Neo8Z95n9tyc8nduVNNgjdvpiBSXT4HrRZD9eoYQkIwhtTEULPwPiQEQ82a6Ly9S/8ZpETB0d/UxCd2J3CFj9bgNtBkcGEyVKvYbktaGqYTJzFFXkh2TlJwMhJrRkaZrk/n7W1PhpwahuPcqBFOdeqgMRqxJCWRsWIF6Ut/pSAqyn6OITgYryFD8Bo8GGPN4GJl5h87RsbyFWT8/hvWpGT7dmPdungNGoTXwAEYqlf9AtCSAF0jSYAck5pTQIepaymw2Pj18U60rFXyPDz5ZitjZu1gx5lU/NyN/PJopyt2XM4x5zB25ViOpx0n0LkWNfNf5N9j2dgUqOfvxu9PdsXFeHHSwayCLBYcXcDcI3Pti5kGuASQmJcIwMfdP6ZPaJ8KvPJbgzU9HcVsRufnd1N9AFrS0shev4GcjRvJ2by52IeHU1gYbl274t61Cy6tW6M1GtXmsJMnyfp7NVl//43pxImLJ2g0uLZurdYM3d77hvhnfyU2k8mexFx6b0lLK5LgXHp/pU6vOl9fXCIicG4WgUtEM1wimt40fUIUq5X8I0fI2byFnM2byd23D8zmiwdoNGiMRhST6YrlaN3d1WQouAY6Ty+05KLLi0WbGYnWFI9Wr6A12NAaFLTBjdA17IU24k4wumHdsxzrodVYow9jNWmxFGixmrRYdf5YDYFYba5Ys/OxpqZiTU8vJQAtxpAQnBqEYaxfH+ewMLQenphOnCD/2DFMx45hOnUKrNbi5xoMGGvVouDMGft+jbMzHn1ux/vuu3Ft1w6N9urjohSLhZytW8lYtpystWtR8vPtP0NDrRB07h5o3d3Rurujc3dDa3/uhs7dvfC5+tgQHIyhRo2rvqYjJAG6RpIAOear9VG8v+oYEcFerJjY+Yofkpn5ZoZ/vY2j5zOp6ePC4kc7EeRVfMFSi83CQ6seZ3fSVrC6k336cRSz2mzWLrQa794dQf0A9xJfI8ecw8JjC/nh8A+kmdIAmNhiIo80f6QCrvYiW14eBWfPotHpQK9HYzCg0evVW+FjLmwrwz+WG4UlJYXcnbvI3bmT3B07MJ1UpzHQurlhrFsXY51QnOrUwVinLsY6dTCG1kbr5HSVUkummM3qh4++YsZjKBYL2Rs3krH0V7LWry/yIaf18MCtUyfcu3bBrUsXDEFXbwYtOHOGzNWryfp7NfkHDxbZ5xwRgVvnTrh16IhLyxZX/hlYLYACugpoXilkSU0lb/9+8vbvxxwTezHBSU/Dmp6BkptbvoK1WnReXuh8fNB5e2PLz8N04iRYLMUONdSupSZDzSJwjojAuVEjtM5lW4C4MikWC+b4eHK2bCFny1Zyt24tlgAbgoNx69wZt06dcOvQHq2XF5akJMyxsZhjYiiIKbwvfG5JTLyu12Co7o9T3To4hTdSbw0aYKxT56o/X5vJpDaPHT9G/lE1Kco/dqxIE5dLixZ43T0EzzvuQOfhcYXSrsyanU3WX3+TsXw5uTt2OHx+tQceIPDFF8r9+iWRBOgaSQJUdlabQrcP1hGXnseH9zRjWJurt20nZZkYNnMLZ1JyaRDozs+PdMTbVe1Ql2+2surQeT7Z+x6pug0oNgO5Zx/GR1ePoa1rcm+bkFITn8vlmnPtExeObDiywmovrNnZpM2bT+r335e5ShqtFo3BgNbVVf025OGOzs0drceFb0MeFx97eKB1c0dXzQfnxo3R+1xlZmtLAUSuAcUGNVqCZw1w4Fotyclqc8COHeTu3HmxOeCy+C9tMipCo8EQHIyxTh2c6oRi8HVB0bthyy3Alp2FNTsbW1a2+jgrG1t2NtbsLGxZ2Sj5+WicnXFt2xa3zp1w79wZY/36Dv+uTCdPkr70VzJ++w1r8sXqeadGjXDv0R33rl1xadbsmhIt87lzZK1eTebq1eTt3lOko6jGyQmXVi1xa98Bt44dcG7SRE2Mz+2B3T/AoSVqP5DqzdW+IDXbqM0gXjXL9LtSCgrIP37CnvDk7d+POboMC/XqdOi8vdH5eKPz9kZfmNTovH3sCc7l+7SensUSdlt+PvlHj5J/8CB5Bw6Sd/AA5rMlvL5ejyG4Boag6hiCgtDXqK4+rlH4vHp1dO5X//tVzGasWVlYMzKwxZ/GemIz1jP7sdmcsXqFY1Nc1fdVZpb6XsrMUt9fmVnYsrKwlZD8ad3dce3QHvfCpMdQq1bZ3mcZsXByNbYjf2E+tImC9ALMOTpsBVpsNiM21xBsztWx6r2x5RVgy8lRb9nZ6n1hLFpPT3TeXui9L/wOvNG5GdEVnEOXdRxd9kl0Ris6JxtGNytawyUfzTojuPqCqx+4Vit8XHhz8gDFqr6/bLZLHhfeKzYUqxlzSjam85kYm3fFqc/DDv2PKAtzQgLm2NjCv+9sbNk5F//WCx+rP5Ns+/8Bn5EjqDamYhePlgToGkkCVHarjyQwYe4uvF0NbJt8G86Gsq2DFZOayz0zt5CQaaJFiDevD2zC8n1x/Lo3jhzntTgH/omiaKhne5xH2w7itkaBGPVVW4tizcoi9ccfSf1hLrbCxEfr6YlGp0Mxm1EsFhSLpcRvytfCUKsWLs2aFd4icGrcGK3RCLmpsHsO7PgWss5dPME9EGq0UpOh4FbqYzdf9RoyMzHHxWGKjCJ31y5yd+yg4HTxOZqcGjTAtV07XNu2xbVtG7Tu7pijozGdPk3BqdMUnD6N6fQpCk6fwZaZWaHXqw8IUL+Vd+6MW6eO6H19SzzOmpFBxh9/kPHrsiK1Mzrfanh1a4lX3QKcDedA7wR6ZzC4gsEZ9C5guOR26XPfehAYAborJ0qWpCSy/91IzrZt5G7bVqQTNYDWxYhrkIKbdzKugSacvCwlf964B6nJUM02KMFtUHwbo2iMWDMyyT90yJ7s5B8+XGLzjLF2TVwa1sGpViA6Tzf0Hq7oPFzRebihc3dB6+qEBgVQ1ARZsakfjAXZ6ugkUxaYMgsfZxTeZxa9NzhD3R5Q/3ao3xs81aY/S1oa+YcOk3fwAPkHDpJ34ADW1NQr/txArYm7kBzpvb2xZudgy8jAmpmJNTMTW2ZmiQmMw3Q6XJo3L3wvdcIlIqJsCbDVDDE74OTfcHI1JB4uut89CMJ6Q/idUK+n+r65AsVmA0VRE+IryU5U+xCd+Ev9e85NhZxksF65Wa5cgttA33ehVvuKL7uKSQJ0jSQBKrvRs7az8WQyj3Svy+Q7Gjl07omELIbN3EpG3sVmCr3HQVxqzgfg4SbP8GSbBys03vKwZmSQOvdHUufOxZaldrI21q2L32OP4tm/f7F/bIqiQGEypFgsanJkLrzPy1W//eRkY8u6rEYkO0f99lr4Dcly/nzRDrkX6PU4V3fDxTURF+9cnH0LMAb5ovEIwBZ3jIJsMOfoMOfoC+91FOS5qN9a80voG6DR4BQejmu7tmrC06bN1WudLrlW66kDFCx9A9OBrRRk6jHn6NFobWiNCjqDTe0XUS0AXe1maOu1R1u7JTovb7QeHujc3TEnJKpNFZs3k7tzZ7EPeqdGjXDvrCZELs2bk7trF+m//kr2mrVqExqAXod7q3C8Gyi4a3ehKShjzVxJjB7qB0PtTlC7i5pI6ksf8qsoCgVRUeT8+RO56/8iJzIJW0HRZF3n5YGhRnWU3AyUvGyU/Dz1/WAFxabBZtOA7crfyLVO4OJnxaVaHi7VTLj4FqAzVsG/78AINQGofzuEtLM36ymKor5nY2OxxMdjPh+P+fw5LOfjMZ8/jzk+3uFkWasvfB+5OaHz8kGrN6MriEert6AzKGiNNnT+IWjDOqFr3BttUKhag+rhgc7Do+wJT0YMnN2qJj1R69Rk8AKNVq21C+uj3oIiKrz2pFSKAuZcyE255JZa9N6UBVq9Wkur0RU+LrzXaIs+N2XDnh/UBBigyRDo/Tr4hF6f67kOJAG6RpIAlU1UUja3fbwBjQb+faEnIdUcn1dnT3Qao7/bjslio33DLI7yAWalgBHhI/hf+/9Vaadba3o6KT/8QNqP8+zt58b69fB77DE8+/W7+je6ioghI4O8g2pNQP729eQdOoI1t3gSc6EmypqWdtUydU5WDO5WXOtXx7VHf1wHPoTON8Dx4ExZsHEabP2i8FuqBlqNgV6vqP+cT/6lfpuN3qZWy1/g7K3WJDToq967XpwSwWYykbdnDzmbN5O9eQumo0evGIJTLX+8G+rx9DiM3pB/cYerHzTsD7U7q7Ue5lww54MlD8x56mNzLljyC5/nqR8K8QfVmo9L6Z3VD8DandWkqGZbMBa+13OSYf9PsGcuJKsdphUb5GsbkGtrTk60mdx9By52FC0rjYKTtxkXXzMuvgW4+BZg9LAW/dzV6MDZE5w81VoIjU79wNNQeK9VfycXHmu06ge3RgtG98JzPdTzL5Tj7HXZc0+1ZuLkaohcDXF7KDK6yclTrR0Ku1A7dFmHVqu58IM6GXJTsCbHYYk9i/lcHOb4BGzJ59HmnEWnN6E1KOiMNnRGG9qgOujCu6MJ6wmhXcDlkoQ8P0MdaXVwMZxaf8l7S6P+jiKGqsPNL7yvFEVNFNLOXLyln734OCOu6PsT1Kal+r3VhKderyLv0ZteVgKsewf2/qi+WXVG6PAYdH1O/f1XJEW5+Pdlzr3sPge8Q8GvbOs3ltVNlQB98cUXfPjhh8THx9O8eXM+//xz2rVrV+rx06dP56uvviI6Oho/Pz/uuecepk6dinNhx7DXX3+dN954o8g54eHhHLtkDoOrkQSobF5fcZg5W87Qu1EA341tW+5yUrJNxOfE8fj68aTmp9KtZjc+7fkpem3VTFRuSUsj9fs5pM2bZ6+KdwoLw++Jx/Ho0+f6dmi2FMDhX2HrDIg/oP4/ydGRZ2hDvhJG3plU8o8cQSkosJ+i9fLCEFwDY3BNdZRFYDUMLiaMmgQMBVFokw+o33gvcPGBiGHQ4j61j8rVkk6bFfbNh7VvQU5hx9DQrtBvqvrt+HJ5aRC5trBJ4W/1+QUaLfg3VGO4/IPY2RNLvo6cE4nkHDxLzv4TWFLS0bk749lAj7f/aZx9LhnJ41MHGt0FDe9SkxRtORJUmxUSDsHZLXB2s3qfm1L0GK1BrRVy81evx1YYg8ENmt4NrcdBcGv7z9FWUED+wYNYMzLQGIxojEY0RgMaoxGtsfC5k5N6n5+CJukgmrSTaAwuRRMRJ6+izw2u168m4oKcZIj6R02IotYW/9kENAEnd/W43GQ1WSkL90A1karbA+p0B6/iw7BLjefwr2ofq+itF7dr9VCznfr6aWfUD9sr0RkhsMnFWp4aLcv3/rmZxB+Cv/4Hpzeoz139oOf/oNXYqzYB25my1ObC6G0Qs02d/+jyZOdKur2gfmGqQDdNArRo0SLGjBnDzJkzad++PdOnT+eXX37h+PHjBAQU/0a6YMECHnjgAWbPnk2nTp04ceIE48aNY8SIEUybNg1QE6DFixezZs0a+3l6vR4/B+bvkATo6nJMFjq8u5Ysk4W5D7SjWwP/cpeVYcpg9MrRnM44fc2zNF9KObWFhDdfJicqHa2nPxpPPzROTmidnNQPHCcnNE5GtE7O9se2zCzSf/3VPoLGqWFD/B5/DI/evSs38blQ1Z2XDvnp6j/u6K2F/XvOq8foXaDFSOjwOPiFXTy1oABTZKS9M7KuLO/Z5Eg1idm/sGj/ocCm0GIURNwL7iX8Tk9vhL8mqzUlANXqQp+3Ibx/2T6MbVaI3QUnVqm1Q5f3r7gCRQFLrha9sw3Nhc+mGq3Ump6Gd6mJVEUnBIqi1uxcSIbObC768wL1w7LVWGg6VE1MbhU2K5zbp9YMnVwNcbspee4bzSUdd/3U/mgXHntWV2ttKuJ3lx4Dh5eqNUPxB4rv96ihNvX41Abv2hcf+4Sq/XpuopGaFUZR1CT+71fsNZj4N4Q+76jNnJfLSlD/L0Vvg+gt6v8BpZTBEZfTORX2tXNV742u0OJ+6PBoxV0PN1EC1L59e9q2bcuMGTMAsNlshISE8OSTT/LSSy8VO37ixIkcPXqUtWvX2rc999xzbN++nU2bNgFqArRs2TL27dtX7rgkAbq6edvO8sqyQ9Txc2PtpO5oteX752W2mnlkzSPsjN9JoGsgC+5cQIBrOZpjLrDZ4PifKBs/Je7nk2TFXrmDYmmcGjfC/4kncO/Zs/TEJz0aEg6r09xbzeqIC6tZfX7hsc2sDoG2FqiPTVkXk5xLk5289Is1CZdzD4J2E6DNAxVfFW+zwql1sHe+2qxwocOlVg9hfaHlfeo34owY+HuKOrMtqLUR3V+Edg9fsX/MVaXHQNLx0jvgXt5BtyBHrWVqeKeadJW1pqCiKIrafHJ2izo6qEE/qN7s+sZwo8pJgbPq/2F1tJKvuvyDi8/1r01JPgkx29W/HZ/a4BWiduYWJbOa1QEV696FvMKO7PVugy7PFr7ft6oJT+qp4ud614JanaBWB/CtryY2F5Icg9vFAQbX6T3gyOd3lS2GWlBQwO7du5k8ebJ9m1arpXfv3mzdurXEczp16sS8efPYsWMH7dq149SpU/z555+MHl10IcqTJ09So0YNnJ2d6dixI1OnTqVWreKzbV5gMpkwXdLxMrOCR7X81yiKwtytZwAY3aF2uZMfRVF4a9tb7IzfiZvBjS9u+6L8yY85X+2LsXUGSlIk57Z5kxXrikanIWhYc/SJm7AVFKBYNCjO/thq90Dxb4ZisWIzmVBMBSj5+Sg2K+7duuPes0fJ/Y/So+HwMjiyrPAbbwXT6gubf7zV/hQt74cmd19bknHF19OpfR3q91abpg4uhn0L1OHbx/9Qb65+apJmM6v9TNqMhx7/s48suybeIeVaFqDKaDSFNQehVR3JjcfNV53Z+EbgF1akllRchc6gfsmKuAf+/Qi2f602cUatvexAjdpUWKujmvDU6nj9v4RUoCpLgJKTk7FarQQGFl0YMzAwsNT+OqNGjSI5OZkuXbqoIw4sFh599FH+97//2Y9p3749c+bMITw8nPPnz/PGG2/QtWtXDh06hEcpEz5NnTq1WL8hUbptp1I5kZCNi0HH0NblX919zuE5/Br5K1q0fNj0UcJdyzGzbm4q7PwOdnwDOUkoNji/y5/MaAPo9QR//hkePXuq3053fAM7voa8aDDNhbQA6Pi4WrNypc5/6dFwZLna1+DSpEejVf8ZGNzUfyA6g9o/RGdQExmd8ZLHhfucPMDFW01wnL0uPr5wb3S7/v06LnDxUf8JtpsACUfUJrIDiyCncIh3vV7q0NkAx0b7CSFuEi4+0PcdaPsgrH5N7R8U0Lgw2emkjvpz8a7qKCtMlTWBnTt3juDgYLZs2ULHjh3t21988UU2bNjA9u3bi52zfv16RowYwdtvv0379u2JjIzk6aefZsKECUyZMqXE10lPT6d27dpMmzaNBx8seUh1STVAISEh0gRWisfn7+bPg/GMal+Ld4eU0Om1DP6J/odn1j2DgsJLaVncl17YMdY9UK1G9a2n3le7cF9Hnc/lgrQz6uijvfPsHe0Uz5qcP9qQjI1HQKcjePoneN5+e9EXLshRR+xsmQGZseo2J081CerwGHgUzg6cHnNJ0rPrkgI06qiUxoOg0UDwcHxl+5uK1QxnNqpJXki7qkvOhBCiDG6KJjA/Pz90Oh0JCQlFtickJBBUyhT1U6ZMYfTo0Tz00EMAREREkJOTw8MPP8zLL7+MtoS+Gt7e3jRo0IDIyMhSY3FycsKpnFP532rOZ+Tx12H1dzamY+1ylXEs9RgvbXwJBYXhuRZGpaepbcbmXMhOUG9nNxc9SaNV2/F966u1KSf/vtj5LigCpeNTxC85RMbGX0CrJfijD4snP6DWsHR4DNo8CIcWw+ZPIekYbJ4O275U58VIPVW4mKH9xdWOmk0G3xpJz6V0BrXmRwgh/mOqLAEyGo20bt2atWvXMnjwYEDtBL127VomTpxY4jm5ubnFkhxd4VwspVVkZWdnExUVVayfkCifn7ZHY7UptKtTjYZBjteOJeUmMXHtRPIseXS06vi/hGg0gRHwwCp1Lo6UqMJb5CW3KCjIUjvjpV8yMWC926DzUyih3Uh4dyrpP/8CGg013n8PzzvuuHIgeqM62qnZCHVE0qZPIHaH2uQDqElPJzUhajTgYs2QEEKI/4QqS4AAJk2axNixY2nTpg3t2rVj+vTp5OTkMH78eADGjBlDcHAwU6dOBWDAgAFMmzaNli1b2pvApkyZwoABA+yJ0PPPP8+AAQOoXbs2586d47XXXkOn0zFy5Mgqu87/CpPFyoId6vo/YzuGOnx+niWPp/55ioTcBOpg5KPYKAzugTBqoTp3CKhLNwS3KnqioqiTsaVEQmqU+rhBPwhqiqIoJL7/AWnz5oFGQ/V33sFrwICyB6XVqsOow+9Qh3ce/hV8w9Skx/PGXu1bCCFE+VVpAjR8+HCSkpJ49dVXiY+Pp0WLFqxatcreMTo6OrpIjc8rr7yCRqPhlVdeIS4uDn9/fwYMGMA777xjPyY2NpaRI0eSkpKCv78/Xbp0Ydu2bfj7l3+eGqFadSie5OwCAj2d6NPEsWYgm2LjlU2vcCjlEN4aI19En8FT6wQjflIXhLwSjUZtdvIIhNDO9s2KopA07RNS58wBIOiN1/G+e4ijl3XxNWp3Um9CCCH+86p8JugbkcwDVLKhX21h99k0nu3dgKd7OzbEdMbeGXx94Gv0Gi3fnjtPm3wT3DNbnTyunJI+n0HyF18AEPjqFKqNGlXusoQQQtz8HPn8vgWnvhTlcSgug91n09BrNYxs59i8Lb+f+p2vD3wNwGtJKWry02PyNSU/yTNnXkx+Jr8kyY8QQgiHSAIkyuTCxId3RFQnwLPsM6ruS9zHq5tfBeCBbBODs7LUxKf7/5U7lpRZs0ia/ikAAS88T7WxY8tdlhBCiFtTlfYBEjeHjFwzy/ep6x85MvQ9LjuOp9c9jdlmppdZw9NJCRDcBgZ9Ua75ZPL27yfl+zlkrVoFgP8zT+NbytxOQgghxJVIAiSuav2JREwWG2EB7rSp7VOmc7ILspm4diKp+ak0wsjUuCi0njVhxAJ1XZgyUqxWstasJXXOHPL27rVv95s4Eb9HK3YRPSGEELcOSYDEVW04oS6F0KtRQMnrY13GYrPwwr8vEJkeib/GyGdnT+Oqd1WHu5dxEkFrdg4ZS5eSOncu5tjCGZsNBrzuvJNq48bi3LBhua9HCCGEkARIXJGiKGw8mQxAt7CyTSXwzYFv2BS3CWeNns9jowmy2mDYd+oq3ldhjo8n9ccfSf/5F2xZWQDovLzwHjkCn1GjMARcw0rxQgghRCFJgMQVHYvPIinLhLNBS5vQqzd/ZRdk8+ORHwF4NTGRJgUF0PsNdbLBK8g7dJjUOXPIXLUKLBYAjKGhVBs3Fq9Bg9C6lL3ZTAghhLgaSYDEFW08qTZ/dajri5Ned9XjF59YTLY5m7pmK3dmZ0OL+6Hz06Ueb83KIu6558j5d6N9m2u7dlQbNw73Ht3RlLC+mxBCCHGtJAESV/TvibI3f5mtZn488A0A49LT0dbqBHd9UuqIL2t2NjEPTSBv/37Q6/G84w6qjRuLS5MmFXcBQgghRAkkARKlyiuwsuNMKgDdGvhd+WBzHn8uH0uiOQt/i4U7/VrCvT+qi46WwJqdQ8yEh8nbvx+tlxe1Zs+SxEcIIcR1IwmQKNX20ykUWGzU8HKmnr976QcmR6L8MpY5+mQwGhnl2xrjwB9AW3KTmTU7h5iHHyZv7160np6S/AghhLjupIOFKNWF0V9dw/xLH/5+aAl8051NmZFEGo246py4t/+XpSY/tpwcYh59hLw9e9B6eFBr9mxJfoQQQlx3UgMkSnWhA3S3BiX0/zHnw1+TYddsAOaEhgEm7gkfjqex5AXobLm5xDz6GHm7dqN1d1drfppK8iOEEOL6kxogUaLzGXmcSMhGo4HO9X2L7kyJglm9C5MfDYfbP8AOjQm9Rs/oxqNLLM+Wl0fMY4+Tu3OnmvzM+g6XiKvPCySEEEJUBkmARIkuNH81q+mNt+slHZkP/wpfd4f4g+DqC/cvZo6rWpHYr04/gtyCipVlT362b0fr5kbIt9/g0rz5dbkOIYQQoiTSBCZK9G/h8hfdwwpHf1lM8NfLsPNb9XmtTnDPLGI1Nv7e8hwA45qMK1aOLT+f2CeeIHfbNrSuroR8+y2uLVtej0sQQgghSiUJkCjGalPYFFnYAbqBPxTkwJy74Nwe9YCuz0GP/4FOz9zt72JTbHSq0YnwauFFyrGZTMQ+MZGcLVvRuLoS8u03uLaS5EcIIUTVkwRIFHMoLoP0XDMeTnpahHjDru/U5MfFB+7+DsJ6A5Cen86yyGVA8dofm8lE7MQnydm8GY2rK7W++RrX1q2v74UIIYQQpZA+QKKYC6O/OtbzxaDVwM7v1B09/mdPfgAWHl9IniWPhtUa0qF6B/t2W0EBsU89Rc7GjWhcXAiZ+RWubdpc12sQQgghrkQSIFGMffmLBv5wZiMkHweDGzQfYT8m35LPT8d+AtTanwvzBClWK+eee56cDf+icXYm5KuvcGvX7vpfhBBCCHEFkgCJIrLyzeyJTgMK1/+6UPvTfDg4X5zfZ0XUClLzU6nhVoM+oX3s2xPef5+s1avRGAyEfPUlbh3aX9f4hRBCiLKQBEgUse1UKhabQqivK7UM6XD0d3VH24fsx1htVuYemQvA6MajMWgNAKT+8ANpc38EoPp7U3Hr2PG6xi6EEEKUlSRAoogLw9+7hvnD7h9AsapD3gMvzti8PmY9ZzPP4mn05O6wuwHI/OtvEt57H4CAF57H6847r3vsQgghRFlJAiSKuNABunt9b9g9R93Y7mLtj6IozD6sLn8xPHw4rgZXcvfs5dwLL4Ci4DNqFNUeeOA6Ry2EEEI4RhIgYRedksuZlFz0Wg2dLdsgOx7cA6HhAPsxexP3ciDpAAatgVGNRmE6fZrYxx9HKSjAvVcvAl/+X+kLpwohhBA3CEmAhN2/hbU/rWr54LL3e3Vjq7Ggv7gUxveH1e0D6w3EO1dDzMOPYE1Px7lZM4I//giNruRV4IUQQogbiSRAwu5C/5/BwRlwdhNodNB6nH3/qYxTrI9ZD8DouvcS89jjmGNiMISEEPLVl2hdXK5/0EIIIUQ5SAIkADBbbWyNSgGgT27hyK+G/cEr2H7M3MPqyK+ewd0xvvkF+QcOoPP2JuSbr9H7+hYrUwghhLhRSQIkANgXk06WyUKwiwXfqF/VjW0n2Pcn5yWzImoFKAoT1mjJ/ucfNEYjNb/8Eqc6daooaiGEEKJ8JAESAGwsbP56yn83moJs8GsAdbrZ9y84ugCzzcwjh4LQL1sNGg01PvxQFjcVQghxU5IESACw4WQyoNAv7w91Q9uHoHA0V645l0XHF9HxiI3bfo8DIPCl/8Ozb59SShNCCCFubFWeAH3xxReEhobi7OxM+/bt2bFjxxWPnz59OuHh4bi4uBASEsKzzz5Lfn7+NZV5q0vPLeBAbDrtNcfwyoostu7X76d+Jzgygyd/twFQbewYqo0dW1XhCiGEENesShOgRYsWMWnSJF577TX27NlD8+bN6du3L4mJiSUev2DBAl566SVee+01jh49yqxZs1i0aBH/+9//yl2mgM2RKSgKPOa+Tt3Q7F5w9gLUiQ9X7lnIC0us6K3gcfvtBLz4YhVGK4QQQly7Kk2Apk2bxoQJExg/fjyNGzdm5syZuLq6Mnv27BKP37JlC507d2bUqFGEhobSp08fRo4cWaSGx9EyhTr83Z80ulq2qhsuWffrUPIhvLcfxz0f9PXqUuPDD2SuHyGEEDe9KkuACgoK2L17N717974YjFZL79692bp1a4nndOrUid27d9sTnlOnTvHnn3/Sv3//cpcJYDKZyMzMLHK7VSiKwsaTSYzS/YNOsUKtjhDU1L7/5xM/0zpSAcBn4CC0zs5VFaoQQghRYcqdABUUFHD8+HEsFku5zk9OTsZqtRIYGFhke2BgIPHx8SWeM2rUKN588026dOmCwWCgXr169OjRw94EVp4yAaZOnYqXl5f9FhISUq5ruhlFJWWTmJHNKP0/6oZLan8yTBmsPbGSpmfUBMi9R/eqCFEIIYSocA4nQLm5uTz44IO4urrSpEkToqOjAXjyySd57733KjzAS61fv553332XL7/8kj179rB06VL++OMP3nrrrWsqd/LkyWRkZNhvMTExFRTxje/fE8ncrt1NoCYN3Pyh0UD7vt9P/U79U3k4WUBfPQinBg2qMFIhhBCi4jicAE2ePJn9+/ezfv16nC9pDunduzeLFi0qczl+fn7odDoSEhKKbE9ISCAoKKjEc6ZMmcLo0aN56KGHiIiIYMiQIbz77rtMnToVm81WrjIBnJyc8PT0LHK7Vfx7MonRutXqk0vW/VIUhZ+P/0yrwuYv9+7dZZFTIYQQ/xkOJ0DLli1jxowZdOnSpcgHYpMmTYiKiipzOUajkdatW7N27Vr7NpvNxtq1a+nYsWOJ5+Tm5qLVFg1ZV9ghV1GUcpV5KzNZrCSd2k8n3REUjRbajLfv252wm1PpUbQp/JW69+hRNUEKIYQQlUDv6AlJSUkEBAQU256Tk+NwDcGkSZMYO3Ysbdq0oV27dkyfPp2cnBzGj1c/iMeMGUNwcDBTp04FYMCAAUybNo2WLVvSvn17IiMjmTJlCgMGDLAnQlcrU1y060waw5S/1Sfh/cGrpn3fLyd+ISQJfDMVNM7OuHXoUEVRCiGEEBXP4QSoTZs2/PHHHzz55JMA9qTnu+++c7iWZfjw4SQlJfHqq68SHx9PixYtWLVqlb0Tc3R0dJEan1deeQWNRsMrr7xCXFwc/v7+DBgwgHfeeafMZYqLth07w8O6jQBoLun8nJqfyuqzq7mzsPnLrX17Gf0lhBDiP0WjKIriyAmbNm3ijjvu4P7772fOnDk88sgjHDlyhC1btrBhwwZat25dWbFeN5mZmXh5eZGRkfGf7g8044PJTMz9kiz3Ong8t9e+9MX3h75n2u5pfPSTkVpncgl6/TV8Roy4SmlCCCFE1XLk89vhPkBdunRh3759WCwWIiIi+PvvvwkICGDr1q3/ieTnVpGUmU/v7N8A0LR90J782BQbv5z4BY9chZDoPEDtAC2EEEL8lzjcBAZQr149vv3224qORVxHR3f8RTdtDPk44d5utH37tvPbiMmKofdZJzS2XJzCwzFUr16FkQohhBAVz+EaoN69ezNnzpxbarbk/yK3A3MBOBbQD1y87dt/Of4LAP3P+wMy+ksIIcR/k8MJUJMmTZg8eTJBQUEMGzaM5cuXYzabKyM2UVkUhXqZheunNR9l35yYm8i6mHXorAo1jyQD0vwlhBDiv8nhBOjTTz8lLi6OZcuW4ebmxpgxYwgMDOThhx9mw4YNlRGjqGCmpCi8ycSk6KnR+OLIvaUnl2JVrNyVEwZZOei8vXFp3qwKIxVCCCEqR7nWAtNqtfTp04c5c+aQkJDA119/zY4dO+jVq1dFxycqQdJRdej7UU1d/L3VXvJWm5UlJ5cAcGe8OmWAe/dusvK7EEKI/6RydYK+ID4+noULFzJv3jwOHDhAu3btKiouUYkKzqjNX7GuTWhROPprU9wm4nPi8Xbyxn9vDGak+UsIIcR/l8M1QJmZmXz//ffcfvvthISE8NVXXzFw4EBOnjzJtm3bKiNGUcFcE3cDkOnf0r7t5xM/AzDCoyfmU6dAp8OtS5cqiU8IIYSobA7XAAUGBuLj48Pw4cOZOnUqbdq0qYy4RGUpyME/5yQA+lpqjd257HNsjFWbxfqe88UMuLZuje4/PAmkEEKIW5tDCZCiKHz22Wfcd999uLq6VlZMojKd24cOG+eVagTXDgNg8YnFKCi0r94e46KD0vwlhBDiP8+hJjBFUXjiiSeIi4urrHhEJTOf3Q7AHlt9wgLdMdvMLD25FIDhIYPI3aH2D3Lv2aOKIhRCCCEqn0MJkFarJSwsjJSUlMqKR1SyvNNqP61j+ob4uzuxLnodKfkp+Ln40SbGiGI2Y6hVC2OdOlUcqRBCCFF5HO4E/d577/HCCy9w6NChyohHVCZFwXh+FwCpPs3RaDT2zs9D6g8h71+1H5B79+5oCkeHCSGEEP9FDneCHjNmDLm5uTRv3hyj0YiLi0uR/ampqRUWnKhg6WdxNqVQoOjQBbfgbOZZtp/fjgYNQ+vfTfaGkQC495D+P0IIIf7bHE6Apk+fXglhiOsiVq39OaKEUjfIl8UnFgPQJbgL1aIzyExKRuPqimvbtlUZpRBCCFHpHE6Axo4dWxlxiOshRu3gvMcWRj1/J2btWQbAveH3kr18PQDunTuhNRqrKEAhhBDi+nA4AYqOjr7i/lq1apU7GFG5bDE70QJ7bfVxNm8n3ZROkFsQXYO7Er3+C0BWfxdCCHFrcDgBCg0NvWIHWavVek0BiUpizkMTfwCASKfG5Mf9CcDQsKEoKankF3Zqd+/WrcpCFEIIIa4XhxOgvXv3FnluNpvZu3cv06ZN45133qmwwEQFO7cPjWIhUfHGPTCUw8mHAegZ0pPsdf8C4Ny0KXp//6qMUgghhLguHE6AmjdvXmxbmzZtqFGjBh9++CF33313hQQmKljsTkDt/xMcYOJYdhZ6rZ66XnVJWD8DkOYvIYQQtw6H5wEqTXh4ODt37qyo4kRFi73QAbo+7h6JANT1qovOqpCzeQsgy18IIYS4dThcA5SZmVnkuaIonD9/ntdff52wsLAKC0xUIEWBGDU53WsLo4HhHAANfBqQu3MnttxcdP5+ODdpXJVRCiGEENeNwwmQt7d3sU7QiqIQEhLCwoULKywwUYEyYiE7HrOi46BShwDLBgDCfcLJXq4+du/eHY22wioEhRBCiBuawwnQunXrijzXarX4+/tTv3599HqHixPXQ2Hz11GlFs6u7pzNigKggU8Y2esXANL8JYQQ4tbicMbSXT4obz6FM0DvsYVRP8DIiSx1Lqd6GS6kxsSgMRhw69ipKiMUQgghrqsyt3ns3r2bnj17FusDBJCRkUHPnj3Zv39/hQYnKkjhDNB7bfXx901FQcHPxQ/9NvX35dq2LTp3t6qMUAghhLiuypwAffzxx/Tq1QtPT89i+7y8vLj99tv58MMPKzQ4UQEsJiicAHGPEoazWwJQ2P9n/XpAhr8LIYS49ZQ5Adq+fTuDBg0qdf+AAQPYsmVLhQQlKtD5/WAtIBUvYpQAzLpYABoba5O7ezcgq78LIYS49ZQ5AYqLi8PDw6PU/e7u7pw/f75CghIVqLD5a7e1PqAhueAMABGnbWC1YqxbF6Os3yaEEOIWU+YEyN/fn+PHj5e6/9ixY/j5+VVIUKICXTIDtLerjjOFI8Cq74sDpPlLCCHEranMCVDv3r1LXetLURTeeecdevfuXWGBiQpSmADtVeoTGlhAjjkHV5sezWZ1ZJhHzx5VF5sQQghRRco8DP6VV16hdevWtG/fnueee47w8HBArfn5+OOPOXHiBHPmzKmsOEV5ZMRBZhw2dOy31aWjTzJRedDvfAC2rGj0QUG4tG5d1VEKIYQQ112Za4Dq1avHmjVryMnJYcSIEbRq1YpWrVoxcuRIcnNzWb16NfXr1y9XEF988QWhoaE4OzvTvn17duzYUeqxPXr0QKPRFLvdeeed9mPGjRtXbH+/fv3KFdtNrbD2J8ZYhzyc0bvEA9DpkAUAzzv7y+zPQgghbkkOTYTYpk0bDh06xL59+zh58iSKotCgQQNatGhR7gAWLVrEpEmTmDlzJu3bt2f69On07duX48ePExAQUOz4pUuXUlBQYH+ekpJC8+bNGTZsWJHj+vXrx/fff29/7uTkVO4Yb1oXmr9samKaRwyu+Qo1D6pD4b3uuqvKQhNCCCGqUrnWrmjRosU1JT2XmjZtGhMmTGD8+PEAzJw5kz/++IPZs2fz0ksvFTu+WrVqRZ4vXLgQV1fXYgmQk5MTQUFBFRLjTaswAfo3rw4ACfmnaX9cQWu2YaxfD6eGDasyOiGEEKLKVGn7R0FBAbt37y7SeVqr1dK7d2+2bt1apjJmzZrFiBEjcHMrOpPx+vXrCQgIIDw8nMcee4yUlJRSyzCZTGRmZha53fQsBXBuHwB7bPXxdrNyPjeOzkcUQK39uXxRWyGEEOJWUaUJUHJyMlarlcDAwCLbAwMDiY+Pv+r5O3bs4NChQzz00ENFtvfr14+5c+eydu1a3n//fTZs2MAdd9yB1WotsZypU6fi5eVlv4WEhJT/om4U8QfBasJk9OGMEkTNwAx8shSanlUTIE9p/hJCCHELu6mXb581axYRERG0a9euyPYRI0bYH0dERNCsWTPq1avH+vXrue2224qVM3nyZCZNmmR/npmZefMnQYUrwMe4NoFMDZ5eSTT9V0GrgEvLlhhr1qziAIUQQoiq41ANkMVi4c033yQ2NrZCXtzPzw+dTkdCQkKR7QkJCVftv5OTk8PChQt58MEHr/o6devWxc/Pj8jIyBL3Ozk54enpWeR20yucAfqgpgEAGuM5uhy2AeB5152lniaEEELcChxKgPR6PR9++CEWi6VCXtxoNNK6dWvWrl1r32az2Vi7di0dO3a84rm//PILJpOJ+++//6qvExsbS0pKCtWrV7/mmG8asepEhxvyQgFwSjxJvXhQtFo877ijCgMTQgghqp7DfYB69erFhg0bKiyASZMm8e233/LDDz9w9OhRHnvsMXJycuyjwsaMGcPkyZOLnTdr1iwGDx6Mr69vke3Z2dm88MILbNu2jTNnzrB27VoGDRpE/fr16du3b4XFfUPLioeMaBSNljUZwYCN+rtOA6Br3wr9ZSPphBBCiFuNw32A7rjjDl566SUOHjxI69ati42+GjhwoEPlDR8+nKSkJF599VXi4+Np0aIFq1atsneMjo6ORnvZZH3Hjx9n06ZN/P3338XK0+l0HDhwgB9++IH09HRq1KhBnz59eOutt26duYAKh7/n+4STfc4Fb480Oq43A+A/eGhVRiaEEELcEDSKoiiOnHB5MlKkMI2m1JFWN5PMzEy8vLzIyMi4OfsD/T0FtnzG6dB76XlsMH1ct/HsgsUUGLREbNuB9rKkVQghhPgvcOTz2+EmMJvNVurtv5D8/CcU1gAd0arrtbWP2gfA+VY1JfkRQgghuMZ5gPLz8ysqDlFRrGY4txeALaZ6aG1WIg6fBcDc+8ody4UQQohbhcMJkNVq5a233iI4OBh3d3dOnToFwJQpU5g1a1aFBygcFH8QLPng7M3mNE9aJEfikW0m0wWCet4incCFEEKIq3A4AXrnnXeYM2cOH3zwAUaj0b69adOmfPfddxUanCiHwuHv1uA2nE3Lp0dhc9jWhhoa+DeuysiEEEKIG4bDCdDcuXP55ptvuO+++9DpdPbtzZs359ixYxUanCiHwhmgk72bYbCY6Xz+MABH2/jj5eRVlZEJIYQQNwyHE6C4uDjq169fbLvNZsNsNldIUOIaFM4AfdLYiHbxR3A1m0n0AmOziCoOTAghhLhxOJwANW7cmI0bNxbbvnjxYlq2bFkhQYlyyk6E9LOAhp3muvSM2QPA5sYaGviFV21sQgghxA3E4YkQX331VcaOHUtcXBw2m42lS5dy/Phx5s6dy++//14ZMYqyKuzvg39DomKzeDhBbZLc1ETLMz6SAAkhhBAXOFwDNGjQIH777TfWrFmDm5sbr776KkePHuW3337j9ttvr4wYRVldSIBC2uK1cxMGxcrZAA0x/hoa+DSo2tiEEEKIG4jDNUAAXbt2ZfXq1RUdi7hWMWoCVBDUmmbHfwFgU2MNLnoXQjxCqjIyIYQQ4oZyTRMhihuI1QLn1D4/Z3KDaJqsLn66qbGGMO8wdFrdlc4WQgghbillqgHy8fFBo9GUqcDU1NRrCkiUU+JhMOeCkxfnNhwmEIUTNauR4pVJz2rS/CWEEEJcqkwJ0PTp0+2PU1JSePvtt+nbty8dO6pLK2zdupW//vqLKVOmVEqQogzObFLva7bBec5aAPY08wAyCZcO0EIIIUQRZUqAxo4da388dOhQ3nzzTSZOnGjf9tRTTzFjxgzWrFnDs88+W/FRiqs7+TcAJpdWeMXNx6zRsblRLgDh1SQBEkIIIS7lcB+gv/76i379+hXb3q9fP9asWVMhQQkHmbLh7BYAMk6ok1HuDqpPgjENgDDvsCoLTQghhLgROZwA+fr6snz58mLbly9fjq+vb4UEJRx0+l+wFqB41yZj7WYANtStBUCwezDuRveqjE4IIYS44Tg8DP6NN97goYceYv369bRv3x6A7du3s2rVKr799tsKD1CUQWHzV56hLZa4LeTqnTjc0BVA+v8IIYQQJXA4ARo3bhyNGjXis88+Y+nSpQA0atSITZs22RMicR0pCpxU52TKjFKHum+p3hSnamnkI/1/hBBCiJKUayLE9u3bM3/+/IqORZRH0jHIjEXROpO59SAA62q2AuM6sEkNkBBCCFGSciVANpuNyMhIEhMTsdlsRfZ169atQgITZVTY/JWjtMSadpYcV0/2+dfBU/kRgAYyB5AQQghRjMMJ0LZt2xg1ahRnz55FUZQi+zQaDVartcKCE2VQ2PyVneQNnGV3SDNwScWqWHDVuxLsHlyl4QkhhBA3IocToEcffZQ2bdrwxx9/UL169TLPEC0qQX4mRG9FUSD7SAIA63zC0DrFA9DApwFajax2IoQQQlzO4QTo5MmTLF68mPr161dGPMIRp9aDzUKBvi7mc/EoBgP7/Orj5rEeG9IBWgghhCiNw9UD7du3JzIysjJiEY660P8nR01Gcxs0JV/vhItbIqDWAAkhhBCiOIdrgJ588kmee+454uPjiYiIwGAwFNnfrFmzCgtOXIGiQKQ683b2WQsAp+s3B8CijwNFaoCEEEKI0jicAA0dOhSABx54wL5No9GgKIp0gr6eEg5B1nlsuJJ7+BQAOwPC0aRmY1LS0aCRJTCEEEKIUjicAJ0+fboy4hCOutD8RQuUgjMYgoPZZvFE63wAgBCPEFwNrlUZoRBCCHHDcjgBql27dmXEIRx1Um3+yknyAsC5cxdi0vPQ+5wHpPlLCCGEuJIyJ0CfffZZidu9vLxo0KABHTt2rLCgxFXkpUHM9sLh7+qQ9/RmbVF2gat7IjakA7QQQghxJWVOgD755JMSt6enp5ORkUGnTp1YsWIF1apVq7DgRCmi1oFipUBfH/P5BDQGA/v96gFRGFziMSFLYAghhBBXUuZh8KdPny7xlpaWRmRkJDabjVdeeaUyYxUXXBj9lVMPANe2bfn9RDpgwaxVa4SkCUwIIYQoXYVME1y3bl3ee+89/v7773Kd/8UXXxAaGoqzszPt27dnx44dpR7bo0cPNBpNsdudd95pP0ZRFF599VWqV6+Oi4sLvXv35uTJk+WK7YZjs9mXv8g5Y1a3te/E9tMpaJ0SsWHBw+BBdbfqVRikEEIIcWOrsHUSatWqRXx8vMPnLVq0iEmTJvHaa6+xZ88emjdvTt++fUlMTCzx+KVLl3L+/Hn77dChQ+h0OoYNG2Y/5oMPPuCzzz5j5syZbN++HTc3N/r27Ut+fn65r++GEb8fchKxadzJPRwFwLaAcGwKhFbPACDMJ0yWKBFCCCGuoMISoIMHD5ZrhNi0adOYMGEC48ePp3HjxsycORNXV1dmz55d4vHVqlUjKCjIflu9ejWurq72BEhRFKZPn84rr7zCoEGDaNasGXPnzuXcuXMsW7bsWi7xxnCh9kdpgWI2YwgJ4ddEHQA1A9MBaf4SQgghrqbMCVBmZmaJt5iYGJYtW8YzzzzD8OHDHXrxgoICdu/eTe/evS8GpNXSu3dvtm7dWqYyZs2axYgRI3BzcwPUvkrx8fFFyvTy8qJ9+/allmkymYpd1w3Lvvq7JwC6Dp3YfiYVAMVYOAReOkALIYQQV1TmUWDe3t6lNqtoNBoeeughXnrpJYdePDk5GavVSmBgYJHtgYGBHDt27Krn79ixg0OHDjFr1iz7tgvNcCWVWVoT3dSpU3njjTccir1K5KRA7E51+PvhcwAcDG6M7Qw0q+lJdLa6RpvUAAkhhBBXVuYEaN26dSVu9/T0JCwsDHd39woLqqxmzZpFREQE7dq1u6ZyJk+ezKRJk+zPMzMzCQkJudbwKl7UP4BCgb4hlvhENEYjv1gDgWx6NnZmdkwqWo2W+t71qzpSIYQQ4oZW5gSoe/fuFf7ifn5+6HQ6EhISimxPSEggKCjoiufm5OSwcOFC3nzzzSLbL5yXkJBA9eoXR0IlJCTQokWLEstycnLCycmpHFdwnUUWNn/l1AH2o2/Vhs2x2QCE1siEGKjtWRtnvXMVBimEEELc+CqsE3R5GI1GWrduzdq1a+3bbDYba9euverM0r/88gsmk4n777+/yPY6deoQFBRUpMzMzEy2b99+c89WbbNenP/njAmAqHrNsCnQPMSb5AJ1QdSGPg2rLEQhhBDiZuHwWmAVbdKkSYwdO5Y2bdrQrl07pk+fTk5ODuPHjwdgzJgxBAcHM3Xq1CLnzZo1i8GDB+Pr61tku0aj4ZlnnuHtt98mLCyMOnXqMGXKFGrUqMHgwYOv12VVvHN7ITcFq9aT3ENqX58VxtqQA3dGBHEkZTEAjX0bV2WUQgghxE2hyhOg4cOHk5SUxKuvvkp8fDwtWrRg1apV9k7M0dHRaLVFK6qOHz/Opk2bSp148cUXXyQnJ4eHH36Y9PR0unTpwqpVq3B2vombhgpHf+UqLcASibZmCCvTDQDc0bQ6S/45AkgCJIQQQpSFRlEUpaqDuNFkZmbi5eVFRkYGnp6eVR2O6puecG4P5xP7k/7PPhJ6D2Scezeah3jzw4NN6LqoKwBbRm7Bw+hRxcEKIYQQ158jn99V2gdIlFF2Epzbow5/PxQHwFoPdR0wtflLrf2p7Vlbkh8hhBCiDMrVBLZ48WJ+/vlnoqOjKSgoKLJvz549FRKYuESU2qHbZGiMJTEJnJz4xRoAWugfUZ2VMf8A0LiaNH8JIYQQZeFwDdBnn33G+PHjCQwMZO/evbRr1w5fX19OnTrFHXfcURkxipNqX6ec7FAAMsKbka810DzEm5o+rvYaIOn/I4QQQpSNwwnQl19+yTfffMPnn3+O0WjkxRdfZPXq1Tz11FNkZGRURoy3NqsFItUaoOzT6mKuW3zDALX5C5AESAghhHCQwwlQdHQ0nTp1AsDFxYWsrCwARo8ezU8//VSx0QmI2w356Vi13uQeVoe/L9HXAtTmr/T8dM7lqMtiNPJtVGVhCiGEEDcThxOgoKAgUlPVxTdr1arFtm3bAHURUhlQVgkuNH/ZWoDFQn5gMHFufsWav6QDtBBCCFF2DidAvXr1YsWKFQCMHz+eZ599lttvv53hw4czZMiQCg/wlnchAUpUV7vfV0Ot5bnQ/HU45TAgHaCFEEIIRzg8Cuybb77BZrMB8MQTT+Dr68uWLVsYOHAgjzzySIUHeEvLiof4A4XD32MB+MOlDqA2f4H0/xFCCCHKw+EESKvVFpmZecSIEYwYMaJCgxKFCtf+MhmbYklMwmp0Yr9vXXvzF0gCJIQQQpRHuSZC3LhxI/fffz8dO3YkLk6dmO/HH39k06ZNFRrcLa+w+Ss7qzYAUcHhmHUG7iqs/ZEO0EIIIUT5OJwALVmyhL59++Li4sLevXsxmdSVyTMyMnj33XcrPMBblqLA6Y0A5ETlArDGU539+Y7Lhr/X8qglHaCFEEIIBzicAL399tvMnDmTb7/9FoPBYN/euXNnmQW6ImWdh7xUrGY9uUfU4e87AxoWbf5KVROgJr5NqixMIYQQ4mbkcAJ0/PhxunXrVmy7l5cX6enpFRGTAEhQk5ucnFpgtZLsE0S8m6+9+QvgcHLhCDDp/yOEEEI4pFzzAEVGRhbbvmnTJurWrVshQQkg4RAA2Ylq09amaurszxeav0A6QAshhBDl5XACNGHCBJ5++mm2b9+ORqPh3LlzzJ8/n+eff57HHnusMmK8NSUeQVEgJyobgB2BjYo0f0kHaCGEEKL8HB4G/9JLL2Gz2bjtttvIzc2lW7duODk58fzzz/Pkk09WRoy3poTDmNL1WDLyKNAbOeRblxcvaf6SDtBCCCFE+TmUAFmtVjZv3swTTzzBCy+8QGRkJNnZ2TRu3Bh3d/fKivHWYzVD0nGyzzsDsMevPmadvmjzl3SAFkIIIcrNoQRIp9PRp08fjh49ire3N40bS9+TSpF8EmxmsuN9ANgV2JAWlzR/gXSAFkIIIa6Fw32AmjZtyqlTpyojFnFB4hEUG+SnqPnpXv8w7ryk+QukA7QQQghxLco1D9Dzzz/P77//zvnz58nMzCxyExUg4RAF2ToUK+TrDJx38y3S/CUdoIUQQohr43An6P79+wMwcOBANBqNfbuiKGg0GqxWa8VFd6tKOIIpQ51kMtojkOa1qhVp/pIO0EIIIcS1cTgBWrduXWXEIS6VcBhThvqrOesRVLz5K1Wav4QQQohr4XAC1L1791L3HTp06JqCEUBeOmTGYspQO0Cf9Qzi0XD/IodcqAGSEWBCCCFE+ZRrNfhLZWVl8c0339CuXTuaN29eETHd2hLV5MaU7QLAGc8gani7FDlERoAJIYQQ16bcCdC///7L2LFjqV69Oh999BG9evXi/9u787Cqqv3x4+/DAQ4yOzGlggMgDmiSElHpTRKtx9TrvZaSqKUmgkOWmfUzzUppUCutvFmK3SwbLPOb84RdyQEhpyRMQrEScGIePWf//jix8wQqyrADPq/nOc/j2eNnsU0+rf1Za+3fv782Y2uasn5EMUJZrvnrhVa34WD4s6NOCqCFEEKImrupV2CZmZnExcXx4YcfkpeXx4gRIygtLWX9+vUyJ1BtyT5Bab41mKDQ2g69u7vFbimAFkIIIWqu2j1AgwcPxt/fn6NHj/Lmm2/y+++/s3Tp0rqMrWm6qgD6tLMH7s6Wr7+kAFoIIYSouWr3AG3evJmpU6cSFRWFr69vXcbUdClKpSHw7s4Gi0OkAFoIIYSouWr3AO3du5f8/HyCgoIIDg5m2bJlXLhwoS5ja3pyMqAsn9I8W6CiB8jO4hApgBZCCCFqrtoJ0J133smKFSs4d+4cTzzxBGvXrsXLywuTycT27dvJz8+vyzibhooRYPnm115nnD1o7fRnD5AUQAshhBC146ZHgTk4OPDYY4+xd+9ejh07xlNPPUVsbCxubm489NBDdRFj05F1HNMVKM8zAeZJEK/uAZICaCGEEKJ21GgeIH9/f1577TV+/fVXPv3001u6xjvvvIOPjw92dnYEBwdz8ODB6x6fk5NDdHQ0np6eGAwG/Pz82LRpk7p/3rx56HQ6i0/nzp1vKbZ6l3WC0jwbUCDfzpEcg6NlAiQF0EIIIUStuOmZoKui1+sZOnQoQ4cOvanzPvvsM2bMmMHy5csJDg7mzTffJDw8nNTUVNzc3CodX1ZWxv3334+bmxtffvklt912G2fOnMHV1dXiuK5du7Jjxw71u7V1rTSz7lksgeEOOp1FEbSsAC+EEELUDk0zg8WLFzNhwgTGjRsHwPLly9m4cSMrV67k2WefrXT8ypUruXTpEt9//z02NuaRUj4+PpWOs7a2xsPDo9L2v7XyErh4irJc86Knvzia5/9xc6r8CkxGgAkhhBA1U+OlMG5VWVkZSUlJhIWF/RmMlRVhYWHs27evynM2bNhASEgI0dHRuLu7061bNxYsWFBpBfqff/4ZLy8vOnToQEREBBkZGXXallpxIRUUIyUF5gTojLMHTnbWNLPVA+YC6N8KfgOkAFoIIYSoKc16gC5cuIDRaMT9LzMdu7u789NPP1V5zi+//MKuXbuIiIhg06ZNnDp1ismTJ1NeXs7cuXMBCA4OJi4uDn9/f86dO8eLL77IPffcw/Hjx3FyqrpwuLS0lNLSUvV7Xl5eLbXyJmT9MQIszwBckQJoIYQQog41kOIYM5PJhJubG++//z56vZ6goCB+++03Xn/9dTUBGjRokHp8YGAgwcHBeHt78/nnn/P4449Xed2FCxfy4osv1ksbrinrOMZyHVfyrgDmHqAeV9f/SAG0EEIIUWs0ewXWqlUr9Ho9WVlZFtuzsrKuWb/j6emJn58fer1e3RYQEEBmZiZlZWVVnuPq6oqfnx+nTp26ZiyzZ88mNzdX/Zw9e/YWWlRD2Sco+6MAutS1BQW29rhXUf8jCZAQQghRc5olQLa2tgQFBbFz5051m8lkYufOnYSEhFR5TmhoKKdOncJkMqnbTp48iaenJ7a2tlWeU1BQQFpaGp6enteMxWAw4OzsbPGpd1ctgXG5dRsAWlcxAkwKoIUQQoia0ywBApgxYwYrVqxg9erVpKSkEBUVRWFhoToqLDIyktmzZ6vHR0VFcenSJaZNm8bJkyfZuHEjCxYsIDo6Wj3m6aefZs+ePZw+fZrvv/+eYcOGodfrGTlyZL23r9oKL0JBpjoEPrPFbQBqD5AUQAshhBC1S9MaoIcffpjz58/zwgsvkJmZSc+ePdmyZYtaGJ2RkYGV1Z85Wtu2bdm6dStPPvkkgYGB3HbbbUybNo1Zs2apx/z666+MHDmSixcv0rp1a+6++272799P69at67191ZZtXt+rtMgJUEh3Nre/oghaCqCFEEKI2qV5EXRMTAwxMTFV7ouPj6+0LSQkhP3791/zemvXrq2t0OpP1h8JUI41UM5PBnOyVjEJohRACyGEELVL01dg4g9ZP3KlVMeVgnIAjuibA5V7gCQBEkIIIWqHJEB/B1k/UvZHAbTe04tcnbmgu2IleEmAhBBCiNolCZDWTEY4/5NaAG30aQ+ASzMb7Gz0UgAthBBC1AFJgLR2+TSUF1Gab37dVezlA1xV/3NVAbSzrQbD84UQQohGSBIgrVUUQBeaR3ddav3HEPiK+h8pgBZCCCFqnSRAWsv6EUWB0svmr7+38AL+XAVe6n+EEEKI2icJkNayf8RYYoWxqBysrDjjYB4C7+YsBdBCCCFEXZEESGtZP6oF0LZt2/J7sQKAu5NBCqCFEEKIOiIJkJbKCuFSuroGmMHPl+z8UsBcAyQF0EIIIUTdkARIS9k/AYpaAG3bqRNZeSUAuDnbSQG0EEIIUUckAdJSxRpghfYAGDr5kp1X0QNkkPofIYQQoo5IAqSlrBPmEWAXrgBQ2taHMqMJMM8CLQmQEEIIUTckAdJS1nGuFOkxlV4Ba2suNfcAoLm9DUalVC2A9mvup2WUQgghRKMjCZBWFMViBJihvQ/ZxUbAXAB9Ou80AM0NzWlu11yrKIUQQohGSRIgrRRkQfElSvP+GAHm62tRAJ2WkwZAe5f2moUohBBCNFbWWgfQZFUsgVHcAriCbadOfw6BdzKQnpsOQAfXDlpFKIRoAIxGI+Xl5VqHIUS9sLGxQa/X18q1JAHSSkUClG8LXPlLD5CBX3J/AaCjS0etIhRC/I0pikJmZiY5OTlahyJEvXJ1dcXDwwOdTlej60gCpJXsEygmKD1v7vWx8/Ul+38XAXMN0P+yzAlQBxfpARJCVFaR/Li5uWFvb1/jXwZC/N0pikJRURHZ2dkAeHp61uh6kgBpJes45YV6lHIjOoMBm7Ztyco3j/pq6WjN2Z/PAvIKTAhRmdFoVJOfli1bah2OEPWmWbNmAGRnZ+Pm5laj12FSBK0F4xU4n6ougWHbsQM6vV6dBFGxPs8V5Qr21va427trGakQ4m+ooubH3t5e40iEqH8Vf+9rWvsmCZAWLp4CYxmlBRUzQHdCURSy8801QIXKOcA8Aky6tYUQ1yL/PoimqLb+3ksCpIWKJTBKzPP7GHx9uVxUTrnRvBL8pbI/Xn9J/Y8QQghRJyQB0kLFCLAc87vLq0eAtXSw5Uy+DIEXQoirzZs3j549e97UOf369WP69Ol1Eo9o+KQIWgtZFSPAigDzIqhXT4L4S455BJhMgiiEEGZPP/00U6ZMualzvvrqK2xsbOooItHQSQ+QFrJ/pCzfGowmrOztsfHyVAug3Zxt/pwEUV6BCSGaOEVRuHLlCo6Ojjc94q1FixY4OTnVUWSioZMEqL6V5EFOhroGmK1vJ3RWVmoPkJNDASXGEqytrGnr1FbLSIUQok6UlpYydepU3NzcsLOz4+677yYxMRGA+Ph4dDodmzdvJigoCIPBwN69eyu9Arty5QpTp07F1dWVli1bMmvWLMaMGcPQoUPVY/76CszHx4cFCxbw2GOP4eTkRLt27Xj//ffrqdXi70YSoPqWnQJAaUkLwDwCDFCXwbCxOw+At5M31lbyhlIIUT2KolBUdkWTj6IoNxXrM888w7p161i9ejXJycl06tSJ8PBwLl26pB7z7LPPEhsbS0pKCoGBgZWu8eqrr7JmzRpWrVpFQkICeXl5rF+//ob3XrRoEXfccQc//PADkydPJioqitTU1JuKXzQO8hu2vmUdB6C0yBnIw+Dra978Rw+Q0ToLkAJoIcTNKS430uWFrZrc+8T8cOxtq/frpLCwkPfee4+4uDgGDRoEwIoVK9i+fTsffvghvXv3BmD+/Pncf//917zO0qVLmT17NsOGDQNg2bJlbNq06Yb3f+CBB5g8eTIAs2bNYsmSJezevRt/f/9qxS8aD+kBqm/ZJwAovWT+PyY1AfqjB6hI+R2Q+h8hROOUlpZGeXk5oaGh6jYbGxv69OlDSkqKuu2OO+645jVyc3PJysqiT58+6ja9Xk9QUNAN7391b5JOp8PDw0NdWkE0LdIDVN+yfsRkhLLzBYB5BBhA9h89QJfLfwUkARJC3JxmNnpOzA/X7N61zcHBodavCVQaFabT6TCZTHVyL/H3Jj1A9UlRIOsEZXnWoChYubhg7dYak0nhfH4poHCu6Awgr8CEEDdHp9Nhb2utyedmZubt2LEjtra2JCQkqNvKy8tJTEykS5cu1bqGi4sL7u7uauE0mNdHS05Orv4PTDR50gNUn3J/hdJcSvPM/2dj6NQJnU7HxcJSrpgUrKwLyS/PQ4cOH2cfbWMVQog64ODgQFRUFDNnzqRFixa0a9eO1157jaKiIh5//HGOHDlSretMmTKFhQsX0qlTJzp37szSpUu5fPmyLA8iqk3zHqB33nkHHx8f7OzsCA4O5uDBg9c9Picnh+joaDw9PTEYDPj5+VUqfLvZa9abivqfcjcADL7mEWAVBdCuLpcB8HL0ws7aToMAhRCi7sXGxjJ8+HBGjx5Nr169OHXqFFu3bqV58+bVvsasWbMYOXIkkZGRhISE4OjoSHh4OHZ28m+nqB5Ne4A+++wzZsyYwfLlywkODubNN98kPDyc1NRU3NzcKh1fVlbG/fffj5ubG19++SW33XYbZ86cwdXV9ZavWa8qRoAV2AOlagF0xSSIjk4XuYzU/wghGjc7Ozvefvtt3n777Ur7+vXrV+Ww+nnz5jFv3jz1u7W1NUuXLmXp0qUAmEwmAgICGDFihHpMfHy8xTVOnz5d6bqHDx++pTaIhk/THqDFixczYcIExo0bR5cuXVi+fDn29vasXLmyyuNXrlzJpUuXWL9+PaGhofj4+NC3b1969Ohxy9esV+7doceoP0eAdbIcAm9jdwGQBEgIIW7kzJkzrFixgpMnT3Ls2DGioqJIT09n1KhRWocmGgjNEqCysjKSkpIICwv7MxgrK8LCwti3b1+V52zYsIGQkBCio6Nxd3enW7duLFiwAKPReMvXBPOspHl5eRafOuE3AFP4Isqzc4A/X4FVTIJoss4EpABaCCFuxMrKiri4OHr37k1oaCjHjh1jx44dBAQEaB2aaCA0ewV24cIFjEYj7u7uFtvd3d356aefqjznl19+YdeuXURERLBp0yZOnTrF5MmTKS8vZ+7cubd0TYCFCxfy4osv1rxR1VCalgaAvlUrrFuYZ4Ou6AGSOYCEEKJ62rZtazGSTIibpXkR9M0wmUy4ubnx/vvvExQUxMMPP8zzzz/P8uXLa3Td2bNnk5ubq37Onj1bSxFXVnryZ+DPJTAAsvJKwaqEIpN5GnhZBV4IIYSoW5r1ALVq1Qq9Xk9WVpbF9qysLDw8PKo8x9PTExsbG/T6PyfdCggIIDMzk7Kyslu6JoDBYMBgMNSgNdVXeuqU+Z5/FEADZOeXYGVrXgOsVbNWuBhc6iUWIYQQoqnSrAfI1taWoKAgdu7cqW4zmUzs3LmTkJCQKs8JDQ3l1KlTFrN2njx5Ek9PT2xtbW/pmvWt9OeqeoBKsDKYp2KX119CCCFE3dP0FdiMGTNYsWIFq1evJiUlhaioKAoLCxk3bhwAkZGRzJ49Wz0+KiqKS5cuMW3aNE6ePMnGjRtZsGAB0dHR1b6m1tQE6I8eIKNJ4UJBGVa25gRIXn8JIYQQdU/TeYAefvhhzp8/zwsvvEBmZiY9e/Zky5YtahFzRkYGVlZ/5mht27Zl69atPPnkkwQGBnLbbbcxbdo0Zs2aVe1rasmYl8eVP17PVYwAu1hYitGkYCs9QEIIIUS90XwpjJiYGGJiYqrc99dJrABCQkLYv3//LV9TSxX1P9YeHuidnIA/J0G0sbuAggyBF0IIIepDgxoF1tCpI8CuKoDOyisB3RUUa5kEUQghrmXevHn07Nnzps7p168f06dPr5N4bkV8fDw6nY6cnBytQxH8DXqAmpLyLPNEh5YJUClWthdAp+Bo40jrZq21Ck8IIf62nn76aaZMmXJT53z11VfY2NjUUUSioZMEqB65TZtGy8fHo5SXqdvMQ+D/rP+RlYyFEOJPiqJgNBpxdHTE0dHxps5t8cdks0JURV6B1TO9owPWV614nJVXqg6BlxFgQoimoLS0lKlTp+Lm5oadnR133303iYmJwJ+viTZv3kxQUBAGg4G9e/dWegV25coVpk6diqurKy1btmTWrFmMGTOGoUOHqsf89RWYj48PCxYs4LHHHsPJyYl27drx/vvvVyvmu+66y2LADcD58+exsbHhu+++A+C///0vd9xxB05OTnh4eDBq1Ciys7Nv7Yck6pwkQBrLzruqB0gKoIUQt0pRoKxQm08Vq7dfzzPPPMO6detYvXo1ycnJdOrUifDwcC5duqQe8+yzzxIbG0tKSgqBgYGVrvHqq6+yZs0aVq1aRUJCAnl5eaxfv/6G9160aBF33HEHP/zwA5MnTyYqKorU1NQbnhcREcHatWstVqr/7LPP8PLy4p577gGgvLycl156iSNHjrB+/XpOnz7N2LFjb/wDEZqQV2Aay8qXSRCFELWgvAgWeGlz7+d+B1uHah1aWFjIe++9R1xcHIMGDQJgxYoVbN++nQ8//JDevXsDMH/+fO6///5rXmfp0qXMnj2bYcOGAbBs2TI2bdp0w/s/8MADTJ48GYBZs2axZMkSdu/ejb+//3XPGzFiBNOnT2fv3r1qwvPJJ58wcuRItXThscceU4/v0KEDb7/9Nr1796agoOCmX9+Juic9QBrLyis2F0EjCZAQovFLS0ujvLyc0NBQdZuNjQ19+vQhJSVF3XbHHXdc8xq5ublkZWXRp08fdZterycoKOiG97+6N0mn0+Hh4VGt11StW7dmwIABrFmzBoD09HT27dtHRESEekxSUhKDBw+mXbt2ODk50bdvX8A8p534+5EeIA1dMZq4VJqJvdUVbKxsuc3xNq1DEkI0VDb25p4Yre5dyxwcqtejdLP+OipMp9NZLK90PREREUydOpWlS5fyySef0L17d7p37w6Ye7bCw8MJDw9nzZo1tG7dmoyMDMLDwykrK7vBlYUWpAdIQxcLy0BdAsMHvZX+BmcIIcQ16HTm11BafG5i9GrHjh2xtbUlISFB3VZeXk5iYiJdunSp1jVcXFxwd3dXC6cBjEYjycnJ1f953YIhQ4ZQUlLCli1b+OSTTyx6f3766ScuXrxIbGws99xzD507d5YC6L856QHSUFae5RB4IYRo7BwcHIiKimLmzJm0aNGCdu3a8dprr1FUVMTjjz/OkSNHqnWdKVOmsHDhQjp16kTnzp1ZunQply9frtOpRBwcHBg6dChz5swhJSWFkSNHqvvatWuHra0tS5cuZdKkSRw/fpyXXnqpzmIRNSc9QBrKyitFLwXQQogmJjY2luHDhzN69Gh69erFqVOn2Lp1K82vmiLkRmbNmsXIkSOJjIwkJCQER0dHwsPDsbOzq8PIza/Bjhw5wj333EO7du3U7a1btyYuLo4vvviCLl26EBsbyxtvvFGnsYia0SnKTY5fbALy8vJwcXEhNzcXZ2fnOrvPmgNneCV5Mnr7DF7v+zoDfQbW2b2EEI1HSUkJ6enptG/fvs5/4TcUJpOJgIAARowYIT0vjdz1/v7fzO9veQWmocxcGQIvhBC34syZM2zbto2+fftSWlrKsmXLSE9PZ9SoUVqHJhoIeQWmobO5mej0JeiwwsfZR+twhBCiwbCysiIuLo7evXsTGhrKsWPH2LFjBwEBAbd0vQULFqjLbfz1UzFfkWhcpAdIQ2cL0sEKXG09sNXbah2OEEI0GG3btrUYSVZTkyZNYsSIEVXua9asWa3dR/x9SAKkofOlZ6EZ3ObgrXUoQgjRpLVo0UIWT21i5BWYhnKv/AbIIqhCCCFEfZMESCPlRhNlVucA6NLSV+NohBBCiKZFEiCNXCgoxcpwHoDubn4aRyOEEEI0LZIAaeSXixewss4HoKOrDIEXQggh6pMkQBr58fzPAOhNrjjaOmocjRBCCNG0SAKkkZ9z0gBwtPLSOBIhhKg/iqIwceJEWrRogU6n4/Dhw1qHJG6gX79+TJ8+Xeswap0kQBo5m38agJa2bbUNRAgh6tGWLVuIi4vj22+/5dy5c3Tr1u2G5xQUFBATE0ObNm1o1qwZXbp0Yfny5fUQrWjMZB4gjWSXZADgJXMACSGakLS0NDw9Pbnrrruqfc6MGTPYtWsXH3/8MT4+Pmzbto3Jkyfj5eXFQw89VIfR1q+ysjJsbWVS3PoiPUAayb3yKwDtZQ0wIUQTMXbsWKZMmUJGRgY6nQ4fHx/69etHTEwMMTExuLi40KpVK+bMmcPV63R///33jBkzhn79+uHj48PEiRPp0aMHBw8erNZ9c3JyeOKJJ3B3d8fOzo5u3brx7bffqvvXrVtH165dMRgM+Pj4sGjRIovzfXx8ePnll4mMjMTR0RFvb282bNjA+fPnGTJkCI6OjgQGBnLo0CH1nLi4OFxdXVm/fj2+vr7Y2dkRHh7O2bNn1WPmzZtHz549+eCDDywW9szJyWH8+PG0bt0aZ2dn7rvvPo4cOaKed+TIEf7xj3/g5OSEs7MzQUFB6r3PnDnD4MGDad68OQ4ODnTt2pVNmzap5x4/fpxBgwbh6OiIu7s7o0eP5sKFC+r+wsJCtZ2enp6VfhaNiSRAGii5UkIZFwEIaNlJ42iEEI2BoigUlRdp8rk6Wbmet956i/nz59OmTRvOnTtHYmIiAKtXr8ba2pqDBw/y1ltvsXjxYj744AP1vLvuuosNGzbw22+/oSgKu3fv5uTJkwwYMOCG9zSZTAwaNIiEhAQ+/vhjTpw4QWxsLHq9HoCkpCRGjBjBI488wrFjx5g3bx5z5swhLi7O4jpLliwhNDSUH374gQcffJDRo0cTGRnJo48+SnJyMh07diQyMtLiZ1FUVMQrr7zCRx99REJCAjk5OTzyyCMW1z116hTr1q3jq6++Uuuh/v3vf5Odnc3mzZtJSkqiV69e9O/fn0uXLgEQERFBmzZtSExMJCkpiWeffRYbGxsAoqOjKS0t5bvvvuPYsWO8+uqrODqaB9rk5ORw3333cfvtt3Po0CG2bNlCVlaWxRIgM2fOZM+ePXzzzTds27aN+Ph4kpOTq/N4Gxx5BaaB03mnQaegGJvRsYWH1uEIIRqB4ivFBH8SrMm9D4w6gL2N/Q2Pc3FxwcnJCb1ej4fHn//2tW3bliVLlqDT6fD39+fYsWMsWbKECRMmALB06VImTpxImzZtsLa2xsrKihUrVnDvvffe8J47duzg4MGDpKSk4OdnnnOtQ4c/e94XL15M//79mTNnDgB+fn6cOHGC119/nbFjx6rHPfDAAzzxxBMAvPDCC7z33nv07t2bf//73wDMmjWLkJAQsrKy1LaVl5ezbNkygoPNz2X16tUEBARw8OBB+vTpA5hfe3300Ue0bt0agL1793Lw4EGys7MxGAwAvPHGG6xfv54vv/ySiRMnkpGRwcyZM+ncuTMAvr5/TqabkZHB8OHD6d69e6W2Llu2jNtvv50FCxao21auXEnbtm05efIkXl5efPjhh3z88cf0799fjblNmzY3/Dk3RNIDpIGTl8wjwEylbni4yCJ7Qoim7c4770Sn06nfQ0JC+PnnnzEajYA5Adq/fz8bNmwgKSmJRYsWER0dzY4dO2547cOHD9OmTRs1+fmrlJQUQkNDLbaFhoZa3B8gMDBQ/bO7uzuAmmRcvS07O1vdZm1tTe/evdXvnTt3xtXVlZSUFHWbt7e3mvyA+fVWQUEBLVu2tFiRPj09nbQ08++OGTNmMH78eMLCwoiNjVW3A0ydOpWXX36Z0NBQ5s6dy9GjRy2uvXv3bovrViRRaWlppKWlUVZWpiZsYF4jzd/fv8qfXUMnPUAaqJgDSCl3o7m9jcbRCCEag2bWzTgw6oBm964rxcXFPPfcc3z99dc8+OCDgDkZOXz4MG+88QZhYWHXj62WVnKveMUEqMlaVdtMJtNNXdfBwcHie0FBAZ6ensTHx1c61tXVFTDXDo0aNYqNGzeyefNm5s6dy9q1axk2bBjjx48nPDycjRs3sm3bNhYuXMiiRYuYMmUKBQUFDB48mFdffbXStT09PTl16tRNxd7QSQKkgZ8vm7N1B52Xxf/1CCHErdLpdNV6DfV3dOCAZeK2f/9+fH190ev1lJeXU15ejpWV5QsLvV5frWQjMDCQX3/9lZMnT1bZCxQQEEBCQoLFtoSEBPz8/NQ6oVt15coVDh06pL7uSk1NJScnh4CAgGue06tXLzIzM7G2tsbHx+eax/n5+eHn58eTTz7JyJEjWbVqFcOGDQPMrxQnTZrEpEmTmD17NitWrGDKlCn06tWLdevW4ePjg7V15V//HTt2xMbGhgMHDtCuXTsALl++zMmTJ+nbt28NfhJ/T/IKTAMZf8wB1ELmABJCCDIyMpgxYwapqal8+umnLF26lGnTpgHg7OxM3759mTlzJvHx8aSnpxMXF8dHH32k/sK/nr59+3LvvfcyfPhwtm/fTnp6Ops3b2bLli0APPXUU+zcuZOXXnqJkydPsnr1apYtW8bTTz9d43bZ2NgwZcoUDhw4QFJSEmPHjuXOO+9UE6KqhIWFERISwtChQ9m2bRunT5/m+++/5/nnn+fQoUMUFxcTExNDfHw8Z86cISEhgcTERDWpmj59Olu3biU9PZ3k5GR2796t7ouOjubSpUuMHDmSxMRE0tLS2Lp1K+PGjcNoNOLo6Mjjjz/OzJkz2bVrF8ePH2fs2LGVks/G4m/RqnfeeQcfHx/s7OwIDg6+7tDGuLg4dDqdxadi6GCFsWPHVjpm4MCBdd2MarliusL5EvMQeK9mMgeQEEJERkZSXFxMnz59iI6OZtq0aUycOFHdv3btWnr37k1ERARdunQhNjaWV155hUmTJlXr+uvWraN3796MHDmSLl268Mwzz6j1Pb169eLzzz9n7dq1dOvWjRdeeIH58+dbFEDfKnt7e2bNmsWoUaMIDQ3F0dGRzz777Lrn6HQ6Nm3axL333su4cePw8/PjkUce4cyZM7i7u6PX67l48SKRkZH4+fkxYsQIBg0axIsvvgiA0WgkOjqagIAABg4ciJ+fH++++y4AXl5eJCQkYDQaGTBgAN27d2f69Om4urqqSc7rr7/OPffcw+DBgwkLC+Puu+8mKCioxj+LvyOdUt3xi3Xks88+IzIykuXLlxMcHMybb77JF198QWpqKm5ubpWOj4uLY9q0aaSmpqrbdDqdWoAG5gQoKyuLVatWqdsMBgPNmzevVkx5eXm4uLiQm5uLs7NzDVpX2enc0wxePxjFZMM/m8cxf2jgjU8SQoirlJSUkJ6ebjF3TEPVr18/evbsyZtvvql1KLUqLi6O6dOnk5OTo3Uojc71/v7fzO9vzXuAFi9ezIQJExg3bpw6vbm9vT0rV6685jk6nQ4PDw/1c3XyU8FgMFgcU93kp679kvsLAKay1ri7NMz39UIIIURDp2kCVFZWRlJSkkUVv5WVFWFhYezbt++a5xUUFODt7U3btm0ZMmQIP/74Y6Vj4uPjcXNzw9/fn6ioKC5evHjN65WWlpKXl2fxqStqAlTqhrtzw/4/NyGE0NqaNWsshnVf/enatavW4Ym/MU1HgV24cAGj0VipB8fd3Z2ffvqpynP8/f1ZuXIlgYGB5Obm8sYbb3DXXXfx448/qpM1DRw4kH/+85+0b9+etLQ0nnvuOQYNGsS+ffuqrOpfuHCh+v60rv2SU5EAtcbNyVAv9xRCiL+rqoZ734yHHnrIYt6aq109TL2+jR07tlbqiETdaXDD4ENCQggJCVG/33XXXQQEBPCf//yHl156CcBiqvHu3bsTGBhIx44diY+PV2e3vNrs2bOZMWOG+j0vL4+2betmhNafr8CkB0gIIWrKyckJJycnrcMQDZCmr8BatWqFXq8nKyvLYvvVU4nfiI2NDbfffvt1J3Dq0KEDrVq1uuYxBoMBZ2dni09dUBSF9Nx0oOIVmPQACSGEEFrQNAGytbUlKCiInTt3qttMJhM7d+606OW5HqPRyLFjx/D09LzmMb/++isXL1687jH1Iasoi6IrRSiKFdam1rg0k1mghRBCCC1oPgpsxowZrFixgtWrV5OSkkJUVBSFhYWMGzcOMM8PMXv2bPX4+fPns23bNn755ReSk5N59NFHOXPmDOPHjwfMBdIzZ85k//79nD59mp07dzJkyBA6depEeHi4Jm2soNb/lLXE3dlBZoEWQgghNKJ5DdDDDz/M+fPneeGFF8jMzKRnz55s2bJFLYzOyMiwmIXy8uXLTJgwgczMTJo3b05QUBDff/89Xbp0AczTox89epTVq1eTk5ODl5cXAwYM4KWXXlJX1tXK1UPg3Zyk/kcIIYTQiuYTIf4d1dVEiGtS1vBu8krOZ3YlzGMM70Y0ztk1hRB1qzFNhCjEzWo0EyE2JREBETzU8h3Kzg+QHiAhRJOkKAoTJ06kRYsW6HQ6Dh8+rHVI4gb69evH9OnT6+1+Op2O9evX1/l9JAGqZ9l5pYBOhsALIZqkLVu2EBcXx7fffsu5c+fo1q3bDc8pKCggJiaGNm3a0KxZM3XVACFqQvMaoKYmO78EQIbACyGapLS0NDw9Pbnrrruqfc6MGTPYtWsXH3/8MT4+Pmzbto3Jkyfj5eXFQw89VIfR1q+ysjJsbW21DqPJkB6gepaVZ06A5BWYEKKpGTt2LFOmTCEjIwOdToePjw/9+vUjJiaGmJgYXFxcaNWqFXPmzOHq8tTvv/+eMWPG0K9fP3x8fJg4cSI9evTg4MGD1bpvTk4OTzzxBO7u7tjZ2dGtWze+/fZbdf+6devo2rUrBoMBHx8fFi1aZHG+j48PL7/8MpGRkTg6OuLt7c2GDRs4f/48Q4YMwdHRkcDAQA4dOqSeExcXh6urK+vXr8fX1xc7OzvCw8M5e/asesy8efPo2bMnH3zwgUU9S05ODuPHj6d169Y4Oztz3333ceTIEfW8I0eO8I9//AMnJyecnZ0JCgpS733mzBkGDx5M8+bNcXBwoGvXrmzatEk99/jx4wwaNAhHR0fc3d0ZPXo0Fy5cUPcXFhaq7fT09Kz0s7ie5557rspZuXv06MH8+fMBSExM5P7776dVq1a4uLjQt29fkpOTq32P2iQJUD3LyisFpAdICFG7FEXBVFSkyae6Y2neeust5s+fT5s2bTh37hyJiYkArF69Gmtraw4ePMhbb73F4sWL+eCDD9Tz7rrrLjZs2MBvv/2Goijs3r2bkydPMmDAgBve02QyMWjQIBISEvj44485ceIEsbGx6rJISUlJjBgxgkceeYRjx44xb9485syZQ1xcnMV1lixZQmhoKD/88AMPPvggo0ePJjIykkcffZTk5GQ6duxIZGSkxc+iqKiIV155hY8++oiEhARycnIsVioAOHXqFOvWreOrr75S66H+/e9/k52dzebNm0lKSqJXr17079+fS5cuARAREUGbNm1ITEwkKSmJZ599Vl32Izo6mtLSUr777juOHTvGq6++iqOjI2BOrO677z5uv/12Dh06xJYtW8jKymLEiBFqPDNnzmTPnj188803bNu2jfj4+GonKBERERw8eJC0tDR1248//sjRo0cZNWoUAPn5+YwZM4a9e/eyf/9+fH19eeCBB8jPz6/WPWqVIirJzc1VACU3N7dWr1tcdkXxnvWt4j3rWyWnqKxWry2EaDqKi4uVEydOKMXFxeo2Y2GhcsK/syYfY2FhtWNfsmSJ4u3trX7v27evEhAQoJhMJnXbrFmzlICAAPV7SUmJEhkZqQCKtbW1Ymtrq6xevbpa99u6datiZWWlpKamVrl/1KhRyv3332+xbebMmUqXLl3U797e3sqjjz6qfj937pwCKHPmzFG37du3TwGUc+fOKYqiKKtWrVIAZf/+/eoxKSkpCqAcOHBAURRFmTt3rmJjY6NkZ2erx/zvf/9TnJ2dlZKSEouYOnbsqPznP/9RFEVRnJyclLi4uCrb0717d2XevHlV7nvppZeUAQMGWGw7e/asAiipqalKfn6+Ymtrq3z++efq/osXLyrNmjVTpk2bVuU1/6pHjx7K/Pnz1e+zZ89WgoODr3m80WhUnJyclP/7v/9TtwHK119/fc1zqvr7X+Fmfn9LD1A9Op9v7v2xs7HC2U7Kr4QQAuDOO++0mBg2JCSEn3/+GaPRCMDSpUvZv38/GzZsICkpiUWLFhEdHc2OHTtueO3Dhw/Tpk0b/Pz8qtyfkpJCaGioxbbQ0FCL+wMEBgaqf66Yp6579+6VtmVnZ6vbrK2t6d27t/q9c+fOuLq6kpKSom7z9vamdevW6vcjR45QUFBAy5YtLVa2T09PV3tWZsyYwfjx4wkLCyM2Ntaix2Xq1Km8/PLLhIaGMnfuXI4ePWpx7d27d1tct3PnzoC5NistLY2ysjKL11gtWrTA39+/yp9dVSIiIvjkk08Ac6/kp59+SkREhLo/KyuLCRMm4Ovri4uLC87OzhQUFJCRkVHte9QW+S1cjyrqf9yd7WQWaCFErdI1a4Z/cpJm964rxcXFPPfcc3z99dc8+OCDgDkZOXz4MG+88QZhYWHXPb9ZLcV29cryFf9+V7XNZDLd1HUdHBwsvhcUFODp6Ul8fHylY11dXQFz7dCoUaPYuHEjmzdvZu7cuaxdu5Zhw4Yxfvx4wsPD2bhxI9u2bWPhwoUsWrSIKVOmUFBQwODBg3n11VcrXdvT0/O6a2pW18iRI5k1axbJyckUFxdz9uxZHn74YXX/mDFjuHjxIm+99Rbe3t4YDAZCQkIoKyur8b1vliRA9aii/sfNSep/hBC1S6fTobO31zqMW3LgwAGL7xW1IXq9nvLycsrLyy1WBADzrP/VSTYCAwP59ddfOXnyZJW9QAEBASQkJFhsS0hIwM/PT60TulVXrlzh0KFD9OnTB4DU1FRycnIICAi45jm9evUiMzMTa2trfHx8rnmcn58ffn5+PPnkk4wcOZJVq1YxbNgwANq2bcukSZOYNGkSs2fPZsWKFUyZMoVevXqxbt06fHx8sLau/Ou/Y8eO2NjYcODAAdq1aweYV184efIkffv2rVab27RpQ9++fVmzZg3FxcXcf//9uLm5qfsTEhJ49913eeCBBwA4e/asRRF2fZJXYPVIHQEmcwAJIYQqIyODGTNmkJqayqeffsrSpUuZNm0aAM7OzvTt25eZM2cSHx9Peno6cXFxfPTRR+ov/Ovp27cv9957L8OHD2f79u2kp6ezefNmtmzZAsBTTz3Fzp07eemllzh58iSrV69m2bJlPP300zVul42NDVOmTOHAgQMkJSUxduxY7rzzTjUhqkpYWBghISEMHTqUbdu2cfr0ab7//nuef/55Dh06RHFxMTExMcTHx3PmzBkSEhJITExUk6rp06ezdetW0tPTSU5OZvfu3eq+6OhoLl26xMiRI0lMTCQtLY2tW7cybtw4jEYjjo6OPP7448ycOZNdu3Zx/Phxxo4dWyn5vJGIiAjWrl3LF198YfH6C8DX15f//ve/pKSkcODAASIiImqtl+5mSQJUj0qvmLCzscJdhsALIYQqMjKS4uJi+vTpQ3R0NNOmTWPixInq/rVr19K7d28iIiLo0qULsbGxvPLKK0yaNKla11+3bh29e/dm5MiRdOnShWeeeUat7+nVqxeff/45a9eupVu3brzwwgvMnz+fsWPH1rhd9vb2zJo1i1GjRhEaGoqjoyOfffbZdc/R6XRs2rSJe++9l3HjxuHn58cjjzzCmTNncHd3R6/Xc/HiRSIjI/Hz82PEiBEMGjSIF198EQCj0Uh0dDQBAQEMHDgQPz8/3n33XQC8vLxISEjAaDQyYMAAunfvzvTp03F1dVWTnNdff5177rmHwYMHExYWxt13301Q0M0t2/Svf/2LixcvUlRUxNChQy32ffjhh1y+fJlevXoxevRopk6datFDVJ9kLbAq1NVaYGAuCrtiUrDRS+4phLg1jWktsH79+tGzZ0/efPNNrUOpVXFxcUyfPp2cnBytQ2l0amstMKkBqmc6nQ4bvRRACyGEEFqSbgghhBAN1po1ayyGdV/96dq1q9bhNSr/+9//rvmzrphssSGRV2BVqMtXYEIIUVON6RVYTeXn55OVlVXlPhsbG7y9ves5osaruLiY33777Zr7O3XqVC9xyCswIYQQTZ6TkxNOTk5ah9EkNGvWrN6SnPogr8CEEEII0eRIAiSEEA2UVDCIpqi2/t5LAiSEEA1MxRIMRUVFGkciRP2r+Ht/9VIkt0JqgIQQooHR6/W4urqqC2/a29vL+oKi0VMUhaKiIrKzs3F1da3xUiWSAAkhRAPk4eEBWK4+LkRT4Orqqv79rwlJgIQQogHS6XR4enri5uZGeXm51uEIUS9sbGxq3PNTQRIgIYRowPR6fa39QhCiKZEiaCGEEEI0OZIACSGEEKLJkQRICCGEEE2O1ABVoWKSpby8PI0jEUIIIUR1Vfzers5kiZIAVSE/Px+Atm3bahyJEEIIIW5Wfn4+Li4u1z1GVoOvgslk4vfff8fJyanWJxfLy8ujbdu2nD17ttGuNN8U2gjSzsZG2tl4NIU2grSzKoqikJ+fj5eXF1ZW16/ykR6gKlhZWdGmTZs6vYezs3Oj/gsLTaONIO1sbKSdjUdTaCNIO//qRj0/FaQIWgghhBBNjiRAQgghhGhyJAGqZwaDgblz52IwGLQOpc40hTaCtLOxkXY2Hk2hjSDtrCkpghZCCCFEkyM9QEIIIYRociQBEkIIIUSTIwmQEEIIIZocSYCEEEII0eRIAlSP3nnnHXx8fLCzsyM4OJiDBw9qHVKtmjdvHjqdzuLTuXNnrcOqse+++47Bgwfj5eWFTqdj/fr1FvsVReGFF17A09OTZs2aERYWxs8//6xNsDVwo3aOHTu20vMdOHCgNsHeooULF9K7d2+cnJxwc3Nj6NChpKamWhxTUlJCdHQ0LVu2xNHRkeHDh5OVlaVRxLemOu3s169fpec5adIkjSK+Ne+99x6BgYHqBHkhISFs3rxZ3d8YniXcuJ2N4Vn+VWxsLDqdjunTp6vbavt5SgJUTz777DNmzJjB3LlzSU5OpkePHoSHh5Odna11aLWqa9eunDt3Tv3s3btX65BqrLCwkB49evDOO+9Uuf+1117j7bffZvny5Rw4cAAHBwfCw8MpKSmp50hr5kbtBBg4cKDF8/3000/rMcKa27NnD9HR0ezfv5/t27dTXl7OgAEDKCwsVI958skn+b//+z+++OIL9uzZw++//84///lPDaO+edVpJ8CECRMsnudrr72mUcS3pk2bNsTGxpKUlMShQ4e47777GDJkCD/++CPQOJ4l3Lid0PCf5dUSExP5z3/+Q2BgoMX2Wn+eiqgXffr0UaKjo9XvRqNR8fLyUhYuXKhhVLVr7ty5So8ePbQOo04Bytdff61+N5lMioeHh/L666+r23JychSDwaB8+umnGkRYO/7aTkVRlDFjxihDhgzRJJ66kp2drQDKnj17FEUxPzsbGxvliy++UI9JSUlRAGXfvn1ahVljf22noihK3759lWnTpmkXVB1p3ry58sEHHzTaZ1mhop2K0rieZX5+vuLr66ts377dol118TylB6gelJWVkZSURFhYmLrNysqKsLAw9u3bp2Fkte/nn3/Gy8uLDh06EBERQUZGhtYh1an09HQyMzMtnq2LiwvBwcGN7tkCxMfH4+bmhr+/P1FRUVy8eFHrkGokNzcXgBYtWgCQlJREeXm5xfPs3Lkz7dq1a9DP86/trLBmzRpatWpFt27dmD17NkVFRVqEVyuMRiNr166lsLCQkJCQRvss/9rOCo3lWUZHR/Pggw9aPDeom/82ZTHUenDhwgWMRiPu7u4W293d3fnpp580iqr2BQcHExcXh7+/P+fOnePFF1/knnvu4fjx4zg5OWkdXp3IzMwEqPLZVuxrLAYOHMg///lP2rdvT1paGs899xyDBg1i37596PV6rcO7aSaTienTpxMaGkq3bt0A8/O0tbXF1dXV4tiG/DyraifAqFGj8Pb2xsvLi6NHjzJr1ixSU1P56quvNIz25h07doyQkBBKSkpwdHTk66+/pkuXLhw+fLhRPctrtRMaz7Ncu3YtycnJJCYmVtpXF/9tSgIkas2gQYPUPwcGBhIcHIy3tzeff/45jz/+uIaRidrwyCOPqH/u3r07gYGBdOzYkfj4ePr3769hZLcmOjqa48ePN4o6teu5VjsnTpyo/rl79+54enrSv39/0tLS6NixY32Hecv8/f05fPgwubm5fPnll4wZM4Y9e/ZoHVatu1Y7u3Tp0iie5dmzZ5k2bRrbt2/Hzs6uXu4pr8DqQatWrdDr9ZWq1bOysvDw8NAoqrrn6uqKn58fp06d0jqUOlPx/JraswXo0KEDrVq1apDPNyYmhm+//Zbdu3fTpk0bdbuHhwdlZWXk5ORYHN9Qn+e12lmV4OBggAb3PG1tbenUqRNBQUEsXLiQHj168NZbbzW6Z3mtdlalIT7LpKQksrOz6dWrF9bW1lhbW7Nnzx7efvttrK2tcXd3r/XnKQlQPbC1tSUoKIidO3eq20wmEzt37rR4h9vYFBQUkJaWhqenp9ah1Jn27dvj4eFh8Wzz8vI4cOBAo362AL/++isXL15sUM9XURRiYmL4+uuv2bVrF+3bt7fYHxQUhI2NjcXzTE1NJSMjo0E9zxu1syqHDx8GaFDPsyomk4nS0tJG8yyvpaKdVWmIz7J///4cO3aMw4cPq5877riDiIgI9c+1/jxrXrMtqmPt2rWKwWBQ4uLilBMnTigTJ05UXF1dlczMTK1DqzVPPfWUEh8fr6SnpysJCQlKWFiY0qpVKyU7O1vr0GokPz9f+eGHH5QffvhBAZTFixcrP/zwg3LmzBlFURQlNjZWcXV1Vb755hvl6NGjypAhQ5T27dsrxcXFGkd+c67Xzvz8fOXpp59W9u3bp6Snpys7duxQevXqpfj6+iolJSVah15tUVFRiouLixIfH6+cO3dO/RQVFanHTJo0SWnXrp2ya9cu5dChQ0pISIgSEhKiYdQ370btPHXqlDJ//nzl0KFDSnp6uvLNN98oHTp0UO69916NI785zz77rLJnzx4lPT1dOXr0qPLss88qOp1O2bZtm6IojeNZKsr129lYnmVV/jq6rbafpyRA9Wjp0qVKu3btFFtbW6VPnz7K/v37tQ6pVj388MOKp6enYmtrq9x2223Kww8/rJw6dUrrsGps9+7dClDpM2bMGEVRzEPh58yZo7i7uysGg0Hp37+/kpqaqm3Qt+B67SwqKlIGDBigtG7dWrGxsVG8vb2VCRMmNLgEvqr2AcqqVavUY4qLi5XJkycrzZs3V+zt7ZVhw4Yp586d0y7oW3CjdmZkZCj33nuv0qJFC8VgMCidOnVSZs6cqeTm5mob+E167LHHFG9vb8XW1lZp3bq10r9/fzX5UZTG8SwV5frtbCzPsip/TYBq+3nqFEVRbq3vSAghhBCiYZIaICGEEEI0OZIACSGEEKLJkQRICCGEEE2OJEBCCCGEaHIkARJCCCFEkyMJkBBCCCGaHEmAhBBCCNHkSAIkhBDVoNPpWL9+vdZhCCFqiSRAQoi/vbFjx6LT6Sp9Bg4cqHVoQogGylrrAIQQojoGDhzIqlWrLLYZDAaNohFCNHTSAySEaBAMBgMeHh4Wn+bNmwPm11PvvfcegwYNolmzZnTo0IEvv/zS4vxjx45x33330axZM1q2bMnEiRMpKCiwOGblypV07doVg8GAp6cnMTExFvsvXLjAsGHDsLe3x9fXlw0bNtRto4UQdUYSICFEozBnzhyGDx/OkSNHiIiI4JFHHiElJQWAwsJCwsPDad68OYmJiXzxxRfs2LHDIsF57733iI6OZuLEiRw7dowNGzbQqVMni3u8+OKLjBgxgqNHj/LAAw8QERHBpUuX6rWdQohaUuPlWoUQoo6NGTNG0ev1ioODg8XnlVdeURTFvPr5pEmTLM4JDg5WoqKiFEVRlPfff19p3ry5UlBQoO7fuHGjYmVlpa5o7+XlpTz//PPXjAFQ/t//+3/q94KCAgVQNm/eXGvtFELUH6kBEkI0CP/4xz947733LLa1aNFC/XNISIjFvpCQEA4fPgxASkoKPXr0wMHBQd0fGhqKyWQiNTUVnU7H77//Tv/+/a8bQ2BgoPpnBwcHnJ2dyc7OvtUmCSE0JAmQEKJBcHBwqPRKqrY0a9asWsfZ2NhYfNfpdJhMproISQhRx6QGSAjRKOzfv7/S94CAAAACAgI4cuQIhYWF6v6EhASsrKzw9/fHyckJHx8fdu7cWa8xCyG0Iz1AQogGobS0lMzMTItt1tbWtGrVCoAvvviCO+64g7vvvps1a9Zw8OBBPvzwQwAiIiKYO3cuY8aMYd68eZw/f54pU6YwevRo3N3dAZg3bx6TJk3Czc2NQYMGkZ+fT0JCAlOmTKnfhgoh6oUkQEKIBmHLli14enpabPP39+enn34CzCO01q5dy+TJk/H09OTTTz+lS5cuANjb27N161amTZtG7969sbe3Z/jw4SxevFi91pgxYygpKWHJkiU8/fTTtGrVin/961/110AhRL3SKYqiaB2EEELUhE6n4+uvv2bo0KFahyKEaCCkBkgIIYQQTY4kQEIIIYRocqQGSAjR4MmbfCHEzZIeICGEEEI0OZIACSGEEKLJkQRICCGEEE2OJEBCCCGEaHIkARJCCCFEkyMJkBBCCCGaHEmAhBBCCNHkSAIkhBBCiCZHEiAhhBBCNDn/HzOQUGzwVmdSAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "for history in histories:\n", + " plt.plot(history['train_auc_1'])\n", + " plt.plot(history['val_auc_1'])\n", + "\n", + "plt.title('Model Area Under Curve')\n", + "plt.ylabel('Area Under Curve')\n", + "plt.xlabel('Epoch')\n", + "plt.legend(['origin', 'origin_val', 'fp8_compressed', 'fp8_compressed_val'], loc='lower right')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "可以看到,两个模型的验证集auc均在0.85左右波动,使用8位量化对此任务的训练精度影响不大,而理论通讯消耗减少了3/4(从32位减少到了8位)。\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 自定义通讯压缩算法\n", + "\n", + "我们也可以自定义一个压缩算法,SecretFlow提供了SparseCompressor和QuantizedCompressor基类,对应稀疏化方法和量化压缩方法。\n", + "\n", + "这里以量化压缩方法为例,来实现一个基于K-means的压缩算法。\n", + "\n", + "K-means压缩论文是\"Deep Compression: Compressing Deep Neural Networks with Pruning, Trained Quantization and Huffman Coding\"提出的方法中的其中一个步骤,其思想是把对传输参数进行聚类,保存聚类中心的值,然后把其他值用聚类序号来表示。\n", + "\n", + "继承QuantizedCompressor后,只要实现_compress_one(将一个numpy向量打包为QuantizedData) 和 _decompress_one(将QuantizedData还原回numpy向量)函数即可。" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "from secretflow.utils.compressor import QuantizedCompressor, QuantizedData\n", + "import numpy as np\n", + "\n", + "class QuantizedMyKmeans(QuantizedCompressor):\n", + "\n", + " def __init__(self, quant_bits: int = 8):\n", + " super().__init__(quant_bits)\n", + " from sklearn.cluster import KMeans\n", + " self.km = KMeans(2**quant_bits, n_init=1, max_iter=50)\n", + "\n", + " def _compress_one(self, data: np.ndarray) -> QuantizedData:\n", + " ori_shape = data.shape\n", + " self.km.fit(np.expand_dims(data.flatten(), axis=1))\n", + " quantized = self.km.labels_ - (1 << (self.quant_bits - 1))\n", + " quantized = np.reshape(quantized, ori_shape)\n", + " q = self.km.cluster_centers_\n", + "\n", + " return QuantizedData(quantized.astype(self.np_type), q, None, data.dtype)\n", + "\n", + " def _decompress_one(self, data: QuantizedData) -> np.ndarray:\n", + " label = data.data.astype(data.origin_type) + (1 << (self.quant_bits - 1))\n", + " dequantized = np.zeros_like(label)\n", + " for i in range(data.q1.shape[0]):\n", + " dequantized[label == i] = data.q1[i]\n", + "\n", + " return dequantized" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "我们来实例化这个算法,再跑一遍联邦学习模型:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:root:Create proxy actor with party alice.\n", + "INFO:root:Create proxy actor with party bob.\n", + "INFO:root:SL Train Params: {'x': VDataFrame(partitions={PYURuntime(alice): Partition(data=), PYURuntime(bob): Partition(data=)}, aligned=True), 'y': VDataFrame(partitions={PYURuntime(alice): Partition(data=)}, aligned=True), 'batch_size': 128, 'epochs': 40, 'verbose': 1, 'callbacks': None, 'validation_data': (VDataFrame(partitions={PYURuntime(alice): Partition(data=), PYURuntime(bob): Partition(data=)}, aligned=True), VDataFrame(partitions={PYURuntime(alice): Partition(data=)}, aligned=True)), 'shuffle': True, 'sample_weight': None, 'validation_freq': 1, 'dp_spent_step_freq': None, 'dataset_builder': None, 'audit_log_params': {}, 'random_seed': 91222, 'audit_log_dir': None, 'self': }\n", + "\u001b[2m\u001b[36m(pid=30232)\u001b[0m 2023-08-16 01:46:19.445573: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcudart.so.11.0'; dlerror: libcudart.so.11.0: cannot open shared object file: No such file or directory\n", + "\u001b[2m\u001b[36m(pid=30250)\u001b[0m 2023-08-16 01:46:19.623566: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcudart.so.11.0'; dlerror: libcudart.so.11.0: cannot open shared object file: No such file or directory\n", + "\u001b[2m\u001b[36m(pid=30232)\u001b[0m 2023-08-16 01:46:20.187158: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer.so.7'; dlerror: libnvinfer.so.7: cannot open shared object file: No such file or directory\n", + "\u001b[2m\u001b[36m(pid=30232)\u001b[0m 2023-08-16 01:46:20.187299: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer_plugin.so.7'; dlerror: libnvinfer_plugin.so.7: cannot open shared object file: No such file or directory\n", + "\u001b[2m\u001b[36m(pid=30232)\u001b[0m 2023-08-16 01:46:20.187315: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Cannot dlopen some TensorRT libraries. If you would like to use Nvidia GPU with TensorRT, please make sure the missing libraries mentioned above are installed properly.\n", + "\u001b[2m\u001b[36m(pid=30250)\u001b[0m 2023-08-16 01:46:20.343446: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer.so.7'; dlerror: libnvinfer.so.7: cannot open shared object file: No such file or directory\n", + "\u001b[2m\u001b[36m(pid=30250)\u001b[0m 2023-08-16 01:46:20.343542: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer_plugin.so.7'; dlerror: libnvinfer_plugin.so.7: cannot open shared object file: No such file or directory\n", + "\u001b[2m\u001b[36m(pid=30250)\u001b[0m 2023-08-16 01:46:20.343556: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Cannot dlopen some TensorRT libraries. If you would like to use Nvidia GPU with TensorRT, please make sure the missing libraries mentioned above are installed properly.\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m 2023-08-16 01:46:22.002165: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcuda.so.1'; dlerror: libcuda.so.1: cannot open shared object file: No such file or directory\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m 2023-08-16 01:46:22.002204: W tensorflow/compiler/xla/stream_executor/cuda/cuda_driver.cc:265] failed call to cuInit: UNKNOWN ERROR (303)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m Model: \"sequential\"\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m _________________________________________________________________\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m Layer (type) Output Shape Param # \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m =================================================================\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m dense (Dense) (None, 100) 500 \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m dense_1 (Dense) (None, 64) 6464 \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m =================================================================\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m Total params: 6,964\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m Trainable params: 6,964\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m Non-trainable params: 0\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m _________________________________________________________________\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m Model: \"model\"\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m __________________________________________________________________________________________________\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m Layer (type) Output Shape Param # Connected to \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m ==================================================================================================\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m input_2 (InputLayer) [(None, 64)] 0 [] \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m input_3 (InputLayer) [(None, 64)] 0 [] \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m concatenate (Concatenate) (None, 128) 0 ['input_2[0][0]', \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m 'input_3[0][0]'] \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m dense_2 (Dense) (None, 64) 8256 ['concatenate[0][0]'] \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m dense_3 (Dense) (None, 1) 65 ['dense_2[0][0]'] \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m ==================================================================================================\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m Total params: 8,321\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m Trainable params: 8,321\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m Non-trainable params: 0\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30232)\u001b[0m __________________________________________________________________________________________________\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30250)\u001b[0m 2023-08-16 01:46:22.167943: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcuda.so.1'; dlerror: libcuda.so.1: cannot open shared object file: No such file or directory\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30250)\u001b[0m 2023-08-16 01:46:22.167979: W tensorflow/compiler/xla/stream_executor/cuda/cuda_driver.cc:265] failed call to cuInit: UNKNOWN ERROR (303)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30250)\u001b[0m Model: \"sequential\"\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30250)\u001b[0m _________________________________________________________________\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30250)\u001b[0m Layer (type) Output Shape Param # \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30250)\u001b[0m =================================================================\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30250)\u001b[0m dense (Dense) (None, 100) 1300 \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30250)\u001b[0m \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30250)\u001b[0m dense_1 (Dense) (None, 64) 6464 \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30250)\u001b[0m \n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30250)\u001b[0m =================================================================\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30250)\u001b[0m Total params: 7,764\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30250)\u001b[0m Trainable params: 7,764\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30250)\u001b[0m Non-trainable params: 0\n", + "\u001b[2m\u001b[36m(PYUSLTFModel pid=30250)\u001b[0m _________________________________________________________________\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 29/29 [00:13<00:00, 2.39it/s]\u001b[2m\u001b[36m(_run pid=27350)\u001b[0m /tmp/ipykernel_25795/600430472.py:13: ConvergenceWarning: Number of distinct clusters (248) found smaller than n_clusters (256). Possibly due to duplicate points in X.\n", + "100%|██████████| 29/29 [00:15<00:00, 1.83it/s, epoch: 1/40 - train_loss:0.4416384696960449 train_accuracy:0.8701704740524292 train_auc_1:0.518036961555481 val_loss:0.40735140442848206 val_accuracy:0.8729282021522522 val_auc_1:0.5592570304870605 ]\n", + "100%|██████████| 29/29 [00:13<00:00, 2.10it/s, epoch: 2/40 - train_loss:0.36653003096580505 train_accuracy:0.8817349076271057 train_auc_1:0.5584944486618042 val_loss:0.3673045337200165 val_accuracy:0.8729282021522522 val_auc_1:0.6474628448486328 ]\n", + "\u001b[2m\u001b[36m(_run pid=27350)\u001b[0m /tmp/ipykernel_25795/600430472.py:13: ConvergenceWarning: Number of distinct clusters (237) found smaller than n_clusters (256). Possibly due to duplicate points in X.\n", + "100%|██████████| 29/29 [00:11<00:00, 2.35it/s]\u001b[2m\u001b[36m(_run pid=27349)\u001b[0m /tmp/ipykernel_25795/600430472.py:13: ConvergenceWarning: Number of distinct clusters (251) found smaller than n_clusters (256). Possibly due to duplicate points in X.\n", + "100%|██████████| 29/29 [00:13<00:00, 2.18it/s, epoch: 3/40 - train_loss:0.32427504658699036 train_accuracy:0.890625 train_auc_1:0.6890991926193237 val_loss:0.35910260677337646 val_accuracy:0.8729282021522522 val_auc_1:0.6932856440544128 ]\n", + "100%|██████████| 29/29 [00:13<00:00, 2.23it/s, epoch: 4/40 - train_loss:0.31370726227760315 train_accuracy:0.8875584006309509 train_auc_1:0.7440165281295776 val_loss:0.34864872694015503 val_accuracy:0.8729282021522522 val_auc_1:0.7281122803688049 ]\n", + " 0%| | 0/29 [00:00" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.plot(history_kmeans['train_auc_1'])\n", + "plt.plot(history_kmeans['val_auc_1'])\n", + "\n", + "plt.title('Model Area Under Curve')\n", + "plt.ylabel('Area Under Curve')\n", + "plt.xlabel('Epoch')\n", + "plt.legend(['kmeans','kmeans_val'], loc='lower right')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "最终验证集auc在0.855左右,也还不错~" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 压缩算法的压缩效果\n", + "\n", + "我们在ImageNet预训练的ResNet网络为例,试一下Int8、Fp8和Kmeans方法对模型参数的压缩效果,看看有什么差异。" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/usr/local/lib/python3.8/site-packages/torchvision/models/_utils.py:208: UserWarning: The parameter 'pretrained' is deprecated since 0.13 and may be removed in the future, please use 'weights' instead.\n", + " warnings.warn(\n", + "/usr/local/lib/python3.8/site-packages/torchvision/models/_utils.py:223: UserWarning: Arguments other than a weight enum or `None` for 'weights' are deprecated since 0.13 and may be removed in the future. The current behavior is equivalent to passing `weights=ResNet50_Weights.IMAGENET1K_V1`. You can also use `weights=ResNet50_Weights.DEFAULT` to get the most up-to-date weights.\n", + " warnings.warn(msg)\n" + ] + } + ], + "source": [ + "from secretflow.utils.compressor import QuantizedZeroPoint, QuantizedFP, QuantizedKmeans\n", + "from torchvision import models\n", + "import ssl\n", + "import time\n", + "\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "\n", + "ssl._create_default_https_context = ssl._create_unverified_context\n", + "net = models.resnet50(pretrained=True)\n", + "net_params = [p.detach().numpy().flatten() for p in net.parameters()]\n", + "\n", + "coms =[QuantizedZeroPoint(8), QuantizedFP(8, format='E4M3'), QuantizedFP(8, format='E5M2'), QuantizedKmeans(8, n_clusters=100)]\n", + "losses = []\n", + "durations = []\n", + "\n", + "for c in coms:\n", + " start = time.time()\n", + " c_params = c.compress(net_params)\n", + " dc_params = c.decompress(c_params)\n", + " losses.append(sum([np.sum((a-b)**2) for a,b in zip(net_params, dc_params)]))\n", + " durations.append(time.time()-start)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABCIAAAGzCAYAAAD607YSAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAACUIUlEQVR4nOzdeXiM1///8ddEVlmFLEIEoWKN1tbYt4pYi1apvUqX0FpLWmppNWi1VG21Uz5aLVpaO6ELStCiKGqpJdSSRKiI5P794Zf5miYhIZkQz8d13deV+5wz537PPUnmnvec+xyTYRiGAAAAAAAArMAmtwMAAAAAAACPDxIRAAAAAADAakhEAAAAAAAAqyERAQAAAAAArIZEBAAAAAAAsBoSEQAAAAAAwGpIRAAAAAAAAKshEQEAAAAAAKyGRAQAAAAAALAaEhF45NWvX1/169fP7TAsREVFyWQyKSoqKrdDwf/XvXt3FS9ePLfDAIBHHv9PHx25eT0yb948mUwmnThxwurHRvoexmtmPL5IRCDL9u3bp+eee04BAQFydHRUkSJF9Mwzz2jy5MkW7W7evKlJkybpySeflJubmzw8PFS+fHn17t1bhw4dMrdLfaPKaNu+fbu1nyKQrerXr2/xO+3k5KRKlSpp4sSJSklJybHjFi9eXCaTSX379k1Tl3px+vXXX2e537Nnz2rkyJHau3dvmrqRI0em+3fs6OiYbl+zZ89W2bJl5ejoqNKlS6f5PwLAOu72PnznRoIdwP1IvSZJ3ZydnVW9enUtWLAgR4+berwJEyakqUv9DLJr164s9/vHH39o5MiR6Sbaunfvnu7/z6CgoDRtU1JSNH78eJUoUUKOjo6qVKmS/ve//2U5nkeRbW4HgEfLL7/8ogYNGqhYsWLq1auXfH199ffff2v79u2aNGmSxQeedu3aafXq1erYsaN69eqlpKQkHTp0SKtWrVLNmjXT/DGOHj1aJUqUSHPMUqVK5fjzym5169bVv//+K3t7+9wOBf/fzJkzc/RD/70ULVpUkZGRkqSLFy9q8eLF6t+/v/755x+NGTMmR489c+ZMRUREyM/PL1v6O3v2rEaNGqXixYurcuXK6baZNm2aXFxczPv58uVL02bGjBl69dVX1a5dOw0YMEA//vij3njjDV2/fl1DhgzJllgBZM7ChQst9hcsWKD169enKS9btmyu/z9F5uXm9UiXLl3UoUMHOTg4WP3YSN+6dety9fiVK1fWwIEDJUnnzp3TrFmz1K1bNyUmJqpXr145euwPP/xQr732mvLnz58t/f3xxx8aNWqU6tevn+4IMQcHB82aNcuizN3dPU27d955R2PHjlWvXr1UrVo1ffvtt3rxxRdlMpnUoUOHbIn1oWUAWdCsWTPDy8vLuHLlSpq68+fPm3/+9ddfDUnGmDFj0rS7deuWcfHiRfP+3LlzDUnGzp077yumevXqGfXq1buvx8L6kpOTjX///Te3w7CqevXqGeXLl7co+/fff42AgADD1dXVuHXrVo4cNyAgwChfvrxha2tr9O3b16Ju8+bNhiRj6dKlWe53586dhiRj7ty5aepGjBhhSDL++eefu/Zx/fp1o2DBgkbz5s0tyjt16mQ4Ozsbly9fznJcALJPeHi4wWUikDOSkpKMxMTE3A7DqgICAtK851+4cMFwcXExypYtm2PHlWRUrlzZkGRMmDDBou5BPoMsXbrUkGRs3rw5TV23bt0MZ2fne/Zx+vRpw87OzggPDzeXpaSkGHXq1DGKFi2aY9eHDwtuzUCWHDt2TOXLl5eHh0eaOm9vb4t2klSrVq007fLly6eCBQvmWIySdOHCBfXs2VM+Pj5ydHRUcHCw5s+fn6bdkiVLVKVKFbm6usrNzU0VK1bUpEmTzPVJSUkaNWqUSpcuLUdHRxUsWFC1a9fW+vXr73r89O7JrF+/vipUqKA//vhDDRo0UP78+VWkSBGNHz8+08/riy++UPXq1ZU/f34VKFBAdevWTZPdnjp1qsqXLy8HBwf5+fkpPDxcsbGxFm1SY/n9999Vr1495c+fX6VKlTIP09+yZYtq1KghJycnlSlTRhs2bLB4fOrw+0OHDql9+/Zyc3NTwYIF9eabb+rGjRsWbU0mk/r06aNFixaZ41qzZo0k6cyZM3rppZfk4+MjBwcHlS9fXnPmzEnzvCdPnqzy5cubn3fVqlW1ePFic/3Vq1fVr18/FS9eXA4ODvL29tYzzzyj3bt3m9v8957mEydOyGQy6aOPPtLnn3+uwMBAOTg4qFq1atq5c2eaGJYuXapy5crJ0dFRFSpU0PLlyx/oPmlHR0dVq1ZNV69e1YULFyzqvvjiC1WpUkVOTk7y9PRUhw4d9Pfff1u0OXLkiNq1aydfX185OjqqaNGi6tChg+Li4izaFS9eXF27dtXMmTN19uzZe8Z1r9ckKipK1apVkyT16NHDPNxw3rx5Fv0YhqH4+HgZhpHucTZv3qxLly7p9ddftygPDw/XtWvX9P33398zVgC5427/T6dMmaKSJUsqf/78atKkif7++28ZhqH33ntPRYsWlZOTk1q3bq3Lly+n6Xf16tWqU6eOnJ2d5erqqubNm+vAgQOZiik2Nlb9+/c3vw8ULVpUXbt21cWLF81tMnNtkB3PZdeuXQoNDVWhQoXk5OSkEiVK6KWXXjLXZzRvQ+qx7/x/2r17d7m4uOjUqVNq0aKFXFxcVKRIEU2ZMkXS7dtlGzZsKGdnZwUEBFi8N2Z0rKxcj5w8eVKtWrWSs7OzvL291b9/f61duzZTt+mkN0dE8eLF1aJFC/3000+qXr26HB0dVbJkyUwPz09JSdGkSZNUsWJFOTo6ysvLS02bNrUYWn/r1i2999575vf14sWL6+2331ZiYqJFX6mxREVFqWrVqnJyclLFihXNz2vZsmXm41SpUkV79uyxeHzqa/PXX38pNDRUzs7O8vPz0+jRoy3e++78nZo4caI5rj/++EOSdOjQIT333HPy9PSUo6Ojqlatqu+++87iWJm5Ho2JiVGPHj1UtGhROTg4qHDhwmrdurXF+f/vHBGpvx9fffWVxowZo6JFi8rR0VGNGjXS0aNH05z/1L8JJycnVa9eXT/++OMDzTvh5eWloKAg8+eGVCkpKZo4caLKly8vR0dH+fj46JVXXtGVK1cs2t3rby1VrVq11LBhQ40fP17//vvvPeO612syb948Pf/885KkBg0aZHjrWnJysuLj4zM8zrfffqukpCSLayGTyaTXXntNp0+f1rZt2+4Z66OMRASyJCAgQNHR0dq/f/8920nSokWLdOvWrUz1HRcXp4sXL1psly5dynKM//77r+rXr6+FCxeqU6dO+vDDD+Xu7q7u3btbJBnWr1+vjh07qkCBAho3bpzGjh2r+vXr6+effza3GTlypEaNGqUGDRros88+0zvvvKNixYpZfMDNiitXrqhp06YKDg7WhAkTFBQUpCFDhmj16tX3fOyoUaPUpUsX2dnZafTo0Ro1apT8/f21adMmi3jDw8Pl5+enCRMmqF27dpoxY4aaNGmipKSkNLG0aNFCNWrU0Pjx4+Xg4KAOHTroyy+/VIcOHdSsWTONHTtW165d03PPPaerV6+mial9+/a6ceOGIiMj1axZM3366afq3bt3mnabNm1S//799cILL2jSpEkqXry4zp8/r6efflobNmxQnz59NGnSJJUqVUo9e/bUxIkTzY+dOXOm3njjDZUrV04TJ07UqFGjVLlyZe3YscPc5tVXX9W0adPUrl07TZ06VYMGDZKTk5MOHjx4z/O6ePFiffjhh3rllVf0/vvv68SJE2rbtq3F+fr+++/1wgsvyM7OTpGRkWrbtq169uyp6Ojoe/Z/N6kXJ3cm9saMGaOuXbuqdOnS+vjjj9WvXz9t3LhRdevWNSeUbt68qdDQUG3fvl19+/bVlClT1Lt3b/31119pkk7S7WF/t27d0tixY+8aT2Zek7Jly2r06NGSpN69e2vhwoVauHCh6tata9FXyZIl5e7uLldXV3Xu3Fnnz5+3qE+9oKtatapFeZUqVWRjY5Pmgg/Aw2/RokWaOnWq+vbtq4EDB2rLli1q3769hg0bpjVr1mjIkCHq3bu3Vq5cqUGDBlk8duHChWrevLlcXFw0btw4DR8+XH/88Ydq1659z8kOExISVKdOHU2ePFlNmjTRpEmT9Oqrr+rQoUM6ffq0pMxfGzzoc7lw4YKaNGmiEydOaOjQoZo8ebI6der0QPNdJScnKywsTP7+/ho/fryKFy+uPn36aN68eWratKmqVq2qcePGydXVVV27dtXx48fv2WdmrkeuXbumhg0basOGDXrjjTf0zjvv6JdffnngW+eOHj2q5557Ts8884wmTJigAgUKqHv37plKOvXs2VP9+vWTv7+/xo0bp6FDh8rR0dHi/L788st699139dRTT+mTTz5RvXr1FBkZme4w96NHj+rFF19Uy5YtFRkZqStXrqhly5ZatGiR+vfvr86dO2vUqFE6duyY2rdvn+aWpOTkZDVt2lQ+Pj4aP368qlSpohEjRmjEiBFpjjV37lxNnjxZvXv31oQJE+Tp6akDBw7o6aef1sGDBzV06FBNmDBBzs7OevbZZ7V8+XLzYzNzPdquXTstX75cPXr00NSpU/XGG2/o6tWrOnXq1D3P69ixY7V8+XINGjRIERER2r59uzp16mTRZtq0aerTp4+KFi2q8ePHq06dOnr22WfNf2P349atWzp9+rQKFChgUf7KK69o8ODBqlWrliZNmqQePXpo0aJFCg0N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dOvXrVunsLAwXblyxXwu4uLiVKBAAa1bt06NGzfWxYsX5eXlpa1bt6pOnTqSpKtXr8rNzU3r169X48aNzf1NmzZNX375pd599101atRIV65ckYeHxwOcCUAyReV2BHmTUT+3I0BuehTeC/MCzjMA5JykUQNzO4Q8yW7EhGztL7Pvhbl+a0Z6W/fu3SVJ/v7+2rJliy5duqQbN27oyJEjGj9+fJ57c89o2dJU165d09y5c1WiRIkMJ95MSUnR999/ryeeeEKhoaHy9vZWjRo1tGLFCnObxMREmUwmizk0HB0dZWNjY54ktGDBgipTpowWLFiga9eu6datW5oxY4a8vb1VpUoV8+P++OMPjR49WgsWLJCNzUO1CiwAAAAA4CHGJ8hcltGypZI0depUubi4yMXFRatXr9b69etlb2+fbj8XLlxQQkKCxo4dq6ZNm2rdunVq06aN2rZtqy1btkiSnn76aTk7O2vIkCG6fv26rl27pkGDBik5OVnnzp2TdHuUyoYNG7Rnzx65urrK0dFRH3/8sdasWaMCBQpIup3Q6Nixoz788EMVK1YsB88OAAAAACCvIRGRy1KXLV2yZEmauk6dOmnPnj3asmWLnnjiCbVv3143btxIt5/UOR5at26t/v37q3Llyho6dKhatGih6dOnS5K8vLy0dOlSrVy5Ui4uLnJ3d1dsbKyeeuop86gGwzAUHh4ub29v/fjjj/r111/17LPPqmXLluZkRUREhMqWLavOnTvnxCkBAAAAAORhJCJyUeqypZs3b0532VJ3d3eVLl1adevW1ddff61Dhw5p+fLl6fZVqFAh2draqly5chblZcuWtVg1o0mTJjp27JguXLigixcvauHChTpz5oxKliwpSdq0aZNWrVqlJUuWqFatWnrqqac0depUOTk5af78+eY2S5cula2trWxtbdWoUSNzDCNGjMiWcwMAAAAAyJtsczuAx5FhGOrbt6+WL1+uqKioTC1bmjp/RmJiYrr19vb2qlatWpplQP/8808FBASkaV+oUCFJt5MKFy5cUKtWrSRJ169fl6Q08z7Y2NiYR11888035pVNJGnnzp166aWX9OOPP2Y4kSYAAAAAABKJiFwRHh6uxYsX69tvvzUvWyrdHgHh5OSkv/76S19++aWaNGkiLy8vnT59WmPHjpWTk5OaNWtm7icoKEiRkZFq06aN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PXo0w0SEg4MDE1oCAPCQqlevnvnnl156yTw64k6XLl1S48aNs3UOiaFDhyo+Pl5BQUHKly+fkpOTNWbMGHXq1EmSFBMTI0ny8fGxeJyPj4+57r8YhQkAwP3JciLCx8dH7du31/Tp083fYCQnJ+v111+Xm5ubPvzww2wPMtXp06d16dIlFS5cOMeOAQAArCP1Foz/SkhIyPY5Gb766istWrRIixcvVvny5bV3717169dPfn5+953wYBQmAAD3J8uJiDlz5uinn36yGEaZL18+DRgwQDVr1sxSIiIhIUFHjx417x8/flx79+41rx8+atQotWvXTr6+vjp27JjeeustlSpVSqGhoVkNGwAAPCRSP7ybTCYNHz5c+fPnN9clJydrx44dqly5crYec/DgwRo6dKj5FouKFSvq5MmTioyMVLdu3cwrdJ0/f97iC4/z589nGAujMAEAuD9ZTkTcunVLhw4dUpkyZSzKDx06pJSUlCz1tWvXLjVo0MC8n3ph0q1bN02bNk2///675s+fr9jYWPn5+alJkyZ67733eNMHAOARtmfPHkm3R0Ts27dP9vb25jp7e3sFBwdr0KBB2XrM69evy8bGxqIsX7585muXEiVKyNfXVxs3bjQnHuLj47Vjxw699tpr2RoLAACPuywnInr06KGePXvq2LFjql69uiRpx44dGjt2rHr06JGlvurXry/DMDKsX7t2bVbDAwAAD7nNmzdLun1NMWnSpGxdpjMjLVu21JgxY1SsWDGVL19ee/bs0ccff6yXXnpJ0u3RGf369dP777+v0qVLm5fv9PPz07PPPpvj8QEA8DjJciLio48+kq+vryZMmKBz585JkgoXLqzBgwdr4MCB2R4gAADIm+bOnWu1Y02ePFnDhw/X66+/rgsXLsjPz0+vvPKK3n33XXObt956S9euXVPv3r0VGxur2rVra82aNdk+XwUAAI87k3G3IQn3kLpMlTW+ybhf8fHxcnd3V1xcXLbFaYrKlm6QDqN+bkeA3MbfV87gb+vxlhPvhUiL8wwAOSdpFF965wS7EROytb/MvhfaZFiTCW5ubtqzZ49Wr16tK1euPEhXAAAAAADgMZDpWzPGjRunhIQEvffee5JuTzAVFhamdevWSZK8vb21ceNGlS9fPmciBQAAAAAAj7xMj4j48ssvVaFCBfP+119/ra1bt+rHH3/UxYsXVbVqVY0aNSpHggQAAAAAAHlDphMRx48fV6VKlcz7P/zwg5577jnVqlVLnp6eGjZsmLZt25YjQQIAAAAAgLwh04mIW7duycHBwby/bds21axZ07zv5+enixcvZm90AAAAAAAgT8l0IiIwMFBbt26VJJ06dUp//vmn6tata64/ffq0ChYsmP0RAgAAAACAPCPTk1WGh4erT58++vHHH7V9+3aFhISoXLly5vpNmzbpySefzJEgAQAAAABA3pDpRESvXr2UL18+rVy5UnXr1tWIESMs6s+ePauXXnop2wMEAAAAAAB5R6YTEZL00ksvZZhsmDp1arYEBAAAAAAA8q5MzxEBAAAAAADwoEhEAAAAAAAAqyERAQAAAAAArIZEBAAAAAAAsBoSEQAAAAAAwGqytGqGJN24cUOTJ0/W5s2bdeHCBaWkpFjU7969O9uCAwAAAAAAeUuWExE9e/bUunXr9Nxzz6l69eoymUw5ERcAAAAAAMiDspyIWLVqlX744QfVqlUrJ+IBAAAAAAB5WJbniChSpIhcXV1zIhYAAAAAAJDHZTkRMWHCBA0ZMkQnT57MiXgAAAAAAEAeluVbM6pWraobN26oZMmSyp8/v+zs7CzqL1++nG3BAQAAAACAvCXLiYiOHTvqzJkz+uCDD+Tj48NklQAAAAAAINOynIj45ZdftG3bNgUHB+dEPAAAAAAAIA/L8hwRQUFB+vfff3MiFgAAAAAAkMdlORExduxYDRw4UFFRUbp06ZLi4+MtNgAAAAAAgIxk+daMpk2bSpIaNWpkUW4Yhkwmk5KTk7MnMgAAAAAAkOdkORGxefPmnIgDAAAAAAA8BrKciKhXr15OxAEAAAAAAB4DWU5EbN269a71devWve9gAAAAAABA3pblRET9+vXTlJlMJvPPzBEBAAAAAAAykuVVM65cuWKxXbhwQWvWrFG1atW0bt26nIgRAAAAAADkEVkeEeHu7p6m7JlnnpG9vb0GDBig6OjobAkMAAAAAADkPVkeEZERHx8fHT58OLu6AwAAAAAAeVCWExG///67xfbbb79pzZo1evXVV1W5cuUcCBEAAODBnTlzRp07d1bBggXl5OSkihUrateuXeZ6wzD07rvvqnDhwnJyclLjxo115MiRXIwYAIC8Kcu3ZlSuXFkmk0mGYViUP/3005ozZ062BQYAAJBdrly5olq1aqlBgwZavXq1vLy8dOTIERUoUMDcZvz48fr00081f/58lShRQsOHD1doaKj++OMPOTo65mL0AADkLVlORBw/ftxi38bGRl5eXrxBAwCAh9a4cePk7++vuXPnmstKlChh/tkwDE2cOFHDhg1T69atJUkLFiyQj4+PVqxYoQ4dOlg9ZgAA8qos35oREBBgsfn7+5OEAAAAD7XvvvtOVatW1fPPPy9vb289+eSTmjlzprn++PHjiomJUePGjc1l7u7uqlGjhrZt25Zun4mJiYqPj7fYAADAvWU6EbFt2zatWrXKomzBggUqUaKEvL291bt3byUmJmZ7gAAAAA/qr7/+0rRp01S6dGmtXbtWr732mt544w3Nnz9fkhQTEyPp9uTbd/Lx8THX/VdkZKTc3d3Nm7+/f84+CQAA8ohMJyJGjx6tAwcOmPf37dunnj17qnHjxho6dKhWrlypyMjIHAkSAADgQaSkpOipp57SBx98oCeffFK9e/dWr169NH369PvuMyIiQnFxcebt77//zsaIAQDIuzKdiNi7d68aNWpk3l+yZIlq1KihmTNnasCAAfr000/11Vdf5UiQAAAAD6Jw4cIqV66cRVnZsmV16tQpSZKvr68k6fz58xZtzp8/b677LwcHB7m5uVlsAADg3jKdiLhy5YrFcMUtW7YoLCzMvF+tWjW+CQAAAA+lWrVq6fDhwxZlf/75pwICAiTdnrjS19dXGzduNNfHx8drx44dCgkJsWqsAADkdZlORPj4+JhXzLh586Z2796tp59+2lx/9epV2dnZZX+EAAAAD6h///7avn27PvjgAx09elSLFy/W559/rvDwcEmSyWRSv3799P777+u7777Tvn371LVrV/n5+enZZ5/N3eABAMhjMr18Z7NmzTR06FCNGzdOK1asUP78+VWnTh1z/e+//67AwMAcCRIAAOBBVKtWTcuXL1dERIRGjx6tEiVKaOLEierUqZO5zVtvvaVr166pd+/eio2NVe3atbVmzRpWBwMAIJtlOhHx3nvvqW3btqpXr55cXFw0f/582dvbm+vnzJmjJk2a5EiQAAAAD6pFixZq0aJFhvUmk0mjR4/W6NGjrRgVAACPn0wnIgoVKqStW7cqLi5OLi4uypcvn0X90qVL5eLiku0BAgAAAACAvCPTiYhU7u7u6ZZ7eno+cDAAAAAAACBv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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.figure(figsize=(12.8, 4.8))\n", + "x=[1,2,3,4]\n", + "x_label=['Int8','Fp8-E4M3','Fp8-E5M2','Kmeans']\n", + "\n", + "plt.subplot(121)\n", + "p1 = plt.bar(x, losses, color='deepskyblue')\n", + "plt.bar_label(p1, label_type='edge')\n", + "plt.xticks(x, x_label)\n", + "plt.title('SSE loss in compressing ResNet50')\n", + "plt.ylabel('Sum Square Error')\n", + "\n", + "plt.subplot(122)\n", + "p2 = plt.bar(x, durations, color='salmon')\n", + "plt.bar_label(p2, label_type='edge')\n", + "plt.xticks(x, x_label)\n", + "plt.title('Time comsuming in compressing ResNet50')\n", + "plt.ylabel('time')\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "可以看到,kmeans压缩在控制精度损失方面表现最好,但压缩时间非常长。\n", + "\n", + "浮点数(Fp8-M4E3)对ResNet模型参数压缩的效果略优于整型(Int8)压缩,时间消耗是整型压缩的3倍。\n", + "\n", + "实际应用压缩算法时,可根据计算资源和压缩精度进行平衡。" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 总结\n", + "\n", + "本篇示例介绍了通讯压缩算法,并在拆分学习的基础之上使用了SecretFlow提供和自行设计的压缩算法。\n", + "\n", + "从实验数据可以看出,将32位数压缩为8位的精度损失不大,而理论通信消耗仅为不作压缩时的1/4,因此在需要频繁传输数据和梯度的拆分学习中,加入通讯压缩不失为一个好的选择。\n", + "\n", + "本教程使用明文聚合来做演示,同时没有考虑隐藏层的泄露问题,SecretFlow提供了聚合层AggLayer,通过MPC,TEE,HE,以及DP等方式规避隐层明文传输泄露的问题。如果您感兴趣,可以看相关文档。" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "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.8.15" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/secretflow/utils/compressor.py b/secretflow/utils/compressor.py index 16e59bf6b..dec65650a 100644 --- a/secretflow/utils/compressor.py +++ b/secretflow/utils/compressor.py @@ -19,6 +19,7 @@ import jax.numpy as jnp import numpy as np from scipy import sparse + from secretflow.utils.communicate import ForwardData from secretflow.utils.errors import InvalidArgumentError @@ -527,3 +528,117 @@ def _compress_one(self, data: np.ndarray) -> QuantizedData: def _decompress_one(self, data: QuantizedData) -> np.ndarray: return (data.data.astype(data.origin_type) + float(data.q2)) / float(data.q1) + + +class QuantizedFP(QuantizedCompressor): + """Quantized compressor with low-bit floating points, fp16/32/64 will be change directly in numpy format, while fp8 will be stored as int8 object. + + Reference paper "FP8 FORMATS FOR DEEP LEARNING". + + Link: https://arxiv.org/pdf/2209.05433.pdf + """ + + def __init__(self, quant_bits: int = 8, format='E4M3'): + super().__init__(quant_bits) + if quant_bits not in [8, 16, 32, 64]: + raise RuntimeError( + f"The quantized bits for QuantizedFP must in 8/16/32/64, got {quant_bits}" + ) + + if quant_bits == 8 and format not in ['E4M3', 'E5M2']: + raise RuntimeError( + f"The format for fp8 quantized must in E4M3/E5M2, got {format}" + ) + config = { + 'E4M3': {'max_value': 448, 'mant_len': 8, 'exp_offset': 6}, + 'E5M2': {'max_value': 57344, 'mant_len': 4, 'exp_offset': 14}, + } + self.config = config[format] + + def _compress_one(self, data: np.ndarray) -> QuantizedData: + if self.quant_bits > 8: + # fp 16/32/64 + return QuantizedData( + data.astype(getattr(np, f'float{self.quant_bits}')), + None, + None, + data.dtype, + ) + else: + # fp8 with a scale factor, store as np.int8. + q_sign = np.sign(data) + + out = np.abs(data) + max_division = np.max(out) if np.max(out) > 0 else 1 + scale = self.config['max_value'] / max_division + out = out * scale + mant, exp = np.frexp( + out + ) # frexp mantissa range is (-1, 1), not (-2, -1] and [1, 2) + q_exp = np.where( + exp > -self.config['exp_offset'], exp + self.config['exp_offset'], 0 + ) + q_mant = np.round((2 * mant - 1) * self.config['mant_len']) + + quantized = q_sign * (q_exp * self.config['mant_len'] + q_mant) + return QuantizedData( + quantized.astype(self.np_type), scale, None, data.dtype + ) + + def _decompress_one(self, data: QuantizedData) -> np.ndarray: + if self.quant_bits != 8: + return data.data.astype(data.origin_type) + else: + # decompose fp8(stored in int8) to default fp + quantized = data.data + sign = np.sign(quantized) + abs_quantized = np.abs(quantized) + exp = (abs_quantized // self.config['mant_len']) - self.config['exp_offset'] + mant = ( + (abs_quantized % self.config['mant_len']).astype(data.origin_type) + / self.config['mant_len'] + + 1 + ) / 2 + + ori_data = sign * np.ldexp(mant, exp) / data.q1 + return ori_data + + +class QuantizedKmeans(QuantizedCompressor): + """Quantized compressor with Kmeans, a algorithm which replace float with relatived centroid's index. + + Reference paper 2016 "Deep Compression: Compressing Deep Neural Networks with Pruning, Trained Quantization and Huffman Coding". + + Link: https://arxiv.org/abs/1510.00149 + """ + + def __init__(self, quant_bits: int = 8, n_clusters=None): + super().__init__(quant_bits) + from sklearn.cluster import KMeans + + if n_clusters is None: + self.n_clusters = 2**quant_bits + else: + self.n_clusters = n_clusters + self.km = KMeans(self.n_clusters, n_init=1, max_iter=50) + + def _compress_one(self, data: np.ndarray) -> QuantizedData: + if data.flatten().shape[0] <= self.n_clusters: + return QuantizedData(data, None, None, None) + ori_shape = data.shape + self.km.fit(np.expand_dims(data.flatten(), axis=1)) + quantized = self.km.labels_ - (1 << (self.quant_bits - 1)) + quantized = np.reshape(quantized, ori_shape) + q = self.km.cluster_centers_ + + return QuantizedData(quantized.astype(self.np_type), q, None, data.dtype) + + def _decompress_one(self, data: QuantizedData) -> np.ndarray: + if data.data.flatten().shape[0] <= self.n_clusters: + return data.data + label = data.data.astype(data.origin_type) + (1 << (self.quant_bits - 1)) + dequantized = np.zeros_like(label) + for i in range(data.q1.shape[0]): + dequantized[label == i] = data.q1[i] + + return dequantized diff --git a/tests/utils/test_compressor.py b/tests/utils/test_compressor.py new file mode 100644 index 000000000..2e9efb169 --- /dev/null +++ b/tests/utils/test_compressor.py @@ -0,0 +1,45 @@ +import numpy as np + +from secretflow.utils.compressor import (ForwardData, QuantizedData, + QuantizedFP, QuantizedKmeans) + + +def compressed_test(compressor): + a = np.random.normal(0, 5, size=(128, 256)) + c_a = compressor.compress(a) + assert isinstance(c_a, QuantizedData) + + a_list = [a, a, a] + c_a_list = compressor.compress(a_list) + assert np.all([isinstance(x, QuantizedData) for x in c_a_list]) + + a_forward = ForwardData(hidden=a) + c_a_forward = compressor.compress(a_forward) + assert isinstance(c_a_forward.hidden, QuantizedData) + + +def abs_max_equal_test(compressor): + a = np.random.normal(0, 5, size=(128, 256)) + c_a = compressor.compress(a) + res = compressor.decompress(c_a) + np.testing.assert_almost_equal(np.max(np.abs(res)), np.max(np.abs(a))) + + +def all_zeros_test(compressor): + a = np.zeros((128, 256)) + c_a = compressor.compress(a) + res = compressor.decompress(c_a) + assert (res == a).all() + + +def test_qfp(): + compressor = QuantizedFP() + compressed_test(compressor) + abs_max_equal_test(compressor) + all_zeros_test(compressor) + + +def test_qkm(): + compressor = QuantizedKmeans() + compressed_test(compressor) + all_zeros_test(compressor)