{ "cells": [ { "cell_type": "code", "execution_count": 134, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The watermark extension is already loaded. To reload it, use:\n", " %reload_ext watermark\n", "CPython 3.5.4\n", "IPython 6.2.1\n", "\n", "numpy 1.14.0\n", "scipy 1.0.0\n", "sklearn 0.20.0\n", "pandas 0.22.0\n", "matplotlib 2.1.2\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/anaconda3/envs/mlbook/lib/python3.5/site-packages/sklearn/utils/deprecation.py:77: DeprecationWarning: Function fetch_mldata is deprecated; fetch_mldata was deprecated in version 0.20 and will be removed in version 0.22\n", " warnings.warn(msg, category=DeprecationWarning)\n", "/anaconda3/envs/mlbook/lib/python3.5/site-packages/sklearn/utils/deprecation.py:77: DeprecationWarning: Function mldata_filename is deprecated; mldata_filename was deprecated in version 0.20 and will be removed in version 0.22\n", " warnings.warn(msg, category=DeprecationWarning)\n" ] }, { "data": { "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%load_ext watermark\n", "%watermark -v -p numpy,scipy,sklearn,pandas,matplotlib\n", "# 파이썬 2와 파이썬 3 지원\n", "from __future__ import division, print_function, unicode_literals\n", "\n", "# 공통\n", "import numpy as np\n", "import os\n", "\n", "# 일관된 출력을 위해 유사난수 초기화\n", "np.random.seed(42)\n", "\n", "# 맷플롯립 설정\n", "%matplotlib inline\n", "import matplotlib\n", "import matplotlib.pyplot as plt\n", "plt.rcParams['axes.labelsize'] = 14\n", "plt.rcParams['xtick.labelsize'] = 12\n", "plt.rcParams['ytick.labelsize'] = 12\n", "\n", "# 한글출력\n", "matplotlib.rc('font', family='NanumBarunGothic')\n", "plt.rcParams['axes.unicode_minus'] = False\n", "\n", "# 그림을 저장할 폴드\n", "PROJECT_ROOT_DIR = \".\"\n", "CHAPTER_ID = \"classification\"\n", "\n", "def save_fig(fig_id, tight_layout=True):\n", " path = os.path.join(PROJECT_ROOT_DIR, \"images\", CHAPTER_ID, fig_id + \".png\")\n", " if tight_layout:\n", " plt.tight_layout()\n", " plt.savefig(path, format='png', dpi=300)\n", "\n", "from sklearn.datasets import fetch_mldata\n", "mnist = fetch_mldata('MNIST original')\n", "X, y = mnist[\"data\"], mnist[\"target\"]\n", "\n", "%matplotlib inline\n", "import matplotlib\n", "import matplotlib.pyplot as plt\n", "\n", "some_digit = X[36000]\n", "some_digit_image = some_digit.reshape(28, 28)\n", "# plt.imshow(some_digit_image, cmap = matplotlib.cm.binary,\n", " #interpolation=\"nearest\")\n", "# plt.axis(\"off\")\n", "\n", "save_fig(\"some_digit_plot\")\n", "def plot_digit(data):\n", " image = data.reshape(28, 28)\n", " plt.imshow(image, cmap = matplotlib.cm.binary,\n", " interpolation=\"nearest\")\n", " plt.axis(\"off\")\n", "# 숫자 그림을 위한 추가 함수\n", "def plot_digits(instances, images_per_row=10, **options):\n", " size = 28\n", " images_per_row = min(len(instances), images_per_row)\n", " images = [instance.reshape(size,size) for instance in instances]\n", " n_rows = (len(instances) - 1) // images_per_row + 1\n", " row_images = []\n", " n_empty = n_rows * images_per_row - len(instances)\n", " images.append(np.zeros((size, size * n_empty)))\n", " for row in range(n_rows):\n", " rimages = images[row * images_per_row : (row + 1) * images_per_row]\n", " row_images.append(np.concatenate(rimages, axis=1))\n", " image = np.concatenate(row_images, axis=0)\n", " plt.imshow(image, cmap = matplotlib.cm.binary, **options)\n", " plt.axis(\"off\")\n", "plt.figure(figsize=(9,9))\n", "example_images = np.r_[X[:12000:600], X[13000:30600:600], X[30600:60000:590]]\n", "#plot_digits(example_images, images_per_row=10)\n", "#save_fig(\"more_digits_plot\")\n", "X_train, X_test, y_train, y_test = X[:60000], X[60000:], y[:60000], y[60000:]\n", "import numpy as np\n", "\n", "shuffle_index = np.random.permutation(60000)\n", "X_train, y_train = X_train[shuffle_index], y_train[shuffle_index]\n", "y_train_5 = (y_train == 5)\n", "y_test_5 = (y_test == 5)\n", "from sklearn.linear_model import SGDClassifier\n", "\n", "sgd_clf = SGDClassifier(max_iter=5, random_state=42)\n", "sgd_clf.fit(X_train, y_train_5)\n", "sgd_clf.predict([some_digit])\n", "from sklearn.model_selection import cross_val_score\n", "cross_val_score(sgd_clf, X_train, y_train_5, cv=3, scoring=\"accuracy\")\n", "from sklearn.model_selection import StratifiedKFold\n", "from sklearn.base import clone\n", "\n", "skfolds = StratifiedKFold(n_splits=3, random_state=42)\n", "\n", "for train_index, test_index in skfolds.split(X_train, y_train_5):\n", " clone_clf = clone(sgd_clf)\n", " X_train_folds = X_train[train_index]\n", " y_train_folds = (y_train_5[train_index])\n", " X_test_fold = X_train[test_index]\n", " y_test_fold = (y_train_5[test_index])\n", "\n", " clone_clf.fit(X_train_folds, y_train_folds)\n", " y_pred = clone_clf.predict(X_test_fold)\n", " n_correct = sum(y_pred == y_test_fold)\n", " # print(n_correct / len(y_pred))\n", "from sklearn.base import BaseEstimator\n", "class Never5Classifier(BaseEstimator):\n", " def fit(self, X, y=None):\n", " pass\n", " def predict(self, X):\n", " return np.zeros((len(X), 1), dtype=bool)\n", "never_5_clf = Never5Classifier()\n", "cross_val_score(never_5_clf, X_train, y_train_5, cv=3, scoring=\"accuracy\")\n", "from sklearn.model_selection import cross_val_predict\n", "\n", "y_train_pred = cross_val_predict(sgd_clf, X_train, y_train_5, cv=3)\n", "from sklearn.metrics import confusion_matrix\n", "\n", "confusion_matrix(y_train_5, y_train_pred)\n", "y_train_perfect_predictions = y_train_5\n", "\n", "confusion_matrix(y_train_5, y_train_perfect_predictions)\n", "from sklearn.metrics import precision_score, recall_score\n", "\n", "precision_score(y_train_5, y_train_pred)\n", "from sklearn.metrics import f1_score\n", "f1_score(y_train_5, y_train_pred)\n", "y_scores = sgd_clf.decision_function([some_digit])\n", "threshold = 0\n", "y_some_digit_pred = (y_scores > threshold)\n", "threshold = 200000\n", "y_some_digit_pred = (y_scores > threshold)\n", "y_scores = cross_val_predict(sgd_clf, X_train, y_train_5, cv=3,\n", " method=\"decision_function\")\n", "from sklearn.metrics import precision_recall_curve\n", "\n", "precisions, recalls, thresholds = precision_recall_curve(y_train_5, y_scores)\n", "def plot_precision_recall_vs_threshold(precisions, recalls, thresholds):\n", " plt.plot(thresholds, precisions[:-1], \"b--\", label=\"정밀도\", linewidth=2)\n", " plt.plot(thresholds, recalls[:-1], \"g-\", label=\"재현율\", linewidth=2)\n", " plt.xlabel(\"임계값\", fontsize=16)\n", " plt.legend(loc=\"upper left\", fontsize=16)\n", " plt.ylim([0, 1])\n", "\n", "y_train_pred_90 = (y_scores > 70000)\n", "\n", "def plot_precision_vs_recall(precisions, recalls):\n", " plt.plot(recalls, precisions, \"b-\", linewidth=2)\n", " plt.xlabel(\"재현율\", fontsize=16)\n", " plt.ylabel(\"정밀도\", fontsize=16)\n", " plt.axis([0, 1, 0, 1])\n", "from sklearn.metrics import roc_curve\n", "\n", "fpr, tpr, thresholds = roc_curve(y_train_5, y_scores)\n", "def plot_roc_curve(fpr, tpr, label=None):\n", " plt.plot(fpr, tpr, linewidth=2, label=label)\n", " plt.plot([0, 1], [0, 1], 'k--')\n", " plt.axis([0, 1, 0, 1])\n", " plt.xlabel('거짓 양성 비율', fontsize=16)\n", " plt.ylabel('진짜 양성 비율', fontsize=16)\n", "from sklearn.metrics import roc_auc_score\n", "\n", "roc_auc_score(y_train_5, y_scores)\n", "from sklearn.ensemble import RandomForestClassifier\n", "forest_clf = RandomForestClassifier(n_estimators=10, random_state=42)\n", "y_probas_forest = cross_val_predict(forest_clf, X_train, y_train_5, cv=3,\n", " method=\"predict_proba\")\n", "y_scores_forest = y_probas_forest[:, 1] # 점수는 양상 클래스의 확률입니다\n", "fpr_forest, tpr_forest, thresholds_forest = roc_curve(y_train_5,y_scores_forest)\n", "y_train_pred_forest = cross_val_predict(forest_clf, X_train, y_train_5, cv=3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Chapter 3. 분류\n", "---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "## 3.4 다중 분류" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 이진 분류기 : 두 개의 클래스를 구별
\n", "- **다중 분류기**multiclass classifier(또는 **다항 분류기**multinomial classifier) : 둘 이상의 클래스를 구별
\n", " - **일대다**one-versus-all, one-versus-the-rest(OvA) 전략 : 이진 분류기를 여러 개 사용해 다중 클래스를 분류하는 기법. 이는 이미지를 분류할 때 각 분류기의 결정 점수 중에서 가장 높은 것을 클래스로 선택
\n", " - **일대일**one-versus-one(OvO) 전략 : 0과 1 구별, 0과 2 구별, 1과 2 구별 등과 같이 각 숫자의 조합마다 이진 분류기를 훈련. 클래스가 N개라면 분류기는 N*(N-1)/2개가 필요" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "다중 클래스 분류 작업에 이진 분류 알고리즘을 선택하면 사이킷런이 자동으로 OvA(SVM 분류기일 때는 OvO)를 적용
\n", "SGDClassifier를 적용해보겠습니다." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([5.])" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sgd_clf.fit(X_train, y_train)\n", "sgd_clf.predict([some_digit])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "이 코드는 5를 구별한 타깃 클래스(y_train_5) 대신 0에서 9까지의 원래 타깃 클래스(y_train)를 사용
\n", "내부에서는 사이킷런이 실제로 10개의 이진 분류기를 훈련시키고 각각의 결정 점수를 얻어 점수가 가장 높은 클래스를 선택
\n", "이를 확인하기 위해 decision_function() 메서드를 호출" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[-311402.62954431, -363517.28355739, -446449.5306454 ,\n", " -183226.61023518, -414337.15339485, 161855.74572176,\n", " -452576.39616343, -471957.14962573, -518542.33997148,\n", " -536774.63961222]])" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "some_digit_scores = sgd_clf.decision_function([some_digit])\n", "some_digit_scores" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "np.argmax(some_digit_scores)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sgd_clf.classes_" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5.0" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sgd_clf.classes_[np.argmax(some_digit_scores)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 사이킷런에서 OvO나 OvA을 사용하도록 강제하려면 OneVsOneClassifier나 OneVsRestClassifier를 사용" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([5.])" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from sklearn.multiclass import OneVsOneClassifier\n", "ovo_clf = OneVsOneClassifier(SGDClassifier(max_iter=5, random_state=42))\n", "ovo_clf.fit(X_train, y_train)\n", "ovo_clf.predict([some_digit])" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "45" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(ovo_clf.estimators_)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 아래 코드는 RandomForestClassifier를 훈련" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([5.])" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "forest_clf.fit(X_train, y_train)\n", "forest_clf.predict([some_digit])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 랜덤 포레스트 분류기는 직접 샘플을 다중 클래스로 분류할 수 있기 때문에 OvA나 OvO를 적용할 필요가 없음\n", "- predict_proba() 메서드를 호출하면 분류기가 각 샘플에 부여한 클래스별 확률을 얻을 수 있음" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0.1, 0. , 0. , 0.1, 0. , 0.8, 0. , 0. , 0. , 0. ]])" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "forest_clf.predict_proba([some_digit])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 이제 교차 검증을 사용하여 분류기를 평가함
\n", "cross_val_score() 함수를 사용해 SGDClassifier의 정확도를 평가 " ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0.84063187, 0.84899245, 0.86652998])" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cross_val_score(sgd_clf, X_train, y_train, cv=3, scoring=\"accuracy\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "분류기의 성능을 더 높이기 위해 입력의 스케일을 조정함" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([0.91011798, 0.90874544, 0.906636 ])" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from sklearn.preprocessing import StandardScaler\n", "scaler = StandardScaler()\n", "X_train_scaled = scaler.fit_transform(X_train.astype(np.float64))\n", "cross_val_score(sgd_clf, X_train_scaled, y_train, cv=3, scoring=\"accuracy\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "## 3.5 에러 분석" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 모델의 성능을 향상시킬 한 가지 방법은 만들어진 에러의 종류를 분석하는 것
\n", "먼저 오차 행렬을 살펴보기위해 cross_val_predict() 함수를 사용해 예측을 만들고 confusion_matrix() 함수를 호출함" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[5725, 3, 24, 9, 10, 49, 50, 10, 39, 4],\n", " [ 2, 6493, 43, 25, 7, 40, 5, 10, 109, 8],\n", " [ 51, 41, 5321, 104, 89, 26, 87, 60, 166, 13],\n", " [ 47, 46, 141, 5342, 1, 231, 40, 50, 141, 92],\n", " [ 19, 29, 41, 10, 5366, 9, 56, 37, 86, 189],\n", " [ 73, 45, 36, 193, 64, 4582, 111, 30, 193, 94],\n", " [ 29, 34, 44, 2, 42, 85, 5627, 10, 45, 0],\n", " [ 25, 24, 74, 32, 54, 12, 6, 5787, 15, 236],\n", " [ 52, 161, 73, 156, 10, 163, 61, 25, 5027, 123],\n", " [ 43, 35, 26, 92, 178, 28, 2, 223, 82, 5240]])" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y_train_pred = cross_val_predict(sgd_clf, X_train_scaled, y_train, cv=3)\n", "conf_mx = confusion_matrix(y_train, y_train_pred)\n", "conf_mx #행은 실제 클래스, 열은 예측한 클래스" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "오차 행렬을 맷플롯립의 matshow() 함수를 사용해 이미지로 표현" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/anaconda3/envs/mlbook/lib/python3.5/site-packages/matplotlib/font_manager.py:1320: UserWarning: findfont: Font family ['NanumBarunGothic'] not found. Falling back to DejaVu Sans\n", " (prop.get_family(), self.defaultFamily[fontext]))\n" ] }, { "data": { "image/png": "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\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "plt.matshow(conf_mx, cmap=plt.cm.gray)\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "이 오차 행렬은 대부분의 이미지가 올바르게 분류되었음을 나타내는 주대각선에 있으므로 매우 좋음" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "그래프의 에러 부분에 초점을 맞추기 위해 오차 행렬의 각 값을 대응되는 클래스의 이미지 개수로 나누어 에러 비율을 비교(MNIST는 클래스별 이미지 개수가 동일하지 않음)" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[5923],\n", " [6742],\n", " [5958],\n", " [6131],\n", " [5842],\n", " [5421],\n", " [5918],\n", " [6265],\n", " [5851],\n", " [5949]])" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "row_sums = conf_mx.sum(axis=1, keepdims=True) # column합\n", "row_sums" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[9.66570994e-01, 5.06500084e-04, 4.05200068e-03, 1.51950025e-03,\n", " 1.68833361e-03, 8.27283471e-03, 8.44166807e-03, 1.68833361e-03,\n", " 6.58450110e-03, 6.75333446e-04],\n", " [2.96647879e-04, 9.63067339e-01, 6.37792940e-03, 3.70809849e-03,\n", " 1.03826758e-03, 5.93295758e-03, 7.41619697e-04, 1.48323939e-03,\n", " 1.61673094e-02, 1.18659152e-03],\n", " [8.55991944e-03, 6.88150386e-03, 8.93084928e-01, 1.74555220e-02,\n", " 1.49378986e-02, 4.36388050e-03, 1.46022155e-02, 1.00704935e-02,\n", " 2.78616986e-02, 2.18194025e-03],\n", " [7.66595988e-03, 7.50285435e-03, 2.29978796e-02, 8.71309737e-01,\n", " 1.63105529e-04, 3.76773773e-02, 6.52422117e-03, 8.15527646e-03,\n", " 2.29978796e-02, 1.50057087e-02],\n", " [3.25231085e-03, 4.96405341e-03, 7.01814447e-03, 1.71174255e-03,\n", " 9.18521054e-01, 1.54056830e-03, 9.58575830e-03, 6.33344745e-03,\n", " 1.47209860e-02, 3.23519343e-02],\n", " [1.34661502e-02, 8.30105147e-03, 6.64084117e-03, 3.56022874e-02,\n", " 1.18059399e-02, 8.45231507e-01, 2.04759270e-02, 5.53403431e-03,\n", " 3.56022874e-02, 1.73399742e-02],\n", " [4.90030416e-03, 5.74518418e-03, 7.43494424e-03, 3.37952011e-04,\n", " 7.09699223e-03, 1.43629605e-02, 9.50827982e-01, 1.68976005e-03,\n", " 7.60392024e-03, 0.00000000e+00],\n", " [3.99042298e-03, 3.83080607e-03, 1.18116520e-02, 5.10774142e-03,\n", " 8.61931365e-03, 1.91540303e-03, 9.57701516e-04, 9.23703113e-01,\n", " 2.39425379e-03, 3.76695930e-02],\n", " [8.88736968e-03, 2.75166638e-02, 1.24764997e-02, 2.66621090e-02,\n", " 1.70910955e-03, 2.78584857e-02, 1.04255683e-02, 4.27277388e-03,\n", " 8.59169373e-01, 2.10220475e-02],\n", " [7.22810556e-03, 5.88334174e-03, 4.37048243e-03, 1.54647840e-02,\n", " 2.99209951e-02, 4.70667339e-03, 3.36190956e-04, 3.74852916e-02,\n", " 1.37838292e-02, 8.80820306e-01]])" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "norm_conf_mx = conf_mx / row_sums\n", "norm_conf_mx" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "다른 항목은 그대로 유지하고 주대각선만 0으로 채워서 그래프를 그림" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/anaconda3/envs/mlbook/lib/python3.5/site-packages/matplotlib/font_manager.py:1320: UserWarning: findfont: Font family ['NanumBarunGothic'] not found. Falling back to DejaVu Sans\n", " (prop.get_family(), self.defaultFamily[fontext]))\n" ] }, { "data": { "image/png": "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\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "np.fill_diagonal(norm_conf_mx, 0)\n", "plt.matshow(norm_conf_mx, cmap=plt.cm.gray)\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 8, 9 열이 상당히 밝음 -> 많은 이미지가 8과 9로 잘못 분류되었음\n", "- 8, 9 행도 밝음 -> 숫자 8과 9가 다른 숫자들과 혼돈\n", "- 오차 행렬을 분석하면 분류기의 성능 향상 방안에 대한 통찰을 얻을 수 있음
\n", "이 그래프를 살펴보면 3과 5가 서로 혼돈되고 8과 9를 더 잘 분류할 수 있도록 개선할 필요가 있어 보임\n", " - 이 숫자들에 대한 훈련 데이터를 더 모음\n", " - 분류기에 도움 될 만한 특성을 더 찾아봄(동심원의 수를 세는 알고리즘 - 8은 두 개, 6은 하나, 5는 0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "개개의 에러를 분석하기 위해 3과 5의 샘플을 그려보겠습니다." ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/anaconda3/envs/mlbook/lib/python3.5/site-packages/matplotlib/font_manager.py:1320: UserWarning: findfont: Font family ['NanumBarunGothic'] not found. Falling back to DejaVu Sans\n", " (prop.get_family(), self.defaultFamily[fontext]))\n" ] }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAegAAAHZCAYAAABNdqgEAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMS4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvNQv5yAAAIABJREFUeJzsnXd8Tef/wN83RqJqtYgtihqtGlVixywlSmrWqtasrUooTdBasVqt2rNa1KytiFHzawUltGaIEZtEjOT5/XF+z+Pe7HFHEs/79bovufece87Hued5Puf5TJMQAo1Go9FoNCkLJ0cLoNFoNBqNJjpaQWs0Go1GkwLRClqj0Wg0mhSIVtAajUaj0aRAtILWaDQajSYFohW0RqPRaDQpEK2gNRqNRqNJgWgFrdFoNBpNCkQraI1Go9FoUiBaQWs0Go1GkwJJ72gBYkHXH9VoomNytABJQI9ljSY6CRrLegWt0Wg0Gk0KRCtojUaj0WhSIFpBazQajUaTAtEKWqPRaDSaFIhW0BqNRqPRpEBSahS3RpOmuHr1KrNnzwZg/PjxPH36FIDPP/+cWbNmAZAuXTqHyZeauH//PgA5cuQAYMOGDQB89NFHVj/Xo0ePGDJkCAAzZsyw2FayZEkAvLy8APD29gbg9ddft7ocmlcTkxApMgsiRQqVGBYuXAjAsWPH1Gd37tzh119/BaBz584UKFCAcuXKAVCuXDnc3NwAcHJKGYaNnTt3snPnTkaOHBnrPh4eHgD4+/vbVJbhw4czffp0AO7du0epUqUA6N+/P61btyZbtmw2PX9SCAsLA2DJkiUMGTKEe/fuxblfpkyZ4jukTrMCHj9+DMB7773HpUuXcHd3B2Dfvn3WPhW3bt2iQoUKgPFg8OTJk1j37du3LwBTpkyxuhy24NKlSwA8ePCAfPnykStXLofIcfLkSQCuX7/O6dOn1ZwZHBzMtm3b1H7169dn69atDpFREhAQQHBwMMWKFQOgePHiMe7n6+vLyJEj2bRpEwANGzaMukuCxnKaUtArV64EYMyYMRaKUQiByfTyevzyyy907949mSLGzuLFixk2bBgA165di3W/qHK1aNECgO7du1OxYkW7KR1zJbxz584kHcPW99HgwYPx8/OLcds777yDj48PAM2bNyd9escZhp4/fw5AYGAgrVu3BuDMmTOx7t+zZ09++uknIEEPZlpBmzFu3DiGDRtG5syZAeNBqGnTprY6HUuXLsXX11e9lw8K169ft9gvIiLCZjIkh6dPn3L69GnA+L+sXr0agH///Rc/Pz8GDRoEwPr169VDY6FChdQDkDW4cuUK9+7dY926dYAxZ589exaA8PBwSpQooR6+q1Wrph6I/vzzTw4fPkxkZKTVZImN3bt306ZNG2UhCQ4O5s6dO4Dxmz99+pTXXnsNgEqVKinLWNGiRdUxfH19GTVqFBs3bgSSrqBTxlJNo9FoNBqNBWnGB920aVNl/nj+/LnFyhSweN+vXz+10undu7fVZUmXLh1vv/02AI0aNbLYJk3ax48fB+Cvv/4C4PLly6xYsQKAFStWMGfOHD7//HOryxYTtWvXtst5kkPdunV54403AHj33XfV5ytXruT333+nVatWgGHydpSJcfv27QwYMAB4abaLDWdnZ8BwdaQUl0ZqITw8HIBdu3YBKIuJrS1Obdq0oU2bNur9p59+CsCyZcuAl77olIQ0Yx86dIhJkyZx+PBhAIuVaJYsWfjll1+YNm0aAIULF2bPnj2AMVcmdQUt59i//vqLb775BjDmubCwMPLlywdAy5YtlTsQDJOxdPU8ffqU0aNHq/+HNBfbii+++AIw5t9Hjx5x48YNwLi/3nrrLQCaNGnCnTt31PjeuXMn77//PvAyNkKSPXt2atSokSyZ0oyJu0GDBurmW7RokfIbSbZs2QLAN998w82bN6lXr57F59ZECKHMXPGZW2WwUEBAAKNGjQJg48aNpE+fnv/++w8wzEy2JOrDjMTDw4NatWqp976+vsrEF9Uv7cj76NChQzRu3BgwzIvyukmFbi8aNGigHrji49tvvwWiX8d4eCVM3IGBgeo3BEMBSmU8YsQIpYilsixcuDAAFy9eTLawCeXu3btUrVoVMEzE+fPnV+ZM8wdIRyCV78yZMxk/fjxgKEZz3N3d1fX77LPPePHihfLhf//99zRr1gyAgQMHkjFjxkTLEBwcrNxOc+fOVebfTz75hGbNmsWp9C9cuAAYyls+YGzatIlKlSolWo7EIIMAN23aRIsWLQgODgYM33dUffLvv/8CUKJECUqUKAFYurJ8fX3Ztm0bf//9d2yn0yZujUaj0WhSK2nGxJ0jRw4WLVoEGGaIqGTIkAEwnnxtjclkSnCgkjR1VqpUie+//x4wVtAvXrzgypUrgO1X0P7+/io4zDwIJiq1a9eOFkRm6+jthFCpUiXlDpgwYYKyStibokWLJmgFXahQIZsGKaZWbt26BYCnpyfnz5+32CYtNFGv22uvvcbAgQPtI6AZBw8eVKsoMFaGjl45S7p06QLA/Pnz1WdVqlShbdu2fPjhhwAUKVJEzYkBAQH07dtXuQ6GDh2qXHNJWT0DnDp1iuXLlwPGSr5r167xfmf16tWMGTNGBd19++23dOvWLUnnTwo9evSw+Dc2bt++rdwBgHKvRaVixYrJlinNKOhJkyZRoECBWLdL/4D0i/Ts2dMuciWU8+fPK7MSQJ8+fahcubJdzu3h4aHSpcyR0d2xRXb7+/vH+D1HIKM98+bNq6J67c2XX37JixcvAMMHffDgwRj3W7dunfLBaV4iFURU5RwXderUoU+fPrYSKVbMlR8YsQQphY4dOwLGtZF88sknFml8ERERTJ06FTCi4Tt06KDmROlvTQ5vv/22UmJSHnPkPLxhwwZlhv/nn39o2rQpixcvBl7mmackQkJCqFevHidOnACMVL/Y3FRVqlRJ9vm0iVuj0Wg0mpSIECIlvqzKwYMHRdmyZUXZsmWFk5OTaNeunXjw4IF48OCBtU+VKK5duyZ69OghevToIbJmzSpMJpMwmUzCzc1NXLlyxSEy+fv7Cw8PD+Hh4SEwAnxifPn7+ztEvphYtmyZcHFxES4uLmL48OEOlWXBggViwYIFonDhwrFeu/nz5yf18I4elzYdyxERESIiIkIsXLhQlChRQpQoUUK4uroKk8mkrp0cI/KVMWNGMXr0aDF69OjEnCrJzJkzR8yZM0dkzpxZODk5CScnJ9GqVSvx/Plzu5w/OVy8eFFs3bpVbN26VbRo0UJkz55dZM+eXUydOlU8fvzYLjKEh4eLadOmierVq4vq1auLzJkzixYtWogWLVqINWvW2EWG5NCvXz9hMpmEu7u7cHd3F6GhoTHu5+PjIw4ePBjXoRI0ftJMFHdMyAjtRo0aWUQqnz59WkXe2RuZXvXzzz8zZ84cC7mKFCkCwNatWy2S3m3Nzp07E5Rq5eHhkSJ8zoCqyjVr1iwLv/natWtp0KCB3eU5fPgwo0aNUql+cfnBXVxcVCGdRJrxXokobnhp5g4LC+PBgwcW26QpvGPHjty4cUPFccyePZv27dsnR9Y4CQkJUWZjWfADjHtQpuikJAIDAzl37hxgzCk///yzxfb8+fMDRvRxlixZbCqLeWT2sWPHlN92+PDhsfru//77bxWZ36RJE1Xa1d7cvHlTlXFdsGABNWvWVEVeYssUOXLkCO+88w4uLi6xHTZBYznN+KBjQgZEZMuWjYcPHwLw/vvvkzNnTofJJP25c+fOjbZNVpSyp3KG+POgZbpEXAFktubKlSsEBAQAsH//fhYsWAC8rOIky4DaWzn/8ccfAPTq1YuQkJAEfSc8PFxVUkqJfraUQELGwIYNG6hXr556WIurap81eP78eYwPXr169bIIVEuXLh1ff/01YASvxhd0ZG1kSlW5cuUs5H377beVXKdOnVI+4sqVK7Nu3TqrzzuPHj0CjMXI0KFD1edjx45VisvPz08pbzD80LGVxK1fv76qdlarVi31YGYLQkND2bFjB2CUcJXXtEePHvj6+sabwilzo5OL9kFrNBqNRpMCSdMmbsnYsWMZPny4ej9u3Dj1JGlvvvrqK8AoqC+i1OKWhddlbVp7IQu7x4ePj4+K2o4renvnzp1Wie6WSf69evXiwoULqvZxTEgz2bJlyyhdunSyz50QNm/eHK1SnCRPnjyMGDECgHbt2lGxYkWL4hvyCVzW+E0gr4yJO6GsX78eT09PAJo1a6aqeiU1PSg+ZLWw9evXq4j9qEQd1/AyVXLEiBGqAlkCmqMkCRk1vXjxYgoWLAgYteyjVk2U1p/27dvz0UcfqV4G1qpsJ62F5tHk8tpIF6OLi0uC3BJPnjxh7ty5qnBJo0aNlBUyb968VpFX8uWXX7Jp06ZoxV0Atm3bZvH/SQavXrOM2Ni/fz9169YFDN9gs2bN1M3oKObPn4+Xl5ca8H///TfPnj0DjBQwabK1F3IwxZRSZa684+peZX4Ma5jDf/nlF8AYMPAyZ7xZs2YqhSF//vxMnTpVVZFLnz69aj9oXgXNmsgHhb59+1qk22TKlEnl4Pv6+lo8KEyaNEmZ5wDlZkmoWfz/0Qo6CuvXr7dokCHTKbNmzWrL07Jy5UrlXpk5c6aFWTYiIkLldMeEfBDfs2ePTTpIyZLBixcvVilMcblSatWqxe7du9W1s1bJ1E8++QQwGl3I+7169ep8/fXXlClTBkjcQ8q9e/c4dOgQAB06dFDVIOfPn28Vc7dsVypLispyzUWLFuXIkSOAkas/dOhQxowZk9zT6UpiGo1Go9GkWhIa7m3nl9Xx9PQUnp6ewmQyiebNm9viFMli6NChKnUkR44c4uzZs44WKRrmqVceHh7RtsttPj4+VjnfkSNHxJEjR8SYMWPEmDFjxNq1a8XatWtj3Fem2jg7O4smTZqIJk2aWEWGqDx58kSULFlSlCxZUgAqvatnz55i5cqVsX5v2rRpFqlWOXPmFDlz5kzs6R09LlPEWI6MjBSRkZFi6dKlwsPDQ42btm3bimfPnolnz57Z4rQJ5tmzZ2L+/Pli/vz5YvLkyaJEiRIiS5YsIkuWLCo1y8nJSRw6dMjhcj579kzUqVNH5MyZU5w/f16cP3/easc/e/asOHv2rNi5c6fVjimZPn26+t23bt1qlWPK+WXgwIFi//79IiwsTISFhQkhhDhx4oQ4ceKEaNSokciYMaNYtmyZWLZsWXJOl6Dx4+jBa7dBPW3aNDFt2jTh5OQkvLy8bHGKZLF7926RMWNGkTFjRmEymUSfPn0cLVKMmCsZc/z9/a2uoJNC48aNldK8c+eO1Y8fGhqaaEV77do1kTNnTovvlStXTpQrVy6xp3f0uEwRY/n+/fvi/v370XKix40bZ4vTWYVZs2aJWbNmWSjo5s2bp4gHigEDBghAbNu2TWzbts2hsiSU48ePq7FkLQWdEDZu3ChMJpOoUKGCqFChQnIOlaDxk6bTrMyReacplRo1atC/f3/ASD24du2a6kqTGtoRenh4qHQsR/Lmm2+qFA5bXLeoKSChoaGAkRcbNThNlvocPHgwt2/fttg2a9Ysq8v2KrBr1y5++OEHR4uRaGRpSHMOHDigUpES03nt/Pnzaj4rX748uXPnBkh0LrPsuHf37l0yZ86s7uXUwJw5c5Tf2dY53DFhr2uV8md+jUaj0WheQV6JFfTZs2dV+kVKRkYA+/n5sXr1arXqkk/Ijia+yGy5PbbmGrZk3rx5APz22280b94cMBqm2xrZpGPVqlWUKlVKvV+zZo1Ka4m66m7btm2K6XzkSCIjI7l69SpgpNvEdZ+bF4qImoYov2fryO2kcOLECY4ePcrSpUujbevdu3eSepZ/+umnqrjHw4cPVSS4q6sr77zzDvXr1wcMK415lkCvXr1wdXUFDIvPb7/9Bhi9jX19fS2i4VMqsufyokWLGDt2LECcvaWtjT17jsMroqAnTpyoJk54mebgCObMmQMYIf1ROxqZm+EbNGhg9dJ25hXDElOyU37PXPHGZc62Z4erR48eMW/ePNWq8/XXX2fw4ME2O19sv8mIESPYt2+fGsCBgYHR9pHlDRcsWGCzPN3UxKNHj3BzcwOgWLFitGjRAjDufXkPzZgxgytXrqjJOGp+sbe3tyqzae8KfOb8+++/dOjQATBSOaWcV69ejZbrLnOTZYvUxNK4cWO+++47wKhuJiuoXbt2jaNHj6puUFHp27ev+tvJyUm5gDw8PFQpy5TMhQsXVLqsu7u7aqtpDyZMmADAkCFDyJ49u1oQ2Bpt4tZoNBqNJiWS0GgyO7+SzYsXL8SoUaPEqFGjhMlkUpGTXbt2tVvnlqgEBAQIZ2dn4ezsLIYOHWqx7dmzZ6J+/fqifv36wmQyiU6dOln13D4+PhZRxLJjVWz7ylfUrlZxfc/WrFu3Tqxbt048fvxYTJ48WUyePFkUKFDAQr6FCxfaVIbIyEgxe/ZsMXv2bJEhQ4Y4O37JV6ZMmUSXLl3EixcvxIsXL5JzekePS6uO5cjISHWfmUdju7i4qE5LGTJkiNbNSo6h9u3bi9u3byfnelqFs2fPiiJFiqg5xny+ifoqXLiwCAgIEAEBAck657Fjx8SxY8dEnz59RJUqVUSVKlWEm5ubyJgxo8icObPInDmzxT1YsGBB0bRpU+Hm5ibc3NxE586dxYYNG8SGDRtSZEqn5OHDh+Lhw4di7NixAhCdOnUSnTp1UulPSeHevXvi3r17ws3NTVy4cEFcuHAhxv2CgoJEUFCQaNu2rbrnsmfPLhYvXpzkc5uRoPHj6MFrMwX9888/xzhgrl27Zo3DJwlvb281CZUpU0YsWrRIDZKaNWtaTFIHDhyw6rnN06CS+nKUYhZCiFWrVokMGTKIDBkyRJt8smfPLvz8/ISfn5+IiIiwm0xNmjSJ83rJdK+lS5da65SOHpdWH8tyEqxQoUK0tCnzl7ymixcvFvv37xf79+9P6jW0Ovv377fIcTafb9544w3Rv39/sXfvXrF3715x9+5dm8oSGBgoLl68KC5evKgeBAICAhyeypUQrl27Jo4fPy5Wr14tVq9eLdq1a6fSGF1dXcXPP/9slfPIHOaSJUuKx48fR1uwXb9+XUybNk2d22QyiRo1aogaNWqIvXv3WkUGkcDxo03cGo1Go9GkQNJULe7nz58DRnuzWbNmqWhPIYQKQFmwYAGvvfaalcRMHI8fP6Zz586AUctXiOhF9cGI5v7111+tHpUqo6wT0hjDnJTQbnLw4MH4+fmp9/LaeHl5MWDAAN577z27y/To0SN1bVasWEFQUJDaVrx4cZU5UL58eWudMs3W4r58+TJLliwBsGhsA0bUcrVq1QBo3bp1kiKfbc3kyZNVA56hQ4eqSOoPPvjAYfONo5H1yBctWqSCu/Lmzcv9+/dZv3692u/XX38F4MaNGxY1zMuVK0fLli0B+Oyzz6zWFEMGFc6fP5+yZcsCWPRtPn/+PLdv31a/25AhQ+jTpw9g1cyQV69ZhgzBj5rC4unpyfLlywHbdblJKKdOnQKMftBTp05VCvrdd99VaVbDhg3j9ddft5kMCe1eJYuPJCQq2zzC2xZR3NWrV1f9oIcPH64aT6RLl87q50rBpFkFrUl7XLlyBYCvv/5adc6KaVEis1nq1atHqVKlqFChAoB6yLE2ch5xd3ePsb93vnz5aNWqlWrSU6xYMVuI8eopaJnO0LBhQ/755x+1uhkyZIj1JNNoHIdW0BpN2kB3s9JoNBqNJrWSplbQGk0aR6+gNZq0gV5BazQajUaTWtEKWqPRaDSaFIhW0BqNRqPRpEC0gtZoNBqNJgWiFbRGo9FoNCkQraA1Go1Go0mBvBL9oDVJ58cff1SVf2bOnMnp06cBo590rVq1HCmaRqPRpGm0gnYgR44coVGjRgCEhIRE216xYkUAxowZY7Oyd3ExevRofHx8LErzyb+nTJlC5syZlYyvIsOHD2fMmDHAyxKGOXPmBIwa4dWrVwegRo0aFC5c2GFypka+++47AL7//nty586tPi9btizFixcHjJrytiyJC0aN8Nu3b6v3q1evBuD06dOsWbMGePnbd+vWDYDmzZvz4Ycf2lSu1MydO3dUaeANGzawYsUKAAoUKMCsWbPUuEkpnDhxgh9++MHis8OHDwPw9OlTvLy8AOjWrRtubm5WPbc2cWs0Go1GkwJJU5XE9u/fD6C6Cl29ehWAP/74gwMHDgBQsGBBli1bRpUqVawhZ6K5ceMGYHS/WbRokUX3ltioV68eW7dutbVo0ejfv7+FidscIQSurq6sW7cOwOor6bt371qsXJ49e8asWbOi7Xfz5k2WL1+Ou7s7AK1atWLAgAFWlSUqq1atAqBFixbq2shVlBxP5n/nzp2b6dOnqyftZPDKVBLLkSMHYDRe6dWrl+petmPHDoYOHQrAwYMHqVSpkpXEfIm0ZnXs2JGjR4+q+9C80YMQgtKlSwOGhWT16tVqLJtMJj799FMAFi9ebHX54iM4OBiAhQsXqs/+/PNPDhw4gL+/P2CbhjYJYceOHXz88cc8fvw4xu2dOnViwYIF9hUqFmQ3ui+++ILQ0FAAXn/99WjdyR4+fAgYuuXcuXMJPXyCxnKqN3HLLlVTpkxRSjgugoKC2L9/v90U9IsXL7h48SJgmEPatm0LwD///GOxn7Ozs2pplidPHmbPnq3aZQYFBfHgwQMAsmXLZhe5E8KtW7eU0rS2gvb19eWnn35K0L4mk4mDBw8CRgvIzz//HLD9tTJ/uH3//fcttgUGBqpJ6NatW/To0UOZuaPuq4mObN04bNgwi88zZcqkFLSTk20MgDVr1gSM+2r06NGUKlUKMBRxbPzyyy/q74EDBzJlyhTA+K379+9vEzkl8+fPB+B///sfS5YsISIiAoCwsDCL/UwmkzK9r1+/3q5uM9nIaODAgTx+/JiSJUsC0KtXL7JkyQLAN998w6JFi+jbty+A6mrlKKSCzpUrF+PGjQOgTp066sFMIttXmrfQtBapXkHLtoPmvXjBeJrJnz8/gIXidnd3Z+DAgXaTb+7cufTs2TPW7dJn0axZMyZMmAAYq+yxY8eqfQoWLOgQxTx16lS8vLyU380c6ZPZs2ePVc+5fft2AGbMmKE+y5Ahg8Uqvnjx4jRs2BCAa9eusXTpUrUtc+bMZMqUyaoyRUWuhHv06EHz5s0BaNCggcU+gYGBqr/xmDFjuH37No0bNwZeWlE0sRNVMUsOHDigFHOuXLlscm65uhw6dCiFChVK9PdLliyp7lf5kG0rSpQowYULFwCUYo6LZ8+eAfDJJ5/w77//AuDq6mo7Af+f77//HoAnT56wevVqPvroI8Bo/ytX9devX8fNzU31aHY0skVmQEAAGTJkAIimnLdt28Zvv/0GoOYCa6J90BqNRqPRpEBS/Qp67969gOF/btWqlcW2yZMnA8ZTd8GCBYGXJnF7ce3atVi3NWvWjEmTJgFQpEgR9fmePXuUSQiMVaGjqFmzpjL5mRM1qtFayObob731Fs7OzgBs2rRJNXWXSItJvXr1AEif3riVW7VqRcaMGW0iW1TMzZpRKVmyJF26dAFg1qxZ3Lp1ixQa75GqOH/+PB9//DGAzSLj4/pd40L6KTdv3qx+axnVbytu3LiRoJVzVFxdXdWq0Nb4+/szdepUwFhlNmvWTPltx4wZw/jx4wGIjIykR48epEuXzi5yxYeUIyZTu4xzWbBgASVKlAAMa6m1SfUKWipe+a9k//79fPXVV+q99ANF3c/WFC9eXPlnAwICqFu3LgDvvvsu9erVU4rZfFCPHDkSQCko8/+HI7l27Rpdu3a1+MzaE5CcdP/66y81QKIq56tXr6r0NGmmkya0lHKtAGbPng3A7du3MZlM1ggSe2WR5saZM2c6JPAqPs6cOcM333wDwNq1a5XfWvrLbcVbb71FQECAel+pUiXl2nNzc1O+8Kj07t2bN954w6aySapVq8b//vc/wDARb9y4kdGjRwOW7sfatWvb1f2YFI4ePcrPP//MvHnzACNQVN6btkCbuDUajUajSYkIIVLiK9m0bNlSYKR4iIIFC1rjkEkmLCxMhIWFiStXrojHjx+Lx48fi19++UWMHTtWfPDBB+KDDz4QJpMp2svb21t4e3s7VHYhhPjnn3/EP//8Izp16iScnJyEk5OTMJlMwsnJSWzevFls3rzZ5jJERkaKNWvWiDVr1oh3331XXaP06dOL8ePHi4iICBEREWFzORLKypUr1f1nMplE4cKFRUhIiAgJCUnOYR09Lu0+liMjI8WOHTtEiRIlRIkSJcTy5cuTe0ircevWLXHr1i3RvXt3kTt3bvV7e3l52U2GoKAgUb9+fVG/fn3RuXNnsW/fPjFv3jwxb968GOeUvHnzirx584rQ0FC7yWjOgQMHhJOTk7pW5q/06dOLvHnzip49e4qePXuKhw8fOkRGyfPnz8Xz589FSEiI8PT0FJ6ensLFxUW4uLiItm3birZt24qwsLCkHj5B4ydN5UFL9u/fT9WqVWPd3rJlS+X7tafJ+7///lOpDZcvX453f+m/zps3r03lisr9+/cBI4dTCKHMx+bVzoQQuLm5sXHjRgBl0rOVPMOGDbOI7JZ++d9//50mTZrY7NyJReZId+rUSfkkS5cuzejRo60R5fnK5EFLxo4dy7BhwyhTpgxg+DPffPNNqwiWFM6cOQMYvtO///4bMMZy6dKl8fb2Bowo/6i5svZg3bp1dOrUSY3fqOTOnZsdO3YA0aOR7cWxY8fo3r27itR+++232bRpEwDnzp2ziNlxc3NjxIgRACp10lbMnTtX1XSQEfgyTXL79u0W9Q3y5MmjfOrOzs7KVRBXGl4MJGgsaxO3RqPRaDQpkDS1gpaRvdWqVYuWFx0VuXLeu3evzVfRly5dAqBu3bqqaElMyKfa119/nUOHDuHn5wfYN/ApJCSETp06AbBlyxaL6knmFCpUiD///FOtbGyBLM7SsmVLtm3bZrFNrqDnzJlDvXr1HLqqkoSEhKi60SaTSa2iFi9ebK0cyVdmBS0jk9u3b8/JkydVYZ88efKoqnq2vPckoaF89EAVAAAgAElEQVShqibBypUrVV5zoUKFVHbD0KFDbWpBig+5kvf09FRjJibKly/Pu+++q97LFX/WrFnJlSuX3bIfYuPevXsEBASo2tw///yzynn39PRUtc9twaeffsrvv/8OEG2+y549u8oVv3TpEk+fPrXYLqPh69atq7KEElAjPkFjOc0o6KCgIKpVq6b+BmNiByMkXlYOW758OYMGDVL7uLu7q4tqK0UtTTiyUIU5stjCl19+iY+PD2DcBG+99RYuLi6AkXZlr+pTmzZtsjAZx6agT506ZfNJ6cWLFwDMmzeP4cOHW5T+NCd79uzKvOTp6amqtdkzPS0kJISPPvqII0eOAMYgb9euHQCLFi2y1mleGQVtzvPnz1UXtU6dOqn7ccmSJTY31ZYuXVopZfOxUKFCBYuMBi8vL5unVMVGYGAgYERB37x5M859zU215rRs2VIp7OLFi9u8CUlcPH/+HDAak/Tr1w8wqvFNnz6d7t272+y8+/bti/Hz3Llzq/TPY8eO8eTJE4vtJ06cAIw5XBZQWrVqlZq/Y+HVUtCTJ0+2WGm2bNky1pzn/fv307p1a8BQ5lKR2ypH+uTJk4DxA8oSd61btyZHjhxq5WfeBUUqaMmoUaMYPny4TWSLyuXLl5WCPn36dKwKetmyZbRo0cIuMgH89ttvahUt/YBg5MVGVdxyMA0YMIDWrVvbJZ3kyJEjfPDBBzHW4h4+fDiffvqpNR5oXkkFbU54eLi67y5cuMDhw4dt6u/t0KGDUoCxcfjwYYvfu1SpUha1A7p27ap+e1vKOmvWLL7//vs4rYexKWhzcubMyZYtWwBj1e1IpM+8bt26FChQIF7LqCPp2rUrc+bMAYzUynisetoHrdFoNBpNaiXVr6DlE5V5zdyCBQty5cqVOL8nq4x99dVXyrQd33eSS2RkZIIK/D98+JCqVasqs56julmtWLGCn376id27d0fb1qdPH5tVE0sM58+fZ9++fapQfdSiAR4eHiqyOnv27DaTIywsjFWrVqn+0GfPnrVYrRQqVEgVa0iGKfSVX0HDyyYQRYsW5ccff1QWMEdx9OhRTp8+raoamnPmzBl2796tTPGjRo2yacGan376SXWziotJkyaputwxIWv/b9++3aHNXWSHMFdXV7JmzRqnj93RrFmzRv22ISEhVllBOzpH0mq5kwULFlSvffv2xbv/vn37xL59+yzy8JYtW5aUU1udx48fi3Llyqncxfr16ztMlvv374vPPvtMfPbZZyJPnjwqD/rjjz92WC5lTMg86Hv37gkPDw/h4eGhrl+VKlVElSpVxKNHj2wuh8yNnTlzpqhZs6aoWbOmMJlMAhClSpUSpUqVEqdPn07q4R09Lu2eBx0XjRs3Ft27d7flKazCrl27RMOGDUXDhg0t7oNbt245TKZHjx6Jhw8fiocPH4qRI0eKPHnyxJg3PW3aNIfJKIQQN2/eFDdv3hSAyJAhgzhy5Ig4cuSI1Y7/+++/i99//z05Y1LRu3dvdd1u374d3+4JGj/axK3RaDQaTUokoZrczi+7gdkKesCAAXY779WrV8XFixdjfO3Zs8fiKXbEiBFJPo885uPHj5N8jEOHDolDhw4JV1dXi0pi//33X5KPmVgOHjwobty4IW7cuJHg7wwcOFDkzJlTXcdatWqJO3fuiDt37thQ0pdcvnxZXL58WXh5eanKa05OTsLNzS2pVcUcPS7tPpb/++8/ceDAgRi39ejRQ7i6uib3FHZl8+bNwtXVVbi6uoqKFStao7qcVQgICBDFixcXxYsXt5h7rFGF0d/fX4wdO1aMHTs20d+9dOmSuHTpkgBE1qxZky2LEC8rhA0ePFikT59epE+fXuzduzfJx5NzbM6cOUWZMmVEmTJlElJhLEHjJ9U3y7AmV69etfoxAwMDVZrGsWPHOHToEGDkLspKNbEhq+18+eWXST6/bNBeunRp1YiiRo0afPLJJwk+xqxZswDLSmK1atWyeVrJvXv3ACO1JiQkJNEN0SdNmkTlypVp06YNALt371apFPaoPibjIlauXImXl5fK47x06ZLy38umARpLpH+0TZs2rFy5MsZ9Nm3apO5vR3LmzJkER+h/+OGHjBo1CoDu3buraG8Zb+IIgoOD6dGjB//991+0bV988UWSj7t//34A2rZtq66PTOVKCBEREarTFbxsHpRcZKU1Pz8/VdnRPD88scj5MTQ0lIkTJwJYrSf9K6ugY0qpsmba0NGjRwFo1KiRhWKT5MiRg5YtWxIZGQkYyi9qMJa8kfLkyZNkOYQwYnTWrl2rPvvxxx/V3z169KBUqVJqEixdurRFSU0hYk6zeu+991Qgia2QAV/r169nyZIlSSpG0qRJE4oXLw687HzlCH799VcqVaoEGJOxDCbTCjpmZODX/PnzLQJA4WXa4r179/Dw8LC3aAoZfDh27FgVAJgQZCDR1KlT1cO7vQkKCmLp0qWAUezHfGxkzpyZBg0aANCzZ88kn0M+YN+4cUONwfDw8PjygxX+/v6q9afJZGLw4MFJliUmhBD4+voCRrGWxCCLT3399deqsErt2rXVdbMW2get0Wg0Gk0KJM2soIOCglTvU3d3d1q1ahXnvoMGDVLv3d3dAeL8TmIZMmQIYKyMpSnYz89PVeipX78+WbNmVcXhN2/eHG0FfePGDcBId0rq6l6ufmMrTDBz5kzg5Up7z5490fY1f//OO+8AqCL2tuTYsWPq702bNqkKYYlh5cqVdlk5h4aGEhgYqApRRDV5vvbaa/Tp0wcwrBaSxJhHXyWkKyJqkZnQ0FD69u0LGCs9R66gZYGcw4cP8+uvv9K+ffsEfU+Oc+lntBdBQUGqnOX8+fOjrd5lEaVBgwZZpTmFtBiVK1eOPXv2AEZJzaVLl8ZZVlTOieZzXuXKlS3mbGtgMplUb/GKFSuqkp1xsXv3bg4cOKCskNI9ANgk7TTV50FLli9frqqDgTHAZXnPqPuZl/osWLCgyl+0VqnPS5cuKZ9GWFgY/fv3BwxzpsxR/PPPPwFYsGABgKo1DC/rOMuSd3Xq1FFdoxKLNLkcPHgwTp+3vA+iKmchBFmyZAEMs/avv/4KoPzZtuTUqVOA4SYIDg5WfvOEVnzbvn07n3zyCQ8fPgSgQIEC6jrL/5O18PLyYu3atUpBlyxZUpUfLVmyJLdu3WLu3LnAy+5H8PL/mEBemTzoRo0aAcZD6sGDB9WEPmDAAKZPnw4YbhtZWtERSAWdO3duTCYTFSpUAIyHs+rVq6u/a9Sooczhq1evVrEIoaGhSb0PYiU0NNSinoO898ePH8/p06eVUo46zt3c3Pjtt9+AlwsWa3Hq1CnVXfDRo0fUqFFDPQA0btzYoqzoypUrVfe8wMBAVdv+9OnTVqu3b/67SWrUqMHAgQNj3P/ff/9V9c79/f159OiRmt/79u1Lly5dkiKGriSm0Wg0Gk1qJc2uoFu2bKlMJFevXlUBRwcOHABerpYnTpxoVdM2GE+tsnGH+co4PqQp/Mcff6RNmzaqCHv27NmjBcoklj///FM9We/Zs0cFNkhiWkG3bNmS6tWrq9Wyp6dnsmRIKn/88YeF1aN06dJqFZ0pUyZef/117ty5AxgRlTL4JSQkhIiICAoUKAAY/WYTGqCSWEqVKhWteljUv+W1FUIod4y0riSQV2YF/dNPPwFGxbqmTZuq1c7ChQtVveOOHTtaScTkERISQseOHdm8eTMQ/28v/37//fdVIx1rZUT06NFDRRXHhJQrf/78KqI6W7ZsNGrUyKZZGTJKfeDAgWzdujVBpv0KFSqoCorW7FYnA3P/+OMPFaQp+w5AzO5Aua1YsWI0atRIReInozrhq9UsA176kKOWe4yKu7u7KvUZkxncGsjGDk2bNiU8PDza9jJlyiglDoavrVu3bgDJVsZpkcDAQGVKitp1pkCBAqqLj3QLSDw9PZVZ3tpmbXMuX75M5cqVVWnC0qVLU6JECcAoASiEUObM5s2bq05IifytXxkFLeelH374AR8fH+U62Lp1q13aTCYFmblx+vRpVq9eDUT/7WvUqKF8ve3atbO6UqxZs6Yyx0bF2dlZubz++OMPh7WX9Pf3V7EvGzZssHC9ffjhh8rE3rdvX5s3upGdqUaPHq3cDEFBQVSsWFHtU7x4ceWyKF++vLVSqLSJW6PRaDSa1EqaWkFLli9fzpQpU5Q5293dXeVVVqlSxWarZo1tkZaIHTt2qCC7qOa8fPnyqWCyKlWq0KRJE4f2trUyr8wKWpM0Ll26pFbJ5oVHnJ2d2bFjh577Ug6vnolbo0njaAWt0aQNtIlbo9FoNJrUilbQGo1Go9GkQLSC1mg0Go0mBaIVtEaj0Wg0KRCtoDUajUajSYFoBa3RaDQaTQpEK2iNRqPRaFIgWkFrNBqNRpMC0Qpao9FoNJoUSHpHC6BxDDdu3ABQHXhkN6ivv/7aYr958+apUpnmDdQ1Gs1LJk+erBrkyA5VAC4uLrRq1YqJEycCkCtXLofIp0md6FKfdmDLli0ABAQEsHfvXgBVSzo2Ro8eTe/evYFktTRTPH78mMDAQAAGDx7M/fv3ATh58iQRERGkS5cuxu9FRESoTkKVKlXi999/B8DV1TXZMiWW69evA9C6dWv27NkT4z4lSpRQDd9lTW5rIMfJpk2bVKu5gwcPqu1vvPEGvXr1Uu/bt2/P22+/bbXz/z9prtSnfDBctWqVqp0/b968WPefNGmSaj3Zvn17a8mYaE6dOqU6Vq1YsYKTJ0+qB9k6depQr149wGh1O378eMqXLw+87HhlK8LCwoiIiIhxmy27uSUUX19fi/cjR46MdV8PDw/8/f1tLFHiCA8PZ/369axduxZAdcoDo+fDypUrAaMnQDzoUp8ajUaj0aRWUvUK+uDBgyxevBgwGpVLFi5cyP/+9z/1/sSJE2TOnBkwGpXny5cPFxcXAIoWLUrr1q0BKFmyJOnTW8fqv3HjRgAmTpyoVgYx9YWOi1q1agFGd67kmsYWLFigehBHJb4VtPm2SZMmAUavVnty6tQp1e87MDAwzubq8rc9evSo6r2bXJ4+fQqQ4F6wLVq0oGbNmgD07Nkz1uubSNLcCjogIABArTATguxjXK5cOX766SeL3r22Ztq0aQB89dVXak5p3LgxU6ZMIWvWrIDROUoSHBxM/vz5adSoEfByXrAWL168ICQkBIBly5YxceJEgoODY9x3wIABDBkyBEBZIexF7dq1Adi5c2eivufh4QHg0JX07du3mTx5MmBY0B48eEDjxo0B+PTTT5U1snHjxnz11VcA+Pn5xXfYtN/NqnXr1qxYscL4ghAxTtaJ2bZ3717VLDw5TJ8+ncGDBwOGySlDhgwANGzYkGrVqgHQtGnTaN978eIFAN988w2bN2/m+fPnABw4cIBKlSolS6bNmzfTrl07AB4+fKjM5j/++CNVq1aN9XtFihSxUC5S6Wzfvj1Z8iSGU6dO0aBBA+U3BywUdOXKlbl79y4A//77r9rm7e3N2LFjrSLDs2fPAMPdkNgHrW+++YbRo0dbQwytoKNQqFAhZWpO6jESysmTJ9W47tSpkxrD0gVkzpMnTwBo27YtmzdvVq6t999/3yqySNPq2rVrlVk1Kjly5CAyMhKADBkyEBISolxT586dUw8UtsbX1zeaKdvHxweAXbt2qYWIJCaztz31VEREBBcuXOC3334DYOrUqeqBZvDgwbRv397iIWzMmDHqX3k/Fy1aNL7TaBO3RqPRaDSplVQdxd2tWzcVgPXw4cNo29944w0A0qVLp1bJbdq0YdeuXcoMdOvWLbX/999/z7p165IlU9OmTdmxYwdhYWHqM2n2kE9a8bF27VqyZcumVtAbN25M9gq6YcOG6onw5s2b5MmTB0A1d4+JOXPmRPusS5cuyZIjKWzcuNFi9QwwYcIEwAgEK1CggLpW9erVUy4FayLNqps2bVKWiEaNGlGhQgXACPo7efKkWsmbr7IXLVrE559/TpEiRawuV2qnTJkywMsVp+SPP/4AYO7cuezatSvW71+5coXz588Dtl9BlylTxiJCOy4WLlwIGAGiPXv2tNrKGWDKlCn88ssvAPz3339qBf/222/ToUMHChYsCBguO2mVc3Z2pnTp0mq+W7VqFZ999pnVZEoM8a2G5e+dWHN4cpFzzNSpU5kwYQLFihUD4IcffqBjx44xfic4OJhly5YBRtBiAlbOiSJVK+i6dety5MgRAI4dO6Yi6z788ENcXFyU8jE35Tx79ozly5fTp0+faMc7fPhwsmW6ceMGefPmVYOkd+/eCZ445I07e/ZswsPDVVRo8+bNky0XGNclIXz33XcAfPvttwghVFRouXLl4lTotkaa5b29vaOlg0kFaq0YgtioVasWV69ejfZ5z549AVSqzaBBgzhx4gQAQUFB/Pvvv1pBx4CTk2HEc3Z25sGDBwAMHDiQ//77D0D5V2OjUqVKyk/paORD+YwZMxg6dCgA7dq1Y8qUKVY9z5o1a5TCqFixoprfYnJVyYdVuZCRpll7ZmF4eHgos3VcvuSdO3cycuRIC8VsL9/zn3/+SadOnQDIli0bs2fPVrFJch42Rz74dOnShfz58wOGIrc2qVpBw0tbf9GiRROUpztjxgwGDBgQ47Y2bdokW55Dhw4l6XuRkZEsWrQIeBnw9vHHHwNQtmzZZMsVH/KG++mnnxg3bhxgWB4iIiLUDbh69WrefPNNm8sSE2+++aZKY5J+QHN2794NwN9//60+s/cK4enTp+pBQaYPSa5cuWJXWVIjUukk1IpVv359unXrRs6cOW0pVoI4deqUCpzcv38/3bp1A14GVVqTuCwK8HIM+Pn5KcUsYyj69esHoILW7IGHh4fFqtk81SquNCv5XVvy7bffAkYgmJz/J02aFGNcgTlS19y5c0dZM8z90tZC+6A1Go1Go0mBpPoVdEK4fv06zZo1A7BIvwLDrNy/f38AVeDC3rIBjB8/nh9//NFim73MycePH1erd5lGYo5cFRYuXNgu8kSlS5cutGvXTq3kzYmMjGT58uUMGjQIMKK6pUvBHiZluTKZMGEC+/btU5XZonL06FG1ii5UqBDnzp0DwM3NTV3fV5FTp04BUKNGDWXijouPP/6YgQMHAvDBBx+olDpHEhwczEcffaTSrtasWZNgd1JyefToEd27dwfgzJkzgJHJAFjEwUhkHMr//vc/ZamrUaOGikmxNbFl08SGXG1HLXBiLWT6V82aNROUCnn79m2GDh2qihTt2rXLFgWJFKk6zSo+pB+rdu3aseYG/vDDD6oikTUqdsWEnMSlKUQydepUJZcMcpJ069ZN7Z/YmzoxBAYGUrNmTe7duxfj9oiICKVA8uXLp/KKBw8eTJ48eayWZ5xYpAm5d+/eKkhDIlPZYqs2llykO2Dfvn0qjUuaEuNCVheqVKmS8lVXq1ZN+SurVq0anw/9lU+zqlWrljJpT5w4ERcXF4dUtYOXpuSuXbuSPXt2VWXPzc3NbjL8+++/lChRIsZtLVq0sKgeNm/evBjnknz58lGmTBnlOpJKy1pI37L8Oy7MTdrm+/r4+NhMSScE6QufNGkShw8fVuM9Ge5HnWal0Wg0Gk2qRQiREl/Jwt/fX3Tr1k1kzZpVZM2aVZhMJuHk5CScnJxE+vTpRfny5cXKlSvFypUrk3uqeNm9e7do3ry5aN68uTCZTAl+ZcmSRTRq1Eg0atRI/PXXXzaTr3PnzsJkMon06dPH+IprW/HixUVgYKAIDAy0mXzmnDt3Tpw7d07kypVLZM+eXWTPnj3adcuRI4c4dOiQOHTokM3kuH37trh9+3aifs+EvEaMGCFevHgR16kdPS6tPpZv3Lghbty4Idzd3ZN0zYoVKyZGjx4tRo8eHd+prMq0adNExowZRcaMGUXJkiXF7du37Xp+ya1bt0S/fv1Ev379RLVq1US/fv1EQECACAgIEM+fP7fY9/r16+o1Z84c0bZtW9G2bVt1LV1cXISLi4vw9fUVoaGhIjQ01Coy+vj4CAxLSpwvHx8fIYQxf/v7+wsPDw+L7f7+/laRJ7H89ddfomzZsqJs2bKiZMmS4urVq9Y4bILGT5oxcYeGhjJ8+HDASHU5ffr0y4OZVQvr0KEDCxYssI6UcXDy5EnA8G3E5lvLmTOnyqMFVDOLqBG/r7/+Ohs2bAAMf5E1mTNnDl27dlWpGlEbTJhfO0BVbnv8+DFCCFVhZ8uWLZQrV86qskVFRsR+/fXX0Up9SvfE5MmTbR69LdMpYssGSA6nTp2idOnSsW1OcyZuyaNHj9ixYwcA8+fPt9h24MABi3oFUZH3QO7cuVXFtjx58tCkSZMkCRwXMq2zatWqyrS8ffv2FNGl6tmzZ4mKZ5Dpk9u3b+fChQvKDH3z5k01zr28vJItV0yVxCQeHh6qqljUiO2dO3damNvt2Tzj0aNH6hqMHz9e5esvW7ZMpQYmk7Rf6tOcb7/9NtYgL3Mlc+bMGZs69SXmCjpbtmyAUThFKlhPT0/eeOMNCwV99uxZwMibPXDggOrqc+nSJRV08uuvv1o91WnXrl2qxnR8BVFmzpwJGL5f8zrdffr0UcVDbJWLLCfB27dvR9smzx01P9oWSMWc0LzHN9980+J3NufatWsWD5NDhgyJqzxpmlXQcXHgwAH1m3/11VfqgTcupZ0uXTo++ugjwPidrOUXlkGm1apVU8rwk08+4e7du1SpUgWAJk2a8N5771nlfPZEBplVr15dpa/u2LEjxjzgxGJeizsupRyVqD5zW+qroKAgwEjzmzZtmpqPR4wYofzzMhDQCmgftEaj0Wg0qZU0s4IeMWJEglbQgYGBdllBS/7++2+VwiBLxyUUWQS/ZcuW6rMpU6aoYgOO4NKlS4BRvtLX19euna5mzJgBwJIlS9TT7f3793nx4oXqaLR3717VnMRWzJ49GzBKt16+fBkwojkLFCigfivzyNps2bLFGu1+69Yt3nnnHcCITNcr6Ph5/PgxYBTiuHDhAkuWLIlz/4CAAGWitBaTJ0+OlpIoMzKcnJyoXLkyYFSUs4Wp3ZaUL19eRdcPGzZMVRZ0BFG7YNlSX33++ecArF+/nty5c6txGRgYqPTH06dPKVOmjCpUIjvsJYGEjeWEOqvt/Eo0jx8/FgMGDBADBgwQrVu3FnPnzhU7d+4UO3futAgSK1KkiNi5c2dSTmF3VqxYIVasWGERFOPq6prk4x07dkwcO3ZMXLx40SryTZo0ySJorE6dOqJOnTpWOXZCmTBhgsX12bt3r93OfeXKFXH06FFx9OhRcefOnUR9d8uWLWLLli3i22+/VcFGJpNJeHt7x/U1R49Lu4zlxBAWFqYCFUuWLBljINmJEydsLYYQQojjx4+L48ePix9//FHkyZNH5MmTRzg7O4uuXbuK8PBwER4ebpPzynHduXNnceDAgWQfb+7cueralStXzgoSJh0fHx+LIDP53hbcuXNH3LlzR1y4cCHWfR4/fixatGghcufOLXLnzi2uXbuW1NMlaPxoE7dGo9FoNCmQNFNJLHPmzKqpdlxcvnwZPz+/aD1IUwt16tRJ8ndlBaYHDx6wdetWgGQFnMluYY6kd+/eLFq0iH/++cfu5y5YsKBqipIYNm3apMxpN2/etLZYrxSZMmVS7oQvvvgixjrt9kIWrShbtixdu3YFjOBVPz8/5eYaNWqU1c8ru+StWLGCzZs3q/7Y0syeWNasWaP+fuutt5IvoBkySCwlIuezuOa1zJkzs2TJEpW9cuLECVWAyBakGQWdGGIqgZcSGTFihPo7R44cADF24UoossLW999/r6LJ//zzz0T7xs2RqRrwstrOwoULVWcYW5MpUyaKFSvmEAWdUIKDg5k5cyZLly4FjDS6p0+fRtuvdOnSNvPfp2Vka0/ZMCUlIEuQ9u/fHz8/vxgzD6yFfNjbvn07N27cUN3vZsyYofyo8bVBvHv3rmqLa57KJKOtk0vUdKmEIquHyTQt+a8jq4plzJjRptUdzUmVClp2jAoODlY1tmNC5kWbkylTphg/tyY//PCDmixkoFdiKVu2rOpzC0ZZUEClciQFWbN3+vTpql5vs2bNyJUrF97e3oAR3FSgQAHgZbqULG1p3mZx7ty5rFixwiJITKaWNGzYMMkyJpaZM2daPPE7krt377Jq1SrAyJeUpWbDw8MTtFJet24defPmtamMaY179+7x5ZdfAkZwj6RkyZKqO5E9g0IdgRxvS5YswcfHR6WCNWvWTK3ufv75Z959990YFXVAQADt27e3eMgdMmQIgFLwycW8bKevr2+CFawjFXFsnDp1Ss2Jtq6jr33QGo1Go9GkQFLlCtrPzw8wChjIp2Pz6ku3bt3Cz89Ppf0Is9D8ggULJsuPGx/r1q1j6NChSSqiLldcixcv5syZM+opLVu2bMkyQ0vkyrhv374qdeLs2bOcPn3aoo+yNKNny5YNIQQPHz4Eone6Mi9UUrNmTWXWtnXzgvDwcL755hvAsAaYTCZlSUlo0wVbEBwcrPoAJwbZxcxR3cJSGpGRkarBjKzaFBkZqbbL+3HQoEFcv36d7du3RzvGsGHDVBOc5CLTp+7evUvx4sWB+Hv/Snm3b9+Oi4uLRaqkrWjYsCGurq6qb/qBAweU7M2bN8fV1TXGrlVhYWHKogbg7e2tTMkJ6fCUEKTZvHbt2owcOVL1tK5Vq1asq+SYKpBZy+QORtEZ6XpIaH/soKAgunTpolLnbKlLIJUq6HfffReAVatWKfNO0aJFVa7j+vXruXz5soWfQP4tJ3Zb8eLFC4QQKk93zZo1Klgjto5RAL///rvK55S5xtJnPGHChGSZtqPSr18/dbzx48dHK59nrojNlXBMSLN2p06d6NixY7Jle/ToEWB09zIP1rh+/brq7rV+/XqOHTsGvPxdW7duDaAqojmCxHTPkpqmFN4AACAASURBVOVIPTw8lCKxUgnBVM/MmTOVkilfvjxhYWFqPMVHhw4dAGLt8JQUZOe0ypUrq0pSVatWVfdn3bp1AVS53Bo1aqjArUmTJlG5cmWrd4iKjfLly6sA0JEjR3L37l3AqEB448aNWF0tjRs3VoGz/fr1s3otgZjKeMp/pRKW+8TW8crDw8OqJu/AwEAV87Fs2TJq1qwJEK2FaUREBMePHweMB4QnT56oKo+2Rs8IGo1Go9GkQFJlJbFNmzYBRg1cGQ0rojR1sDiYEMqk6+3tbfOVypgxY1Q6hYuLiwr8SegqAIy609OnTweiN7CwNgsXLlRug6gyxrWCrlq1KsuXLwesZ9aWQSx3796lVKlSgGH6f/bsmTJtwku3Ra5cuZg9ezaenp6AY1ehp06dirMGs3THfPPNN7Rr1w5ItLyvRCWxzz77jEWLFiV4f2l2btu2reqtHZ8JOimEhISoFCZzq1NAQABvvvmmhZtI1q/u0KGDGseO5ObNmzx79ky5/cwpUKAAffv2tXnAk6R27drx9oWOiq0aZcjeAnPnzuX69euAYbauW7euSjEbN24cGzduBAwLxcaNG60x36X9ZhkrV66kc+fOgFH+LzYFPWLECGUys1fXmfHjxwMwdOhQ5a8wjzKV5M+fHyBa+U57DpiURPXq1QHYt29ftI5V5sgBsmXLlhTTmODUqVM0aNAAMKKIZSpbkSJFaNeunfp/JKOZyCuhoO/fv0/btm0B4/eNjSZNmvDxxx8rP2CRIkWSKKLGUfj6+ip/dFSl7ePjo8zets6dfv78ubrXNm7cyPXr17lw4QJguBplqd5PP/2UnDlzWuOUulmGRqPRaDSplVS9ggZUrvCIESNUwYL8+fOTL18+Pv74Y4C4+utqNKmJV2IFrdG8AqR9E7dG84qhFbRGkzbQJm6NRqPRaFIrWkFrNBqNRpMC0Qpao9FoNJoUiFbQGo1Go9GkQLSC1mg0Go0mBaIVtEaj0Wg0KZBU2SxDo9FoNIlj2LBhqmTl+++/r8rNVqxYUTX60KQs0lQetHmnk127dsVY79XDw4NatWrZrYRcXDx8+FA1Vx8/fjx//fWX2rZixQqb1+AGOHnyJKNHj+aPP/6Itu2tt97itddeY9CgQQCqnaQj+O2335gzZw4AXl5e9O7d22GyJJaDBw8yY8YMli5dCsDXX3+tarUnkjSdBy3bqx49epQhQ4YARu3jWrVqUaFCBQBy585tkzrbrwJPnjxRbSV///13Nm/erD6vUaOGKk9s3kXuVWXTpk2qVefWrVs5ffo0ABcvXmT48OF4e3sn9xQ6D1qj0Wg0mtRKql9By1VyUvutenh4qCbg9lxNnzhxgkGDBrFt2zYAsmXLpvo/nzt3jqtXr6oVV9OmTa1+fnnepk2bqhKpkrJlywJGD+Zbt26pzxs2bMjChQsB+zUdkTRo0IDt27cDRgco2UyhUqVKKXI1vWvXLnr06AEY/b1l1zWAdOnSMXbsWABlnUggaXoFPWPGDADV2EYdwKxTXZ06ddQKr169enTt2tVackYjIiICPz8/Vq1aBaCsXRJpUWrevDnu7u5W6+hmS+R9OGvWLAYPHgzA/Pnz6dOnj+p5LBv42Irw8HA1/6xbt45Zs2YB0KpVKzp37kzDhg1tev6YuH37NmvXrgVgwIABqi89GPdcixYtAEP2ESNGsGHDBgDVQzsJvBqlPqVZWzb9Nie+BuBR97NFO7Oo7Nu3D4DevXsTFBTEl19+CUDPnj3JkycPAFeuXKFw4cLKjCInc2tx584d1dbRZDLRr18/vLy81HbZHer69esEBQWpVpQbNmxQ39uxY4ddlfSECRM4cOAAgBpIkrx586r7oGTJkqojlr25f/8+Y8aMAcDPz8+iC5ezszPPnj0DDIUjO+JMnz5dDf4EkKYVtHwQPXjwoHrgPnfuHMePH4+xo1nGjBmZMmWKehCyNqGhoWTJkoUMGTIAxoOhbL1qMpkIDQ1V+7733nvKv5svXz6byGMNpGvF3B2YLVs2OnfuzOTJk21+/nHjxvHnn3+qsRyVihUrcujQIZvLIQkJCQHgiy++YN26dQCUKlWKPn360LJlS8Aw+cu2sOfPn6dYsWIMGDAAIDnXTJu4NRqNRqNJtQghUuIr0fj4+KiXv79/gvfHeMIXxqWwLXv37hU5cuRQr0OHDsW6LyC8vb2Ft7e31eW4f/++yJ8/v8ifP78IDg5O8PcWLFggTCaTMJlMYs2aNVaXKz7Cw8NFeHi4GD16tChevLgoXry4cHJyEiaTSTg5OQknJyeRI0cOsW7dOrFu3Tq7yubj4yPee+89dX0A9XeXLl1EUFCQ8PLyEl5eXupzk8kkqlWrlpjTOHpc2nQsDxw4UAwcOFDs2bNHffbw4UMRFBSkXjt27BC1a9cWtWvXTsr1SxTPnz8Xv/32mzh+/Lg4fvy4uHr1qggLCxNhYWFCCCGWL18uli9fLtq2bSucnJxEr169RK9evWwmT3I4e/as6Ny5s8icObPInDmzMJlMah4aP368uHHjhtXPGRkZKSIjI8X169dF9erVRfXq1UWGDBmEk5OTcHZ2Fs7OzqJXr16iXbt2ol27dsJkMglXV1eryxEbt27dEmXLlhVly5YVOXLkEMuWLRPLli0TT58+jfU7U6ZMES4uLtaYYxI0ftJMmpW5ySYhSLN2TKZxayN9vOa+0vXr1/PBBx/EuL+tTU3ZsmXj6tWrif7exYsXyZIlCwBlypSxtljxIqN3hw8fTqtWrQDo06ePRfT7gwcPmD9/PgBNmjSxuUzS3D569GiEmbuocOHCqgF80aJFSZ8+PYULF7a5PKmZSZMmRfssS5Ys6p4DKFCgAHv27AEMl9SRI0c4evQogIr0thbp06dXsQ4xIU2gnp6erFixQvlwHcnff/+tfOarVq2iRo0aAFy+fFldNzDMuDKeJLZ5KLk8f/4csDT558qViz59+jB8+HD12bhx42xy/vjInj27mpO9vLzijF7/+++/ASMDo3z58naZW0CbuDUajUajSZGkmRV0Yklq1HdSkCvoy5cvM2XKFACqVq0a6/6rVq0ia9as9OzZ0y7yxYeMspwwYQIlSpQAjBxpRyKD1apXr26xggYoVKiQ3eQwD/xKly4drVu3BuDHH3+M9kQu9zUnY8aMthcyDTF8+HB++OEHwAjUatKkidVXzglF5sYOGjSIFy9ecPPmTYfIIfN1fX19mT17tkVAnZTxv//+o2DBgnz++eeAIfPrr79uU7kmTpyo/paR4Rs2bFBBqFFlBPvOKxkyZKBLly7x7nfixAllsStWrBgrV660tWiKV05B+/r6RjNr2zq9Knv27IDxQ8tI7ZhYtmwZAMePH6djx452VTSxMWPGDBVN/uTJE7uZdmJCmuXnz5/P4cOHAcNVYE7r1q2TWgQkSciI8eHDh5MtWza++uqrGPf7448/mDt3rnovf9uhQ4faXshUTlhYmMp2WLNmDWFhYQC4urqq1Cx7IYupzJ07V/129+/fp3z58jEW+7EHnp6eABw7dgwAFxcXwLg+srjPP//8w7Zt27hw4QKAzZXzX3/9ZeF2/OWXXwCiKefbt28rk3yGDBkYMWKETeVKDPJBcMSIERQoUACAzZs3U7BgQbvJkGYVtK+vL7t27QLiTrPy8PCwS3oVxJ1feP36dbp37w4YE5J52pOjGDZsGNOnT+fhw4eAoYxkeUB7cuvWLWbMmKF8y1euXLHYnjVrVj788EMA5syZw2uvvWY32fLmzQsQ40PBkydPAMM/PXHiRDW5Z8yYUU1e9evXt4+gqQCZezpnzhz1sHr9+nWeP38e4+o0PDycv/76izZt2thNRul3Xrt2raoX8MMPP9CuXTuVimNvZDzIkydPOHPmjMW2Bg0aAJAjRw7c3NwICAgAjAptcmHi7OzMZ599xptvvglYp8bBxo0b1f1etmxZ3n///Rj3a9GihXrgGjBgAI0aNUr2uZPL9u3bLdI6fXx81Eo7W7ZsdpVF+6A1Go1Go0mBpPpCJTERU1GDWE/kwP9/WFiYMu/07t1brVR79erFtGnTHCJTcHCwqpC0bds2XFxc+Oyzz4CXZip74+7uHq2Kk1ytfPLJJ/Tv3x93d3dHiGbBixcvuH79OgALFy5Uq8B//vkHeOk3/+6775SvOpGkuUIlcvU0YcIE5XqKOn6FWSWxmD6XK+gJEyYoU6Qt2LJlCx999JE6tyyoUbFiRZudM7Hs27ePc+fOAfD222+rz999910A7t27BxjR8fL/cujQIYQQNG/eHEDNScmhYcOGyjJ5+fLlaK496Zro16+fivZetGgR7du3T/a5k8L27dtVQabt27fz4sULlTWSL18+NV47dOhA6dKlrXHKV6OSWEwkRkH7+PgkOkXLGkyePJmFCxdy8uTJaNu8vb1VRSp7s3jxYjp27AgY1zFdunRqAly8eLFDZIpJQcvgKlnRR5ZrdVQjhdOnT9O9e3f27t0LxKxUTpw4AbycLJNAmlPQ8uFv8eLF6mE56nXLmDEj3377LQDFixdXn48fP16lWIGhkGQ5WFtU87p7964Kkrxz545ypbRq1YohQ4aobamRmjVrqjG2Zs0a5TJKKh9//DF3794FsEjvAmOsyIca8zLDzs7OZM2aVZX6HD58uPL3Sr+6tZENQoYNG0amTJkAI+WqePHiam4+c+aMKnl8//59GjZsqJq5xBXsGw+6kphGo9FoNKmVNLmCBsvCJTGtkM2f0h1xDby8vFi9ejXFihUDDLOKjAIdNGgQQ4cOVRGN8snOHty7d4++ffsChon7yZMnyvT+3nvvqdQvGdBmD6ZPn46fnx+XL1+Ocbv5atXb21tFUsugF3swcuTIeC0xclWyYsWKpEbRprkVtFyBHDx4MMYV9Lfffounp2eMqVR37tyhV69e7Njxf+ydd1hUx9fHv6ugxCBoROyKURQ0QTFqNDas0WDFkhi7UYk1xhY0KvZujMYau1hjL8QWK7GLP6MI2Cs2UEB6nfeP+85xl7rANvB8nodHdvfuvcfLnTkzp56k17LoRnq1nnNKWFgYAGXnf+nSJQDAq1evUKJECWoPO3z48Fy3m/7hhx8oCPObb77BgQMHACDbgW/Pnj2jwL60AsSWLl0KQGl3Ky1Lt27dQmRkpMZxNWvWBKCYzDt06IC6desCyJqVNCNmzJgBQMm0cXV1BQBUrFgx1XGyZveDBw8wbNgwCrZzc3PDunXrACCrwakfrolbG9SbbEjzqCFN3eHh4fDx8cGXX34JQImclHmy9evXx7Vr10hhZ6GZgs4JDAzEoEGDACimKjlgZEqHoYiJiSH/7uLFi3H27FkAitk4pTlZDuJ9+/YZrMNQXFycRgORkiVL0j0aPny4RuT5iBEj8Pvvv2fnMnlOQcsKTfv370dycjIATZ9fqVKlqEFFZudo27Yt+bT37Nmj95RAmfY3fvx4/PXXX+RLtbS0pBSsbt26UeyBKaOuoIH3CsmQi1w/Pz+cOnWKmo7ISnzqyE2LISpApkd8fDxtVNatW4cOHToAUOabLKDdWNa2JqiBf/SOei1uFxcX4eLiYojLaoWUS1+1uLPLX3/9RTV0P/roI+Hp6Wk0WcLCwkRYWJgICgoSW7ZsEfXq1RP16tWjmtz58uUTDRo0yFKtcX1x/fp1YW9vT/W3CxYsKHx8fDRqTmuJscelSY5lycKFC6mu/vDhww15aRESEkLjtUaNGvS3dnFxEbt379brtUNDQ0VoaGiOvq9eR97a2jrH58wJCQkJIiEhQaxYsYLuqYWFhVCpVMLMzEyYmZmJI0eOGEU2iZx/2rdvTzItX748K6fQavywD5phGIZhTJA8W6gkM2QREyDzftGGxhQqiKVF165dkZSUBEDxk0+bNo0qabVo0cKgssiCAdbW1vj++++pwEG1atUo4vLChQsICAigYiLGokaNGnB2dsa9e/cAKCYy6ctkdMfLly/J1aEe3W0IihUrRn3bJ02ahH79+gFQKsjdunWLynGqN8zRBT4+PlQkJ2XJ28yQz2C7du1w48YNunfNmjWj6ofGwMxMUUvqfb5r1qyJAQMGUA/ulMWKDI2cfzZv3ozq1asDUOJfdF2e+YNU0KdPn9ZQytIHbSrcvXvX2CKki0y5qlWrFmrUqEF1aQ2toFNStGhRAEq5QFPj1atXFFQCKD5KOaiZnPP06VMAwIYNG+g99RxgQ1OoUCFs3rwZgOJC3LVrF6XzNG/eHI6Ojjm+hvR3b9iwIUud6eT3du3aRXW5ZaqTTHkzRtppZnz77beYMWMG1RTYs2cPBg4caGSplHxyWTdiyZIlOj8/m7gZhmEYxgT54HbQTZs21dg9u7i46H3FKM1tdnZ2GfYclfj4+JhUN6u0KFeuHKpUqYKHDx8aWxQAgK+vLwDNjlFWVlawsrIyijwJCQkUqb106VLa5QFAz54900zl+JBR7+uc1Z2RrJP85s0b6h39008/6VbALCItOZs2bUJMTAy8vb0BKNHH27dvz/H5ZWrP+vXrYWtrCwAICgpKVe9f1sO+fPky5syZg9u3bwPQtNKpVCosWLCAzO+m2mHN3d2dUkCN0RMgLUJDQyllTB/kOgWdUplmpFylIj59+nS6Haz00SgjMTERW7ZsAaA0I3/06BEAJecuvW5HcXFxZAYzpW5WKZH3dObMmbhx4wbGjBljkOvGxMTQfUzJr7/+SqkZCQkJpJTd3Nx0VoZRTnQvXrzIsJuNjG3w8PCgPFmJVDzGalBvynzxxRfUlSk8PFwjzUr6JFMSEBCATp06UWlLAGjfvj0AUCMLY2NhYYGuXbuSgpYLyZwSHh5Ov8uYiylTpqBt27ZUAcvX15fyik+cOJEqHVGOE3d3d4waNUoncukT+f8ElHGmi7KgMoe+Ro0atLjTZoEiUwL/++8/8uXr45nLVXnQKVtFuri4oEmTJqmOO3PmTIaBX9qU9zx9+nS221AuXbqUVnrqg2Lfvn00gUj8/PwAKJO3nNArVKiAM2fOGExBHzp0iPKKpcxA6mIAXl5eCAkJAQAkJSWhUqVK2L9/PwDoqj5tuly+fBn169dP87OUE4/0CcldRk6JiYlB48aNASh/LxsbGwDAuHHjaKL877//cPjwYdrBJyQkkEw1a9bEtGnTqIRhegpHC/JcHrQ6crJU7/Dl6uqKVq1aUbGLHTt2UD78zp07Nf7ubdu2pXrKxvBBy3ETGRlJk718LQuovHr1ikpq5iQ/Wo7Xnj17avigU46FlPLJwkht2rTBr7/+CgAGqxWQFitXrqT64Bm1Xr106RJcXFwQFxcHADhw4IBO8txl/fHDhw/TvalVqxa6dOlCgacp41oiIyOp9OyiRYuokNSBAweyEovDpT4ZhmEYJreSq0zc6qlRQOpo7IyQkdouLi5a7Yyzu3sGkKpzi6R3794a5hMhBEVQRkVF4fvvvweg9BY2pHk7NDSUmpMnJCSku4NWX52XK1cO3t7eBtup5M+fn8pjpiwHWLBgQSqm7+HhQZXPdMWLFy/I0hEXF4egoCAAGfs569atSwX127RpY9ByrbmVBg0aAFBMrqtWrQIAeHt749ChQ+nuCm1sbOhYZ2dnVKhQwTDCpoF0g3z55Zc0BzRr1gx2dnb0fIaGhpK88+bNy/a1pEXnwIEDWLZsmcZncpf56tUrjXmkX79+cHZ2BmDY8sEZERwcTNbMxMRE9OnTh+5VVFQUFi5cCAD4888/kZiYSLtZXfV837t3LwAgJCSEfMmXLl1Cx44dqVxryjkuJiaGUtpsbGyoQYuTk5NOZFInV5m4T58+jaZNm2p1AnXzt6HTBmJiYqjV4Llz57Br1y4AiuKWQRqAovDkQPHw8CATjzFShdauXQtAWchIBZRyUmzQoAGVHe3fv7+GGc8QvHnzBoBSl1emW3h6eqJevXppujp0yfTp0wEoQTnqvnA7OzsA0DCJAdD6Oc0iedrELYmLi6NSk2vWrIG3t3eaec2TJk3CkCFDKEjK2Mi5dOPGjZgwYQIAJTdbfWErhMDw4cMB6CctJ7fxv//9T6NWd8GCBam8qMwdl5QrV47mKX2ndd68eZMC6fz8/ODv7w9AiXuIj49Hr169AID+ztmATdwMwzAMk1vJVTtohvnA+SB20HkBGci2YcMG7Nmzh6K3mzdvTsV9jJUCaEokJSVRJTRppVJHuj3atWuHvn37moy1RAdwNyuGyWOwgmaYvAGbuBmGYRgmt8IKmmEYhmFMEFbQDMMwDGOCsIJmGIZhGBOEFTTDMAzDmCCsoBmGYRjGBGEFzTAMwzAmCCtohmEYhjFBclWzDObDQQiBly9fAgBWrFih8ZmXl1e6vaFXrlxJzTLSa7DAMAyTG+BKYnpCdpQJDAzE5cuXASiF4Tdv3gwAiIiIAAAqXTdhwgT06NEDAKjfsKkRFRWFsWPHAlCU5tixY6mnrLW1tU6v9ejRI3z66aeZHmdmZgYhBJKSkug9WVC/X79+OpUpp9y8eRMAsHXrVsyZM4fel4X3N23alNkpcuOKQ69jOb2+7QcPHsSdO3cAAKNHj9bJtUJCQjB37lwAQHh4ODVQKF++PHx8fNCoUSMAQMOGDTFkyBCdXFMboqKiAACxsbFISEigDlCHDx8mGSVyvi9RogT1lDZG72xTYdGiRQCAOXPm4PXr1wCUhb0QAo6OjgCUeyabHO3atQtubm66uHTeLfUpJ7rbt2/D29s7/ZOk0zaxaNGi1OzbyclJ5wrx/v371DpSNmfXBtkKbtGiRdROzpjExMTg2LFjVDv41q1b1FVI3tOKFSsCAK5du6ZTJf3u3Tt07NgRgPL3LlWqFH744YdUx9nZ2SEqKgpjxowBoHQPGjBgAAClRZ0pEBISgsmTJ5MCjo6O1vjc3t4egPKsZFKfmRU0QO0aL126hG3btmm0cJVIZQUACxYswKhRo3J83REjRlBLwsz4/fff6Tv64N69ewAUJSyfqwcPHiAsLExj3pMbAKl85GcVKlTA+fPnAQClSpXSuXxRUVGYPXs2AGDmzJka3bykApQyyt8dHR015j0bGxvqFqWr9pLqBAQEoHr16qnkkL+nlBkAatWqlaU5PQO41CfDMAzD5FZylQ86JCQErVq1ItNVyp1IStLbQQPvV7jFixdH2bJlAQDjxo1Du3btAGR/xXbjxg3UrVsX8fHxAIAyZcrQDl2aMtUJCQkBoPQZ/t///gdA6XUaHBysc7Oxtsgm5vPnz8fFixfTPObzzz9HgQIFqEtPbGysTuW1srLCyZMntTr2+vXr5FIwFRITEzFjxgwAwLp16/Ds2bN0jy1evDgA7m6UHpcuXSITo4eHB169egXg/fiW/YNbtmxJ34mOjsbOnTvp+7ogOTmZfq9bty5d799//0XDhg2xfPlyAEBoaCjGjRsHABg0aBAsLCx0cn11pHVImrMBoHTp0qhbt66GtUCOT+mKqlmzJgBg+fLletk5S2bPnk07aJVKpTEHV6tWje5l9erVyUJ3+/ZtBAYGprlzTavTVU5xdHTE6dOnSUbpogCAI0eOoE+fPgAU64Mco1q4oXRKrlLQSUlJCAkJSVcx58+fH4DilzQzMyMTl7m5OWJjY+kc6gQHB1Nz+O7du9Mg+/HHH7Mlo5WVFQoXLow3b94AUNrNNW/ePNPvVapUCQMHDgSgTO7G5MCBAwCQpnKWk82yZcsQHR2N1q1bA1Ca1MtJydAsWbIEoaGh9NrV1dUocgCKWwBQnqX9+/cDyDxYzRTcGaaANMOeO3eOxuSiRYvw6tUrhIWFAVAm7cqVKwNQFomjRo1ChQoVAADlypWjcwUFBZGC3rlzJ3bs2JFj+WbOnEkmV1tbW5iZaU6fUv7Vq1eTafnZs2ckry6R/lEA5NKZOHEiypUrh5UrVwJQXAEPHz6k4+bOnUtKRyocfWFhYUExNSmVWkBAAI4cOQJAUcrqblYhBMlWq1Yt/PTTT3qVU33syWdu9uzZ+P3332ncNmnSBL/99hsAzftuCNjEzTAMwzAmSK7aQZcoUQKrVq2iFaJ6sBcAfPLJJwCATz/9FHZ2drSDyZ8/P/755x8Aym4rI+bNmwcg+ztoOzs7eHt7kwk9s0hkuTM4d+4cvVeoUCGjpghJk7yrqyuqVatGpvnmzZtj/fr1ABS5J02aRN+xs7MzuJyS69ev0+9WVlaoU6eO0WSROwNphdAGY1keTInTp09j8ODBAEDmbEnVqlVp59qkSRPancqdc1rIICoAGDp0qE5ktLa2TteNs3nzZmzdupVeW1paAtDfuOjduzcAZYwWKVIEAFCgQAH0798fGzZsAKBYbmTWxahRo2BjY4N8+QyzJ5s4cWKa7589exZ9+vTB48ePSUY513Xu3Bk2NjZkSaxVq5be5ZRy+Pj4YObMmQCU569QoUL0zMl/jUGuUtAA0KZNG7Rp0ybDY3x9fdGuXTsapFnxT1atWjVH8gGKf0p9sKbH6dOn0bdvXwDAkydPaFBv3brVqP7I+fPnp/m+p6cnDh06BOB9brIcRMYyKx89elQjJ7pjx44oXbq0UWQB3kdkFyhQgKKLa9SogaSkJA2XgXTHjB49mn3P/49UNF9++SWZOUeOHIkqVapQnEhGREZGkq91586dpMBr166tJ4kVDh48CA8PD0p3Kly4MEWapzSD6wr5/MjFCqDUB9iwYQNKlCgBADhx4gSqVauml+tnhc2bN1Oq2+vXr6FSqTBy5EgAgIODA9UtMDQzZszAH3/8AUCJBVKPWWrcuLHR5FKHTdwMwzAMY4Lkuh10RkjTzuDBgxEXF0c70sKFC6NVq1YAlFxeZ2dnHD9+HADQrVs3Wq1XqVKFjtMHCQkJePv2LQBg1qxZWLlyJQWyFS5cGF26dAEADbO9KSBNjjdu3KCdgUqlQqVKlTB58mQAwMcff2xQmaQpuUePHuQmAED30Fh89tlnABTrQpsijwAAIABJREFUyPPnzwEAW7Zsoch4icx5Vy9Y8iHj4uKCCxcuaHWsHEMymvv+/fsAlIjmM2fOAFAiu//9918AmbuZssO7d+8wZcoUAMCaNWsQGRmJunXrAlAsUOoRwYZEpVJRIJgp7J4BZXcqs1WkSTsgIACAcc3H+/fvT1WcRHL48GGyTjRq1AidOnUCoMw3+g6wUydXFipJD6mQo6Oj4eLiQtGD2pjH9Ik0sbu7u2tENKpUKvLjjhgxgnzoxkYq5JUrV8Lf359MsxEREfQQe3h4YMyYMZTiYggCAwMBKBHk0oWgHr0NKBP9L7/8AgBo2rRpmkUs9MWhQ4cocnj37t2UapcyKt/Z2RmHDx8GoGmi1IIPslBJWFgYmY8nTJhAz6es0JcWZcuWxZMnT3J66VTIdLnWrVtrVOnq2LEjVbArWrSozq+rDV5eXujbty8983PmzEHPnj0BwKDjNCPOnj2L0aNH4+rVqwCUOVDOiW5ubnopSJIegYGBaWYE+fv749y5c7SolmZ5QHHpHT58WBfFrfJuJbH0kAra0tISAQEBRhsoKZE7qXLlyiHl/XZwcEh1vL29PX799Vfy7+rLj6XOgwcPAACLFy/Gtm3bALzP0VZHDvQ7d+4Y9P5OmTKFUuDSkistRo4ciaFDh6JSpUp6kSk+Pp528uPGjcP9+/c10vjSy8O/ceMG7bSzyAenoJOTkzF48GCsXr1aOZlabiygjHXp9//6668pL7hgwYIUL6GrNLY//viD0m1kcBEA1KtXD6tWraIdq/QPG5rQ0FD8+uuvFESrUqnIemBpaYkff/yRFGCDBg30YlnQhpCQELi7uwMA9u3bR+OkU6dOmDFjhsFTmTLj7Nmz2LdvHwDFnx4SEkL+6YEDB+KLL77Izmm5khjDMAzD5Fby1A5aFgQ5deoUypUrR/7Rrl27mkSk7Nq1a6kC0OvXr/Hq1SsqoJIWMgp13Lhx+O677wDox3x2+vRp8rGEh4dr7PwqVKiAbt26AVCqDzVt2hRA1tKIckpkZCTatGmjkYomd/IpUzFevHgBPz8/el22bFkyiX700Uc6lWvevHnw8PBI9/P0dtDDhg3LNN0vHT64HXRQUJBGARL1HfSECRPw888/a5hv5e5xyJAhVBVQplvmlGHDhpEVR50mTZogLCyM4jBq166NevXq0edubm4GdbXI4iQrV64kl4vc8ctn0tramooO9e3bFxMnTjR4HAkA7Nmzh1JaQ0JCYGNjQ1YKaZ43JXx9fTF69GhqNGJra4thw4YBSD+1LB0+PBO3TKuqU6eOhqKpXLkyPQQ1a9bUqrKXvgkODsarV680UsCk8r579y6VoJM0adIEgDLZ6Hqx8ejRIwrW2LZtG+VuTpkyhQJOAKWZg8yrtLe3J7OjvvHz84OTkxMF4YwcOZJKFqZ0EQQHB+Pnn38GoPiB4+LiqBrRwoULdZIHKvOu69evn+ECKz1q1qxJZV2zyAenoAHlmZflOmfNmqXVd/r06UNmyfDw8JyKAEBR+ilbn2qL7KzWs2dPNGvWTCfyZJXly5dTtaxly5alchXJqoBDhgyhMqYFCxbUu1wyVmD16tWYOXMmxWUMGzYsq0rPYMgyvnPnzkVkZCQARUFnoSQpm7gZhmEYJreSp3bQkhs3bmDevHnYsmVLmp/XrVsXnTt3BgD079/f5PovJycnIzw8nHpH//bbb2SiKly4MP0uCzvoAhkkdv78+QxNS7KwyvPnz3Hs2DGdXT8jYmJiEBgYSEEt2jblaNasmYYlIi4uDubm5jmW59SpUwCUoKSUEdqy/eaIESPoGVuwYIGGSbtGjRq8g/5/ZGGgX3/9lapj6YIHDx6Q+0M9DS8nJCQkUPTxrl27NKK47e3t6Xo+Pj4UGHrr1i2NRin58+enGtWLFy82WkOc0NBQatXr7+8PLy8vBAUFAVBcMrK168SJE1G+fHmDySWjvAHg6tWr5M7YtGmTwaO8tWHWrFk0T9++fZvaDHt5eWX2Ve3GshDCFH90wvbt28X27duFs7Oz+Pjjj8XHH38sVCqVxo+tra3Yt2+f2Ldvn4iLi9PVpXVKYGCgKF++vChfvrxQqVTC09NTeHp6GkUWOzs7YWdnJ6ZPn26U62eFZcuWafyt4+PjdXp+FxcXUbduXVG3bl0xePBg4ePjI6Kjo0V0dLTGcRMnTtSQw87OTrx580a8efMmq5c09rjU6VgePny4MDMzE2ZmZqJTp05ZvRcZ8uzZM2FhYSEsLCzEqVOndHrurBAZGSmOHTsmunfvLrp3767xHLRq1cpocqXk4cOHYty4cWLcuHGiYsWKIl++fCJfvnyiVatWIjY2VsTGxhpMluDgYBEcHCwmTpxIcqhUKuHm5mYwGbLC69evxevXr0WvXr0ElAWpmDhxYmZf02r8GHvw6lVBq3P9+nVx/fp10b59e/HRRx+lUtQqlUoMHjxYJCYmisTERH2IkCO8vb2Ft7e3UKlUokGDBqJBgwZGkaNNmzaiTZs2ombNmjo7Z3x8vIiPjxf+/v46Od+dO3fEnTt3RMWKFYVKpRLu7u7C3d1dJCcn5/jcjx49EmPHjhVjx44V48aN0+o7ffv21XjOcnDvjD0udTqWrays6J5MmjQpu/dEgwsXLogLFy6Ibt26CWtra2Ftba2T8+aUuLg4ERcXJ9avX0//5xIlSoh3796Jd+/eGVs8IcT7cfj8+XOaY/LlyyfmzZsn5s2bZxSZ/P39hb+/v3BwcBAqlUo0btxYNG7cWFy9etUo8mREcHCwcHR0FI6OjiJfvnyZzWdajR/2QTMMwzCMCZKnSn1mRI0aNQAoEaFhYWFU0EB2MAGUtIT69esDAHVwMgXi4uI0fBrS9/Xw4UPyeeoTmbbUvn17SuGQJT9zyoIFC6hg/ahRo3RSpECW+5RNNGS6nS46hPXr14/82ubm5iSv9M1L3r59i0WLFgEA+aiY92zYsEGjipOM0M8KL168QFxcHGVvrFu3jgrHxMTE0Bg3BWSalXrjjtevX1NGgLFKg6oj4zNKlSpFRZ8A7QsD6QM5vnx9fTF79myar11dXbOb3qQ3bGxsqCpa3bp10bt3b1y5ciVH5/xgFLQ6RYoUwdSpUwEAP//8MynvoKAgqhBjTAUdGxuL58+fU5DblStXqCoSoDSqBzIvYRoREUFpIQMHDsxWDnVcXBwWLFgAABrN33VVlvTdu3d4+vQpAGUQZocHDx5Q7uTu3bvx5s0b+mzQoEEoWbKkTuQEoBHwk5CQAE9PTwDvS47evHkTAHDy5EmNlnoAqKKZ/M6HzOXLlzWqrg0YMIByjFM+1wcPHqRys8nJyZQq9+LFC8THx5OCdnBwwLRp0wAoqWwNGzbUqcwxMTEUSFWhQoVsBRzKlBxAUYgyF9mUuHfvHtUOEELovH5AdihUqBCmT59ONez79OlDgZe9e/c2aCBbRsixrqt2wWziZhiGYRgTJE+mWWVGXFwcYmJiAABJSUm0g5apEYCyUs8J0rRRsGBBODk5pXucNFfny5ePdl8LFy5MtxFAwYIFaXWb2apx3bp1GDBgAACgdOnSVFEps9qx8j74+/tj7ty5OHHiBH02btw4ALrrwjR58mRK+lepVJQ61q5dOypGkpItW7ZQWhigNKOIiIhIdVyNGjXwzz//6LRRwPfff4/t27drdawcWyqVCpUrV6YiGznouJVn0qxkV6Msn0wIst5ERkaifPnyVAN7zZo1eu00NGnSJDKxOjk5kRyenp5USCgt4uLiyALQo0cPGl/29vY0lrNLTEwMZs+eDUCxkqlXXcsqsptY7969aXx17NiRLHmmtNuvU6cOWdw2bdpkMlXHpFtr9OjRmDZtWkbmd60e/lxp4pYPkrq5EXhfbUu2nJMT5J49ezT8XU+fPiX/pK6qDKnj5+cHV1dXAEDnzp1RuXJluu7ff/9Nx3Xp0oXMeiqVSkMWlUpFOX9ffPEFOnbsCEDxA2trznF2dsZXX30FQMlv7tChAwBg6dKlpPxk1TA5aaxbt4668qg3BACAsWPH6rU9ohCCTMWbNm3S6PylLZ999hn5IYsVK6bzSkidO3fWWkGrs27dOp2bXHMzZcqUQVRUFD3zFhYWKFy4MABQtav0kM/x4cOH8e2332Lw4MEAlOYlcuGtD7Oseue0Gzdu0O/37t1L1XhCfXEWHh6O//77jz6TlbLSq9OQFe7cuUOLhjp16mitoCMiIjTae65cuZIqr6lUKsrhX7t2bY4V8+PHj8mPXb58+WwtoqKiohAYGEj/V19fX3z99dcAdFcSVJbvlF3z1HFzcwOADGtmzJgxg+JpVCqVTnzjbOJmGIZhGBMkV5i45U5SBoek12dX7pZkfWv1Vay2yMADGSGYHTZt2pQqqlcbZIH9KlWqoH379rRqywmyzu2AAQPwzz//0PslSpTQ+FfuDp4+fapx30qXLk21y//44w+d1wFPSkqiXc/8+fOpT7Ks2JQZ/fr1Q9myZal9na2trV7bcyYnJ+Pbb78FoASkZYS8jxUqVICPj0+OzI//T54xcQNKQJ20dlWqVIlacK5YsUKjKUVsbCyNeSFEhuNZ1txPq6lFTjl79ixGjhwJQKkQlpCQkO6xKece2YKydu3aWLp0KYDMXU3a8OTJEzRo0AAAEB0djfLly9O1v/nmG5o75bMqP4uLi8OdO3c0ziXnz/Xr1+Obb74BALJq5IQSJUrQDvqnn36igM702LNnDwAlelz2ZH78+DFu375N8lerVo2yKXRVCVJGjMvryL9dyt8dHR2pB4B6u0x1q6ebm1tmFsC80yxj/fr1AJQb8ODBAzJtnzx5kky0vr6+ZMaW/ydtFbScOHv06EFmiZyUlAsNDaVI3Rs3bpCPLCWfffaZhtlTRmfrKgJQnbi4OCxevBgAMH36dIomTeta0lXg6emJmjVr6rSkaF5ATjZnzpzB/PnzAYBiBmR6SufOncm90LJlS3pOc0ieUtDacuzYMVy7dk05WSYKWi5qq1SpktPLZkhAQACZhHfu3Ino6GiK9H/58iVFojds2BBffPEF6tSpA0B3vanVkala27Ztw5UrV0hxpXWf0poT+/TpgzZt2sDFxQUAdO7Hr1OnDpmNIyMjoVKpSKm6ublpyLRq1ao0FWOhQoXg6OiIgQMH0vd0XaJ50qRJAJTynYMGDaLFgRCCnqszZ85oLBRUKpXGokE2y5DdATMg7yjojJCr2Nu3b1N6jQzw0lZBy0FjrEbrDKMlH6SCZnI/UkFv2bIFs2bNSlPBScWtbjmUiq58+fKpOtflcribFcMwDMPkVnL9DpphPiB4B80weQPeQTMMwzBMboUVNMMwDMOYIKygGYZhGMYEMdVKYrnR18YwTGp4LDNMNuEdNMMwDMOYIKygGYZhGMYEYQXNMAzDMCYIK2iGYRiGMUFYQTMMwzCMCcIKmmEYhmFMEFbQDMMwDGOCsIJmGIZhGBOEFTTDMAzDmCCsoBmGYRjGBGEFzTAMwzAmCCtohmEYhjFBWEEzDMMwjAnCCpphGIZhTBBW0AzDMAxjgrCCZhiGYRgThBU0wzAMw5ggrKAZhmEYxgRhBc0wDMMwJggraIZhGIYxQVhBMwzDMIwJwgqaYRiGYUwQVtAMwzAMY4KwgmYYhmEYE4QVNMMwDMOYIKygGYZhGMYEYQXNMAzDMCYIK2iGYRiGMUFYQTMMwzCMCcIKmmEYhmFMEFbQDMMwDGOCsIJmGIZhGBOEFTTDMAzDmCCsoBmGYRjGBGEFzTAMwzAmCCtohmEYhjFBWEEzDMMwjAnCCpphGIZhTBAzYwuQDsLYAjCMCaIytgDZgMcyw6RGq7HMO2iGYRiGMUFYQTMMwzCMCcIKmmEYhmFMEFbQDMMwDGOCsIJmGIZhGBPEVKO4GRMlODgYAPDkyRMEBARgz549AIB9+/ZBCCVg19fXF7Vq1TKajAzDMIYgLi4Or1+/ptfTp08HAKxZswZt27al+dHMLHuqNtcp6OjoaMyePTvV+/v378fLly8BAO7u7hqf9e7dG+XLlwcAmJubI18+NhxoS3BwMPbu3QsA8PHxwb///gsAePz4MVQqFSlllco4GUCPHj3C4sWL8fvvv6f5+bp169CvXz+DyOLr64s3b97Qa39/fwBAWFgYHjx4gLCwMADAw4cP0blzZwDAlClTDCJbXuHSpUtISEgAAPz777+4e/cunj59CgA4fvw42rVrBwDo1KkTqlWrBgD48ssv9S7Xy5cvceTIEQBAv3790h0P69atg6WlJQCgS5cuepcrOyQmJiImJgYAULhwYb1f7/Tp0/Tv1KlT4enpCQA4c+YMTp06pffrp8fz588BAH5+fli7dq3GZ3LeCw0NxcmTJ1O9r1KpcOnSJbx9+xYAYGtrmy0ZVPKEJkaaQgkhMGrUqHQnY23o27evxs2qU6eO3gfKzZs3AQDNmjXDd999BwDYu3cvgoKC6GE0xYl6xowZmDRpEk02QohUv3/xxRcAAEdHRzRs2BAA4ObmBhsbG53KEh8fjy1btgAAJkyYgKSkJABAQkICwsPD0/1ewYIFce7cOQDQ667+yJEj6NSpE2JjYwGkXrCo3zt1duzYga5du2p7mQ8yD/rFixfo27cvAODUqVNITExUTpzOPZWfmZubAwAqVaqEihUr4u+//86pKBpIOZ49e4ZBgwaRMklKSkL+/PnT/E5SUhKsrKwAABs3bkSHDh10KlN2CAkJwfnz52lejYiIwP379wEAQ4cOpbkpvf9TTtFmcX/q1Cm4uLjo5fppsXLlSsydOxfA+82IOultTOT7X331FWbNmoXGjRundwnOg2YYhmGY3EquMnHHx8fnaPcMABs2bNB4PXDgQL3voJctWwYAePPmDf0OKKsv6bOYM2cOSpYsCQCoWrUqFi1aRCY6Y3Hnzh2oVCqNVaL83d3dHZ06dUKrVq0MIsvu3bvxww8/ZPl7xYoVI5OiqZA/f36yMLRo0cLI0pg248aNw6pVqxAREZHl78odl6WlJZo0aaITefbt2wcAuH79Ot69ewcA+OOPP1IdV6RIEQDA8OHD0/3M0LvnqKgo2uFduXIFixcvBqDsTuX/JSXLli0jF0LKuTMnSLP21KlTNd6XFsWUn50+fdpgO+jBgwdjw4YNiI+P1+r4KlWqAADatm2LRo0aAQBatmyJjz76KMey5CoFrUvkzbO2ttb7teTD/9VXX+H8+fMan8kBU7t2bfosIiKCfEDGZOHChbhy5Qpu374NAChUqBC8vLwAKD4+Q+Lq6pqt723cuJEGkD5p3bo1pk6dilWrVgFQ/MwpKVq0KADFD2kKpk1T5fXr1/jxxx8BKK6g9EygTZo0gaurK8WXpFzsSEWoK9PsxYsXydT+7t27dM/78OFDCgoqU6aMTq6dU65du4ZRo0bh8ePHAJTYDXUsLCxQsWJFAEDZsmXx7Nkz+kzeR12i7neWeHp6arj6pkyZQq8NoZw3b94MQFmwxMXF0ftVq1bFoEGDAADfffcdSpUqpXdZJGziZhiGYRgTJFftoM3MzPDdd9/RijomJoZMTup069YN4eHhGQbfVK5cGQB0ZvrKiPXr1wMAkpOTsXv3bgBKZHnZsmUpDL969eqIjo4GoKz49bFqzSp79+7F7du36X57eXkZfOcsUY+OVqdQoUIaK9o3b97g22+/BaCYmerVq2cQ+QBl5/TgwYNU75cpUwZnz57Fp59+ajBZcjNeXl5pjmtACVrq3r07AMUiZUiWL1+OqKioND9zcHDA2LFjAQAVKlQwpFhaMXToUFy8eJFem5ubo2PHjgCUaPLatWsb/flMa5dsyOBZafK/e/cuVCoVSpcuDUDZWctgWEOTqxR0/vz5sWnTJty7dw+A4l+UkZPqkbx//fUXihcvTtHan376KQYOHAjAOCanggUL0u/qaQvdu3dH7dq16bUufBa6wNfXFwAwadIkCCFQvHhxAIY3a6szb968NN8vVqwYWrRoQa6K2NhYzJkzB4BitjMUAwcOJBMZoPjqv//+ewCKH88QrpS8wuvXr6GeXeLs7IwaNWoAAOrXr29wxXz9+nUASqS+zB4QQtDvAHDo0CGTVMwycv369euwsbGh2If69etj3bp1xhQtFYaM0k7Jtm3baN6TGxIZxf3gwYNUC2/5POrbfcYmboZhGIYxQXLVDhpQTDOOjo70euvWrQBSBxEFBwdTQBMA/PnnnwCAQYMG4YcffkC5cuUMIG3GSEuAqTFq1CgASn6kra0tDh8+bGSJ0ufp06cUmCWRu9WUEaL6QFYROnz4sEZgScuWLbFo0SINeRjtuHnzJu1iDhw4gFatWqFAgQJGk2f//v0AlKIUMjAsZa5zUFAQ7fr79etHVqd58+bBwsKCMjQMyaVLl9CtWzcAimWpVatW2LFjBwDDWpdyA2nlyA8ZMgSAEhCYMlBR5rJ7eXmhbdu2epMrVxUqSQvpt3V1daWIwE8//VTDhxsREYG7d+/S67Jly+Ly5csAYNCIPOD9YO/UqRM+++wzShtr1qyZQeVIj169epGpVqVSwcbGBm5ubgAUM65cHBUqVMhgMj148ABVq1bVMClmhIyg3b9/P9q0aaNP0fDbb78BAMaMGQNAs4CBnZ0dAODrr7+GnZ0dmUrHjBmTXZ/WB1GoxNbWFiEhIQCUuA1jIxd6M2bMoPcyK0ai/ln58uXRu3dvAJppRPri0qVLAIBvvvmGKlkBiqIxRGUwbZC+ZfVFtDF1ka+vL7kbtSkyJGUtU6YMVZD77LPPsnJJrcZyrlfQkv/++w9BQUEAlIpR6ivWkJAQDB48GICy04mKiiJ/x2+//QZnZ2cdiKwdMuWqQYMGuHXrFj7++GMAygPbo0cPADDKaltSp04dDV9MyuphMjd72rRppLj1zf3792Fvb5/l71laWsLPz4/ScPSBvFdubm548uSJ1t+TATmyYpOWfBAK2t7enu6LsRV0SEgI5frLioBA1hS0OiNHjsT8+fN1L+j/ExQUhM8//xyAsuOXHDx4EK6urkYryZseTZs2pY1VyjQrQ7Nr1y4AgLe3t8aGLjk5OVV5aJkSq1KpKBh5+/btWbkcVxJjGIZhmNxKntlBa8vGjRsxdOhQSpcoU6YMVq5cCUAxRcr6vfrmwIED6Nu3LzVQAIDJkycDAH788Uej7aIDAgIo9SwkJCTN+tsAYGNjg6tXr+p1dyp5/vw56tevTxWNWrRogYkTJwJ479999eoVAKVgiDr37t0zSPpIUFAQtm/fTi6Tt2/faviknz9/TsVLLl++TIX4O3XqRBWatDA/mtb2RzuyPJY9PDwoaj8wMNAghWYyIqsm7jVr1lBa4C+//KLxWa9evfQWPR0ZGYnWrVtT7Xl1SpYsia5du1JkcoECBfRWWzsrnD59Gk2bNqXXhq65nV1khsaOHTtQv359AMCxY8ey4vrTbiwLIUzxR69cuXJFlC1bVpQtW1ZAmUAEAPHzzz+LhIQEkZCQoG8RhBBC3LhxQ3Tt2lV07dpVqFQq+vn6668Ncv30ePz4sXj8+LHw9fXV+Jk4caLIly+fyJcvn1CpVMLLy8tgMvn7+4vjx4+L48ePi6SkpFSfP336VDx9+lTjPqpUKnH//n2Dyagt165dI/kAiJcvX4qXL19q81Vjj0uDjOUDBw7QmJw1a1Z2TqFT9u7dK/bu3Sv69etHP3379hWBgYEiMDAw0+/b2dnR37thw4bixYsX4sWLFzqTLzY2VsTGxgpPT0+N+Syjnx49eoioqCgRFRWlMzmyi4uLi3BxcREAhIuLi7HF0Qo53zg4ONCc6OnpmZVTaDV+2MTNMAzDMCZIrkuz0gW1a9fGwYMHAQAjRoyAj48PAGDRokXo1asXABgkcOzzzz/H6tWrAShR3DKsX9bLNRbSbJ3SfL1nzx6KXpT/GgpHR0eN9LrciAzakX9nJm0cHByouM/06dNRqVIlShcyBrLilvw3q6hUKjInFy5cWCcpTrI+/tq1a3HgwAGN92T1vF9//ZWCUH19fbF48WKqsb1lyxbcuHEDAHD16lWjprHJYlMyYEwGjZmyqVs2b5H3XF98kAoaAGrWrAlAqZbVvn17AEquoMwTNFRkt/ShGquUnDYEBAQAAGbNmqURBWrsbluZYWZmZlJRqzLKW73k4ueff045lYyCvb09PDw8ACj+X3d3d+qra8wMB11QsmRJnZTxXbhwIQBg9erVpPBnz56NQoUKUXZF2bJl6fimTZtizJgxWLBgAQAla0RGKvv5+em1V7q2NGnSRMMnbehNQFaQXQjV5xd93MMPVkFLWrZsSYEcU6dOxdq1awEofwBDBYwZChnkYmdnR4M4s6AGX19ffPPNNwA0B8zIkSMNNqh37tyJSpUqZXi9EydOpHpvwIAB1KHH2Ny5cwft2rUDoAzqSpUqAQBKly5tMiVeTQmpoBcvXozw8HB8/fXXAJQ0SVkj2dAEBgbCwcEhS9/5/fffKSBQlyQkJNDvsbGxdK0hQ4Zk2CZRBseamZnR7zLA0tRQ72alDy5cuEALlhcvXlCQ5oABA1C2bFkK/koLmVKlbwXNPmiGYRiGMUW0jSYz8I9BCQ0NFaGhoaJIkSIU5RgXF2dQGS5fvkyRng4ODjo/v7+/v7C0tBSWlpZCpVKJiRMniokTJ2b6ncaNG2tEbsvfz549q3MZU3L58mVx+fJlUaBAAVGkSBGxYsUKsWLFCpGYmKhx3MqVK0WBAgVEgQIFhEqlErVr1xa1a9cWYWFhepHr7du34u3bt2L+/PkiMjJSREZGZnj81atXRYsWLVJFmKtUKvHPP/9k5dLGHpcGH8t79uzRuF/dunXLStS7TpBR3FWqVBE+Pj7Cx8cn0+8EBweL4OBg4ezsLMzMzOinX79+OpHpzZs34s2bN2LgwIHCyspKWFlZ0dxlYWEhLCwshKOjoxgzZowYM2aMqFGjhnB0dNSI5K4LIf3EAAAgAElEQVRSpYqoUqWKSURyCyFSRaFnMSpaa4KCgkRQUJCwtLSk+UzOb3KOc3Z2Fu/evRPv3r1L9f0ff/xR41gHBwfh4OCQ1flGq/GTp0zc0swp802lz2/Tpk1UX3rEiBGpvidNUOpmI0Mj21DqC/VONpGRkZg5cyYAxcQv/XuAUsNclvqcM2cOXr9+TWacQoUKUX3zRo0a6VVeQPGpAcrfJTw8nIKr+vfvr5HDuXv3bo2/nTTJ66sGtmx3ePToUQoqlME46uzcuROA0uovODhY4z7K0qDNmzfXi4x5hU6dOqFq1aoIDAwEoHSqi4mJAQAKjtI3skTr3bt36flv2LBhusdv2LCB8o1lEJEcL7rKgf7kk08AKD0GWrRoAUDpvnTt2jUyeQcEBFD8SEoqVqxI1bAMWbY3I86cOaPxWl/mbVkyOGXrUPH/LrzSpUtj0KBBVKMiKSmJgsK8vb2xatUqDXff8OHDAehnvmETN8MwDMOYIHlmBz18+HCsWbMGwPugCXVkekFwcDB1mgEAf39/WoFGRUVh2LBhAGCwADEZSSm7cgH6WYkVL16culJVq1aNdnN9+vTBwIEDqebx2rVrKc1LpVLRD6B0bjFkT2j1Qv/qeHh4oEOHDgCUIKIrV67QZ1WrVqXe3/ogPDyc0lMAJbgEAEqUKAFfX19K39u1axdu3bql8V256q5SpYpRaw6bIt27d6c0tGbNmlH1sHXr1uHRo0cawTiHDh0CAGzevBk9e/bUu2xFixYFoFTfkjtgW1tbuLu7A3gfLS0bfKhbePLnzw8nJyf89ddfepNPpqC5urriypUr8PPzAwDcunULd+7c0Ti2QYMGAIDRo0ebTJc1GbUt06tk2pW+KFasGABl7tu7dy+A92lTgDKmhw4diqFDhwJQsgrUa3OrP4ujR4/Gt99+qzdZ80ypz969e2u0l0wPa2trODk50cT64sULDVOHNDUbqhHE8ePHAShlRuWk5O3tTVG++qBJkyaU+y2EoKYYADR+t7W1xfDhw6mspqGRKW+TJ0/WGCBpIRdUa9eu1eukHRUVRV1vAgMDKUWqUKFCCAsLo8VhyvSuwoULY9CgQQCU/082uwqZTs6Y9mg1lrdv347x48cD0KwDIJ/PtGjQoAE9x4bg008/pXKzSUlJpLil20x2qrt+/bqGC+a3334jM+iHilS+8l9Jypawhi71KRczQ4cOpWdJm25Wffv2BaC0E5UKP4twswyGYRiGya3kmR10bGwsmZH8/f2xc+dOPHjwIEvnGDZsGJYsWQIg9SpKH/j5+cHV1RUA8PTpU+zbtw8AqHCKvnjy5AlVMPP398e+ffs0dtATJkwAoPR/NkQzjMyIiIhAhw4dUq2+Jebm5hg7diwAzYYG+kIGI7Zr1y6VO0X9PlpaWgIAnJycMHXqVF0EhOXZHXRMTAwFd27bto0sWQEBAemOxTJlyuDatWsaLit9EhQURMGmO3fuTLfZRPHixSnwcu7cuShTpgz1KP9QSav/c0qM2SgjIiKC2hWvXbsWd+7cIVdKyh30ihUryBqWAz6sftApSUxMJL/qpk2bKKJ79erVqFKlCs6ePUvH/vzzzwCUiOY2bdrk9NJacfv2bbRo0YIeCpVKRSaytm3bGkSG3MSxY8fI9zthwgQkJibSZxMnTsS0adMMLpOfnx+8vb0BANeuXcPOnTupiMbYsWPpWdJhN6Y8q6DT4/z589i6dSuWL18OQIkxkJHRTk5OuHDhgkELvfz7778AlD7esq9zynKPp06dyjDK+0MmZfcqY/eANiIftoI2VWTaw/Tp0zUafHt4eGDWrFnGEovJHXxwCpph8ijsg2YYhmGY3ArvoBkm98A7aIbJG/AOmmEYhmFyK6ygGYZhGMYEYQXNMAzDMCYIK2iGYRiGMUFYQTMMwzCMCcIKmmEYhmFMEFbQDMMwDGOCfNgFYo3Mv//+S43c1Wu9Pnz4EBUqVDCWWEw22LdvHxYuXEilIDPj66+/BqB0SJKlD9u2bWvQspWM4UlKSkJ4eDi93rhxIwDg5cuXAJTa3Qwj4R00wzAMw5ggebKSWFJSEhYtWkTdrFq3bq3RIerIkSPo0KEDACA+Pp7eX7VqlS66lGhN//79sWHDBgDQ6IxTp04deHp60i5L16xYsQJDhgyh15aWlujevTsApV92586dAQAff/wxPv/88wzPJbs5xcfHU2/kD5HVq1fD3d09R+fYsmUL/R3SgSuJATRmZs2alWGf8IoVKwIAypcvjy5dumDYsGG6FiVNYmJiqAkOAFy5cgWnTp0CAERGRmLbtm1pfq9ixYpZ7sDH5Fq0Gst50sS9adMmjBs3jl5v3rwZly9fBgAsXrwY27ZtQ0JCAgBN07J6hyRjcuXKFbx69Upv52/RogVKliwJAHj16hWioqKwZs0a+nzhwoUAAAsLC9jb26d5DtmCLS4uDgCwcuVKvbeKk3L5+vqmO8mlpEmTJum2qdQl1atXR4ECBTQWfJLChQvDzMwMoaGhGZ7jwoULmSnoD57x48fTc5CYmJhhW9hHjx7Rvz4+PvS+rhV1cnIyrl69ir///hsAcObMmQyfucqVKwMAPvnkE/Tq1QsAUKpUKbRr106ncmnLu3fvEB8fT53ZXr16hXv37mnICigumGrVquXoWkFBQfjjjz/w/PlzAEi1wHJ3d9eq1a+tra3BOg8akzyloKOiogCk7jkaGRkJZ2dnACCFYmdnB0AZrNLvN3DgQANJCoSFhZHfCQCaN2+OVatW0etixYrp7dr29vbUe3fIkCHYtm0b3Rd1YmNjcfPmzQzPJVt1Ojo66lzOd+/eAQC2bt2KyZMnIyQkhD7Ttl+3Ifp6A8qkZm5urqGgixYtCgAYMWIEEhMTMXPmzDS/K//WmVkrPmRkj/LffvtN64W0vK9fffUVunbtqvMJ/fHjxwCANWvWZNiHvGLFitRy1NramnrOlyhRQqfyZEZERAROnjwJADh37hwpx0uXLmnMRSn7H0v+/vvvHC92J02ahPXr12u8J++DlZWV1h398uXLBy8vLwDK/KBvkpKSACiLwoIFC+r9ehL2QTMMwzCMCZKndtBnz54FADx58iTVZ+o7xKZNm2Lt2rUA3u+kDc3169dx/Phxej1hwgSDRm5bWFgAUHZtmzdvpveLFStGpurKlStj3759+OyzzwAA//33Hz7++GMAgJubG3r06EEyq/vQdYWHhwcAxXyeGVLGihUrYtq0aQCA33//HT/88IPO5UqLhw8fIioqCra2tgAUU525uTl9/tVXX+Gnn36i18+ePQOgmL/lTq9IkSIGkTU38fDhQ2zatImim1PGzNjb2+OTTz6h1/Xr1wcAdOrUCdbW1gAAJycnvcgmZVqxYgXGjh1Lfz8/Pz/07NkTAFCmTBnY2tqSS8lQFh3J/v37ASjuk4sXL9IcmXKXXKVKFeTLp+zXbG1t4erqSp/16NEDAFC8ePEcyzNv3jzcv38f1atXB6DEbly7dg0AULp06RyfXx/cuHEDXbt2BaC4S/z8/NJ1/emaPKWgpenBzMwsXTOYu7s75s2bh8KFCxtStFT069cPSUlJNOEYK1jv0aNHGvcqPj4erVu3Jhlnz55tFLkiIyMRGBio8Z6c5P78809ahC1ZsgR37txBTEwMAOCLL76g38ePH0+mRX0jJy+5EOzZs2eGg9jGxsYgcuUmpIvq5cuXZL708vIid4xEpqUNHToUderUQbly5QwrKJS0utWrVwNQzK1t2rQhuUyF3bt3o0+fPgCA6OhoFClShBasxYsXR9u2benYOnXqaCwo9YWNjQ22bt1KQbuWlpYmq5h9fX0BAA0bNiQ36MKFC7VaqERGRuL8+fMU9Ofv74+wsDAASoyUtrCJm2EYhmFMkDy1g27WrBkAxawkAzgkcuW4bNkyMuUYE5VKhfz581PwgaFNX5Jhw4Zh7969FIAVERFBwXITJ05E/vz56d517tyZojoLFSqkV7k8PDxSBaRYWloCANavX0/BLrLow/379wEAU6ZMwZQpUwAoz0H37t3h6ekJAGSe1wfyeZNmTjZXZ53hw4cDeJ9GlRaNGzfGX3/9BUC/gZSZceLECbI8WVhYmNTuWWaATJgwgbJVJk+eDA8PD3JtGZNNmzaRWXv+/PlGliY1SUlJOHHiBFkfYmNjUaNGDQDArVu3MH78eLJ4qlQq3L59GwA0dE50dDTCwsLIijZ48GB07Ngxy7LkyTzoadOm0SQtuXLlCgDFBGoKjBo1Cn/88Qcp6L59+9ID0aRJE4PK8uzZMxrU06dPx4EDB9I9Vj6o48aN00tKkJxQmjZtivPnz+vknPL/o27S0xVykh49ejT++OMPitzev38/kpOTAaR2X5ibm+Orr77KzuXydB60TOFJ6dpQ5+nTpyhTpkzOpcomAQEBAJQxGhwcDAA4deqU3lMMs4J8zr29vSnL4rfffjOmSERCQgKcnJxoAXv27FmDmNazwpIlS/DTTz/RWM4sPVJuWsqUKUMLtSZNmsDe3j6jZ1WrsWz8rSTDMAzDMKnIUyZuiYeHB27evIndu3fTezJS2dnZ2SRM3L1798Zff/1FCfteXl4UcXnjxg2D7hLKli2LsmXLAlACS2SwDgDs3bsXe/fuBQAcPXoU//33HwAlCGru3Lk4d+4cAN2Zj2VxCfVKTBnRt29ffPrppxrvyXzjtHK7dY10TcicUrnabteunUbNZXXMzMxo1V27dm0y6ZrCc2ksNm7cSKZCU2bFihUAQLtnQMmDVjfLly5dGj/++CMApYqZIYmIiNCoRiZNrPv374elpSWaN29uUHlS8ssvvyAwMBDTp08HAJPbPQOKGRtQiloBSqaLzM6QbpW3b98CUIrNmJkpalQfmSwQQpjiT46JiooSbm5uws3NTahUKvrZuHGjLk6vE/r160dymZmZ0U+3bt2Ej4+PscVLxZ9//knyAhAqlUqMHz9ejB8/XufXatKkicbfTaVSCWdnZ+Hs7CxGjhwpHj16JB49eiSSkpJSfdfKykpYWVnR95ycnISTk5POZRRCec6ioqKEvb19Knm1/Vm4cKFYuHChNpcz9rjU21geMWKEVvdq8ODB2p5SLzRt2lQ0bdpUQDHdp/tTsmRJUbJkSbFo0SKDytemTZtUY1T+mJubi+LFi4vixYuLBQsWiLi4OBEXF2dQ+dq3by8KFiworl69Kq5evWrQa2tLkyZNUv09q1atKqpWrSpOnDihq8toNX7ypA9aIv2ZZcuWpRVvkSJFMGfOHIPW3M4I2f2oa9euGuU9169fTz5pU0EIgSNHjgCARp4koPjhdOk7v3btmobf7Ouvv6bUDJnfmh7y84iICADvfZt+fn46k08iUyfUc3Gzigx+27x5s0bN+DTIsz7ohg0bahVzUKRIEVy4cAEAULVq1ZxJlg1kIOqpU6eohoK7uzsaNmxIu6q1a9dSaVFzc3N8//33VJ5Un1aSx48fw8nJiZ57IQTl71pbW+PKlSu4ceMGfdavXz8ASmClrnb6cozJugQp6dChA44ePYpdu3YB0D4uJCEhAdevX6fXFStW1Fuq4t27d3HixAnUq1eP3pO1Fa5evYpbt27pIk2XfdAMwzAMk1vJ0ztoyZUrV2jHFxISgi+//BJnzpwBABQoUECXl8o2zZo10yjov3btWvTu3duIEqWNrI9dtWpVjR3/yZMnTSaSNeUOWqY37NmzR+fXunjxIgCgTZs2qXzOXbp0AaBEeZYqVYre9/Lywp07dwC8v5+A4uuSPv50yLM76OfPn8Pf3x+AUvP5zZs3AIDDhw9r1GAHgC+//BKAkjJZq1YtXcqaKTL17/LlyzQ+ZQEddaQPWtbXlzXa9elz9fHxQYsWLcgKM3PmzFSFeg4dOgQAGo05BgwYgOXLlxvEH3zy5El06NCBYje6dOmikdFw7ty5NC1diYmJGjvojRs3GnR+lLXWp06dCj8/P11Yb7Qby9rawg38o3M8PT2Fp6cn+WM6dOggOnTooI9LZYumTZtq+KFNyVeeFhUqVCA/FwBx6tQpY4tEpPRBHzx4UBw8eFAv14qNjRWxsbGiTp06QqVSCQcHB+Hg4CDOnDkjkpKS0vSRCyHE0aNHxdGjRzXk7NixY2aXM/a4NPhYPnv2rLC2tk7TH92vX7+cnl5v3Lx5U9y8eVMULVpUABDx8fEiPj5e79e9e/euiI6OFtHR0RkeN3PmTGFubi7Mzc2FSqUSz54907tsEh8fH2FnZyfs7OzS9N/LzxwdHeln0qRJGr79gIAAg8krhBDTp08X06dPF+bm5iIwMFAXp9Rq/LCJm2EYhmFMkDyZZpUWsnDJ7NmzkZCQkJkp0WDINm8y2MjUkUFtL168APC+YpZM0/rQkPXft27dit27d1MaS+3atTP8XqtWrQAA7du3x5YtWwAgVfU7BmjUqBEOHz5MAYjqdePVTZ76JDY2FoCSHidTajJDBkmVKFECoaGhBjFxA5r9mzNiwoQJVO/89u3b2Lp1K8aOHatP0YiGDRvif//7HwBQ0Jo6shiSejCoNMl///33AAAHBwd9i0kkJyeTS8rGxsagwYkfjII2Vbp16wYAGn2XHRwc0KBBA71d8+3bt+Tjy2pXFvmgygj5mjVrAtB+YtAX4v9jKby9vQ2S/5ySypUr45dfftH6eFnZTr1hfW5ZpBma+vXrGy1H/MWLF7ToateuHXWwyozo6GgA7xcU0q8q/ee6Yvny5RgyZEiWv/fo0SMN335mmRG6Ri7sGzdunO4xfn5+pJBlh7Bx48bpRZ74+Hiq6igbY0h8fHxoMZOyl7W++WAUtNydyPKLxmbXrl1YsmQJBYap1+J2c3NDpUqV9HZtf39/WhjMmjULnTt31iptIDk5Gfv27QPwvl1dRnWTs4MsBOHq6pql1A9vb28ASJWmVKFCBYO1hsuIpKQkqj985MgR7NixAwAoMAoAdRFjUiOL5Xz77beIjIwEALx58wb37t3T6+JwwoQJVN5T1grXBtla9N69ewCgt2C2CRMm0DWyUs6zQ4cOtEivUKECKUJjExERgT///BOA0o1ObgS8vLzQokULlChRQqfXa9SoEQClla58ju7fv4/+/fvTHNm9e3faNXfu3Fmn188M9kEzDMMwjAnyQeygExISqOm4NGMYslCJLDO6bt06eu/y5cuIi4ujnXP+/PnJ7KNv2Ro2bAhHR0cAQP/+/XHu3DnqbZsRu3btojQNlUqF0qVLU0F5XbB582aMGDECAPDXX3/h1KlTWn0vMjIyzd1DpUqVMHjwYJ34jKRp//Tp02QOVN/9pvVanaioKCrykhLpr3Rzc8uxnHkRPz8/HD9+HMB7fzCgNM64cuWKXnfQ0lQNKDs66TPt2rUrpQdZWFjg7t27ePr0KQCl+YwsxAEAP//8s37KQAIYMmQI9Wz38/PD1KlTUb9+/TSPPXbsGBU/evnyJaWYenl5UbEcY+Pm5oZ//vkHAGBnZ4ft27cDUGI69HEP5fwbGRlJfnFAMWXLFLmqVavi2LFjAKCLAiVZItcraJmLe/PmTQouSNlQe+7cuamqFGXHb5NdZL5eRg9Y79696ThD1OGeMGECAKUi0oYNG+iaI0eOTLdVonoesZWVFZYsWQIrKyudyZSQkEALqIsXL+Lo0aMAlCpi6eHv749vvvkGT548SfVZr169MGrUqBzJ9Pz5c0ybNo1M0unV184uffr0oQ44LVq00Om5cxOtW7dO994+e/ZM69rsuqZt27a4fPkyAMVvu3LlSgDAypUrab4pUKAAnj59SgGf6owaNQpz5szRmw990qRJVClv4MCBaNasGQUgqsexnDhxAqdOnSKfuJWVFebNmwdAWbAbGlmz/uXLl/S33b17Ny5duoTBgwcDUGph6zuo7uzZswCUzcfSpUvp/aZNm6JTp04AACcnJ73KkBFs4mYYhmEYEyTXVxKTTvu9e/dS3dlmzZohMjKSIif37t1LFZuKFSuG6dOnY+DAgQD01IEkBXL1nPJaDg4OZNYcP368QZupy7SPFi1aUOoUoJiSpJlZBo5Ia8PmzZvJ5NeoUSOqxqYrDh8+TIEZUVFRFPE6Z86cVMfKTjP37t1LVXlozJgxAJT6uTm9p25ubhQYpyvKlSuH/v37A1B2aFnoUZ7nKonJ+7Bt27YsR9/Xq1cPu3btQunSpbMvnRZI98bChQtx8OBBAO/TDNOiZMmS1Id55MiRBqtWuGvXLvTq1YvuowzkVEdGTW/atMlgnbaOHz9O0dfOzs743//+R70RIiMjyXLi7OyMadOm6aVvuwmi1VjO9Qq6V69eAEC5pOkho/+GDx9O5l1D4e7uDkDTBz1v3jy0b99er9Ha2nDv3j20bNlSIwdXPhMy3UDd7yfNsN7e3noxP0lzb1aVv/z7fvvtt1iwYAEAaJ2zmhFTpkzBtGnTyN/o5OREKVK+vr50XOnSpREXF0eLROD9JF6rVi28ePGCmiv07t07zfKQWpDnFLQ0K/7000/QZi4qWLAgJk+eDEBxD+hbOadElo9NTEykqHzpM/3mm28AKKmHhvZVSt6+fUuLiNevX9Pi9fXr1/jll1/I7G3oNo8yx1q2p5UuNfWSo66urtTO8QOAm2UwDMMwTG4l1++gZXGHXbt2YebMmQCU9n2y6TYAuLi4ULs3Z2dnXcqZJ3j27BmZuWfPnq1RNEUdJycnihht06aNXmSRkbAdO3bUiKpMD2tra/Tt25ci32V0eh4lz+2gJUuXLqXxq96ERSKfu+rVq38oJlAmb/NhmLiZvMnz58+xdetWAIqJ/fz589SLV52uXbuiQoUKhhbPWORZBc0wHxisoBkmj8EKmmHyBuyDZhiGYZjcCitohmEYhjFBWEEzDMMwjAnCCpphGIZhTBBW0AzDMAxjgrCCZhiGYRgThBU0kyFv376Fvb097O3tUapUKTx8+BAPHz40tlgMwzBG5+3bt+jZsyd69uxJLXhLly6Nt2/f6uT8rKAZhmEYxgTJ9f2gM2L58uUAgBkzZlDjgvHjx2PWrFnGFCtXER4ejtevXwMA3r17R52XevTogXbt2lF1r8ePHxu98QfDMIwhkD2su3TpgosXL9L7ss91VjuzpUeeVNBRUVH45ZdfsGzZMgCASqWilm9mZmaIi4tDwYIFDSLLq1evqF1jkSJFqFuTra0t7O3tsXPnTgDA77//juLFiwMA/vvvP4PIpg1Xr16lTlGlS5em1nBLly7F8uXL6T727duXFkT64Pbt29T56O7duyhXrhwApRtOq1atDNqqk2FyO69evSJX1e7du7F7924N11XdunUBAAsWLECjRo2MIqMpcubMGezduxcbNmwAAJoPAaVD2Pnz5wEApUqV0sn12MTNMAzDMCZInqzFffPmTdSoUYP6y6rvoD08PDBu3DgUKlQo51JqQXBwMKpVqwYACAkJoR2nNIHI13369MGwYcMAAJ9//rlBZMuMv//+G4sXL0bPnj0BAN27dycTzo0bNzSOtba2Ru3atfUix+bNmzFw4ECNvtTq9OnTh3p8V6lSRS8yZMahQ4eoE5dkx44dAJRVt0qlWXpXdmQ6cOBAVi7DtbhzOVFRUQgMDKTXAQEBAJQubAEBARrd2KQ7SVckJiaSxW7s2LF4/vx5pt+pXbs2zp49CwB6sVJFRUXR77LXdloULlwYH3/8sc6vnx4JCQnYtWsXAOD06dPYv38/AGU+T05O1ji2ZcuWAIANGzZkpT+5VmM5T5q400K2R6xfv77BlDMAFC9eHD/88AMAYO7cuTh06BAAIH/+/PQ5AHz22WcGkykzpH9lxYoVePDgAZo3bw5AcQ9IeeV7hiA5ORnx8fHpfr5x40YcO3YMAHDixAmDtpxct24dAGD06NF49+5dmseoVKpUCtrHx4f+ZRNiaiZOnAgAmDVrFnr06AEvLy/6TCq1I0eO4Pbt2xQjoVKp6PkEgIYNGwIAGjVqpPOOZ7169SJFu2nTJlKwUr6QkBA6dvXq1fR7VFQUbt++DQAQQtBzIX+Xm4rOnTuTMs0J8t6cO3cO8+bNw6VLl1IdY2ZmhkaNGuG7774DoLTrnTp1KgCl/evff/8NAHBzc8uxHNu3b4evry+97+/vT7/7+vqmGieSWrVqoXHjxpgyZQoARWHrk/DwcHz//fdpfmZtbY1atWoBUO7Jjz/+CEC5j7qGTdwMwzAMY4LkSRP3o0ePMGLECBw8eBCAsrLesmULAMVMa2jkKrF69epYv349ACWoylT5v/bOOyyqa/v730FAUJQSBUs0aCCCFX1tWMDexZ4YFRV/sXCtaDRX0ahXLJirWBLFklgTO2DFkhhjjYpYaYkFJApSBJUigu73j3P3YobmzDAN3J/nmcdpzF7OnH322at8F9/dDRkyBFu2bMGAAQP0bBFw8+ZN2ols3Lix2PfVqFEDoaGhAAAXFxet21WvXj0AwOPHj4t9D98dGRlJ18NVqlRB06ZNAQC//PKLxt1iBobKc9nPzw/ffvstAGnuVqpUCXPnzgUABAcH0841MzNTYddZ3H1bW1s0b96cduHVqlVT6z+SmZmJ4OBgANIOurjdb1H3uVdH3k07fvx4ul+tWrVS7VCLIigoCJMnTwYgJYXJY2ZmBgcHBwCSB6DgXElNTQUAvH37Fra2tqWyY+vWrVi1ahUAKdmzuF2yvEehuNf4dxYYGFgqm0oiNTUV//3vf7FixQp6rkGDBgAADw8PeHt707wvBaIfNP+x9b1A89hpw4YNKR7NXd2GRm5uLmxsbABIcdJffvml2EmjS7Kzs1G/fn0AQGJiYonv5aVfp06d0orbSZ6JEycCkE5CxfHNN9/AyMiI8g0WLFig7nD6/yFUR+m5fPLkSQBSToG827ooVzAA1K1bF9WrV6cFd9CgQfRZwcHB5GZOSUlBbGwsLZLyblVVuHHjBmU3y9vRsWNHhbCKk5MTPpsawoIAACAASURBVProIwCg+e7k5AQAOgmvhYWFkV0Fy314fktAQADNE23BY9zt2rWj/AzGGDp27EgLnLOzM/r06UOvFXeuGTFiBBITE8nt36lTJ63ZvW7dOkyfPp0ee3t7Y+HChQBAFS0aQMSg5eFBfn0s0Dy5wszMjOK7ubm5MDEx0bkt7+Pp06fIyMgAICVt8AQRAIiIiKAdwB9//AF/f3+FmJ+2yMvLw6RJk2hhNjY2Jk8EL1v7/fffAQBeXl44e/YsAGDChAkUI9YWX331FQAp2YsvKpylS5cCkBITBSUTFRVFJ2qZTEa7tsGDB2PTpk0YMmQIACkfwdfXF4C0QBe3G54wYQLdT0lJwePHj2n3qy6ZmZmoW7cuAMlLt3PnTgCgJEpDYfXq1QAUa3FNTEwwYcIELF++HIAUZ9Ym9+7dg7u7OwAgPT2dnv/222/x73//W+WEs8uXLytsHrQB97wtXrwYRkZGmD17NgBpHvOcIV0jYtACgUAgEBgijDFDvGkESO41JpPJmIWFBbOwsGA3b97U1MerjLOzM9mUkJBQ4nuzs7NZRkYGy8jI0JF1EuPHjycb33cbN24cS0tLY2lpaVq1KTAwUGHczZs3F/ven3/+mTVv3pw1b96c9erVS6t2yXPq1ClmZWXFjIyM6NasWTPWrFkzTQ6j73mp8bnMj/FBgwYpzNfIyEgWGRmp7vekFZKSkpi9vT2zt7dntra2LC4ujsXFxenbrEIMGjSIDRo0iMlkMro5OzvrZOy9e/eyvXv3KoxdvXp1duLECXbixAmd2KAu48ePp/Nfy5YttT2cUvOnXLu4v/76awDAqlWrqN5uw4YN+P7776kuWpeMGTOG3J1JSUmoUaOGwuvXr18HILlNExMTyZ18+PBhrddG83gRT6yTh8fPvLy8KNb6999/46effiIXr6urq8Zt4io969evV3j++++/x549ewBIrkx7e3u0bdsWgBSrKq48Qpv06NEDISEh6Nu3LwApZn737l0AwLx58zBt2rRCv7cA5HY+fPgwxR93796t01I5Zdm9ezfi4uIAADNmzCB3t6Hh6ekJAAgJCdH52DyPQCaTUSnUpUuX4OjoWOT709LSFBIsQ0NDqVwNAGkruLm5wdzcXKs6B//88w/d5+c1fSNc3AKBQCAQGCDlOov73r17ACSREp6cBUiNHbiWsy5JSUmhrE57e3tcunSJEk0CAgLw999/A5CK8t3d3eHv7w8AaNu2La5cuaITG52dnanEYsCAAZgwYQLtoKtUqUIJH/fu3cOyZcvoalcbLSj5Zzdr1gy5ubnFvs/MzIyyK/fv308iAtrO4C4KnpQmX0LDGMOdO3c0IUZT7rK4+U45JiaGSpN27dpFKlrOzs6Ii4ujZET53RUglSdpWoSkONzd3XHx4kUAUqmffCKaIcErRDw8POi5SpUq4fLly1Tepy14KaFMJsOmTZsASLvR2NhYAMBff/2FpKQk7N69GwCQkJBA52n2njKrTz/9FHv37gWgeZW1rKws8gLeuXMHdnZ2Cnra3OPKvXNcCbBu3brqJvuKMivOhQsX4ObmRo937dqlt8xLrtazb98+uLm5Uc2xiYkJHbRDhgxBVlYWubVtbGwU1He0SV5eHt1/3wJ35coVUmiLjY2FlZWVVmwKDAzEnTt30K5du0KvnTlzBkFBQZR5DoAubObMmaMVe0oiOTkZgBQO4FmhjDF8/vnnVLtZiu+p3C3QPDs7JCREoXaYlyM5OTnh8ePHtEBHRkYqlFzxGmdAmtfq1jgrg7wi3Llz5xTOKYYEP6d069ZN4cJ2zpw5lMWtrdJJ+QWal0IZGRnRhdXTp0+LHZv3VJaHy21mZWUpuM0XLlxICl7m5uYasX3lypUApLJIZWnTpg3pM6gYhhQLNOfixYsKk6lfv36qaiBrDL47bdiwIVJSUjBjxgwAoEJ+zsOHD6l9Y/PmzREeHq5bQ5UgMTGRrjr//PNPTdYIqkRQUBBdWR84cIBO5qdPn9aJWElRpKamYujQoQDytbh5LT6/SFODcrdA8xP36NGjqX63KKGPouqg+X3+XmdnZ7VrnJWhYcOGJNM5cuRI8pIYqlTr1KlTqaMfh+dufPHFF1oZk/cTKE5MiDGGfv36kefE09OTSuqKEkThF7wZGRnw8PBQ+H25ngCX/ywtvCwtNjYWhw4donOKPA4ODrh//z7u378PQMo1adasGQDg1q1bqgyn1FwWMWiBQCAQCAwQsYPWE6mpqXj06FGxHaBiYmIo9jtz5sxCO2x9k5iYiB49etBu9bffftOrPbzDzNChQykzuF69enj48KHebOJ9wDdu3AiZTAZLS0sA+U3d1aDc7aDlUcdLFBwcTIIwMpkMb9++VfkzlOX8+fOYNWsWAEmtS34nP3jwYFIyGzx4sE4b8hRHRkYGyYf++uuvAEAKZ8eOHUObNm00Piavltm4cSN5ROTPcbyyRh1u375N4Qwgf37xPvG6ZNmyZQAAX19fOk8XzI94D8rNZWXrsXR80ygXLlxQqMvr37+/pofQKHl5eax3795Uc6mpWuj169ez9evXa+Sz2rRpwwCwmTNnspkzZ2rkMzXBwYMHqebd3NycXb16VW+2PHnyhD158oQ5OjoyIyMjZmpqykxNTdnatWvV/Uh9z0u9z+WCLFmyhOqnbW1ttT0cS05OZsnJySwgIIC5u7szd3d3JpPJmJGREZ1fGjVqxHx8fJiPjw9LTk7Wuk0l8eLFC/bixQtWpUoVhXNgx44dWU5ODsvJydGrfapw69YtBZ2B/v376+1cfvz4cXb8+HEGgFWtWpVVrVqVxcTEqPIRSs0f4eIWCAQCgcAA+SAWaK7dW1Z49OgRQkND4erqCldXV400Kv/888/x7NmzQl1tlOH69eu4fv06+vTpg1q1aqFWrVq4fv06hg0bhhkzZlCimyEwZMgQODg4wMHBAdnZ2XoNDfDvysfHB4CUIZ+Xl1dIs1ugHkFBQfD396fs6nnz5ml9zGrVqqFatWqYMWMGzp07h3PnzuHdu3d4+/YtAgMDERgYiDp16iAgIAABAQGwtbWFn5+f1u0qjqpVq6Jq1apYt26dQvXAxYsXMWHCBIMtFVMGZ2dntQRtcnNzkZubi4SEBLXHvn37Nm7fvg1AKvM0MzPTyHm6IOVaSYyX23Tp0oVKDywtLTF16lR9mvVe1q1bh48//rjEDkmqYmpqSvWGyvLkyRP4+PhQI4qUlBSKOc+ZMwdTpkxB7dq1NWajpuDlF5MmTaIsUEH5w9fXF5mZmVQTK9+BSB/wxW7ChAlYs2YNAGDFihXw9/dHo0aNACh23NIlY8eORe/evalt4suXL7Fv3z4A0vlx9OjRerFLWW7cuEG5Bhx11eZ+/PFHAFKpFs+dUUWj4M8//8TmzZvpMS//1Ma5sFwv0FxWU762ztvbG927d9eXSSXy8uVLAMC2bdvg6Oio0UST9u3b0063Z8+eGDhwIAAUmph79+4licBr164p7PZMTU2pvyxvv2aIvK8dpa6JiIjQtwnlCr4j5b2FdbFzVhU+1zIyMrBgwQKcPn0agHYWaK5dcOzYMXTv3r3YnZydnR0WL14MAPDx8aGyounTpxvsAj1u3DgAUvlkVlYWPe/v76+2zfyclpSUhK5duwKQJEa5wFFJ3LlzBwEBASS8Ur16da12zPsgXNwCgUAgEJQ1ys0O+tatW9ixYwcA4NSpU3BwcFB4nUu18aJyQ+Pdu3e048/NzS220F9dvL29ceLECQDSlTa/ouelCsVhb29PJWqzZ8/WhFylypw5cwa7d+9G//79AYAEQIoiISGBXFj65vnz5wCkBi3aUm760Bg8eDB5eCpVqoRdu3bpzW1cEvINK2xtbRWkXzUN74l+6dIlWFlZkau/oJfr999/p3OkPNwrpityc3Nx6tQpAJI3j0tlJiYmIjQ0lBT4uIoYIFUbmZqaYuLEiQCkxkNctUxV+G+xY8cOKsPs0qULqlatCkASI2nSpAmFTmxsbEggZdGiRcjOzqaxZ8yYAWtra7XsUIYyXwfNXQ1bt26lmDOvheT/twYNGpDCDf9XX/BJ4+npqXAR8eOPP1IHld69e9Niqkl4x6r58+dj27ZtRb5n+PDhdMANGTIEzZo106p8ojIEBQWRJCQgyaLy2krexYrHhHJycsjlZ2VlhatXr2q1A05xJCYm0gXFjRs3IJPJSJLw+PHj1MxeRcriKq/WCeb8+fMApPCUk5MTSVSuWbOGLnYOHjyo18WZ5zfExcVBJpNRjFRettTd3R3Tpk2jemRtMHfuXABSeCouLo5qnV1dXUm1bufOnbh69Sp1iAPypSmvXLmis7rtFy9ewNPTE8ePHwcg5bJwff9NmzYVeyE7depUdOvWjTSwNcGdO3cwf/58AEV38SsJvpnix6UaCCUxgUAgEAjKKmV6B3337l3qP5qZmUk76JycHIwYMQLt27cHIOnO2tjYaMlU1eBX0idPnkT79u2pp+xPP/2E+vXrAwDOnj2rsw49ZYGEhARcvXqVmk1cvXqVrrqL4uOPPwYgXXXrsmHGrVu3yBX23Xff4c6dOwAkT0779u3JK8IF/9Xgg9hBy3tMnJ2dwRgjDexKlSqRQhVXcNIV58+fpx1TSkoKUlJSAOTvoPm5VCaTkT736tWrlUo+0gSxsbHo1asX/vrrr/e+18PDg5LtdBm2SktLQ//+/Yvszsf+p63OPXYtWrSgiowBAwbozEYdIZplGCJckjIkJAQHDx7EpUuXAAB16tShml1tSPCVJ+Lj40ne78iRI4iOjqbYVHR0NJW78NZw2uTy5csAJDdjXFwc4uPjC72nSZMmWLlyJXr06FHa4cr1As0XDH9/f5KM5Asfb9N68OBBnS/MnFatWtH95ORkKjmsVq2agqt98ODBegsLPX36lLKKg4KCqIHDgAEDEBUVRe7woUOHaqVuVxmys7OxYsUKANLFDYcxhj59+qB169YAJKnecoxwcQsEAoFAUFYRO2iBoBQcO3YMQGEX3Mcff0z1uXx3rwHK9Q6aJwjJu4ttbW0xcuRI+i71nbDISUlJMRhbBGUS4eIWCMoZ5XqB9vb2pvs8f8TNzY3yNASCcoRYoAWCcka5XqAFgg8IEYMWCAQCgaCsIhZogUAgEAgMELFACwQCgUBggIgFWiAQCAQCA0Qs0AKBQCAQGCDlppuVQCAQGCpcInTbtm1KSXEKBIBYoAVlkMzMTERHR9NjT09PREVFAZAkDHmrTl0ISdy7dw8AEBgYiOHDh2Pv3r30Gu/IdO/ePXh5eZGYSatWrVCzZk2t2yYwDP744w8sWrQIAEiiViBQBuHiFggEAoHAAClXQiVnz54FAIwaNQoJCQn0fOPGjamjDGMMBw8eRHZ2dqG/X79+vd77Reub5ORkPH78GAAQFRWFoKAghISEAMjvNgMAAwcOREBAgE67bgUFBQEAwsPDsXLlSoXXeA/wChUqYMOGDQBA/bU1TVJSEgBg0qRJuH79OoD8XtvyyHc3kqd27dqYNm0aAODrr79WZegPTqgkOTkZYWFh9P2OHz8+/4PljsdCgxbxGu8m1qtXr9KYpBJPnjxB165dUbVqVQDAxYsXYWpqqrPxBQbLh6ck1rx5cwDA7du3i528RT3Pv4POnTvTIq8rcnNzAYDaFAJAw4YNYWJiolM7+OI3a9Ys6jDDNZH591Xwvq2tLX7//XcAUltAbbJ7927Mnj0bgLRAVqhQQeH12rVrAwB8fX3RoUMHANprR8hb4G3ZsqXYRRgofoEGgP/3//4fAODatWuqDP3BLNA8THHq1CkcP368yPe8ffu20HFQ0mtcN71nz57qmKQSBw4cAADMnDkT9evXx759+wAANWrU0PrYZZWwsDCsWbMGR48eBSCdB0eOHFnke93d3dGkSROt2ZKWlgYHBwcAwPPnz+mY4aEKIyPJ+cw7b6mBUBITCAQCgaCsUm520Lm5ubSDjoyMRIMGDeDh4QFA6iv66NEj6YMZQ4sWLai/rDw1a9akHq+64Ny5c9i2bRsAYNeuXbTjmjlzJvWG1hV8Bz1mzBhkZGQAeP8OWr65elhYmMabGvDErzFjxiA+Ph6pqakACu+Odu7cCRcXFwDa2zXLw3vsurm5KXxXAPDFF18AkHoClwQ/Vj/99FNVhv5gdtD9+vUDILml5X9rKysrOub4McgT7vr370+eFO6hkIe/r1KlSuqYVCKvXr2iuRwQEEAu+WnTpsHPzw8VK1bU+JjKwD10O3bsUHi+qPAP9x4+fPgQW7ZsASD1bq5fvz4A6TfRdNgoNzcXXl5eAICjR4/i1atXSv2dpaUl9bP+559/NGpTdnY2+vTpgz/++OO9723cuDGaNm0KABg0aBB69+4NQKljTLm5zBgzxJvKBAcHM5lMRrcLFy6o8zE6Y//+/cze3l7BZkgnM2Zpacl69uxJtx49eig8njRpEps0aZJW7GrZsiXZY2RkxAYPHsyMjIyYkZERPSd/f/DgwWzw4MEsMzNT7TEzMjJYRkYG8/X1pc83MjKi70P+Of68t7c38/b21uD/XHWWLFmi8F0ZGRmxKlWqsCpVqrAlS5ZoY0h9z0udzGXGGOvbty/r27cvk8lkzNjYmHl5eTEvLy926NAhdT9S4+Tm5rLc3Fy2ZMkSZmdnx0xMTJiJiQnr3LkzCwkJYSEhIXqxKzo6mkVHR7P69euzypUrs8qVKyucZ2QyGatcuTKzsLBQuHH7C77XzMyMmZmZsblz52rMxqysLJaVlcXGjBmjcP4rODa/dezYkfXo0aPI1zTNmjVrirVDJpOxqlWr0q3ga+bm5szc3JzNnTuXXb58uaRhlJo/wsUtEAgEAoEBUm5c3LNnz8Z///tfAFLCkKbdHpqCJ498/vnnpf4sTf52ycnJAKSkh9jYWACSm/bQoUPw8/MDABw+fBgpKSkAgNjYWI25uG/cuAEAcHV1VXhePjO74PPu7u4AgOHDhwMAJkyYoNbYpYVnuHt6eiIzM1PhNV73vGHDBlSvXh3GxqWWHfhgXdy87l3FkIBW4f2rd+3ahfHjx2PixIkAdBNmKQ4vLy+cO3cOABAXF0cJjW5ubggODqYKjT///LPYDPhq1apR0qe3tzeFDTp27KgRGzMzMzF16lQAwPbt2+l5ViCB19bWFoMGDQIA+Pv7w8TEhHQH5GnVqpVG7OLJsS1atEBaWhqdd0aMGEHfQb9+/RTOczk5OTh48CAAKbQaGhpKz7958wa1atUCAPz8889o27at/HBKzeVyI1RibW1NP+6wYcNKfK98iZW5ublW7ZLHz88PS5cupcfFZZN//PHHsLKyojhWXl4exYEAoFu3bhq3jcfeO3bsSAdqSEgIZs6ciXnz5gEA5s+fTwv0jRs3MGbMGI1eJKjCxYsXFf4NDw8HoPidzp07V+Nx8YIMHDgQAHDhwgVMmDCBLjYA6YIGkL6rZs2aUXaqoGj4Bc7s2bOpJIoxhoCAAINamAHgzZs3OHnyJAApzrxs2TI9WwSsXLkSu3fvpgtbID/r2NbWli5mAeDq1at0PyIiAleuXKH48kcffUQZzJomIyMD06dPV1iYOV9++SW++uorrFixAoC0UPL7HE0txkWxadMmAFIGN5C/YSgYvy/IN998U+i52NhYREZGUg6Ara2tWjaVmx10zZo18ezZMwDA3r174ejoiN9++w0A8Pr1axw6dEj6YMbw+vVrOpH36dMHI0aMAFB0Yokm4NJ+Li4ueP36NdmxYsUKuiq1sLBQ+L9YWVkhMTERgJRIIb9Aa5OoqCj4+voCkBZoxhhdTfv5+b03+UkdsrKyAAAxMTEkiRgSElLiDrqo5wq+t0GDBnBxccGuXbs0bnNRZGZm4l//+hcAaXF++fIlAOmiwcjIiGqelyxZou5uulzvoGfNmgUAWLduHT339u1brFu3zuD0Cfz9/fHvf/8bAPDixQuqc9YHPLmrV69eyMvLQ5s2bQBIxyD3cPGyIH1z7NgxSt4tiKOjIxo1akQJlEOHDqXXzM3NYW9vrzW7kpOTSdPh9evXMDMzoyRVLWk9iDIrgUAgEAjKKuVyBw0UrzJU1PMfffQRAEkUgSuOaRIe92nevDm5T7gd/KqwZ8+eJM6gb/iONigoCKNHj6bvq1KlShQT2rlzp9bt4O4smUyGx48f024gIiICdnZ2AEAubF6eUbARQYsWLUjtS5c8ePAAnTp1AlBYZeynn37CmDFj1PnYcreD5m7tp0+fkhhEfHw8vd6iRQscOHBA66EKZeFzo2vXrqQod/jwYezdu5dcxNrc6RXkzZs3pIx27tw5GBsbU2iFl/wYAjx23KNHD/IMFqS4czYgxcXHjBmDxYsXA9B8qdzatWvh4+MDAKhYsSL27NlD4Sst8WEpidWsWbPQD8/dTq6urqhXrx6AfCWfnJwcAJLL5e7duwCkOPbt27cBAHXq1FHT9OK5d+8e1TcXFYPhdZpnz57Va6JJQXiS2IIFC2gCjRw5UmeuY0BSEuOJYcuXLye1MB6e4HFzLy8viksDUlhBRbUujcHV4Xr16oUnT54ovDZ37lwA+d+tkpS7BZon1RTn9nz79i2GDRtGv7c8PNFIl/CwWUl5IO7u7vD39wcAcjdri4cPHyrEiy0tLUuMmfJQWePGjbVqV0F44lqPHj2Ql5en8BqvETc1NS20QPMLIv43fIOwZ88ejUqmyi/Q1atXx7Nnzyhxdvfu3XSR/e7dO3To0IEufszMzNQdUri4BQKBQCAosyhbMK3jm8o4OTmxihUrsooVK7IffviBJSYmspSUFJaSklLi30VERDBra2tmbW3NZDIZW716NVu9erU6JqhEYmIiW79+PWvcuDFr3LixglBJ27ZttT6+OsiLlrRs2VLf5hTJpk2bSHDBxMSE2dnZkbCLvsRr/v77b/bJJ58UKUoze/ZsErxQAn3PS43P5RMnTrATJ04wY2PjIm9cqKSo24IFC5T5zjRGUlISa9CgAWvQoAEzNzdnderUYXXq1GFjx45lXl5erHbt2qx27doMAKtRowarUaMGu3HjhlZtSklJYfXr12f169cvUVyD36ysrJiVlRWbNm2aVu0qjpUrVzI7Ozu2fPlyul2+fLlYUY/t27ez7du3M0dHR4X/x/r16zVql7w4iampKevYsSOrW7cuq1u3bqF5K5PJ2KhRo9ioUaNKM6RS86fcuLgTEhIovluUjGdJ8FrWYcOGUTw6Li5OJ/J83I3i6+tL8nqVK1dGeHg4PvvsM62PrwqTJk3C5s2bAUix37CwMJ30XFaFlJQUyqTmmeA8s3vDhg0alSq8f/++0uUoUVFR5BaLj4+n8jSZTEahGSVkZsudi5vnB/DYYkHevXsHIyMjkl2UrzVn/4tZ7tmzB0C+zKq2iImJoYYtPj4+6Ny5s8LrPAcmKCiI3KVmZma4fv06HB0dtWYXz3EJDg4GkC9FGx0drZCXA0gZ54DkMubyvh07djT4Dltdu3bF2bNnUaVKFQBSvlBB3YTSIO/iVoVVq1ap9Xf40KQ+NUGNGjXoSun06dM6HXvRokW0q5LJZOz27ds6HV8ZJk6caPA7aMYkO7mtKCAVGh4ezsLDw9X+7EOHDjE7OztmZ2fHtmzZotLfrlmzhq1Zs4aZmpoqSIQeP36cHT9+XJmP0Pe81Ntc3rlzJ9u5cydbv349a9KkCWvSpAntrp2cnJiTk5OmhtIIkydPZpMnT2YAdL7TL4kuXbqwLl26MADMxcWFubi4sBcvXujbrGJ5/Pgxe/z4MXNycmIymYwNGzaMDRs2TOPjrF+/XkHGWP5WsWJF5ubmxtzc3NjkyZMV3uPm5qbukErNHxGDFggEAoHAACk3SmKagunY5c+L4bmKDQA0a9aMOqQYGrr+ftSBZ4Jy13ZxPYNV4cqVKwCk7PU3b94AAGX8K8v06dMBSPKVZ86coedtbGxKbV95x9PTk+4PGTIEgCRLm5iYSNK0c+bMwcKFCwGAOh3pi3HjxgEAfvjhB73aURBLS0u6zxUVudqVoeHn50eiNSkpKbC0tMS0adO0MtaUKVMoxPTDDz+gbt26lDHOjzdACmNs3ryZssq1fT4scwv0vXv36ITGdU5LC6+hzcvLowNY1fgvl86rVauW0iVaT58+JdWuxMREKgtbuXKlSmNrkqioKLpoKEo1jC9+/OA1NM6fP69QZqUpuLY7X5wBSbGOa4Cr0jx+wIABCgs0b7e3du1a9OjRQxPmlmt4OeLmzZvh4eFBJ8uAgAB07doVAKimWl/ISzuqK/OoDK9evaL48ftyZvr06YNTp04BkEpKecya590YAg8ePKDSyTt37lA5rLW1NUaNGlVkuZ2m4DkMJeUynDx5UqFMjNuqLYSLWyAQCAQCA6RM7aCvXLmCTp06kQZucZmfqpCdnU0ayampqZTlq6r+KtcKHj58OGkKl8ShQ4cwa9YsysCsVKkSJk+eDADo3r27SmOXlqioKNotx8TE4D//+Q+AwjvopKQkcukYUva2p6cndTtKTU0ttpPZvHnz0KBBA7XG4A1YFixYQGplqampJJ4yYsQIeHl5UbZuSdrMz58/L9I1JnbP5Qe+U7Wzs8Po0aO1Nk6rVq1IcW/x4sWFNPu5Rn1oaChu3rxJx52TkxNp7OsDriwWGxuLJUuWAJDcxcnJySQ6BOTv7n/66Sf0799f94YWgOuec68F7xamLcrUAn379m3k5uYqxATUhR8gX3/9NU0mmUym9meHhYUBAIyNjdGpUyeFxhu8w1F4eDg17Th16hRkMhlJ1q1atYra1ekSPz8/+Pv7kx0BAQEUK+XwcoyQkBBycWujaUZycjJdsAD58Z2C6kJRUVGFpDLfvXsHoHBTAHNzcyq7c3FxKbVEYHBwsEK5VHp6OgBg48aN2LhxI41VvXp1aoTSuHFjbNu2jRqlhIWF0f9JJpNh7969pbLpDlWJNQAAG4BJREFUQ0a+cxOg/ZggD3GEhobSBVV2djYYYwplVrxr3YgRI7TaSKNBgwb4+eefAQBnzpwhxURAamnLFzv5BiSAdKGpTcLCwmhOAlJJIgBs27YNgCTXC0BB/ZH9r2yOx4L79u1LanG8gYa+4Ops+/fvB5CfY6BtytQCHRsbC8YYHfzz589XOvbHpdrOnDmDQ4cOKbT+4zHtTZs2qR274jusAwcOoGXLlvR8t27d8Ouvvxb5N506daIYNI+d6Yrz588DyJfv5AdgwYU3MzMT8+fPByBNIG6vNnbQwcHBCl2LVOlmxSn4vK+vL8lqagJnZ2eSp1y4cCGOHTsGIF86lst7AvnyhsXpCwNAy5Yttdbar6zBd1Kpqal0sVrULo9r1p86dUrh93Zzc1NZA0FV+GI4btw4ii3zRZtfrFlZWZF3T80aWaWZPn06rK2tAUgXBjdv3iR75FtKAlJN9vjx4wFoT6eb/zYzZsxQOvmMf4/W1tZo164deRK10RdBHRYsWEB5Qbm5uXBwcKCue9pGxKAFAoFAIDBAypSSWE5ODgYNGkSN0s3MzOhqa+jQoQrC5QkJCXTVExUVRe7F9PR0yGQyiiE4Ozvjxx9/BFC6Kza+g/ruu++wb98+cuFwtw0guaP69esHQErdb926tV76tAYFBVGcPC4uDrt37y42G7Fhw4aIiYkBAAwcOJAaZGi6mwwgZeWqu4OWf+/IkSMBAO3bt9eoclhR3Lx5EwCwevVqXLp0SSF+VpyLHgB1ygkMDFRGQYxT7pTE5OnSpQsA4MKFC5SpbWZmBm9vb+rQlJCQQG7RV69eoUKFCtTp6urVq1rPjeDldosWLaLnUlJSMGjQIGpA0bVrV1K80jVhYWF0fjxw4AD1Hf/Xv/6F3r17a6zypTjkQzdFUbFiRVSvXp16PTdv3pzc17pu4FEQrlbXrl07mJiYkFdx4sSJ5JVgjGHOnDnUDKUUlM9uVpGRkQgMDAQAbNmyhRbeggeE/MIoT4MGDTB69GgMGDAAgOqyoMqQm5tLpUryODo6wtzcXOPjqcr8+fOxbNkyANL3NHjwYDqxyWQyqsmWyWRgjJGb8dChQ1pNLClKphNQboGWd+fxMjddJ7IlJydj9+7dAKQuQ5wHDx6gdu3a1LHp008/Vfe4K9cLNJe9Xb58OS3Ijx49KjacwY+DmTNnAoAmTpqCUvL5558DAE6fPq3wPJ/XLVq0oMXZ0Bg1ahQAKRxqb29PErJ88wVInemOHTumiY2V6GYlEAgEAkFZpcztoOUJDw/H2rVrAUjuHL6bBqSdIU/+6t+/P7nPunfvTu6zD5WsrCzKzL506RKCg4Op+TzfNQOSG97JyYmSrLTh1haoRLneQcvDKx+Sk5OxefNmhaROztGjRyGTyShbXhyfgtLAxYN4dj73wDLGSJhp7dq1SgtRvYfy6eIWCD5gPpgFWiDQNS9fvgQAbN++HTNmzKDne/XqRWFVnu+gAYSLWyAQCASCsorYQQsEZQexgxYIygdiBy0QCAQCQVlFLNACgUAgEBggYoEWCAQCgcAAEQu0QCAQCAQGiFigBQKBQCAwQMQCLRAIBAKBASIWaEGZJD4+HvHx8bC0tISnpycyMzORmZmpb7MEAoEAgNSq18fHp1QtR8tUP+jS8P333wMApk2bhkaNGil0bxoyZAgA4LPPPtOLbYYAb/IwZ84cJCQkAABq1KiBYcOGUQ/Z1NRU6l388ccf68fQ/3Hq1CkAQL169ajDlq64desWAGDr1q3Fvuf06dPUpN7KygqVK1fGkydPAEhN3w21YUBZIDs7G4DUOc7Pz4/6Dn/55Zf45Zdf9GlamYJ3Xjtx4gSOHDlCc2rp0qUa7aGuLCkpKejevTvNL3kqVqyIa9euoWnTpjq3SxXu3r0LAJg5cyb++ecfbN++vVSf90Es0Ddu3MCCBQsASPqqkZGRmD9/Pr2+efNmAMDChQsxduxYfZhYJLwjVlZWFt2PjY1VsF1TnD17FgCQmJiInj170rjff/89XdwA0nck/6++iI+PBwB88sknOh+3c+fOAPKlAYGiu6fxx+np6XBxcUH79u0B6P/ipiyRkZGh0Kns/PnzpJn8559/Aii+teGHTmZmJrVDZYxh2bJldPEN5B+/fKHm3+PSpUuxb98+AChysdQ0KSkpAIDx48fj1q1bqFq1KgCp7SPvr3Du3DkkJiYa5ALNW1Hu3buXugQ+ePAAS5cuhZOTU6k+W7i4BQKBQCAwQMr1Djo8PByA1ED91atXxb7v8ePHAIDZs2ejbdu2AFDqKx914Dt5Z2dnREVFYfny5QCkK1x+dVupUiU0atQIAKjDiibgTdOTk5MREhICAMjLy0NCQgLZlZubiy+++EJjY5YGbqMyrmJ+hWtqaqr2eHl5eQCAcePG0c7D1NQUVlZWAIBJkyYp7OQaN26Me/fuAQCsra0xadIkmJiYqD3+hwZ3W3t6elJvaKCwp2Ls2LHo168fAKBVq1a6NdJAuXjxIgCpP/aJEycAFO3hKY7s7Gxy1Wob7tYGpN1669atERwcDACoVasW3r17BwB48eIFLCwsdGKTMvDv+MiRIzhy5AgA4K+//oK9vT0AIDQ0FN26dSv1OOV2gc7NzaUF7uXLl0odnM+fPydXr64W6EmTJgGQFmeui85bPsq3O+NkZGTQhYcmF+gOHToAAKZPn47nz58DAKpXrw57e3ty2xgK6enpSE1NBQBcv34dGzdupNeaNWsGR0dHenz79m2aTIsWLVJ7zOjoaAD5oQD+ed98802xfzN48GC1x/vQ4SfmBw8eAMhvJdm3b1/0798fgPT9mpub683FnZOTAwC4efMmjhw5Ag8Pj/f+TXZ2Nvbs2QNAOi9xVzIAfPPNN1ixYkWp7eIbDr44qwMPc2mb//znP+RGr1+/Po4dO4bq1avT60ZGkpPX2tpaJ/YUxZs3bxAZGQlAWpiDgoJw6dIlANI6w49NNzc3auOrKXuFi1sgEAgEAgOk3O6gV61aRVcz8kyfPh3NmjWjxCe+GwUAW1tbnWfXcneO/C5AJpPBycmJXNnVqlWj16pVq6bRnXNZJC4ujjKiU1NTcfTo0RLf/+9//1srdoSEhFCGu42NjVbG+FDhLm4eJjh06BAA3e3siiInJ4eyxCMjI3Hs2DEAQExMDACovPtt37491q1bB0BK/tRUwiP/zgry0UcfwdXVlR7z84u3t3eh92p7x8qrRtavX09JYZs2bVLYPRsKc+fORUBAAID8UIGZmRkAwMPDAyNHjgQADBw4UONjl9sF+unTpwqP27VrB0ByqVhYWJCbTD5L0dTUFLa2tjqz8fz583RiT05OJle2m5sbzp07pzM7AMDOzg6AlGG8bds2AFLJlTyvX7/G27dvAUguR325Fo8fP073Q0JCUKVKFdy5cweAFAeSx8PDAx07diz1mLVq1QIAtG7dmrKKr169ShndBcfo0aMHxURr1qxZ6vE/dPRZAnn58mUAwMiRIynjGcg/JgDAwcEBderUASBlHPO57OTkVOj379GjBwCgf//+aNiwocbsTEpKwldffYXTp08Xes3T0xPe3t6UY6NPwsPDMWXKFHrMv6vo6Gh06tQJxsaGtSx5e3tTaOuzzz6DpaUlpk2bBkDKNdEmwsUtEAgEAoEBYliXKhpE3i0M5F8Fv3z5EhYWFrRz7dKli85t40RHR5N7TH43qg8XNt8NtGzZUkGRKyYmBmvXrgUAnDx5ErGxsQCk5LZly5ZRFrMuycrKQpMmTQBI3gZzc3PykGgLfrwcOXIEffv2BSDV13N34t27dxV+w40bN6J27doApO+0UqVKlET0+eefa9XW8gDPeOfZ8FycxM/PjxKHAGDy5MmwtLTUmh0PHz4kIaOsrCzKzB00aBCFw169egUbGxuaCw8fPqS/r169OqpUqaI1++R5/fq1gncJyHe77tixQyc2vI/MzEz4+fnhxYsX9ByvsJk6dSrCw8PJ9d60aVPK8NYH/Hc0NTWlmvuKFSvq1gjGmCHeSs2bN2/YgAED2IABA5hMJmNGRkbMyMiIrVmzRhMfX2piY2OZra0tA8AAMJlMRvfnz5+vN7v27t3LHB0dmaOjI1u7di2rWrUqk8lkTCaTsfbt27M2bdqwNm3aMJlMxoKCgnRq2/Pnz9nz589ZzZo12bfffsu+/fZbnY7PycjIYBkZGWzXrl1s8eLFbPHixczOzo7VqFGDGRsbM2NjYzrejIyM6Pjjr40ePZrFxcWxuLg4VYfW97zU2Vx+/fo1e/36NWvSpAkdf3yOyD82NzdnV69eZVevXlV3qBIZP348c3FxYS4uLiw+Pl4rY2iKuLg4hePOyMiIBQYGssDAQH2bRgQEBNB5ruDN1NSUWVpa0mNzc3Pm4+PDfHx8dG7no0ePWNu2bVnbtm3ZJ598wh49esQePXqkySGUmj8yJlfCY0BoxKh58+YBkJI3+O5mxIgR2L59OypUqKCJIVSGK4INGTIEMTExhUqr+P179+7B2dlZ5/YFBQVh2LBhAKSLtypVqtDVd+/evcnGatWqoXPnzu9N0NIkmzZtAiDt3nnM3NPTE3Xq1CHp1oKeE13Dv4+cnBwqpwkODi4Ur+eSqVOmTIGnpycAKOONKIuSWWrN5YyMDACgBCJOu3btyMM0e/ZsAKBd7b59+zSe3DRhwgT67fjxZ6g8fvwY9erVU3iOH2d2dnbw9fVViJvLU6NGDZ0kaHXp0gW///477UTnzJlDJZ7W1tb47LPPcODAAQCSd4R7S7y9vbF69Wqt28f56aefqATWzMwMS5YsASDN0aFDh6Jy5cqlHUKpuSxi0AKBQCAQGCLKbrV1fNMIc+fOZXPnzlVwcRsZGbHo6GhNDaESYWFhzM3Njbm5uRVyaxe8rw+3DufAgQPswIEDbNeuXSw5ObnI91SuXJm1atWKZWZmsszMTK3blJOTw1xdXZmrqysDwOzs7JidnR0zMzNjAFi3bt1Yt27d1HEba52IiAgWERHBbGxsmI2NTSH3d7169Vi9evXY8+fP3/dR+p6XOpvLb9++ZW/fvmXLly9n9evXZ9u3b2fbt29n6enpLCsri2VlZbGjR48yU1NTcndrw5XbpUsXtmHDBrZhwwaNf7amKcrFXTDMUtzN1dWVeXt7M29vb3b//n2t2fjw4UMWEhLCnj59yp4+fVrie8+dO0fnRBsbG63ZVJC7d+8W64YHwMaNG8fS09NZenp6aYYRLm6OTCZTcDFGRUWhQYMGmhyiRLhbu1OnTiQMX61aNcybN4/KcyIjIzF69Giyd+PGjZgwYYLObFQVCwsLZGVlUXmTtssNVq5ciW+//RYAsGHDBgwYMACA5Apdvnw5dY0ZOXIkfvzxR63aUlpOnTqFWbNmAQAiIiLo2HR3d8fhw4cLuXXl+GBc3MpiYWFBSY3a6GbVs2dPKjusVasWfvvtNwBSwqQ8rVu3pmYoxbmRS8v58+cBAI6OjhQCkFfNA6Sywy+//BJAvtIZIG3Ehg8fTroPf//9N73m7u6OP/74gx7Xrl0boaGhALQ/r0vi1atXcHFxAQCkpaWRwqG24J/fp08fXLt2TeG1//u//wMAHDhwAC9fvsQPP/wAoOgaciURLm6BQCAQCMoq5bbMSp6CO2hdw7Wsk5KSyI66deti5MiRlNRUp04d2tU/f/5c78lOylCvXj1K1tImv/76KxYuXIivvvoKgNSwgvPRRx8hMDAQiYmJAIBffvmFEsa6du2qddvUoWfPnrQzWbt2LVatWgVA2iGdPn1a9IpWAQ8PD+zduxcA8OzZM400RpGnZs2aCmqDPj4+AIpW6+JzYdOmTUrpcqsKL/eytrYm9aquXbtSCWDDhg0xcOBAspeLCnEcHByQnJwMAAplTrVq1cKGDRvg5+cHQBJ56tOnD4B8XW99kJSURGWdjDH8+uuvGmlAURz8exw5ciQiIiJIRGbt2rWUsJuXl4cdO3YgKytLa3YooKwvXMc3jaLvGPTSpUvZ0qVLFew4f/68wns2bdpEsTQ7Ozut28RLhdLS0lT6u7Nnz7KzZ88yExMT1qVLFy1Zp8jly5dZ9+7d2YMHD9iDBw+KfM/169fZ9evXWcWKFdm6devYunXrdGJbabl7967Csdm3b9+S3q7veanVufzs2TP27NkzNnnyZIozv4+IiAiFsit+fGqKDh060Gd36tSJbdu2jW3bto3l5uYq3C5cuEAlQUZGRmzq1Kls6tSpGrODMUZ2FIwf161bl9WtW5ddvny5VJ9vZWXFrKysFD5bn1y+fFmhBOvatWt6tYcxxubPn89kMhnz8PBgHh4epfkopeZPmdxB8/jIjRs3KAajShvEiIgIncageWesGTNmUIkIjz1z6cAFCxaA/S8fgMeytMmjR48AAN9//z0CAwOV+ptnz57h66+/BiBdSQ4fPlxr9snj6upapHyhPC1btgQgySjyOGRZbPH44MEDioV9SPreSUlJpEW9YsUK0jp+H87OzlQWeODAAZJk5BKspeXnn3+mzkU9e/Ys9jfp0KEDlQslJCTg4MGDAIDVq1drTLqyV69eAKQcBnn++ecfAEC/fv0we/Zs2uXrXFRDQ/Dd6dKlS+m5Tz75RK/tRPmc5EIwvXv31sm4IgYtEAgEAoEBUuZ20PHx8Rg7diwAqdk3j0mosoP++eefddqrl49VcMyoqCj4+voCkP4vvFGHm5ubzmw7d+4cXr58WVLmMHHz5k3cvHkTAFChQgVqDmBIODg4kFhIenq6xsUXQkJCFBqxcC+CurvdiIgIhcc1a9b8oHbOnNDQUMo6btu2rdI5IzKZTKuiQ3Xr1kXdunVV+ptFixZRM56uXbsqZEiXBt5g4tWrVyRdLE96ejp8fX1x/fp1AICXlxdJ4r6vU9aKFSsUJH71RVZWFrZs2QJA2q3yXIIFCxbo1SaexX3z5k2YmJiQx07blLkFesqUKVSqBOS3i9yzZw8l3vCD0pCJi4vDkCFDqATL3d2dOqTo8uLhr7/+wqtXr0pcoHlCjHzZV8uWLcnlpkkOHz4MQHLzq5Iox3Vzt23bhunTpwOQEsg0RUJCAgBJL5i3uqxdu7Zauunx8fF0gh0+fDgtRsbGxlprjWnoHDt2jH5vVX6358+f48yZM9oySy0aNGhAG4eoqCi8fftWIxcRPHGrbdu2pA195swZbNy4EUB+i86QkBD6l58L69SpU6yS2PPnz7Ft2zaFpDJNzh1lyc7OxubNm8lFD0ilqQBIbU8T7N+/H4Bi/4O2bdsW2nCkpqYCkOY8Py8BUpKqrhZo4eIWCAQCgcAAKXM76O7du1OjdCA/eD9q1CjaBXbu3BlmZmZo06ZNkZ+hy57PBeGlDOvXr0dKSgql7+/cuVNlV1pp4Bq9rVu3hq+vLwl9FAUXCOHJKABIL1fT8DKQoUOHUpnUoEGDShRMiI6OphIsmUxGfyff9ai08GPL1dUVhw4dAiCVo/Ad9OLFi9GzZ88SP2Pfvn0AgOnTp9P/U74EcOnSpdQr+EPj5MmT1MWooJ50SXh5edFOx9jYGOPHj9eKfarC3cxffPEFsrKyNNrRysbGhnbTffr0oV7Z8fHx8Pf3V3jv3bt3AQB37tzBiRMnlB6Da0+rQlpaGnbv3g1A8gwpG156/fo1AMDX1xcBAQH0fPfu3bF161aV7XgfPKz44MEDeu7TTz/FxIkT6fHDhw+xa9cuAFBw/c+ZM4fKOHVBmVMSi4mJoXrA+/fvk1unxA9jTMGdERkZqfEsbu6WDgkJURivpPvu7u7kntJHYwxAiv0NGDCA1IcWLVpEr50+fRpLly6lhblixYoUHxo5cqRWa8uPHj1KtaTGxsaoUqUKtXmU58WLFwgNDUVeXh4AqQ6a/1+0QU5ODlasWAEA8Pf3p7hphQoVYGNjAy8vLwBQyEJmjOHw4cMUKnj37h29ZmFhgfnz5wMAZs2a9b6LinKrJGZvb081t7t376aWnAUzoFNSUui79fPzw+rVq+m3nzp1KrVG1Tf8QsHCwkJh0dEmKSkpmDlzJj0+c+YMkpKSABQ+B5bE2LFj6bykSj35uHHjSHWtfv36dBHh7OxMlSwuLi54+PAhKXV16NABY8aMAQDcunULJiYmNM8DAgJgb2+v9PjKwhXZvvvuOwo1paWlFft+ExMTsnHz5s2aMkMoiQkEAoFAUFYpcztoeX755Rekp6cDkNyvXB+V69TSh+lgB813Prxt5Pt20NWqVUNoaChatGihUTvU4csvvyT3a1HwWnNfX1/SC9c2jDHSC96/fz8OHjyI27dv0+u8kbulpSWsra0xY8YMANLVuq5U4/7880989913AIBr164pZHfLU/D4a9SoEdXpTps2DZ9++qmyQ5bbHfTWrVsVkhC5TkDBHfQ///xDrf5u3bqFihUr0u5m5cqVSlUj6AK+A01NTSUPia65e/cuKYZNmTIF9+/fR3Z2drHv52GkHTt2kAa2Kvz4449Yt24dACn5lLuu3wf/jWvXro3FixfT76kLeMLn2bNnqYaew0OkTZs2Rbt27TQ9tFJzuUwv0AXhsaitW7fi6NGjuHLlivRhcifIuXPnwsfHR+NZitxFzXs888cF+4bymCXvVW0IvHv3jnrd7t+/H02bNqXXhg0bRpJ3H2L5j7IkJCQgLCyMxBV4DBKQjr+BAwfSidrBwUHdhaTcLtCvXr2iBbqki0XGGJUMDRo0CFOnTkX9+vU1YOb7efDgAcUlZ8+eXWxP4KioKGrmcufOHaVFV7TN/v37S3Tl8vCMJmRS09LSyJUcGRlJAh92dnYICgoi17Wrqyv69esHADqN7RoAH94CrU+4+k10dDSAfPWwSpUq6c0mQbmj3C7QZYG0tDQMHDgQgFRzPHXqVABA3759kZCQgCNHjgCQdpI8CVDZXaTgg0PEoAUCgUAgKKuIHbRAUHYQO2g98+rVKwBSFy0ev4yPj4eJiQm52r28vEiYhKt/CQQFEC5ugaCcIRZoAyE9PZ3c15cvX4aDg4NC7oZA8B6Ei1sgEAgEgrKK2EELBGUHsYMWCMoHSs1lQ5X6LIsnIoFAUBgxlwUCNREuboFAIBAIDBCxQAsEAoFAYICIBVogEAgEAgNELNACgUAgEBggYoEWCAQCgcAAEQu0QCAQCAQGiFigBQKBQCAwQMQCLRAIBAKBASIWaIFAIBAIDBCxQAsEAoFAYICIBVogEAgEAgNELNACgUAgEBggYoEWCAQCgcAAEQu0QCAQCAQGiFigBQKBQCAwQMQCLRAIBAKBASIWaIFAIBAIDBCxQAsEAoFAYICIBVogEAgEAgNELNACgUAgEBggYoEWCAQCgcAAEQu0QCAQCAQGiFigBQKBQCAwQMQCLRAIBAKBASIWaIFAIBAIDBCxQAsEAoFAYID8fyfKzoiF/cXuAAAAAElFTkSuQmCC\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "cl_a, cl_b = 3, 5\n", "X_aa = X_train[(y_train == cl_a) & (y_train_pred == cl_a)] #3으로 정확히 분류\n", "X_ab = X_train[(y_train == cl_a) & (y_train_pred == cl_b)] #5로 잘못 분류\n", "X_ba = X_train[(y_train == cl_b) & (y_train_pred == cl_a)] #3으로 잘못 분류\n", "X_bb = X_train[(y_train == cl_b) & (y_train_pred == cl_b)] #5로 정확히 분류\n", "\n", "plt.figure(figsize=(8,8))\n", "plt.subplot(221); plot_digits(X_aa[:25], images_per_row=5)\n", "plt.subplot(222); plot_digits(X_ab[:25], images_per_row=5)\n", "plt.subplot(223); plot_digits(X_ba[:25], images_per_row=5)\n", "plt.subplot(224); plot_digits(X_bb[:25], images_per_row=5)\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 대부분의 잘못 분류된 이미지는 에러인 것 같고 그 원인은 선형 모델인 SGDClassifier를 사용했기 때문
\n", "- 선형 분류기는 클래스마다 픽셀에 가중치를 할당하고 새로운 이미지에 대해 단순히 픽셀 강도의 가중치 합을 클래스의 점수로 계산
\n", "- 따라서, 이 분류기는 이미지의 위치나 회전 방향에 매우 민감
\n", "- 3과 5의 에러를 줄이는 한 가지 방법은 이미지를 중앙에 위치시키고 회전되어 있지 않도록 전처리 하는 것" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "## 3.6 다중 레이블 분류" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- **다중 레이블 분류**multilabel classification : 여러 개의 이진 레이블을 출력하는 분류 시스템
\n", "얼굴 인식 분류기를 예로 들면, 같은 사진에 여러 사람이 등장한다면 인식된 사람마다 레이블을 하나씩 할당해야 함(즉, '앨리스 있음, 밥 없음, 찰리 있음')" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[False, True],\n", " [False, False],\n", " [False, False],\n", " ...,\n", " [False, False],\n", " [False, False],\n", " [ True, True]])" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from sklearn.neighbors import KNeighborsClassifier\n", "\n", "y_train_large = (y_train >= 7) #숫자가 7 이상인지\n", "y_train_odd = (y_train % 2 == 1) #홀수인지\n", "y_multilabel = np.c_[y_train_large, y_train_odd] #두 개의 1차원 배열을 칼럼으로 세로로 붙여서 2차원 배열 만들기\n", "y_multilabel #다중 타깃 레이블이 담긴 배열" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',\n", " metric_params=None, n_jobs=None, n_neighbors=5, p=2,\n", " weights='uniform')" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "knn_clf = KNeighborsClassifier()\n", "knn_clf.fit(X_train, y_multilabel)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "KNeighborsClassifier는 다중 레이블 분류를 지원하지만 모든 분류기가 그런 것은 아님" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[False, True]])" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "knn_clf.predict([some_digit]) #숫자 5 예측" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "다음 코드는 다중 레이블 분류기를 평가하기 위해 모든 레이블에 대한 F1 점수의 평균을 계산
\n", "average=\"macro\" 옵션은 모든 클래스의 FP, FN, TP 총합을 이용해 F1 점수를 계산" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "[Parallel(n_jobs=-1)]: Using backend LokyBackend with 4 concurrent workers.\n", "[Parallel(n_jobs=-1)]: Done 3 out of 3 | elapsed: 21.8min finished\n" ] }, { "data": { "text/plain": [ "0.97709078477525" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y_train_knn_pred = cross_val_predict(knn_clf, X_train, y_multilabel, cv=3, verbose=3, n_jobs=-1)\n", "f1_score(y_multilabel, y_train_knn_pred, average=\"macro\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "이 코드는 모든 레이블의 가중치가 같다고 가정한 것
\n", "앨리스 사진이 밥이나 찰리 사진보다 많다면 앨리스 사진에 대한 분류기의 점수에 더 높은 가중치를 둘 것. 간단한 방법은 레이블에 클래스의 **지지도**support(즉, 타깃 레이블에 속한 샘플 수)를 가중치로 줌. 이렇게 하려면 이전 코드에서 average=\"weighted\"로 설정" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "## 3.7 다중 출력 분류" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- **다중 출력 다중 클래스 분류**multioutput-multiclass classification(또는 **다중 출력 분류**multioutput classification) : 다중 레이블 분류에서 한 레이블이 다중 클래스가 될 수 있도록 일반화한 것(즉, 값을 두 개 이상 가질 수 있음)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "이미지에서 노이즈를 제거하는 시스템은 분류기의 출력이 다중 레이블(픽설당 한 레이블)이고 각 레이블은 여러 개의 값을 가짐(0부터 255까지 픽셀 강도). 그러므로 이는 다중 출력 분류 시스템임" ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [], "source": [ "#넘파이의 randint() 함수를 사용하여 픽셀 강도에 노이즈를 추가\n", "noise = np.random.randint(0, 100, (len(X_train), 784)) #파라미터 : 0~99까지의 랜덤 숫자, 행렬 사이즈\n", "X_train_mod = X_train + noise\n", "noise = np.random.randint(0, 100, (len(X_test), 784))\n", "X_test_mod = X_test + noise\n", "\n", "#타깃 이미지는 원본 이미지\n", "y_train_mod = X_train\n", "y_test_mod = X_test" ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/anaconda3/envs/mlbook/lib/python3.5/site-packages/matplotlib/font_manager.py:1320: UserWarning: findfont: Font family ['NanumBarunGothic'] not found. Falling back to DejaVu Sans\n", " (prop.get_family(), self.defaultFamily[fontext]))\n" ] }, { "data": { "image/png": "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\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "#테스트 세트에서 이미지를 하나 선택\n", "some_index = 5500\n", "plt.subplot(121); plot_digit(X_test_mod[some_index])\n", "plt.subplot(122); plot_digit(y_test_mod[some_index])\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/anaconda3/envs/mlbook/lib/python3.5/site-packages/matplotlib/font_manager.py:1320: UserWarning: findfont: Font family ['NanumBarunGothic'] not found. Falling back to DejaVu Sans\n", " (prop.get_family(), self.defaultFamily[fontext]))\n" ] }, { "data": { "image/png": "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\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "knn_clf.fit(X_train_mod, y_train_mod)\n", "clean_digit = knn_clf.predict([X_test_mod[some_index]])\n", "plot_digit(clean_digit)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "## 3.8 연습문제" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "1 . MNIST 데이터넷으로 분류기를 만들어 테스트 세트에서 97% 정확도를 달성해보세요. 힌트: KNeighborsClassifier가 이 작업에 아주 잘 맞습니다. 좋은 하이퍼파라미터 값만 찾으면 됩니다.(weights와 n_neighbors 하이퍼파라미터로 그리드 탐색을 시도해보세요)." ] }, { "cell_type": "code", "execution_count": 135, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Fitting 5 folds for each of 6 candidates, totalling 30 fits\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "[Parallel(n_jobs=-1)]: Using backend LokyBackend with 4 concurrent workers.\n", "[Parallel(n_jobs=-1)]: Done 30 out of 30 | elapsed: 623.9min finished\n" ] }, { "data": { "text/plain": [ "GridSearchCV(cv=5, error_score='raise-deprecating',\n", " estimator=KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',\n", " metric_params=None, n_jobs=None, n_neighbors=5, p=2,\n", " weights='uniform'),\n", " fit_params=None, iid='warn', n_jobs=-1,\n", " param_grid=[{'weights': ['uniform', 'distance'], 'n_neighbors': [3, 4, 5]}],\n", " pre_dispatch='2*n_jobs', refit=True, return_train_score='warn',\n", " scoring=None, verbose=3)" ] }, "execution_count": 135, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from sklearn.model_selection import GridSearchCV\n", "\n", "#‘uniform’일 때는 np.mean 함수를 사용하여 단순 평균을 계산하고, ‘distance’일 때는 거리를 고려한 가중치 평균(average)을 계산\n", "param_grid = [{'weights': [\"uniform\", \"distance\"], 'n_neighbors': [3, 4, 5]}]\n", "\n", "knn_clf = KNeighborsClassifier()\n", "grid_search = GridSearchCV(knn_clf, param_grid, cv=5, verbose=3, n_jobs=-1)\n", "grid_search.fit(X_train, y_train)" ] }, { "cell_type": "code", "execution_count": 138, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'n_neighbors': 4, 'weights': 'distance'}" ] }, "execution_count": 138, "metadata": {}, "output_type": "execute_result" } ], "source": [ "grid_search.best_params_" ] }, { "cell_type": "code", "execution_count": 139, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.9714" ] }, "execution_count": 139, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from sklearn.metrics import accuracy_score\n", "\n", "y_pred = grid_search.predict(X_test)\n", "accuracy_score(y_test, y_pred)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "2 . MNIST 이미지를 (왼, 오른, 위, 아래) 어느 방향으로든 한 픽셀 이동시킬 수 있는 함수를 만들어보세요. 그런 다음 훈련 세트에 있는 각 이미지에 대해 네 개의 이동된 복사본(방향마다 한 개씩)을 만들어 훈련 세트에 추가하세요. 마지막으로 이 확장된 데이터셋에서 앞에서 찾은 최선의 모델을 훈련시키고 테스트 세트에서 정확도를 측정해보세요. 모델 성능이 더 높아졌는지 확인해보세요! 인위적으로 훈련 세트를 늘리는 이 기법을 **데이터 증식** 또는 **훈련 세트 확장**training set expansion이라고 합니다." ] }, { "cell_type": "code", "execution_count": 141, "metadata": {}, "outputs": [], "source": [ "from scipy.ndimage.interpolation import shift\n", "\n", "def shift_image(image, dx, dy):\n", " image = image.reshape((28, 28))\n", " shifted_image = shift(image, [dy, dx], cval=0, mode=\"constant\") #모드가 constant이면 경계 밖의 값이 cval값으로 채워짐\n", " return shifted_image.reshape([-1])\n", "\n", "X_train_augmented = [image for image in X_train] #numpy.ndarray 타입을 list 타입으로 변경\n", "y_train_augmented = [label for label in y_train]\n", "\n", "for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1)): #오른, 왼, 아래, 위\n", " for image, label in zip(X_train, y_train):\n", " X_train_augmented.append(shift_image(image, dx, dy)) #이동시킨 이미지를 훈련 세트에 추가\n", " y_train_augmented.append(label)\n", "\n", "X_train_augmented = np.array(X_train_augmented)\n", "y_train_augmented = np.array(y_train_augmented)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "어떤 학습 알고리즘은 훈련 샘플의 순서에 민감해서 많은 비슷한 샘플이 연이어 나타나면 성능이 나빠지기때문에 훈련 세트를 섞어서 모든 교차 검증 폴드가 비슷해지도록 하여 이런 문제를 방지함" ] }, { "cell_type": "code", "execution_count": 142, "metadata": {}, "outputs": [], "source": [ "shuffle_idx = np.random.permutation(len(X_train_augmented))\n", "X_train_augmented = X_train_augmented[shuffle_idx]\n", "y_train_augmented = y_train_augmented[shuffle_idx]" ] }, { "cell_type": "code", "execution_count": 143, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.9763" ] }, "execution_count": 143, "metadata": {}, "output_type": "execute_result" } ], "source": [ "knn_clf = KNeighborsClassifier(**grid_search.best_params_)\n", "knn_clf.fit(X_train_augmented, y_train_augmented)\n", "y_pred = knn_clf.predict(X_test)\n", "accuracy_score(y_test, y_pred)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "3 . **타이타닉**Titanic 데이터셋에 도전해보세요. 캐글Kaggle에서 시작하면 좋습니다(https://www.kaggle.com/c/titanic)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "승객의 나이, 성별, 승객 등급, 승선 위치 같은 속성을 기반으로 하여 승객의 생존 여부를 예측하는 것이 목표입니다." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "데이터를 적재합니다:" ] }, { "cell_type": "code", "execution_count": 83, "metadata": {}, "outputs": [], "source": [ "import os\n", "\n", "TITANIC_PATH = os.path.join(\"datasets\", \"titanic\")\n", "\n", "import pandas as pd\n", "\n", "def load_titanic_data(filename, titanic_path=TITANIC_PATH):\n", " csv_path = os.path.join(titanic_path, filename)\n", " return pd.read_csv(csv_path)\n", "\n", "train_data = load_titanic_data(\"train.csv\")\n", "test_data = load_titanic_data(\"test.csv\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "훈련 세트에서 맨 위 몇 개의 열을 살펴 보겠습니다:" ] }, { "cell_type": "code", "execution_count": 84, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
PassengerIdSurvivedPclassNameSexAgeSibSpParchTicketFareCabinEmbarked
0103Braund, Mr. Owen Harrismale22.010A/5 211717.2500NaNS
1211Cumings, Mrs. John Bradley (Florence Briggs Th...female38.010PC 1759971.2833C85C
2313Heikkinen, Miss. Lainafemale26.000STON/O2. 31012827.9250NaNS
3411Futrelle, Mrs. Jacques Heath (Lily May Peel)female35.01011380353.1000C123S
4503Allen, Mr. William Henrymale35.0003734508.0500NaNS
\n", "
" ], "text/plain": [ " PassengerId Survived Pclass \\\n", "0 1 0 3 \n", "1 2 1 1 \n", "2 3 1 3 \n", "3 4 1 1 \n", "4 5 0 3 \n", "\n", " Name Sex Age SibSp \\\n", "0 Braund, Mr. Owen Harris male 22.0 1 \n", "1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 \n", "2 Heikkinen, Miss. Laina female 26.0 0 \n", "3 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 \n", "4 Allen, Mr. William Henry male 35.0 0 \n", "\n", " Parch Ticket Fare Cabin Embarked \n", "0 0 A/5 21171 7.2500 NaN S \n", "1 0 PC 17599 71.2833 C85 C \n", "2 0 STON/O2. 3101282 7.9250 NaN S \n", "3 0 113803 53.1000 C123 S \n", "4 0 373450 8.0500 NaN S " ] }, "execution_count": 84, "metadata": {}, "output_type": "execute_result" } ], "source": [ "train_data.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- Survived: 타깃. 0은 생존하지 못한 것이고 1은 생존을 의미
\n", "- Pclass: 승객 등급. 1, 2, 3등석
\n", "- SibSp: 함께 탑승한 형제, 배우자의 수
\n", "- Parch: 함께 탑승한 자녀, 부모의 수
\n", "- Cabin: 객실 번호
\n", "- Embarked: 승객이 탑승한 곳. C(Cherbourg), Q(Queenstown), S(Southampton)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "누락된 데이터가 얼마나 되는지 알아보겠습니다:" ] }, { "cell_type": "code", "execution_count": 85, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "RangeIndex: 891 entries, 0 to 890\n", "Data columns (total 12 columns):\n", "PassengerId 891 non-null int64\n", "Survived 891 non-null int64\n", "Pclass 891 non-null int64\n", "Name 891 non-null object\n", "Sex 891 non-null object\n", "Age 714 non-null float64\n", "SibSp 891 non-null int64\n", "Parch 891 non-null int64\n", "Ticket 891 non-null object\n", "Fare 891 non-null float64\n", "Cabin 204 non-null object\n", "Embarked 889 non-null object\n", "dtypes: float64(2), int64(5), object(5)\n", "memory usage: 83.6+ KB\n" ] } ], "source": [ "train_data.info()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- Age, Cabin, Embarked 속성의 일부가 null
\n", "- 특히 Cabin은 77%가 null. 이 속성은 무시
\n", "- Age는 19%가 null. null값은 중간값으로 채움
\n", "- Name과 Ticket 속성은 숫자로 변환하기가 까다롭기 때문에 이 두 속성은 무시\n", "- 변환시켜야할 범주형 특성 : Pclass, Sex, Embarked" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "이제 전처리 파이프라인을 만듭니다. DataFrame으로부터 특정 속성만 선택하기 위해 이전 장에서 만든 DataframeSelector를 재사용하겠습니다:" ] }, { "cell_type": "code", "execution_count": 88, "metadata": {}, "outputs": [], "source": [ "from sklearn.base import BaseEstimator, TransformerMixin\n", "\n", "# 사이킷런이 DataFrame을 바로 사용하지 못하므로\n", "# 수치형이나 범주형 컬럼을 선택하는 클래스를 만듭니다.\n", "class DataFrameSelector(BaseEstimator, TransformerMixin):\n", " def __init__(self, attribute_names):\n", " self.attribute_names = attribute_names\n", " def fit(self, X, y=None):\n", " return self\n", " def transform(self, X):\n", " return X[self.attribute_names]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "숫자 특성을 위한 파이프라인을 만듭니다:" ] }, { "cell_type": "code", "execution_count": 91, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[22. , 1. , 0. , 7.25 ],\n", " [38. , 1. , 0. , 71.2833],\n", " [26. , 0. , 0. , 7.925 ],\n", " ...,\n", " [28. , 1. , 2. , 23.45 ],\n", " [26. , 0. , 0. , 30. ],\n", " [32. , 0. , 0. , 7.75 ]])" ] }, "execution_count": 91, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from sklearn.pipeline import Pipeline\n", "from sklearn.impute import SimpleImputer\n", "\n", "imputer = SimpleImputer(strategy=\"median\")\n", "\n", "num_pipeline = Pipeline([\n", " (\"select_numeric\", DataFrameSelector([\"Age\", \"SibSp\", \"Parch\", \"Fare\"])),\n", " (\"imputer\", SimpleImputer(strategy=\"median\")),\n", " ])\n", "\n", "num_pipeline.fit_transform(train_data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "이제 범주형 특성을 위한 파이프라인을 만듭니다:" ] }, { "cell_type": "code", "execution_count": 93, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0., 0., 1., ..., 0., 0., 1.],\n", " [1., 0., 0., ..., 1., 0., 0.],\n", " [0., 0., 1., ..., 0., 0., 1.],\n", " ...,\n", " [0., 0., 1., ..., 0., 0., 1.],\n", " [1., 0., 0., ..., 1., 0., 0.],\n", " [0., 0., 1., ..., 0., 1., 0.]])" ] }, "execution_count": 93, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from sklearn.preprocessing import OneHotEncoder\n", "\n", "\n", "cat_pipeline = Pipeline([\n", " (\"select_cat\", DataFrameSelector([\"Pclass\", \"Sex\", \"Embarked\"])),\n", " (\"imputer\", SimpleImputer(strategy='most_frequent')),\n", " (\"cat_encoder\", OneHotEncoder(sparse=False)),\n", " ])\n", "\n", "cat_pipeline.fit_transform(train_data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "마지막으로 숫자와 범주형 파이프라인을 연결합니다:" ] }, { "cell_type": "code", "execution_count": 94, "metadata": {}, "outputs": [], "source": [ "from sklearn.pipeline import FeatureUnion\n", "preprocess_pipeline = FeatureUnion(transformer_list=[\n", " (\"num_pipeline\", num_pipeline),\n", " (\"cat_pipeline\", cat_pipeline),\n", " ])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "이제 원본 데이터를 받아 머신러닝 모델에 주입할 숫자 입력 특성을 출력하는 전처리 파이프라인을 만들었습니다." ] }, { "cell_type": "code", "execution_count": 144, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[22., 1., 0., ..., 0., 0., 1.],\n", " [38., 1., 0., ..., 1., 0., 0.],\n", " [26., 0., 0., ..., 0., 0., 1.],\n", " ...,\n", " [28., 1., 2., ..., 0., 0., 1.],\n", " [26., 0., 0., ..., 1., 0., 0.],\n", " [32., 0., 0., ..., 0., 1., 0.]])" ] }, "execution_count": 144, "metadata": {}, "output_type": "execute_result" } ], "source": [ "X_train_titanic = preprocess_pipeline.fit_transform(train_data)\n", "X_train_titanic" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "레이블을 가져옵니다:" ] }, { "cell_type": "code", "execution_count": 145, "metadata": {}, "outputs": [], "source": [ "y_train_titanic = train_data[\"Survived\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "이제 분류기를 훈련시킬 차례입니다. RandomForestClassifier를 적용해 보겠습니다:\n", "최적의 하이퍼파라미터를 찾기위해 랜덤탐색을 시행" ] }, { "cell_type": "code", "execution_count": 98, "metadata": {}, "outputs": [], "source": [ "from sklearn.ensemble import RandomForestClassifier\n", "\n", "forest_clf = RandomForestClassifier(random_state=42)" ] }, { "cell_type": "code", "execution_count": 117, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "RandomizedSearchCV(cv=5, error_score='raise-deprecating',\n", " estimator=RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',\n", " max_depth=None, max_features='auto', max_leaf_nodes=None,\n", " min_impurity_decrease=0.0, min_impurity_split=None,\n", " min_samples_leaf=1, min_samples_split=2,\n", " min_weight_fraction_leaf=0.0, n_estimators=10, n_jobs=None,\n", " oob_score=False, random_state=42, verbose=0, warm_start=False),\n", " fit_params=None, iid='warn', n_iter=50, n_jobs=-1,\n", " param_distributions={'n_estimators': , 'bootstrap': [True, False]},\n", " pre_dispatch='2*n_jobs', random_state=42, refit=True,\n", " return_train_score='warn', scoring='neg_mean_squared_error',\n", " verbose=0)" ] }, "execution_count": 117, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from sklearn.model_selection import RandomizedSearchCV\n", "from scipy.stats import randint\n", "\n", "param_distribs = {\n", " 'n_estimators': randint(low=1, high=100),\n", " 'bootstrap': [True, False],\n", " }\n", "\n", "rnd_search = RandomizedSearchCV(forest_clf, param_distributions=param_distribs,\n", " n_iter=50, cv=5, scoring='neg_mean_squared_error', \n", " random_state=42, n_jobs=-1)\n", "rnd_search.fit(X_train_titanic, y_train_titanic)" ] }, { "cell_type": "code", "execution_count": 118, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.44064028507448966 {'n_estimators': 52, 'bootstrap': True}\n", "0.4519567135595372 {'n_estimators': 15, 'bootstrap': True}\n", "0.4393649125440716 {'n_estimators': 72, 'bootstrap': True}\n", "0.4431800195652587 {'n_estimators': 21, 'bootstrap': True}\n", "0.4393649125440716 {'n_estimators': 83, 'bootstrap': True}\n", "0.4393649125440716 {'n_estimators': 75, 'bootstrap': True}\n", "0.4393649125440716 {'n_estimators': 88, 'bootstrap': True}\n", "0.4494665749754947 {'n_estimators': 24, 'bootstrap': True}\n", "0.4519567135595372 {'n_estimators': 22, 'bootstrap': True}\n", "0.47614237371283535 {'n_estimators': 2, 'bootstrap': True}\n", "0.45071336398632533 {'n_estimators': 30, 'bootstrap': False}\n", "0.45071336398632533 {'n_estimators': 2, 'bootstrap': False}\n", "0.4519567135595372 {'n_estimators': 60, 'bootstrap': False}\n", "0.4419119768530779 {'n_estimators': 33, 'bootstrap': True}\n", "0.45071336398632533 {'n_estimators': 58, 'bootstrap': False}\n", "0.4519567135595372 {'n_estimators': 89, 'bootstrap': False}\n", "0.44064028507448966 {'n_estimators': 91, 'bootstrap': True}\n", "0.4419119768530779 {'n_estimators': 42, 'bootstrap': True}\n", "0.4519567135595372 {'n_estimators': 92, 'bootstrap': False}\n", "0.4519567135595372 {'n_estimators': 80, 'bootstrap': False}\n", "0.44064028507448966 {'n_estimators': 62, 'bootstrap': True}\n", "0.4531966520035264 {'n_estimators': 47, 'bootstrap': False}\n", "0.4531966520035264 {'n_estimators': 51, 'bootstrap': False}\n", "0.4544332072404845 {'n_estimators': 55, 'bootstrap': False}\n", "0.45071336398632533 {'n_estimators': 64, 'bootstrap': False}\n", "0.4702125984770659 {'n_estimators': 3, 'bootstrap': True}\n", "0.4431800195652587 {'n_estimators': 51, 'bootstrap': True}\n", "0.4431800195652587 {'n_estimators': 21, 'bootstrap': True}\n", "0.4431800195652587 {'n_estimators': 39, 'bootstrap': True}\n", "0.4469625634310624 {'n_estimators': 4, 'bootstrap': False}\n", "0.4393649125440716 {'n_estimators': 60, 'bootstrap': True}\n", "0.4581228472908512 {'n_estimators': 9, 'bootstrap': False}\n", "0.45566640681373577 {'n_estimators': 53, 'bootstrap': False}\n", "0.4519567135595372 {'n_estimators': 84, 'bootstrap': False}\n", "0.4519567135595372 {'n_estimators': 60, 'bootstrap': False}\n", "0.4419119768530779 {'n_estimators': 44, 'bootstrap': True}\n", "0.4457052822810143 {'n_estimators': 8, 'bootstrap': True}\n", "0.4457052822810143 {'n_estimators': 35, 'bootstrap': True}\n", "0.4519567135595372 {'n_estimators': 81, 'bootstrap': False}\n", "0.4544332072404845 {'n_estimators': 50, 'bootstrap': False}\n", "0.4469625634310624 {'n_estimators': 4, 'bootstrap': False}\n", "0.4494665749754947 {'n_estimators': 6, 'bootstrap': False}\n", "0.4469625634310624 {'n_estimators': 4, 'bootstrap': False}\n", "0.45071336398632533 {'n_estimators': 93, 'bootstrap': False}\n", "0.44821631782492505 {'n_estimators': 18, 'bootstrap': True}\n", "0.4519567135595372 {'n_estimators': 44, 'bootstrap': False}\n", "0.4494665749754947 {'n_estimators': 74, 'bootstrap': False}\n", "0.45071336398632533 {'n_estimators': 14, 'bootstrap': False}\n", "0.4431800195652587 {'n_estimators': 48, 'bootstrap': True}\n", "0.4393649125440716 {'n_estimators': 72, 'bootstrap': True}\n" ] } ], "source": [ "cvres = rnd_search.cv_results_\n", "for mean_score, params in zip(cvres[\"mean_test_score\"], cvres[\"params\"]):\n", " print(np.sqrt(-mean_score), params)" ] }, { "cell_type": "code", "execution_count": 119, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'bootstrap': True, 'n_estimators': 72}" ] }, "execution_count": 119, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rnd_search.best_params_" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "이를 사용해서 테스트 세트에 대한 예측을 만듭니다:" ] }, { "cell_type": "code", "execution_count": 146, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "array([0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1,\n", " 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1,\n", " 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1,\n", " 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0,\n", " 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0,\n", " 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0,\n", " 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1,\n", " 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1,\n", " 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0,\n", " 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0,\n", " 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1,\n", " 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1,\n", " 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0,\n", " 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,\n", " 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0,\n", " 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0,\n", " 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0,\n", " 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1,\n", " 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1])" ] }, "execution_count": 146, "metadata": {}, "output_type": "execute_result" } ], "source": [ "forest_clf = RandomForestClassifier(bootstrap= True, n_estimators= 72, random_state=42)\n", "forest_clf.fit(X_train_titanic, y_train_titanic)\n", "\n", "X_test_titanic = preprocess_pipeline.transform(test_data)\n", "X_test_predict = forest_clf.predict(X_test_titanic)\n", "X_test_predict" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "이 예측 결과를 CSV 파일로 만들어 캐글에 업로드하고 평가를 받아볼 수 있습니다. 하지만 교차 검증으로 모델이 얼마나 좋은지 먼저 평가하겠습니다." ] }, { "cell_type": "code", "execution_count": 123, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.817174838270344" ] }, "execution_count": 123, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from sklearn.model_selection import cross_val_score\n", "\n", "forest_scores = cross_val_score(forest_clf, X_train, y_train, cv=10)\n", "forest_scores.mean()" ] }, { "cell_type": "code", "execution_count": 169, "metadata": {}, "outputs": [], "source": [ "df = pd.DataFrame(test_data['PassengerId'])\n", "df.loc[:,'Survived'] = pd.Series(X_test_predict, index = df.index)\n", "df.to_csv(\"titanic_predict.csv\", index=None)" ] }, { "cell_type": "code", "execution_count": 170, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
PassengerIdSurvived
08920
18930
28940
38951
48961
58970
68980
78990
89001
99010
109020
119030
129041
139050
149061
159071
169080
179091
189100
199111
209121
219131
229141
239151
249161
259170
269181
279191
289201
299210
.........
38812800
38912810
39012820
39112831
39212840
39312850
39412860
39512871
39612880
39712891
39812900
39912910
40012921
40112930
40212941
40312950
40412961
40512970
40612980
40712990
40813001
40913011
41013021
41113031
41213040
41313050
41413061
41513070
41613080
41713091
\n", "

418 rows × 2 columns

\n", "
" ], "text/plain": [ " PassengerId Survived\n", "0 892 0\n", "1 893 0\n", "2 894 0\n", "3 895 1\n", "4 896 1\n", "5 897 0\n", "6 898 0\n", "7 899 0\n", "8 900 1\n", "9 901 0\n", "10 902 0\n", "11 903 0\n", "12 904 1\n", "13 905 0\n", "14 906 1\n", "15 907 1\n", "16 908 0\n", "17 909 1\n", "18 910 0\n", "19 911 1\n", "20 912 1\n", "21 913 1\n", "22 914 1\n", "23 915 1\n", "24 916 1\n", "25 917 0\n", "26 918 1\n", "27 919 1\n", "28 920 1\n", "29 921 0\n", ".. ... ...\n", "388 1280 0\n", "389 1281 0\n", "390 1282 0\n", "391 1283 1\n", "392 1284 0\n", "393 1285 0\n", "394 1286 0\n", "395 1287 1\n", "396 1288 0\n", "397 1289 1\n", "398 1290 0\n", "399 1291 0\n", "400 1292 1\n", "401 1293 0\n", "402 1294 1\n", "403 1295 0\n", "404 1296 1\n", "405 1297 0\n", "406 1298 0\n", "407 1299 0\n", "408 1300 1\n", "409 1301 1\n", "410 1302 1\n", "411 1303 1\n", "412 1304 0\n", "413 1305 0\n", "414 1306 1\n", "415 1307 0\n", "416 1308 0\n", "417 1309 1\n", "\n", "[418 rows x 2 columns]" ] }, "execution_count": 170, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.4" } }, "nbformat": 4, "nbformat_minor": 2 }