diff --git a/002/exercise/lasso_regression.ipynb b/002/exercise/lasso_regression.ipynb
new file mode 100644
index 00000000..b55b330a
--- /dev/null
+++ b/002/exercise/lasso_regression.ipynb
@@ -0,0 +1,471 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Hi - Welcome to the Lasso Regression excercise\n",
+ "\n",
+ "## This notebook is based on the Linear Regression notebook\n",
+ "\n",
+ "## The main task of the assignment is in the second half of the notebook\n",
+ "\n",
+ "### Run cells below which import all required libraries"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "import pandas as pd\n",
+ "import sklearn, sklearn.model_selection\n",
+ "import matplotlib.pyplot as plt"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### The cell below retreives the data and splits it into train_x, train_y, valid_x, valid_y, test_x, test_y"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# pick how big the validation/test portion of the data is, currently set to 20% validation, 20% test, and 60% train\n",
+ "valid_size = 0.2\n",
+ "test_size = 0.2\n",
+ "\n",
+ "# load the data from the .csv file\n",
+ "github_url = 'https://raw.githubusercontent.com/gimseng/99-ML-Learning-Projects/'\n",
+ "data_source = 'master/002/data/housing_prices.csv'\n",
+ "data = pd.read_csv(github_url+data_source)\n",
+ "\n",
+ "def train_valid_test_split(data, valid_size, test_size):\n",
+ " # split into train and test\n",
+ " train, test = sklearn.model_selection.train_test_split(data, test_size=test_size)\n",
+ " # further split train into train and validation. (valid_size needs to be recalculated to properly split train)\n",
+ " valid_size = valid_size/(1-test_size)\n",
+ " train, valid = sklearn.model_selection.train_test_split(train, test_size=valid_size)\n",
+ " return train, valid, test\n",
+ "\n",
+ "train, valid, test = train_valid_test_split(data, valid_size, test_size)\n",
+ "train_x, train_y = train.iloc[:, :-1], train.iloc[:, -1:]\n",
+ "valid_x, valid_y = valid.iloc[:, :-1], valid.iloc[:, -1:]\n",
+ "test_x, test_y = test.iloc[:, :-1], test.iloc[:, -1:]\n",
+ "\n",
+ "print(f'train size: {len(train_x)}, valid size: {len(valid_x)}, test size: {len(test_x)}')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### The cell below helps us view a few of our data points and also view how the selling price varies for different features"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "data.head(10)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "fig, axes = plt.subplots(nrows=1, ncols=2)\n",
+ "fig=(20,7)\n",
+ "data.groupby('YearBuilt').mean()['SalePrice'].plot(ylabel='Sale Price',xlabel='Year Built',ax=axes[0],figsize=fig)\n",
+ "data.groupby('GarageCars').mean()['SalePrice'].plot(kind='bar',ylabel='Sale Price',xlabel='Garage car room',ax=axes[1],figsize=fig)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### The cell below scales the numeric features using a min_max_scaler (scales them between 0 and 1)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "numeric_columns = train_x.select_dtypes(include='number').columns\n",
+ "# scaler = sklearn.preprocessing.MinMaxScaler()\n",
+ "# scaler = sklearn.preprocessing.KBinsDiscretizer(n_bins=10, encode='ordinal')\n",
+ "scaler = sklearn.preprocessing.StandardScaler()\n",
+ "# train on concatenation of train and validation\n",
+ "scaler.fit(pd.concat((train_x[numeric_columns], valid_x[numeric_columns])))\n",
+ "# apply on all data\n",
+ "train_x[numeric_columns] = scaler.transform(train_x[numeric_columns])\n",
+ "valid_x[numeric_columns] = scaler.transform(valid_x[numeric_columns])\n",
+ "test_x[numeric_columns] = scaler.transform(test_x[numeric_columns])\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### The cell below one-hot encodes the data"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def get_dummies_column_keys(dataFrames):\n",
+ " '''returns a column list of get dummies for concatenated dataframes'''\n",
+ " return pd.get_dummies(pd.concat(dataFrames)).columns.tolist()\n",
+ "\n",
+ "def get_dummies_using_keys(dataFrame, column_keys):\n",
+ " '''returns get dummies result with columns matching column_keys'''\n",
+ " result = pd.get_dummies(dataFrame)\n",
+ " result = result.reindex(columns=column_keys).fillna(0.00)\n",
+ " return result\n",
+ "\n",
+ "# get the keys for the concatenation of all datasets \n",
+ "column_keys = get_dummies_column_keys((train_x, valid_x, test_x))\n",
+ "train_x = get_dummies_using_keys(train_x, column_keys)\n",
+ "valid_x = get_dummies_using_keys(valid_x, column_keys)\n",
+ "test_x = get_dummies_using_keys(test_x, column_keys)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### The cell below appends a '1' to all rows by adding a 'Bias' feature with the value of 1 for all rows
This is to simulate a bias without adding any bias functionality to the Linear Regression algorithm "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "train_x['Bias'] = 1\n",
+ "valid_x['Bias'] = 1\n",
+ "test_x['Bias'] = 1"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### This last cell gets the numpy arrays that we will work on from the panda dataframes"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# store the dataframes in df_* variables\n",
+ "df_train_x, df_train_y = train_x, train_y\n",
+ "df_valid_x, df_valid_y = valid_x, valid_y\n",
+ "df_test_x, df_test_y = test_x, test_y\n",
+ "\n",
+ "# store the numpy arrays in the regular variables\n",
+ "train_x, train_y = train_x.values, train_y.values\n",
+ "valid_x, valid_y = valid_x.values, valid_y.values\n",
+ "test_x, test_y = test_x.values, test_y.values"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Proceed with any data analysis in the next cell\n",
+ "\n",
+ "## (Optional but encouraged)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# This is the main task of the exercise\n",
+ "\n",
+ "## You need to implement the following functions which
implement Linear Regression and all it's required functions"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### The first major function is the 'predict' function
Remember the Linear Regression equation is\n",
+ "$$\\hat{Y} = w_0 + w_1 x_1 + w_2 x_2 + ... + w_m x_m$$\n",
+ "\n",
+ "### hint: try to implement a 'vectorized' version by using a matrix multiplication
which will massively increase performance"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "scrolled": true
+ },
+ "outputs": [],
+ "source": [
+ "\n",
+ "def predict(w, x):\n",
+ " '''\n",
+ " Return the linear regression prediction from the weights and input features\n",
+ " Args:\n",
+ " w: The weight vector, shape: (features, 1)\n",
+ " x: The input data, shape: (num_samples, features)\n",
+ " Returns:\n",
+ " The prediction of the model, shape: (num_samples, 1)\n",
+ " '''\n",
+ " ### YOUR CODE HERE\n",
+ " raise NotImplementedError\n",
+ "\n",
+ "def init_weights(x):\n",
+ " '''\n",
+ " Initialize the weights vector, the data is passed in to know how many weights you need\n",
+ " Args:\n",
+ " x: The input data, shape: (num_samples, features)\n",
+ " Returns:\n",
+ " The initial weight vector of random values, shape: (features, 1)\n",
+ " '''\n",
+ " ### YOUR CODE HERE\n",
+ " raise NotImplementedError"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Next you have to implement the Mean Squared Error Loss calculation following the following formula \n",
+ "$$LOSS(W)=\\frac{1}{2n} \\sum_{i=1}^{n} (\\hat{y}_i-y_i)^2$$"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def loss(y_hat, y):\n",
+ " '''\n",
+ " Calculate the Mean Squared Error based on the y_hat (the predicted value) and y (the true values)\n",
+ " Args:\n",
+ " y_hat: the predicted values of the input, shape: (num_samples, 1)\n",
+ " y: the true values of the input, shape: (num_samples, 1)\n",
+ " Returns:\n",
+ " Mean Squared Error, shape: Scaler\n",
+ " '''\n",
+ " ### YOUR CODE HERE\n",
+ " raise NotImplementedError"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Next you have to implement the gradient calculation for the weights accoarding to the formula\n",
+ "\n",
+ "$$\\frac{\\partial LOSS(W)}{\\partial W_j} \n",
+ "= \\sum_{i=1}^{n} (2*(\\hat{y}_i-y_i)*x_{i,j} + \\eta * \\frac{\\lvert{\\partial W_j}\\lvert}{\\partial W_j})$$"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def get_gradient(w, x, y_hat, y, l1_coeff):\n",
+ " '''\n",
+ " Get the gradient of the weights using the parameters passed \n",
+ " (Note: not all parameters passed have to be used)\n",
+ " Args:\n",
+ " w: The weight vector, shape: (features, 1)\n",
+ " x: The input data, shape: (num_samples, features)\n",
+ " y_hat: the predicted values of the input, shape: (num_samples, 1)\n",
+ " y: the true values of the input, shape: (num_samples, 1)\n",
+ " l1_coeff: L1 regularization coefficient\n",
+ " Returns:\n",
+ " The gradients of the weight vector, shape: (features, 1)\n",
+ " '''\n",
+ " ### YOUR CODE HERE\n",
+ " raise NotImplementedError"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Next you have to implement the gradient descent update for the weights accoarding to the formula\n",
+ "\n",
+ "$$w_j:=w_j - \\alpha * \\frac{\\partial LOSS(W)}{\\partial W_j} $$"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def get_updated_weights(w, w_gradients, learning_rate):\n",
+ " '''\n",
+ " Calculate the new value of the weights after applying the gradient descent weight update rule \n",
+ " Args:\n",
+ " w: The weight vector, shape: (features, 1)\n",
+ " w_gradients: The gradients of the weight vector, shape: (features, 1)\n",
+ " learning_rate: The learning rate of the algorithm, shape: Scaler\n",
+ " Returns:\n",
+ " The updated value of the weights, shape: (features, 1)\n",
+ " '''\n",
+ " ### YOUR CODE HERE\n",
+ " raise NotImplementedError"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Finally implement the training loop, this should simply be
a loop that calls the functions you implemented above"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def train(epochs, x, y, learning_rate=1e-3, initial_weights=None, l1_coeff=0, initial_b=0):\n",
+ " '''\n",
+ " The main train loop for the algorithm. This performs the gradient update step 'epochs' times.\n",
+ " Args:\n",
+ " x: The input data, shape: (num_samples, features)\n",
+ " y: the true values of the input, shape: (num_samples, 1)\n",
+ " learning_rate: The learning rate of the algorithm, shape: Scaler\n",
+ " initial_weights: The initial weight to start training, this should be passed to continue training\n",
+ " Returns:\n",
+ " The final weight values after applying 'epochs' number of updates on 'initial_weights', shape: (features, 1)\n",
+ " '''\n",
+ " if initial_weights is None:\n",
+ " weight = init_weights(x)\n",
+ " else:\n",
+ " weight = initial_weights\n",
+ " \n",
+ " for epoch in range(epochs):\n",
+ " ### YOUR CODE HERE\n",
+ " raise NotImplementedError"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### The cell below will run your code\n",
+ "\n",
+ "### After 200,000 epochs our results were the following: \n",
+ "### Train loss:1.9036887e+08
Valid loss: 4.7176443e+08
Test loss: 1.08308357e+09"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%%time\n",
+ "w = None\n",
+ "weights=[]\n",
+ "l1_coeff = .0001\n",
+ "for i in range(20):\n",
+ " w = train(10000, train_x, train_y, learning_rate=5e-3, l1_coeff=l1_coeff, initial_weights=w)\n",
+ " weights.append(w)\n",
+ " print('epoch:', (i+1)*10000, end=' ')\n",
+ " print('Train loss:', loss(predict(w, train_x), train_y), end=' ')\n",
+ " print('Valid loss:', loss(predict(w, valid_x), valid_y))\n",
+ "print('Final Model -- Test loss:', loss(predict(w, test_x), test_y))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### The cell below shows us how the prediction improves while training over the span of 200,000 epochs ( By steps of 10000 epochs)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import IPython\n",
+ "for w in weights: \n",
+ " IPython.display.clear_output(wait=True)\n",
+ " pd.DataFrame({'Actual': train_y.flatten(), 'Predicted': predict(w,train_x).flatten()}).head(15).plot(kind='bar',figsize=(10,8))\n",
+ " plt.show()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "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.7.4"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/002/exercise/readme.md b/002/exercise/readme.md
index 817cfe85..03e770db 100644
--- a/002/exercise/readme.md
+++ b/002/exercise/readme.md
@@ -4,6 +4,7 @@
- Learn your first ML model which is Linear Regression
- Learn how to evaluate models through a 'Loss function'
- Learn how to optimize a model through Gradient Descent
+- Learn how to implement L1 norm for regularization in Linear Regression (Lasso Regression)
# Data
@@ -21,6 +22,12 @@ Data source/credit: [Kaggle](https://www.kaggle.com/c/house-prices-advanced-regr
[](https://nbviewer.jupyter.org/github/gimseng/99-ML-Learning-Projects/blob/master/002/exercise/linear_regression.ipynb)
+`lasso-regression.ipynb`
+
+[](https://colab.research.google.com/github/gimseng/99-ML-Learning-Projects/blob/master/002/exercise/lasso_regression.ipynb)
+
+[](https://nbviewer.jupyter.org/github/gimseng/99-ML-Learning-Projects/blob/master/002/exercise/lasso_regression.ipynb)
+
# Resources on linear regression
@@ -34,4 +41,4 @@ Data source/credit: [Kaggle](https://www.kaggle.com/c/house-prices-advanced-regr
- [[Blog] Everything You Need To Know About Linear Regression](https://towardsdatascience.com/everything-you-need-to-know-about-linear-regression-b791e8f4bd7a)
- [Mandatory for Beginners] After getting the theoretical background for Linear Regression learn how to implement it practically using Sklearn
- [[Video] Linear Regression Python Sklearn [FROM SCRATCH]](https://www.youtube.com/watch?v=b0L47BeklTE)
-
+- [[Lasso Regression] Learn how L1 and L2 work using gradient descent](https://towardsdatascience.com/intuitions-on-l1-and-l2-regularisation-235f2db4c261)
\ No newline at end of file
diff --git a/002/solution/lasso_regression.ipynb b/002/solution/lasso_regression.ipynb
new file mode 100644
index 00000000..aa7ad9be
--- /dev/null
+++ b/002/solution/lasso_regression.ipynb
@@ -0,0 +1,905 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Hi - Welcome to the Lasso Regression excercise\n",
+ "\n",
+ "## This notebook is based on the Linear Regression notebook\n",
+ "\n",
+ "## The main task of the assignment is in the second half of the notebook\n",
+ "\n",
+ "### Run cells below which import all required libraries"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 182,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "import pandas as pd\n",
+ "import sklearn, sklearn.model_selection\n",
+ "import matplotlib.pyplot as plt"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### The cell below retreives the data and splits it into train_x, train_y, valid_x, valid_y, test_x, test_y"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 183,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "train size: 876, valid size: 292, test size: 292\n"
+ ]
+ }
+ ],
+ "source": [
+ "# pick how big the validation/test portion of the data is, currently set to 20% validation, 20% test, and 60% train\n",
+ "valid_size = 0.2\n",
+ "test_size = 0.2\n",
+ "\n",
+ "# load the data from the .csv file\n",
+ "github_url = 'https://raw.githubusercontent.com/gimseng/99-ML-Learning-Projects/'\n",
+ "data_source = 'master/002/data/housing_prices.csv'\n",
+ "data = pd.read_csv(github_url+data_source)\n",
+ "\n",
+ "def train_valid_test_split(data, valid_size, test_size):\n",
+ " # split into train and test\n",
+ " train, test = sklearn.model_selection.train_test_split(data, test_size=test_size)\n",
+ " # further split train into train and validation. (valid_size needs to be recalculated to properly split train)\n",
+ " valid_size = valid_size/(1-test_size)\n",
+ " train, valid = sklearn.model_selection.train_test_split(train, test_size=valid_size)\n",
+ " return train, valid, test\n",
+ "\n",
+ "train, valid, test = train_valid_test_split(data, valid_size, test_size)\n",
+ "train_x, train_y = train.iloc[:, :-1], train.iloc[:, -1:]\n",
+ "valid_x, valid_y = valid.iloc[:, :-1], valid.iloc[:, -1:]\n",
+ "test_x, test_y = test.iloc[:, :-1], test.iloc[:, -1:]\n",
+ "\n",
+ "print(f'train size: {len(train_x)}, valid size: {len(valid_x)}, test size: {len(test_x)}')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### The cell below helps us view a few of our data points and also view how the selling price varies for different features"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 184,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " Id | \n",
+ " MSSubClass | \n",
+ " MSZoning | \n",
+ " LotFrontage | \n",
+ " LotArea | \n",
+ " Street | \n",
+ " Alley | \n",
+ " LotShape | \n",
+ " LandContour | \n",
+ " Utilities | \n",
+ " ... | \n",
+ " PoolArea | \n",
+ " PoolQC | \n",
+ " Fence | \n",
+ " MiscFeature | \n",
+ " MiscVal | \n",
+ " MoSold | \n",
+ " YrSold | \n",
+ " SaleType | \n",
+ " SaleCondition | \n",
+ " SalePrice | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " 1 | \n",
+ " 60 | \n",
+ " RL | \n",
+ " 65.0 | \n",
+ " 8450 | \n",
+ " Pave | \n",
+ " NaN | \n",
+ " Reg | \n",
+ " Lvl | \n",
+ " AllPub | \n",
+ " ... | \n",
+ " 0 | \n",
+ " NaN | \n",
+ " NaN | \n",
+ " NaN | \n",
+ " 0 | \n",
+ " 2 | \n",
+ " 2008 | \n",
+ " WD | \n",
+ " Normal | \n",
+ " 208500 | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " 2 | \n",
+ " 20 | \n",
+ " RL | \n",
+ " 80.0 | \n",
+ " 9600 | \n",
+ " Pave | \n",
+ " NaN | \n",
+ " Reg | \n",
+ " Lvl | \n",
+ " AllPub | \n",
+ " ... | \n",
+ " 0 | \n",
+ " NaN | \n",
+ " NaN | \n",
+ " NaN | \n",
+ " 0 | \n",
+ " 5 | \n",
+ " 2007 | \n",
+ " WD | \n",
+ " Normal | \n",
+ " 181500 | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " 3 | \n",
+ " 60 | \n",
+ " RL | \n",
+ " 68.0 | \n",
+ " 11250 | \n",
+ " Pave | \n",
+ " NaN | \n",
+ " IR1 | \n",
+ " Lvl | \n",
+ " AllPub | \n",
+ " ... | \n",
+ " 0 | \n",
+ " NaN | \n",
+ " NaN | \n",
+ " NaN | \n",
+ " 0 | \n",
+ " 9 | \n",
+ " 2008 | \n",
+ " WD | \n",
+ " Normal | \n",
+ " 223500 | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " 4 | \n",
+ " 70 | \n",
+ " RL | \n",
+ " 60.0 | \n",
+ " 9550 | \n",
+ " Pave | \n",
+ " NaN | \n",
+ " IR1 | \n",
+ " Lvl | \n",
+ " AllPub | \n",
+ " ... | \n",
+ " 0 | \n",
+ " NaN | \n",
+ " NaN | \n",
+ " NaN | \n",
+ " 0 | \n",
+ " 2 | \n",
+ " 2006 | \n",
+ " WD | \n",
+ " Abnorml | \n",
+ " 140000 | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " 5 | \n",
+ " 60 | \n",
+ " RL | \n",
+ " 84.0 | \n",
+ " 14260 | \n",
+ " Pave | \n",
+ " NaN | \n",
+ " IR1 | \n",
+ " Lvl | \n",
+ " AllPub | \n",
+ " ... | \n",
+ " 0 | \n",
+ " NaN | \n",
+ " NaN | \n",
+ " NaN | \n",
+ " 0 | \n",
+ " 12 | \n",
+ " 2008 | \n",
+ " WD | \n",
+ " Normal | \n",
+ " 250000 | \n",
+ "
\n",
+ " \n",
+ " | 5 | \n",
+ " 6 | \n",
+ " 50 | \n",
+ " RL | \n",
+ " 85.0 | \n",
+ " 14115 | \n",
+ " Pave | \n",
+ " NaN | \n",
+ " IR1 | \n",
+ " Lvl | \n",
+ " AllPub | \n",
+ " ... | \n",
+ " 0 | \n",
+ " NaN | \n",
+ " MnPrv | \n",
+ " Shed | \n",
+ " 700 | \n",
+ " 10 | \n",
+ " 2009 | \n",
+ " WD | \n",
+ " Normal | \n",
+ " 143000 | \n",
+ "
\n",
+ " \n",
+ " | 6 | \n",
+ " 7 | \n",
+ " 20 | \n",
+ " RL | \n",
+ " 75.0 | \n",
+ " 10084 | \n",
+ " Pave | \n",
+ " NaN | \n",
+ " Reg | \n",
+ " Lvl | \n",
+ " AllPub | \n",
+ " ... | \n",
+ " 0 | \n",
+ " NaN | \n",
+ " NaN | \n",
+ " NaN | \n",
+ " 0 | \n",
+ " 8 | \n",
+ " 2007 | \n",
+ " WD | \n",
+ " Normal | \n",
+ " 307000 | \n",
+ "
\n",
+ " \n",
+ " | 7 | \n",
+ " 8 | \n",
+ " 60 | \n",
+ " RL | \n",
+ " NaN | \n",
+ " 10382 | \n",
+ " Pave | \n",
+ " NaN | \n",
+ " IR1 | \n",
+ " Lvl | \n",
+ " AllPub | \n",
+ " ... | \n",
+ " 0 | \n",
+ " NaN | \n",
+ " NaN | \n",
+ " Shed | \n",
+ " 350 | \n",
+ " 11 | \n",
+ " 2009 | \n",
+ " WD | \n",
+ " Normal | \n",
+ " 200000 | \n",
+ "
\n",
+ " \n",
+ " | 8 | \n",
+ " 9 | \n",
+ " 50 | \n",
+ " RM | \n",
+ " 51.0 | \n",
+ " 6120 | \n",
+ " Pave | \n",
+ " NaN | \n",
+ " Reg | \n",
+ " Lvl | \n",
+ " AllPub | \n",
+ " ... | \n",
+ " 0 | \n",
+ " NaN | \n",
+ " NaN | \n",
+ " NaN | \n",
+ " 0 | \n",
+ " 4 | \n",
+ " 2008 | \n",
+ " WD | \n",
+ " Abnorml | \n",
+ " 129900 | \n",
+ "
\n",
+ " \n",
+ " | 9 | \n",
+ " 10 | \n",
+ " 190 | \n",
+ " RL | \n",
+ " 50.0 | \n",
+ " 7420 | \n",
+ " Pave | \n",
+ " NaN | \n",
+ " Reg | \n",
+ " Lvl | \n",
+ " AllPub | \n",
+ " ... | \n",
+ " 0 | \n",
+ " NaN | \n",
+ " NaN | \n",
+ " NaN | \n",
+ " 0 | \n",
+ " 1 | \n",
+ " 2008 | \n",
+ " WD | \n",
+ " Normal | \n",
+ " 118000 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
10 rows × 81 columns
\n",
+ "
"
+ ],
+ "text/plain": [
+ " Id MSSubClass MSZoning LotFrontage LotArea Street Alley LotShape \\\n",
+ "0 1 60 RL 65.0 8450 Pave NaN Reg \n",
+ "1 2 20 RL 80.0 9600 Pave NaN Reg \n",
+ "2 3 60 RL 68.0 11250 Pave NaN IR1 \n",
+ "3 4 70 RL 60.0 9550 Pave NaN IR1 \n",
+ "4 5 60 RL 84.0 14260 Pave NaN IR1 \n",
+ "5 6 50 RL 85.0 14115 Pave NaN IR1 \n",
+ "6 7 20 RL 75.0 10084 Pave NaN Reg \n",
+ "7 8 60 RL NaN 10382 Pave NaN IR1 \n",
+ "8 9 50 RM 51.0 6120 Pave NaN Reg \n",
+ "9 10 190 RL 50.0 7420 Pave NaN Reg \n",
+ "\n",
+ " LandContour Utilities ... PoolArea PoolQC Fence MiscFeature MiscVal \\\n",
+ "0 Lvl AllPub ... 0 NaN NaN NaN 0 \n",
+ "1 Lvl AllPub ... 0 NaN NaN NaN 0 \n",
+ "2 Lvl AllPub ... 0 NaN NaN NaN 0 \n",
+ "3 Lvl AllPub ... 0 NaN NaN NaN 0 \n",
+ "4 Lvl AllPub ... 0 NaN NaN NaN 0 \n",
+ "5 Lvl AllPub ... 0 NaN MnPrv Shed 700 \n",
+ "6 Lvl AllPub ... 0 NaN NaN NaN 0 \n",
+ "7 Lvl AllPub ... 0 NaN NaN Shed 350 \n",
+ "8 Lvl AllPub ... 0 NaN NaN NaN 0 \n",
+ "9 Lvl AllPub ... 0 NaN NaN NaN 0 \n",
+ "\n",
+ " MoSold YrSold SaleType SaleCondition SalePrice \n",
+ "0 2 2008 WD Normal 208500 \n",
+ "1 5 2007 WD Normal 181500 \n",
+ "2 9 2008 WD Normal 223500 \n",
+ "3 2 2006 WD Abnorml 140000 \n",
+ "4 12 2008 WD Normal 250000 \n",
+ "5 10 2009 WD Normal 143000 \n",
+ "6 8 2007 WD Normal 307000 \n",
+ "7 11 2009 WD Normal 200000 \n",
+ "8 4 2008 WD Abnorml 129900 \n",
+ "9 1 2008 WD Normal 118000 \n",
+ "\n",
+ "[10 rows x 81 columns]"
+ ]
+ },
+ "execution_count": 184,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "data.head(10)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 185,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 185,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAABKUAAAGpCAYAAABcR4WXAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/MnkTPAAAACXBIWXMAAAsTAAALEwEAmpwYAACqm0lEQVR4nOz9eXxkZ3nn/X/u2qu0b71vdnd7aRtswBgHbAIYbCchwcnAg7PhZJw4w8BMtnkmkHlmSGD8S5iZhBkmgQDBwZBMsAPJ4LA5xhiwjbHdBi94abfavbe6W/tWe9X9++OcU6qSSqUqqaq01Pf9eunV0q06R0eyJJ+6dF3f21hrERERERERERERaSbfal+AiIiIiIiIiIi0HhWlRERERERERESk6VSUEhERERERERGRplNRSkREREREREREmk5FKRERERERERERabrAal/AWtHf32/37Nmz2pchIiIiDfTkk0+OWGsHVvs6ZI7uwURERDa2SvdfKkq59uzZw8GDB1f7MkRERKSBjDHHV/sapJTuwURERDa2SvdfGt8TEREREREREZGmU1FKRERERERERESaTkUpERERERERERFpOhWlRERERERERESk6VSUEhERERERERGRplNRSkREREREREREmq7hRSljjN8Y8yNjzFfdt3uNMfcbYw67//YUPfaDxphBY8whY8yNReuvMcY8677v48YY466HjTF3u+uPGWP2FB1zq/sxDhtjbm305ykiIiIiIiIiItVrRqfUbwMvFL39AeABa+1+4AH3bYwxB4BbgMuAm4BPGGP87jGfBG4H9rsvN7nrtwHj1tp9wMeAj7rn6gU+BLwOuBr4UHHxS0REREREREREVldDi1LGmB3AzwB/XbT8DuAu9/W7gJuL1r9orU1Za48Cg8DVxpitQKe19lFrrQU+P+8Y71xfAq53u6huBO631o5Za8eB+5krZImIiIiIiIiIyCprdKfU/wT+I5AvWttsrR0CcP/d5K5vB04WPe6Uu7bdfX3+eskx1tosMAn0VThXCWPM7caYg8aYg8PDw8v49EREREREREREZDkaVpQyxrwdOG+tfbLaQ8qs2Qrryz1mbsHaT1trr7LWXjUwMFDlZYqIiIiIiIiIyEo1slPqDcDPGWOOAV8E3mKM+VvgnDuSh/vveffxp4CdRcfvAM646zvKrJccY4wJAF3AWIVziYiIiIiIiIjIGtCwopS19oPW2h3W2j04Aebfttb+CnAv4O2GdyvwFff1e4Fb3B31LsAJNH/cHfGbNsZc4+ZFvWfeMd653ul+DAvcB9xgjOlxA85vcNdERERERERERGQNCKzCx/xT4B5jzG3ACeBdANba54wx9wDPA1ngfdbanHvMe4HPAVHgG+4LwGeBLxhjBnE6pG5xzzVmjPkI8IT7uA9ba8ca/YmJiIiIiIiIiEh1jNNYJFdddZU9ePDgal+GiIhIy8rm8hw+P8PWrgjdsVBDPoYx5klr7VUNObksi+7BRERENrZK91+N3n1PREREpCoTiQw/9b8e4t6nFQMpIiIi0gpWY3xPREREZIFMLg9AyK+/mYmIiHj2fOBrq30JdXPsT39mtS9B1hjd9YmIiMiakMk6kQJBFaVEREREWoLu+kRERGRNSLudUsGAbk9EREREWoHu+kRERGRNmBvfM6t8JSIiIiLSDCpKiYiIyJqQzrqdUhrfExEREWkJuusTERGRNcHrlFJRSkRERKQ16K5PRERE1gQvUyqkTCkRERGRlqC7PhEREVkTMjntviciIiLSSnTXJyIiImtCJusFnev2RERERKQV6K5PRERE1oRCplRAu++thDEmYox53BjztDHmOWPMH7vrvcaY+40xh91/e4qO+aAxZtAYc8gYc2PR+muMMc+67/u4Mca462FjzN3u+mPGmD1Fx9zqfozDxphbm/ipi4iIyDqjopSIiIisCWkFnddLCniLtfYK4ErgJmPMNcAHgAestfuBB9y3McYcAG4BLgNuAj5hjPG75/okcDuw3325yV2/DRi31u4DPgZ81D1XL/Ah4HXA1cCHiotfIiIiIsV01yciIiJrQlrje3VhHTPum0H3xQLvAO5y1+8CbnZffwfwRWttylp7FBgErjbGbAU6rbWPWmst8Pl5x3jn+hJwvdtFdSNwv7V2zFo7DtzPXCFLREREpITu+kRERGRN8ILOtfveyhlj/MaYp4DzOEWix4DN1tohAPffTe7DtwMniw4/5a5td1+fv15yjLU2C0wCfRXONf/6bjfGHDTGHBweHl7BZyoiIiLrme76REREZE3IaHyvbqy1OWvtlcAOnK6nyys8vFyIl62wvtxjiq/v09baq6y1Vw0MDFS4NBEREdnIdNcnIiIia8JcUUpB5/VirZ0AvoMzQnfOHcnD/fe8+7BTwM6iw3YAZ9z1HWXWS44xxgSALmCswrlEREREFlBRSkRERNYEBZ3XhzFmwBjT7b4eBd4KvAjcC3i74d0KfMV9/V7gFndHvQtwAs0fd0f8po0x17h5Ue+Zd4x3rncC33Zzp+4DbjDG9LgB5ze4ayIiIiILBFb7AkRERERgLuhcRakV2wrc5e6g5wPusdZ+1RjzKHCPMeY24ATwLgBr7XPGmHuA54Es8D5rbc4913uBzwFR4BvuC8BngS8YYwZxOqRucc81Zoz5CPCE+7gPW2vHGvrZioiIyLqlopSIiIisCZlcHr/P4PdpfG8lrLXPAK8qsz4KXL/IMXcAd5RZPwgsyKOy1iZxi1pl3ncncGdtVy0iIiKtSH+KFBERkTUhk7PKkxIRERFpISpKiYiIyJqQzuYJaXRPREREpGXozk9ERETWhEwuTyigWxMRERGRVqE7PxEREVkTMrm8Qs5FREREWoju/ERERGRNcDKldGsiIiIi0ip05yciIiJrQjqbV9C5iIiISAtRUUpERETWhLTG90RERERaiu78REREZE3I5PKEFXQuIiIi0jJ05yciIiJrgoLORURERFqL7vxERERkTchkFXQuIiIi0kp05yciIiJrQjqXJ6jxPREREZGWoTs/ERERWRPS2Twh7b4nIiIi0jJUlBIREZE1QZlSIiIiIq1Fd34iIiKyJqgoJSIiItJadOcnIiIia0ImZwkpU0pERESkZejOT0RERNaEtDqlRERERFqK7vxERERkTcjkFHQuIiIi0kpUlBIREZE1IZ1Vp5SIiIhIK9Gdn4iIiKwJmVyeoDKlRERERFqG7vxERERk1VlryeSsOqVEREREWoju/ERERGTVZXIWgLA6pURERERahu78REREZNVlcnkAggo6FxEREWkZKkqJiIjIqpsrSunWRERERKRV6M5PREREVl06q6KUiIiISKvRnZ+IiIisurTbKRVSUUpERESkZejOT0RERFadF3QeDChTSkRERKRVqCglIiIiqy5T6JTyr/KViIiIiEizqCglIiIiq24uU0qdUiIiIiKtomFFKWNMxBjzuDHmaWPMc8aYP3bX/8gYc9oY85T78tNFx3zQGDNojDlkjLmxaP01xphn3fd93Bhj3PWwMeZud/0xY8yeomNuNcYcdl9ubdTnKSIiIitX2H0voL+XiYiIiLSKQAPPnQLeYq2dMcYEgYeNMd9w3/cxa+3/KH6wMeYAcAtwGbAN+JYx5iJrbQ74JHA78APg68BNwDeA24Bxa+0+Y8wtwEeBdxtjeoEPAVcBFnjSGHOvtXa8gZ+viIiILJOXKaWgcxEREZHW0bA7P+uYcd8Mui+2wiHvAL5orU1Za48Cg8DVxpitQKe19lFrrQU+D9xcdMxd7utfAq53u6huBO631o65haj7cQpZIiIisgbNje+pKCUiIiLSKhp652eM8RtjngLO4xSJHnPf9X5jzDPGmDuNMT3u2nbgZNHhp9y17e7r89dLjrHWZoFJoK/CueZf3+3GmIPGmIPDw8PL/0RFRESkag8dHubRI6Mla4XxPWVKiYiIiLSMhhalrLU5a+2VwA6crqfLcUbx9gJXAkPAn7kPL3cXaiusL/eY4uv7tLX2KmvtVQMDAxU+ExEREamXP/uXl/j4A4dL1tI5dUqJiIiItJqm3PlZayeA7wA3WWvPucWqPPAZ4Gr3YaeAnUWH7QDOuOs7yqyXHGOMCQBdwFiFc4mIiMgqS2ZyJDK5kjWvUyqsoHMRERGRltHI3fcGjDHd7utR4K3Ai25GlOfngR+7r98L3OLuqHcBsB943Fo7BEwbY65x86LeA3yl6BhvZ713At92c6fuA24wxvS444E3uGsiIiKyytLZPMlFilLqlBIRERFpHY3cfW8rcJcxxo9T/LrHWvtVY8wXjDFX4ozTHQN+C8Ba+5wx5h7geSALvM/deQ/gvcDngCjOrnveLn6fBb5gjBnE6ZC6xT3XmDHmI8AT7uM+bK0da+DnKiIiIlVKZfPkbelUfSbrvB1Up5SIiIhIy2hYUcpa+wzwqjLrv1rhmDuAO8qsHwQuL7OeBN61yLnuBO6s4ZJFRESkCZKZHNl8afxjSkHnIiIiIi2nkZ1SIiIiIguksnnmTe+RyTpFqZDG90RERERahopSIiIi0lSpbA4nJnKOMqVEREREWo+KUiIiItI0ubwlk7OAJZe3+H1OccorSoWUKSUiIiLSMnTnJyIiIk2Tys7N7RXvwJfOOUHnAZ8ypURERERahYpSIiIi0jSpTL7wenFRKpPLE/L7Foz1iYiIiMjGpaKUiIiINE0qO1eUShR3SmXz2nlPREREpMWoKCUiIiJNs9j4XiaXJ6g8KREREZGWors/ERERaZpkyfje3OuZXF4774mIiIi0GN39iYiISNMUd0qVju9ZQipKiYiIiLQU3f2JiIhI05RkSqXnBZ1rfE9ERESkpejuT0RERJqmePe9xPxMKQWdi4iIiLQUFaVERESkaYrDzRcEnWt8T0RERKSl6O5PREREmqZ4fK+4KJXKqihVL8aYncaYB40xLxhjnjPG/La7/kfGmNPGmKfcl58uOuaDxphBY8whY8yNReuvMcY8677v48YY466HjTF3u+uPGWP2FB1zqzHmsPtyaxM/dREREVlnAqt9ASIiItI6SoLO52dKqShVL1ng9621PzTGdABPGmPud9/3MWvt/yh+sDHmAHALcBmwDfiWMeYia20O+CRwO/AD4OvATcA3gNuAcWvtPmPMLcBHgXcbY3qBDwFXAdb92Pdaa8cb/DmLiIjIOqS7PxEREWmakk6potczOUswoEyperDWDllrf+i+Pg28AGyvcMg7gC9aa1PW2qPAIHC1MWYr0GmtfdRaa4HPAzcXHXOX+/qXgOvdLqobgfuttWNuIep+nEKWiIiIyAIqSomIiEjTFI/sqVOq8dyxulcBj7lL7zfGPGOMudMY0+OubQdOFh12yl3b7r4+f73kGGttFpgE+iqca/513W6MOWiMOTg8PLz8T1BERETWNd39iYiISNMslimVVqZU3Rlj2oEvA79jrZ3CGcXbC1wJDAF/5j20zOG2wvpyj5lbsPbT1tqrrLVXDQwMVPo0REREZAPT3Z+IiIg0TSrjFKU6IoGFu+8FdFtSL8aYIE5B6u+stf8IYK09Z63NWWvzwGeAq92HnwJ2Fh2+Azjjru8os15yjDEmAHQBYxXOJSIiIrKA7v5ERESkaVLZHAGfoT0cIFHcKaXxvbpxs50+C7xgrf3zovWtRQ/7eeDH7uv3Are4O+pdAOwHHrfWDgHTxphr3HO+B/hK0THeznrvBL7t5k7dB9xgjOlxxwNvcNdEREREFtDueyIiItI0yUyecMBHNOgnkSkKOs9agn4FndfJG4BfBZ41xjzlrv0h8IvGmCtxxumOAb8FYK19zhhzD/A8zs5973N33gN4L/A5IIqz69433PXPAl8wxgzidEjd4p5rzBjzEeAJ93EfttaONeSzFBERkXVPRSlZNclMjnDAh/PHVxERaQWpbI5I0E846F84vqdOqbqw1j5M+Wynr1c45g7gjjLrB4HLy6wngXctcq47gTurvV4RERFpXbr7k1URT2e5+o5v8dVnhlb7UkREpIlSWa9TylcadJ7LE1KmlIiIiEhL0d2frIqhySRTySxHhmdW+1JERKSJUtk84aCfaMhPIl3aKaVMKREREZHWors/WRWjM2kAJuKZVb4SERFpJm90OxLwk8wWF6WsxvdEREREWozu/mRVjMykAJhKqCglItJKvE6pSFGnVC5vyeVVlBIRERFpNbr7k1XhFaUmVJQSEWkpKbdTKhr0k3R338vknH+DAW18ISIiItJKVJSSVTEy7Ral4ulVvhIREWkmL+g8UhR0nnaLUsqUEhEREWktuvuTVTHsZkpNqlNKRKSlOEUpP9Ggn4RblMpk3U4pFaVEREREWoru/mRVeON7KkqJiLSWVCZHJOgrFKWstWRyFoBQQLclIiIiIq1Ed3+yKoqLUtbaVb4aERFpFq9TKhz0Y63zdiFTSp1SIiIiIi1Fd3+yKkbd8b1MzhJP55Z4tIiIbBSpbI6w2ykFkMrkC5lSQb+CzkVERERaiYpSsipGZlJ0hAOAduATEWklqYwTdB4NOUWpRCZHOqugcxEREZFWpLs/abp4Oks8nePCTe0ATMZVlBIRaRXJbI5I0E8k6NyCJDI5je+JiIiItCjd/UnTjUw7o3t7B9oAmEikV/NyRESkSXJ5J9Q8HJgb30sWF6UUdC4iIiLSUnT3J0037Iac73M7paY0vici0hK8Mb1wwE8kWDy+5+6+p04pERERkZYSWO0LkNbj7by3d8ApSk1ofE9EpCWkss7GFuGAr1CUSqZzZPNuUSqgoHMRERGRVqKilDSdt/Oe1ymloHMRkdaQzDidUpGgf258L5vDOjUpZUqJiIiItBgVpaTpvE6pnT0xAj7DpIpSIiItobhTqrD7XjqPz22QUlFKREREpLWoKCVNNzKToisaJBTw0R0LanxPRKRFpLxMqaCPSGAuUyrod6pSKkqJiIiItBYVpaTpRmZS9LeHAOiMBhV0LiLSIlKZoqDzkFOASmZygFOgUtC5iIiISGtRUUqabmQ6TX97GIDuaJCJRHqVr0hERJoh6Y7vRYK+uUypTA6/O78XCqgoJSIiItJKdPcnTTcyk6K/wy1KxUIa3xMRaRElnVJBL1MqRybnrHtjfCIiIiLSGlSUkqYbnkkx4HZKdUWDCjoXEWkRxUHnQb+PgM+QzOZIu1lTQXVKiYiIiLQU3f1JU6WyOaaTWfranEyprmiQSXVKiYi0hOKgc4Bo0E8inSftdkopU0pERESktejuT5pqdMbJj/LG97qiQaZTWbLuExIREdm4nFBzCjvvRUJ+EpkcmawFtPueiIiISKvR3Z801chMCmAu6DwWBGAqmV21axIRkeaY3ykVCfpIZZxMKZ+hEHguIiIiIq2hYUUpY0zEGPO4MeZpY8xzxpg/dtd7jTH3G2MOu//2FB3zQWPMoDHmkDHmxqL11xhjnnXf93FjjHHXw8aYu931x4wxe4qOudX9GIeNMbc26vOU2swVpZzxPa8oNRHXDnwiIhtdKuNlSjmdUtGg2ymVy6tLSkRERKQFNfIOMAW8xVp7BXAlcJMx5hrgA8AD1tr9wAPu2xhjDgC3AJcBNwGfMMb43XN9Ergd2O++3OSu3waMW2v3AR8DPuqeqxf4EPA64GrgQ8XFL1k9I9Pu+F5R0DmgsHMRkRZQ6JQKFGVKZXKkc3lCCjkXERERaTkNuwO0jhn3zaD7YoF3AHe563cBN7uvvwP4orU2Za09CgwCVxtjtgKd1tpHrbUW+Py8Y7xzfQm43u2iuhG431o7Zq0dB+5nrpAlq2h43vheV9TpmJpQUUpEZMNLZkqLUuGgn0Ta6ZRSyLmIiIhI62noHaAxxm+MeQo4j1MkegzYbK0dAnD/3eQ+fDtwsujwU+7advf1+eslx1hrs8Ak0FfhXPOv73ZjzEFjzMHh4eEVfKZSrZGZFG0hP9GQ0wRXyJRSUUpEZMNLZXMEfIaAf65TKpnNk85qfE9ERESkFTX0DtBam7PWXgnswOl6urzCw8ulm9oK68s9pvj6Pm2tvcpae9XAwECFS5N6GZ1JF3beg7nxvYm4ilIiIhtdKpsvdEmBW5RK58jkLMGAQs5FREREWk1T/ixprZ0AvoMzQnfOHcnD/fe8+7BTwM6iw3YAZ9z1HWXWS44xxgSALmCswrlklY3MpAqje6CilIhIK0llc4SD/sLbkaCvkCmlTikRERGR1tPI3fcGjDHd7utR4K3Ai8C9gLcb3q3AV9zX7wVucXfUuwAn0Pxxd8Rv2hhzjZsX9Z55x3jneifwbTd36j7gBmNMjxtwfoO7JqvMKUqFCm8H/T7aQn4FnYuItIBkJk+kuFMq5CeZyZHJKlNKREREpBUFGnjurcBd7g56PuAea+1XjTGPAvcYY24DTgDvArDWPmeMuQd4HsgC77PW5txzvRf4HBAFvuG+AHwW+IIxZhCnQ+oW91xjxpiPAE+4j/uwtXasgZ+rVGlkJs1r9/SWrHXHQkwk0qt0RSIi0iypbH5ep5Sz+15Gu++JiIiItKSGFaWstc8AryqzPgpcv8gxdwB3lFk/CCzIo7LWJnGLWmXedydwZ21XLY2UzeUZj6dLxvfAGeFT0LmIyMaXyuRKMqUiQbdTKmc1viciIiLSgnQHKE0zNpvGWkqCzsEpSilTSkRk4ysXdJ7JWRKZHEG/gs5FREREWo2KUtI0IzPOiF5/W6hkvTsWZEKdUiIiG14yUxp0HnVfn0pk1CklIiIi0oJ0ByhNMzKTAsp3SinoXERk45vfKRUJOq9PJTMKOhcRERFpQboDlKYpFKXmZ0rFgkzGMzgbJ4qIyEblFKVKg84BppNZdUqJiIiItCDdAUrTeN1Q3dFgyXp3NEQ6lyeZya/GZYmISJOksjnCwaJMqZBTlIqncwS1+56IiIhIy9EdoDRNIpMD5p6EeLrcItVEIt30axIRkeZJZfJEijulil7X+J6IiIhI69EdoDRNMp3DGEryRMAJOge0A5+IyAa3WKcUQCig3fdEREREWo2KUtI08XSOaNCPMaVPPLxOKYWdi4hsbKnM/KDzuaKUMqVEREREWo/uAKVpEpkcsXmje1A0vqdOKRGRDW1h0PncbYiKUiIiIiKtR3eA0jSJTK7kr+Ieb3xvSp1SIiIbVj5vSefyJYWoqDqlRERERFqa7gClaRLu+N58CjoXEdn4Ullnh9XiTqmSTCm/MqVEREREWo2KUtI0i43vtYcD+H1G43siIhtYKuvswFqSKVW8+15AtyQiIiIirUZ3gNI0iXT58T1jDF3RoILORUQ2sEKn1CK772l8T0RERKT16A5QmiaRyZU8ASnWHQ0yoaKUiMiGlco4Rani7qjirikVpURERERaj+4ApWkS6fLjewBdsaCCzkVENrCkN75X1ClljCkEnwc1viciIiLScnQHKE0TX2R8D5ywc2VKiYhsXF6nVHHQOcztwKegcxEREZHWo6KUNE0yU373PfDG97T7noi0hkQ6x3/75oskM7nVvpSmKRd0DnNFKY3v1Y8xZqcx5kFjzAvGmOeMMb/trvcaY+43xhx2/+0pOuaDxphBY8whY8yNReuvMcY8677v48YY466HjTF3u+uPGWP2FB1zq/sxDhtjbm3ipy4iIiLrjO4ApWkW230PnE6pSXVKiUiLeOzoKJ/4zhGePD6+2pfSNF7Q+fyO2Yj7/wXtvldXWeD3rbWXAtcA7zPGHAA+ADxgrd0PPOC+jfu+W4DLgJuATxhjvP9QnwRuB/a7Lze567cB49bafcDHgI+65+oFPgS8Drga+FBx8UtERESkmO4ApSmstU7Q+WLje7EQU8ksubxt8pWJiDSf1yE1k8qu8pU0j/c5z++U8oLP1SlVP9baIWvtD93Xp4EXgO3AO4C73IfdBdzsvv4O4IvW2pS19igwCFxtjNkKdFprH7XWWuDz847xzvUl4Hq3i+pG4H5r7Zi1dhy4n7lCloiIiEgJ3QFKU6SyeayFaChQ9v3d0SAA00l1S4nIxhdPOwWa2RYqSnmdUsVB50BhV9aQilIN4Y7VvQp4DNhsrR0Cp3AFbHIfth04WXTYKXdtu/v6/PWSY6y1WWAS6KtwrvnXdbsx5qAx5uDw8PAKPkMRERFZz3QHKE2RcJ+ARYPlv+W63KKUws5FpBUkMq1YlPI6pcoHnatTqv6MMe3Al4HfsdZOVXpomTVbYX25x8wtWPtpa+1V1tqrBgYGKlyaiIiIbGS6A5SmiLtPwKKLZEp1x9yiVEJFKRHZ+LxC/UyqhYLOM16m1LzxPfftoHbfqytjTBCnIPV31tp/dJfPuSN5uP+ed9dPATuLDt8BnHHXd5RZLznGGBMAuoCxCucSERERWUBFKWmKQqfUIuN7XqfUpIpSItICEi04vjeXKTUv6NzrlFLQed242U6fBV6w1v550bvuBbzd8G4FvlK0fou7o94FOIHmj7sjftPGmGvcc75n3jHeud4JfNvNnboPuMEY0+MGnN/gromIiIgsUL5CIFJn3pORxYLOC51S8XTTrklEZLUkWjDovJApNa/45P1/QZlSdfUG4FeBZ40xT7lrfwj8KXCPMeY24ATwLgBr7XPGmHuA53F27nuftdZr43sv8DkgCnzDfQGn6PUFY8wgTofULe65xowxHwGecB/3YWvtWIM+TxEREVnnVJSSpoinKxeluqIhQJ1SItIaVJSa43VKhdQpVTfW2ocpn+0EcP0ix9wB3FFm/SBweZn1JG5Rq8z77gTurPZ6RUREpHXpDlCaIrFEplRhfE9B5yLSApItGnQe8BkC/vK77ynoXERERKT16A5QmiKxRKdUKOAjFvIr6FxEWkI83XqdUslMfkGXFBRlSinoXERERKTlaHxPmiKRcZ54LdYpBU63lMb3RKQVtGLQeSqbI1zmDxPe7nvKlBIREZH1Ys8Hvrbal1A3x/70Z1b14+sOUJoikXayRGJLFKUmNL4nIi0gURjfyy3xyI0jtUin1OaOCOGAj7aw/k4mIiIi0mp0ByhN4T0BiywyvgfODnyTCe2+JyIbX6IFx/dS2XzZ/we848ptvO7CXhWlRERERFqQOqWkKRJpd3yvQlFK43si0ipacfe9ZCZXtlMq4Pexoye2ClckIiIiIqtNRSlpikTG2XWp0pbf3dGQxvdEpCUkinbfs9au8tU0RypbfnxPRERERFqX7g6lKRLpfMUuKYCumDqlRKQ1eON72bwllc2v8tU0RyqbIxyo/P8BEREREWktKkpJUyQyWSIVQs7BGd9LZfMkM60T/CsirSmRyeEzzuutsgNfKpsnHNRth4iIiIjM0d2hNEUinau48x44QeeARvhEZMNLpHP0toWA1tmBL5nJq1NKREREREqoKCVNkcjklh7fizpFKY3wichGlndH9vrbw0B9ws6ttXzr+XPE02u36yqVzalTSkRERERK6O5QmiKezpXdCrxYd9TpGpiIp5txSSIiq8ILOR/ocIpSs3UoJH3/yCi/8fmD/PrfPLFmC1OpjILORURERKSU7g6lKZKZpcf31CklIq2gUJTyOqWSKy8i/ejEOABPHBvjX3/uiUKQ+lqSyuaX/OOEiIiIiLSWwGpfgLSGeDrHls5gxccUMqVUlBKRDcwrGPV31G9876mTk1w40MZvX7+f3737Kf71557gzl97LdEl/hjQTKlMTp1SIiJr3J4PfG21L6Fujv3pz6z2JYhIFXR3KE2RyOSW3n3PLUpNKuhcRDaw+Z1SK919z1rLUycnuHJHN++4cjt//v9cyWNHR/nYt15a8bXWUyqroHMRERERKVVVUcoYc60x5tfd1weMMRc09rJko0mmc8SWGNtoDwXwGY3vicjGNtcp5eTorbRTamgyychMiit2dgNw86u2c9HmDo6OzK7ovPWUz1vSOWVKLYfuwURERGQjW/Lu0BjzIeAPgA+6S0Hgbxt5UbLxxDO5JcdIfD5DVzTIREJB5yKycXmdUv2FTqmV5T89fXICoFCUAiejby11naZzeQDtvlcj3YOJiIjIRlfN3eHPAz8HzAJYa88AHY28KNl4Eumli1IA3bEQk4m1uXOUiEg9eJ1S7eEAkaBvxbvvPXVqgqDfcOnWuf81d8eCa6rrNOkW4iIa36uV7sFERERkQ6umKJW21lrAAhhj2hp7SbLR5POWVDZPtIpdlzqjQSbi6pQSkY3L65SKhvy0hwNMr3D3vadPTnBga2dJXlNXdG0VpYanU4A6pZZB92AiIiKyoVVzd3iPMeZTQLcx5jeBbwGfaexlyUZSeAJWRVGqe409kRIRqTevUyoWDNAWDqwo6DyXtzx7arJkdA+crtO1Mgo9m8ry77/4FB3hANftG1jty1lvdA8mIiIiG1pgqQdYa/+HMeZtwBRwMfBfrLX3N/zKZMPwilKxKsb3uqJBjo2unXBeEZF6834nRkI+2kIrK0odGZ5hNp3jih3dJetd0SDJTJ5kJkekij8INEoub/ntLz7FobNT/M2vX82uvtiqXct6pHswERER2eiWLEq5u7w85N0EGWOixpg91tpjjb442Ri8roBqnhh1x4JMrKFwXhGRevN+J0aDzvjeSnbfKxdyDs4oNMBUIrOqRan/9s0X+dYL5/jjn7uMn7xIXVK10j2YiIiIbHTVjO/9A5AvejvnrlVkjNlpjHnQGPOCMeY5Y8xvu+t/ZIw5bYx5yn356aJjPmiMGTTGHDLG3Fi0/hpjzLPu+z5ujDHuetgYc7e7/pgxZk/RMbcaYw67L7dW8XlKgxTnpyylOxpkKpkhn7eNviwRkVVRPNLcFvavKOj86VMTdIQDXNhfGjXU7RalVnMc+oEXzvGp773Mr16zm1tfv2fVrmOdW9Y9mIiIiMh6UU1RKmCtLQRTuK+HqjguC/y+tfZS4BrgfcaYA+77PmatvdJ9+TqA+75bgMuAm4BPGGO8KsYngduB/e7LTe76bcC4tXYf8DHgo+65eoEPAa8DrgY+ZIzpqeKapQEK+SlVFKU6o0GsZcXBvyIia1UikyPk9xHw+2iPBJlN5ZZ9rqdPTvLKnV34fKZkvcstSk2sYlHq0SOjhAM+/svPHlj6wbKY5d6DiYiIiKwL1RSlho0xP+e9YYx5BzCy1EHW2iFr7Q/d16eBF4DtFQ55B/BFa23KWnsUGASuNsZsBTqttY+6O9B8Hri56Ji73Ne/BFzvdlHdCNxvrR2z1o4D9zNXyJImK+SnVDW+59xrr5WAXhFpLX9073M8eXysoR8jkc4RcXehaw/7lz2+l8zkeGFoilfOy5MCZxQaYHIVx6GPj8XZ1Rsj6NeOeyuwrHswERERkfWimjvFfwP8oTHmhDHmJPAHwG/V8kHcsbpXAY+5S+83xjxjjLmzqINpO3Cy6LBT7tp29/X56yXHWGuzwCTQV+Fc86/rdmPMQWPMweHh4Vo+JalBcX7KUrrWwMiJiLSmTC7P575/jAdfbOz/DxLpHLGQE+nYFgows8zO0OeHpsjm7YKQc1gbnVInx+LsVrD5Sq34HkxERERkLVuyKGWtPWKtvQY4AByw1r7eWjtY7QcwxrQDXwZ+x1o7hTOKtxe4EhgC/sx7aLkPX2F9ucfMLVj7aWvtVdbaqwYGFMDaKHO77y2Zq1/4677CzkWk2ZLu76p0Lr/EI1cmkckVMvbawgESmRy5ZeToeSHnV84LOQfojjpdp6tV4LfWcmIszq7etqUfLIta6T2YiIiIyFq3aJXAGPMr1tq/Ncb83rx1AKy1f77UyY0xQZyC1N9Za//RPe5c0fs/A3zVffMUsLPo8B3AGXd9R5n14mNOGWMCQBcw5q6/ad4x31nqeqUxaumUWgvhvCLSmpIZpxiVzja2KBVP5wrjzO1h53/Ds+ksnZFgTecZPD9DdyzIlq7Igvd1RAIYs3q/S4dnUsTTOXVKLVM97sFERERE1oNKnVLenzc7FnmpyM12+izwQvHNk5sR5fl54Mfu6/cCt7g76l2AE2j+uLV2CJg2xlzjnvM9wFeKjvF21nsn8G03d+o+4AZjTI87HniDuyarIO5lSoWWnhZdCyMnItKamtUplczkiLqZUm1eUWoZuVLnplJs6VxYkALw+Qwd4QCT8dXJ5zsxGgdgl4pSy7WiezARERGR9WLRTilr7afc3e+mrLUfW8a53wD8KvCsMeYpd+0PgV80xlyJM053DDcbwVr7nDHmHuB5nJ373met9bYkei/wOSAKfMN9Aafo9QVjzCBOh9Qt7rnGjDEfAZ5wH/dha21jk2tlUcl09eN7nV6n1Co9kRKR1pXKukWpBndKJTJzmVLtkeUXpc5PJ9m8SFEKnI0jVqtT6rhblNrdq6LUctThHkxERERkXahYJbDW5txdX2q+IbLWPkz5bKevVzjmDuCOMusHgcvLrCeBdy1yrjuBO6u9Xmmcwu57gaU7pSJBP5GgT+N7ItJ03vheptGZUukcPe5Oo+1hZ4xvJpWrdEhZ56aSXLJl8aaZrmhw1bpOj4/FMQa290RX5eNvBCu5BxMRERFZL5ZuXYHvG2P+ArgbmPUWrbU/bNhVyYYST+cI+X0EqtwWvDsaUtC5iDRdYXyvCZ1ShaDz0PI6pXJ5y/B0ik0dlTqlgqtW4D8xOsu2rijhwNJZglKR7sFERERkQ6umKPV6998PF61Z4C31vxzZiJJFT8CqsZpPpESkdTWzUyoWnNt9D2A6WVtRanQmRd7C5s7woo/pjAY5PZ5Y1jUmMzk+9JXn+O237mdbd+3dTsfH4uzS6F496B5MRERENrRqilLvstaONPxKZMNKpHNV7bzn6VzFkRMRaV1ep1SqiZ1S7csMOj8/nQJgU4VMqa7o8gv8L56d5u6DJ9m/uZ3fuO7Cmo8/MRrnbQc2L+tjSwndg4mIiMiGtug8lTHmZ40xw8AzxphTxpjXL/ZYkUritXZKRYNManxPRJos0azxvXSOyLxOqdl0bUWpc1NJgMpB525RytmUtjbDbtHruTNTNR87k8oyOpvWznsroHswERERaRWVQn7uAK6z1m4D/hXwJ825JNloau2UWslf90VElsvrlGrk+F42lyedyxd+J3a4u+/N1NgpdW7KKRpVGt/rigbJ5i2z6dpD1M9PO0Wv585M1nzsicLOe201HysFugcTERGRllCpKJW11r4IYK19DFh8ix+RChKZbM2ZUhOJdAOvSERkoaTbIZVuYFHK+xgx93diOODD7zM1j++dm0piDPS3L16U6o4FAZZV5Pc6pY4MzxaKddU6Mebkce9Wp9RK6B5MREREWkKlTKlNxpjfW+xta+2fN+6yZCNJpHPEQtXElzm6YyGSmTzJzNyIi4hIo6W8Tqls7eNu1Yq7Y3oRtyhljKEt5Gc2VVvh5/x0kr62MMEKu5p2RZ2i1EQ8zfYaw8q9olQubzl0dpordnZXfexxt1NK43sronswERERaQmVOqU+g/OXOe9l/tsiVUlk8jV1SnW6T6SmNMInIk3kdQQ1tFMq7Zy7eKS5PRxY1vjepo7Fu6QAuqIhYPmdUt5oYa25UsfH4vTEgnRGgjV/XCnQPZiIiIi0hEXbV6y1f9zMC5GNK5HO1pQp1e39dT+RqbizlIhIPSUz7vheA4POvTD1WFGhvi0cYCZZ+/hepTwpmOuUWs7GEcMzKa7Y0c0zpyZqzpU6MRpnV6+6pFZC92AiIiLSKip1SonURSJTe9A5LO+v+yIiy9WMTimvKFX8O7EtHKh5973z06mKO+8BdK0gU+q824l1YFvnMjqlZtnVp5BzEREREVmailLScIl0ruagc4CJZfx1X0RkuZLZxu++V8iUKipKdURqG9/L5vKMzKSW7CTtXmaB31rL8EyKgY4wl23r4sWzU+Ty1eVsZXJ5zkwk2a1OKRERERGpgopS0nCJTI1FqRXkoMj68+LZKX58uvZt50XqrRnje8ly43uhQE27743MpLGWJcf3YiE/AZ9hosbfpVPJLOlsnoGOMAe2dpLM5Hl5eKaqY89MJMjlrULORURERKQqSxaljDGbjTGfNcZ8w337gDHmtsZfmmwEmVyeTM4SW8b43kQ83ajLkjXkv371BX7tb56oedt5kXorjO81MlPKCzqflylVy+5756aSAGzuqNwpZYyhOxasucDv7bw30BHmsu2dQPVh597Oe+qUqg/dg4mIiMhGV02n1OeA+4Bt7tsvAb/ToOuRDcZ7kldLp1RHJIAx6pRqFbPpLCMzKf7h4MnVvhRpcV6nVDZvyVc5rlYrb3yvdPc9f03je4WiVBUbQXRGgzUHnRcXpfYOtBMK+KoOOz8+5hallClVL59D92AiIiKygVVTlOq31t4D5AGstVlALQ1SlUTa+VaJ1NAp5fMZOiO1/3Vf1ievK+WvvvtyQ7N8RJaSys79r61RYedeoT4yP+g8lcXa6gph59yi0aYlxvfA6Tyt9Xfp+Wmn6LWpI0zQ7+OSLR1Vd0qdGJ0lHPCxqWPpa5Oq6B5MRERENrRqilKzxpg+wAIYY64BFAAjVSm3/Xk1umNBBZ23iHQ2T29biNMTCb7y1JnVvhxpYcUjpI0qkJb7ndgWDpDNW1JVjg2en0riM9DXFlrysd3RIBOJ2kahC51S7U4n1mXuDnzVFM2Oj8bZ1RvD5zM1fUxZlO7BREREZEOrpij1e8C9wF5jzCPA54F/19Crkg2j3Pbn1ehexl/3ZX1K5/Jct7+fS7d28onvDFa9y5dIvXnje9C4XKl4me7R9nAAoOoRvvNTKfrbwwT8S/8vfDmdUsMzKUJ+H51R57oObOtiMpHh9ERiyWOPj8bZrZDzetI9mIiIiGxoS97RWmt/CPwk8Hrgt4DLrLXPNPrCZGMoPAGrsVOqMxqseccoWZ9SmTzhgI/3vXkvLw/Pct9zZ1f7kqRFlXZKNaY4msjkCAV8+Is6ibyiVLU78J2bTlaVJwXQHQvV3HU6PJ1ioCOMMc41XraturDzXN5ydHSWvQPtNX08WZzuwURERGSjCyz2DmPMLyzyrouMMVhr/7FB1yQbSNItStWy+x44T6ROuoG5srGlc3lCAR8/dflWLux/ib98cJCfunxL4QmxSLMkizOlGtQplUznFowzt9XYKXVuKsX27uqKUp3RINPJLLm8LSmEVeIVpTyXbunEZ+BLT57i4LExXjw7TTjg49O/elXJmN7p8QTpbF5FqTpY6T2YMeZO4O3AeWvt5e7aHwG/CQy7D/tDa+3X3fd9ELgNJ6/q31tr73PXX4MTth4Fvg78trXWGmPCOF1brwFGgXdba4+5x9wK/H/ux/iv1tq7avvsRUREpJUsWpQCfrbC+yygopQsKbGM3fcAuqIBje+1iHQ2Tzjgx+8z/Js37eU/fukZvvPSMG++eNNqX5q0mGQmT0c4wHQqSzrXmCzpRCa3YJx5rlOquo95firJq3Z1V/XY7mgQgOlkhu7Y0hlU4BSldvbOjeBFQ34u3drJ/c+fIxTw0dcWYmgyyYmxOHv653bZOzI8A8DeTdp5rw5Weg/2OeAvcApHxT5mrf0fxQvGmAPALcBlOLv8fcsYc5G1Ngd8Ergd+AFOUeom4Bs4Baxxa+0+Y8wtwEeBdxtjeoEPAVe51/mkMeZea+34EtcrIiIiLWrRopS19tebeSGyMXnje7VnSoWYTGTI560Ccze4dNbplAK4+crt/M/7X+ITDw6qKCVNl0znnM6iVJZ0tjHje/H0wqJUW9h5u5rxvXQ2z+hsms0d1XVKdblFqYl4bUWpV+/uKVn73K9fzWQizZ6+Nn58Zoqb//IRXjw7XbYodWG/OqVWaqX3YNba7xlj9lT58HcAX7TWpoCjxphB4GpjzDGg01r7KIAx5vPAzThFqXcAf+Qe/yXgL4zT3nojcL+1dsw95n6cQtbfr+TzERERkY2rUqdUgTHmZ3D+gla4C7bWfrhRFyUbx3I7pbpjQfIWZtJZOiPBRlyarAH5vHXG99zA5lDAx2/95F4+dO9zPPbyKK+7sG+Vr1BaSTKbY3skCjhjpQ35GJncgt+HXqfUdBVFqeEZZ2e8TZ3hJR7p8IpS1XaeZnJ5xuJpBtpLzz/QES6M9F20uR1j4NDZaW66fEvhMUeGZ+htC9FTxa6AUr0634O93xjzHuAg8PtuB9N2nE4ozyl3LeO+Pn8d99+T7rVkjTGTQF/xeplj5n9et+N0YbFr165lfjoiIiKy3i0ZdG6M+Svg3Ti7vRjgXcDuBl+XbBDJZe6+1+k9kaoxoFfWF++Jv9cpBfDu1+6kvz3EX37nyGpdlrSgXN6SydlCETzToKJUufG9thqCzs9NJQHYXGVRqjvmdkpVWZQam01jLSWZUvPFQgF29cZ48Wxp8PmR87PsHdDoXj3V+R7sk8Be4EpgCPgz78OUeaytsL7cY0oXrf20tfYqa+1VAwMDFS5bRERENrKl95OG11tr34OTHfDHwE8AOxt7WbJRFMb3au2UKho5kY3LK0qFi4pSkaCf2669kO+9NMwzpyZW6cqk1XgF9I6IUyBqVNB5PF2mUypSfVHqvFuU2lTj+F61nVLD004nVqWiFMAlWzo4dHa6ZO3I8IxCzuuvbvdg1tpz1tqctTYPfAa42n3XqXnn3AGccdd3lFkvOcYYEwC6gLEK5xIREREpq5qiVML9N26M2YbTzn1B4y5JNpKEW5SKBGoNOq/tiZSsT94T/+KiFMCvXLOLzkiATzyobilpDq8o5XVpNmp8L5HOEZnfKRWqfve9827RaHNnlUWpmNd1mq7q8eenvaJX5aLUxVs6OTY6W/i6TcTTjM6mVZSqv7rdgxljtha9+fPAj93X7wVuMcaEjTEXAPuBx621Q8C0MeYaNy/qPcBXio651X39ncC3rbUWuA+4wRjTY4zpAW5w10RERETKqiZT6qvGmG7gvwM/xGnD/kwjL0o2jmQmRyToqzms3AvknUhU90RK1ievKBWaV5TqiAT5tdfv4ePfHuTwuWn2b+5YjcuTFpJ0vxcb3SmVzOSIzeuU8vsM0aC/6vE9v8/QV2VuU7kC/2wqWxgZnK+WTqm8hcPnZnjFji6ODM8C2nmvAZZ1D2aM+XvgTUC/MeYUzo54bzLGXOme4xjwWwDW2ueMMfcAzwNZ4H3uznsA78XZyS+KE3D+DXf9s8AX3FD0MZzd+7DWjhljPgI84T7uw17ouYiIiEg5SxalrLUfcV/9sjHmq0DEWjvZ2MuSjaLcTlPV8HJQ1Cm1saUWKUoB/MpP7Obj3x7kwUPnVZSShit0SrmZUo0qSpXLlALojAaq+n13birFpo5w1YX+cMBPJOgrnPvJ4+O8+1OPcuevvZY3XrQwx8crSvW3L9Up5fxMvnh2yi1KOTvvqVOqvpZ7D2at/cUyy5+t8Pg7gDvKrB8ELi+znsTJtyp3rjuBO5e6RhERERGoML5njHmtMWZL0dvvAe4BPmKM6W3Gxcn6l8jkiIWq2uSxRJcypVpCoVPKv/BJen+b86R4Jrl094jISs3PlGpU0Hm8zPgeQF9bmNGZpTtDz00l2VTl6J6nOxpiIp7BWsv/7+svkM1b/vnp8jE/w9MpOiOBstdYbE9fG+GAjxfdXKkjwzOE/D529MRqujYpT/dgIiIi0ioqZUp9CkgDGGPeCPwp8HlgEvh04y9NNgInP6Wa6LJSkaCfcMCnTqkNbrFMKQCfz9AW8jOTyi14n0i9JTPO92IhU6qJ43sA/R1hRmYXFqWGp1P8wice4Zf/+gf83j1P8cLQ1JJ5T/N1RYNMJjLc99w5njw+Tl9biG+/eJ5cfuGmaMMzqSVH98AZObxo81zY+ZHzs1zQ34a/xlFtWZTuwURERKQlVKoW+ItyAN4NfNpa+2Vr7X8G9jX+0mQjSGQW7jRVra5okEl1Sm1o6ZxTcCo3vgfQFg5UlbMjslKpeeN7jeiUyuTyZHK27Phef1uI0ZnUgvVnTk3wwxMTjEyn+cGRUSbiGa7c2V3Tx+2KBRmdTfPfvvkiewfa+M9vP8DobJofnRhf8NjzU6mqd/a7eEtHoVPq5eEZLhxQnlQd6R5MREREWkKluSq/MSZgrc0C1wO3V3mcSEEinSMWXN63S3csqKDzDS6VWTxTCqA9HGAmraKUNF4yWzq+l2pAp1TCLXyVK9T3tYfKju95GU93/vpr2d4dxVqLsxFa9bqiQe5//hwAn3nPVbzuwl6CfsP9L5zjqj2lk2DDMymu2NFd1Xkv2dLBl548xdnJJMfH4vz0K7YufZBUS/dgIiIi0hIqdUr9PfBdY8xXcLYkfgjAGLMPp31cZEnxTI7IMjuluqMhje9tcKlc5aKUOqWkWeaP72VyC0fbVvwx0pWKUmESmdyC7/e54HFnt71aC1Iwl9H32j09vPXSTXRGglxzYR/fcgtV8z9eNeN7MBd2ft9zZ8nlrXbeqy/dg4mIiEhLWLQo5e7E8vs4WwFfa621Rcf8u8ZfmmwEyXSO6DIypcB5cqig841tLuh8saKUX0UpaYr5QeeNyJQqdEqVDTp3ik7zu6WGZ5zg8XBgecV9gF733B/86UsLRa23XrqZI8OzvOzumgcwm8oST+dqLkp9/dkhQDvv1ZPuwURERKRVVKwWWGt/YK39J2vtbNHaS9baHzb+0mQjWO7ue+CM76lTamPznvgvFobfHg4o6FyawuuUioX8+H2mkHdWT/H04kWpfrcQNDJbmitVS+fSYt7zE7v55C+/mlfv6imsXX/pJgC+9cJct5TXlTXQXt3HG2gP09sW4vFjTvTRhSpK1ZXuwURERKQVLK+FRaRKi21/Xo3uqIpSG91cp1T57xGN70mzJIu6mEJ+X0PG97xOqXIjzf1tTiFoQadUHYpSO3pi/NS8vKcdPTEObO0sZE0BnPeKUlV+PGMMl2zpwFrY0hmhPayoIxERERGpjYpS0lCLbX9eja5okHg6RyqrTpmNyguTVqaUrDYv6DwS9BP0m2WP7w1Pp/jdu59iKrmwoO5lSsXKje+1e+N78zqlZlIMVLkbXq3eemAzTx4fL3xMr1NqU2f1RTBvhE95UiIiIiKyHCpKScNYa0lkcmVHVarRHXPCedUttXGl3UJAxd33VJSSJvDG98IBH6GAn3RueUWp7740zD/96DTfe2l4wfsq7b7n5T6NzC9KTaeqHqer1dsu3Uzewl3fP8YDL5wrXHMtH+8Sryil0T0RERERWQb12kvDpHN5cnlb9glYNbxdsKYSGTY1qFNAVpf3xD+8WKdUKEAqmyebyxNYJAxdpB6SmRzhgA9jDOGAb9mdUsdGnPifp05M8PZXbit5X6VMqUjQT0c4wEjR+F6tweO1unx7J9u7o3z824OFtb62ED2xUNXnuHhLJwAX9qtTSkRERERqp6KUNEwy7TypW36nlPPESDvwbVzpJcf3nO+d2XSOrqiKUtI4ycxc/l3Qb8gss1Pq6KhTlPrRyYkF76vUKQVO2Pno7FxRyuuaalRRyhjDF2+/hpNjcdrCAdrCATZ3hvH5TNXneOX2Lv7TT1/Kza/a3pBrFBEREZGNTUUpaZilnoAtpTuq8b2NLpXNYwwEFnkS3OYGJ8+msnS53w8ijeAUpZzCZ2gFnVJHh52i1I9PT5LJ5QkWdfgVh6mX09cWKsmUGq4xeHw5dvbG2NkbW/bxPp/hN994YR2vSERERERaiVoPpGHiaScLaLmdUl4RQp1SG1c6myfkd0amyikuSok0UjKTL+qUWl5RylrLsdFZtnRGSGXzvDg0XfL+wvjeIoX6vvZQye57haJUgzKlRERERERWm4pS0jAr7pRyg84n1Cm1YaWy+UXzpADa3fE9hZ1LoyUzOSIB5/stFPAtK+j8/HSKeDrHO650sqSeOjle8v6EW5TyPs58fe3hkqDz4QaP74mIiIiIrDYVpaRhlhpVWUpHRON7G106lye0yBN0cILOAWZTuWZdkrSoZDZfGN9bbqfUUTfk/A37+ulvDy3IlfJGBBfLbOpvCzEWT5PLW8DplPKZuZ35REREREQ2GhWlpGGWGlVZit9n6IwEmIynl36wrEvpJTqlvPE9dUpJoyUzOcJuAT0c8C0r6NwrSl3Q38aVO7t5al5RKp7OVSzS93eEsRbG3d95w9Mp+trD+GsIHhcRERERWU9UlJKGSVTY/rxa3bGQOqU2sFQ2v+jOewDtypSSJkmV7L63vPG9YyOzhPw+tnVHuXJnNy8PzzLpZuJZa3ns6GjFUPG+NmdMz8uVGp5OKU9KRERERDY0FaWkYVaaKQVO2LkypTaudDZHyL90p9RsWkUpaaxkJk/ELZCG/D4yWVvzOY6OzLK7L4bfZ7hyZw8AT5+aAOC7Lw3z0rkZfu31exY9vq/dGdPzduAbnkkpT0pERERENrSGFaWMMTuNMQ8aY14wxjxnjPltd73XGHO/Meaw+29P0TEfNMYMGmMOGWNuLFp/jTHmWfd9HzfuVl3GmLAx5m53/TFjzJ6iY251P8ZhY8ytjfo8ZXH16ZQKave9DSydzRMOLt0ppfE9abRkdq5TarlB50dHZtnT3wbAK3d2YQz86MQEAJ956GW2dEZ4+yu3LXp8v1uUGpmd65TqV6eUiIiIiGxggQaeOwv8vrX2h8aYDuBJY8z9wK8BD1hr/9QY8wHgA8AfGGMOALcAlwHbgG8ZYy6y1uaATwK3Az8Avg7cBHwDuA0Yt9buM8bcAnwUeLcxphf4EHAVYN2Pfa+1tnQrpA0mn7dk8nnCFYKjm8nrlIqtoFOqMxrk9HiiXpcka0w6l6/YKRUJ+vAZje9J43kh5LC8oPNc3nJ8LM5bLtkEQGckyN6Bdp46Oc6PT0/yyOAoH/ypSyqOq3rjeyPTKay1jKhTSkQ2uD0f+NpqX0LdHPvTn1ntSxARWZca1illrR2y1v7QfX0aeAHYDrwDuMt92F3Aze7r7wC+aK1NWWuPAoPA1caYrUCntfZRa60FPj/vGO9cXwKud7uobgTut9aOuYWo+3EKWRva5x89xnUffZDsMv7C3whe0HlkJZ1SGt/b0FKZyplSxhjawgHtvicNl8zkV9QpdWYiQTqbL3RKAYWw88889DLt4QC/+LpdFc/RFQ0S8BlGZ1NMJjJkclZFKRERERHZ0JqSKeWO1b0KeAzYbK0dAqdwBWxyH7YdOFl02Cl3bbv7+vz1kmOstVlgEuircK7513W7MeagMebg8PDwCj7DteGpkxOcn07xsrsD1GpLZnIYQ8Xd1ZbSHQsymcjg1CNlo0nnKhelwBnhU6eUNFqyKOg85Dc1d0odG3V+7+7pKy1Kjccz3Pv0GW557U46I8GK5/D5DL1tIUZn0gxPO7lSKkqJiIiIyEbW8KKUMaYd+DLwO9baqUoPLbNmK6wv95i5BWs/ba29ylp71cDAQIVLWx+OjcYBeGGo0pe5eRLpHLGgHzcCbFm6okFyeatMoQ0qnc0vWbRsCwcUdC4NZa0llS3tlMrU2Cl11P1jwIUDpUUpAJ8x/Pq1F1R1nr72MCPFRSllSomIiIjIBtbQopQxJohTkPo7a+0/usvn3JE83H/Pu+ungJ1Fh+8AzrjrO8qslxxjjAkAXcBYhXNtaMfdv9Q/v0aKUvFMbkU77wF0R53gX4Wdb0zpbJ7QEhlobeEAMxrfkwZKuV1RK8mUOjoySyzkZ1NRZ9MlWzrojAT4uSu2sb07WtV5+ttDjM6mGJ5Rp5SIiIiIbHyN3H3PAJ8FXrDW/nnRu+4FvN3wbgW+UrR+i7uj3gXAfuBxd8Rv2hhzjXvO98w7xjvXO4Fvu7lT9wE3GGN63N39bnDXNqzJeIZxt3DzwtD0Kl+NI5nOrShPCpygc4BJ5UptSKls5aBzgLaQX+N70lBJd1OGSGCuUyqbt+Tz1Y8NHxuZZU9fW0lnaMDv45//3bXc8fOXV32evrYQIzMpje+JiIiISEto5O57bwB+FXjWGPOUu/aHwJ8C9xhjbgNOAO8CsNY+Z4y5B3geZ+e+97k77wG8F/gcEMXZde8b7vpngS8YYwZxOqRucc81Zoz5CPCE+7gPW2vHGvR5rglenklPLMjzZ9ZGp1Qik1vRznvgZEqBilIbVSq7dKZUWzjA2Gy8SVckrSiZ8TqlnN9XQbdQms7lifiq+x12dGSWy7Z1LVjfXZQxVY3+9nAhUyoU8NEZaeT/pkVEREREVlfD7nattQ9TPtsJ4PpFjrkDuKPM+kFgwZ+arbVJ3KJWmffdCdxZ7fWud15R6oYDW7j74EmGp1d/K/F4Okd0hZ1SKkptbOlsbslMqfZwQJli0lCFTil3fM/7nszk8lV1e2ZyeU6OJ3j7K7et+Fr62sPE0zmOj8YZaA+vKJNPRERERGSta8rue9J4x92Q85su3wKsjbDzRGbl43td7vieMqU2pnSumqBzje9JYyUKRam58T2g6lypk2NxcnnLnv7auqLK6Wt3cvQOnZte9T8siIiIiIg0mopSG8Sx0Vm2dUV41a5uYG2EnSfrMb7nBZ0n0vW4JFlDrLVu0HkVu+8p6FwaaH6nVPH4XjW8TtUL+mMrvpZ+tyh1bHRWRSkRERER2fBUlNogjo/G2d3XRncsxNauyJrolIqnV777XiToI+T3aXxvA8rmLXnLkkHn7aEA6Vy+5t3QRKpVyJTygs7d78lMtrqg85eHvaJU+4qvpa/NKURZq5BzEREREdn4VJTaII6PzrLH/Sv9ga2da6IolajD7nvGGLpiQSY1vrfheEWmajqlAI3wScMks06nVNgLOvfG93LVdegdG52lMxKgx83AWwlvfA9goF1FKRERERHZ2FSU2gCmkxlGZtKFXZ4u3drJkeHZwkjKaqnH+B5AdzSoTqkNyCtKVRN0DijsXBomNW98z+uUSlfZKXV6PMHO3lhdQsn7iwpR6pQSERERkY1ORakNwAs539PndEpdurWTXN5y+NzMal5WXXbfAyfsXEHnG4+X1xMKVP4eKXRKpVWUksYojO8Vgs6d4lK1mVJDk0m2dkXqci2RoL9QiO1Xp5SIiIiIbHAqSm0AXsjuXKdUB7C6O/BZa0lkckRDgRWfqzsWZEKdUhtO9eN7TqFAYefSKMn5u+/5nX8zVRalzk4l2VKnohTMjfCpU0pERERENjoVpTYAr1Nqt9sptbuvjVjIv6o78KXcgkM9OqU6o0GmVJTacFJujs9SRal2ZUpJgxWKUu73ovc9WU24fiKdYyKeYWtXtG7X09fmFKU2qSglIiIiIhucilIbwLGRWTZ3hom5XUl+n+HiLR2r2ikVTztP8qLBlX+LdUdDTMTTKz6PrC1e4XKp3fcUdC6NlsyWju8F/e74XhVFqaHJBEDdxvcA+tyxPY3viYiIiMhGp6LUBnB8NF4Y3fNcurWT54emsLa6oN56S7idB7E6je/NpnNVj9LI+lAIOl+icNkWUtC5NNaC8b3C7ntL/845O5kEqOv43pbOCN2xINE6bBQhIiIiIrKWqSi1ARwdnS2EnHsObO1kOpnl9ERiVa4p4XZKRerwpKor6myzrh34NpZCUWrJTikvU0pFKWmMZCZPyO/D73M6pOZ236umU8opStVzfO/fvnkvf/2eq+p2PhERERGRtUpFqXVuNpVleDpVtlMK4IWh6dW4rEJRqh6ZUt0xpyi1VnfgS2Vz5POr05G2nqWqDjr3dt9T0Lk0RjKTK+nY874nq+nOPDvlFaXq1ym1tSvKVXt663Y+EREREZG1SkWpdc4LOd8zryh1yZYOjFm9HfjmxvfqE3QOa7dT6qb/+RB/9b0jq30Z6061u++FAz4CPqPxPQHg/HSS//3A4boWglPZXGF0DyBYQ6fUmYkEPbFgyfEiIiIiIlIdFaXWueOjs8DcznuetnCA3b0xnj+zukWpejxR6y4UpdZe2PlUMsPRkVkOn5tZ7UtZd7y8nnCg8veIMYa2cEDjewLAN398lj+7/yUGh+v3M5fM5Ikst1NqMsmWOo7uiYiIiIi0EhWl1rljXqdUf9uC9x3Y1skLZ1epKJV2Cgj1Gd9ztkdfi51Sp8edzK6RmdQqX8n6U22nFEB7OKBOKQHmxnhPjcfrds5kJkcksLBTKlXUKTU0meC9f/sk08nS30NDk8m6ju6JiIiIiLQSFaXWueOjs/S3h2kPL9zl7tItnRwfja/Kk/l6ju95QedrMVPqlFuUGptde11ca10tRam2sF+dUgIUF6Xqt4lDMlM6vhcus/ve40fH+MaPz/LEsbGSY89OqSglIiIiIrJcKkqtc0dHFu685/HCzg+tQrdU3As6r0emVMQpuK3NopTTrTE6o6JUrVJZ53sktMTue4A7vqegc5nrmKxvUap0fM/rlMpk53KrppJOUfTFs9NFx+UYm02rKCUiIiIiskwqSq1zx0fjC3be8xzY5hSlViNXytt9rx6ZUgG/j45wYE2O7xV3SlmrHfhqUe3ue6DxPZnjZcudHKvf+F5iXqeU32fw+wzp3Fwh1Bvbe6moKHV20tl5T5lSIiIiIiLLo6LUOpZI5zg7lVy0U2prV4SuaJDnh6bLvr+RknUc3wPoigXXaFHKeWKczuWZVtGkJnNB51V0SoUCxNP6+krjxvfmB+6H/D4yuaJOqcTCTqkhtyilTikRERERkeVRUWodO+F2CuwuE3IOzq5ll27t4IWh1RnfC/hMYQxmpbrXaFHq9MTcE+MxjfDVpJAppfE9qYH3e+BkHYPOU9nS8T2AoN8UvkfB2WkT4OXh2cKufEOTzs+/ilIiIiIiIsujotQ6dmx0FmDRTilwcqUOnZ0ml2/uaFkik6vLznuermiQifjaK/qcGk+ws9cZ3Rmd1Q58tUhl8wT9Bp/PLPnY9rBf43sCwIRblJqIZxbshLdc84POAUIBf0nQ+ZT7cdO5PMdGnN+9Q4XxPRWlRERERESWY+GWbbJuHHeLUotlSgEc2NpJIpPj2Ogsewfam3VpJDO5uoSce7qjIYYmm9/xVcl0MsNEPMO1+/o5OZZQ2HmN0tl8VV1S4HVKZbHWYszSRSzZmKy1TCYy7OyNcnIswanxBJduDa74vE5RqvR7MTSvU2o6mSUa9JPI5Hjx7DT7N3dwdjJJVzRILKT/lYqsB3s+8LXVvoS6OPanP7PalyAiIlI36pRax46NxultC9EVXfxJmbcDX7NH+OLp+halOqPBQqfCWuGN7l2xoxuA0VkVpWqRzuYJV9lN1xYOkM3bQji6tKZkJk86m+fybV1A/XKlkpk8kfmZUgHfgvG9V+zowu8zvHTOyZUamkxqdE/WJGPMncaY88aYHxet9Rpj7jfGHHb/7Sl63weNMYPGmEPGmBuL1l9jjHnWfd/HjftXAWNM2Bhzt7v+mDFmT9Ext7of47Ax5tYmfcoiIiKyTqkotY4dG5lld4XRPYD9m9sJ+EzTi1KJdH3H97pjQSbimTW1w91p9wnxK3Y4T5DHVJSqSU2dUm6Bc1YjfC1twt157/Ltzs9cPXbgs9aSzC4c3wv6fYXsKHDG9wbaw1zQ31YIOx+aTKgoJWvV54Cb5q19AHjAWrsfeMB9G2PMAeAW4DL3mE8YY7wfiE8CtwP73RfvnLcB49bafcDHgI+65+oFPgS8Drga+FBx8UtERERkPhWl1rHjo3H2VBjdAwgH/Ozb1M7zZ5pclKr7+F6QbN4ST6+dsGuvS2PvQDvt4QAjM6uXKfW9l4b5D//w9Kp9/OVI5/KEqth5D5xOKUBh5y3OCzm/oL+NaNBfl06pdC6PtSwc31vQKZWlMxrg4i0dHHKLUmcnk2zpiq74GkTqzVr7PWBs3vI7gLvc1+8Cbi5a/6K1NmWtPQoMAlcbY7YCndbaR63zF6HPzzvGO9eXgOvdLqobgfuttWPW2nHgfhYWx0REREQKVJRap5KZHGcmE0t2SoEzwvfC0PSSj6unendKeSOKE2tohO/UeJxwwEd/e4i+9tCqZkp9+8XzfOnJU6taGKtVKpuruijV7halFHbe2ibizs9/dzTIzt4op+qwA18y4xSeFgad+0qCzqeTGToiQS7e3MGJsTjjs2lGZ9PqlJL1ZLO1dgjA/XeTu74dOFn0uFPu2nb39fnrJcdYa7PAJNBX4VwLGGNuN8YcNMYcHB4eXsGnJSIiIuuZilLr1KnxONayZKcUwKVbOzg7lWS8ieNl9d59rzvmFqXW0A58p8YT7OiJYoyhty20quN7XgfJS2ebW3xciVqDzgFm0ypKtTKvKNUZDbKjJ8bJOnRKpTJO91258T2vUyqVzZHM5OmMOJ1SAA8NjgDaeU82hHK7R9gK68s9pnTR2k9ba6+y1l41MDBQ1YWKiIjIxqOi1Dp1bMTpEKimU+rAVid/pZm5UvUe3+uKhoC54stacGo8wfYe5+vf1xZe1S4lr1jnBTCvB6lsnnCwtqKUOqVam7fZQXcsyM6exnZKhQNzmVLTSef7rjMa5BK3KPWdQ+cB2KbxPVk/zrkjebj/nnfXTwE7ix63Azjjru8os15yjDEmAHThjAsudi4RERGRslSUWqeOjc4CTrbKUi7d6jyJer6ZRakGje9NxtdOUer0hNMpBdC3yp1S3ljjoXMzq3YNtaqlU6q9kCmlolQr84LOu2MhdvTEmE5mV/w7IZn1OqVKvxeD/rnxPa8Y1hkJsrMnRjTo53svOeNG6pSSdeRewNsN71bgK0Xrt7g76l2AE2j+uDviN22MucbNi3rPvGO8c70T+LabO3UfcIMxpscNOL/BXRMREREpK7DaFyDLc2x0lq5okO5YaMnH9rWH2dQRbm5RKpMjVs+gc3d8b610Ss2msozNpueKUu1OUcpai7tjdlN5T8zXW6dUR6S6X0FtYe2+J874nt9naAv52dnr/OydHI/TFeta9jmT3vheYF6mlN9HJutMHXmdUh2RAD6f4aLN7Tx9ahJQUUrWJmPM3wNvAvqNMadwdsT7U+AeY8xtwAngXQDW2ueMMfcAzwNZ4H3WWm9Xiffi7OQXBb7hvgB8FviCMWYQp0PqFvdcY8aYjwBPuI/7sLV2fuC6iIiISIGKUuuUs/Pe0qN7nou3dDB4vnldNIl0jkhdx/fWVtD56Qkny2aHN77XHiabt0wlsnS5BbRmmijKlFqtwlit0tk84RqDzrX7XmubTGTojgYxxhR+9k6Nx7l8+8Ki1EvnpvnHH57md966f8FoXrHFxveCRUHnU8m5LCuAizZ38PSpSToigcL3pshaYq39xUXedf0ij78DuKPM+kHg8jLrSdyiVpn33QncWfXFioiISEvT+N46dWx0lt1VhJx79g60c+T8DE53fWPl8pZUNl/X8b1YyE/QbwpBx6vNy7IpHt8DGJltfq5UPm+ZiKfpiQWZTmUZmkw2/RqWI53LEw5U9z3SpvG9devpkxO8///8sNCRtBITiUyh6Ov97J0qE3Z+6Ow0t3z6B/zVd4/wtz84XvGchU6peeN7oaKg86mEmykVcT62F3auPCkRERERkZVRUWodSmfznB5P1NQptXdTO7PpHGenGl+w8J7k1XN8zxhDVzS0Zsb3vCfCO7rnxveAVcmVmklnyVt47Z5eAA6tkxG+dDZPqMpOqaDfRyjgY0a7760733rhHF99ZojPPnx0xeeajGcKXZNd0SAd4QAnx0rDzg+dneaXPvMDAj7DFTu7+avvHiFe4fsmucjue6GynVJOcfSSLZ2ARvdERERERFZKRal16NR4nLylpk6pfQPtAE0Z4Uu4T/Lq2SkF0BUNMJlYvTDxYqfHE4QCPvrbwwD0up1So6uwA5+XJ3X1BU5R6qWz66goVWXQOTgjfOqUWn+8otEnHhzk/PTKiuLe+B44hertPdGSTqnD55yClN9n+OLt1/Bf3n6AkZk0d31/8W6pE+71dc8buw35TaFTatotSnW4nVIXbXF+n25VUUpEREREZEVUlFqHjo86T6L29FffKbVvUxOLUunynQcr1R1rbqfUj06ML9r5dGo8wY7uKD6fk93kFadGZppfNPNGGnf3tbG5M7xuOqVS2VzVnVLgdN4pU2r9Oel2daayef78X15a0bkmEumSzR129sYKRalEOsdvfv4gPp/h72+/hgsH2nnN7h7edPEAn/rekUJhab5/fvoMl23rLGRUeUIBH5nc3Piez0Cb2/050B7mp1+xhTdfsmlFn4+IiIiISKtTUWodOjoyC8CeGjql+ttDdEWDTe2UioXqGwDcFQ02LVPq8Llp/tUnv8+nvnek7PtPjcfZ3jOXJ9MTW73xvQm3e6w7FuSizR0cPte8QPuVqCXoHJxOqZkGdUrl87apGwGsV8lMjlS2tsLgqfE4r93Ty3t+Yg93HzzJ82eWvwvoRNH4Hji5UifH41hr+bN/OcSx0Tj/65Yr2et2hgL87lsvYiKe4XOPHFtwviPDMzx9apKff9X2Be8LFmdKJTN0ugHr4HRpfeKXX8ONl21Z9uciIiIiIiIqSq1Lx0dn6QgHCiNj1TDGsHegjSPDzeuUiobq++3V3cSi1Ee/+SJ5Wz5EGdxOqaLOilDAR2cksCrje97XpDsa5OLNHRw+P00u3/hA+5VK56rPlAIn7LxR43vfOzzMW//8uzx1cqIh519rJhMZ3v9/flgI7K/Wv/7cE/zu3U9V/fhkJse5qRQ7e2P89vX76YoGuePrzy9rw4Vc3jKdzJYUpXb2xIinczzwwnk++8hRfvl1u3j93v6S467Y2c1bL93MZx56eUGn5Vd+dBpj4Gev2Lbg44UCPrJ5S979uF7IuYiIiIiI1I+KUuvQsdE4u/tjhb/aV2vfpnYGz8826KrmxBs0vtcVCzLVhPG9H7w8yrdeOI8xcLbMTnbxdJbR2XRh9y9PX3uY0VXplHK+Jl2xIBdt6SCZyS8If15r8nlLJmdrKkp1RAING988PeEUH7/x7FBDzr/W/MPBk3z1mSG+c2i46mPyecuPTkzwL8+dYyJe3ff5GffruqMnSlcsyG9fv59HBkd5ZHC05mv2fvaLs5+8n8HfvecptnVF+eBPX1r22N9720VMJbP87wcOF9astfzTU6d5w95+NncuzIYKunln6VyeqUSGjkh9Oz9FRERERERFqXXp+OhsTSHnnn2b2hmZSRWCsRsl2cDxvelUlqyb89II1lr+5OsvsLUrwk2XbSlblCp+ol2sry3E6CpkSk26BYKuqDO+B8vbge/Fs1OcGG1OMcvb1ayWotS27miheFRvXrfZfc+dXVYXz3pireXvHjsBzIV8V+P0RIJEJkc2b/nmj8+WvC+by/PXD728oFPwpNtpuLPX6Sq85bW7AHj61ETN110ovhZ3SrnnnU5m+ZNfeAXt4fK/cw5s6+QXr97FnY8c5UcnxgH44YlxTo4luLnM6B5QGC1N5/LO+J46pURERERE6k5FqXUmk8tzyg0OrlUh7Hy4sUHYXqdUvXff83bdmko2bge2rz07xNOnJvm9t13E7r42zk0lyc8bhfOeaC/slAqtTqZUPEMs5Ccc8LPf/W+8nB34fv+ep/mTb7xQ78srK+Vm9dSy+97OnhgT8cyigdUrMe7+dzs2Gl83QfHL9eiR0UIu3bGR6jsnvcytkN/HPz9zpuR9X3t2iP/6tRf4px+dLln3xgO9n5VoyE9vW2hZxcXJMp1SO3tjhAI+3n3VTt540UDF4//wpy9hS2eE//dLz5DM5Pi/PzpDJOjjxss2l3281ymVyeaZSmTpjKpTSkRERESk3lSUWmfOTCTI5u2yOqW88N8jDR7h84LO612U6nKfjFY7OlSrdDbPf/vmIS7Z0sEvvHoHW7siZPN2wUjeqUJRqrQw2NsWZnR2FTKlEplCwa4tHGBnb3RZhZWx2XTTimpegHS4hu+RXW5XzMmx+ndLTSQytIcDGAP3/fhc3c+/lvzd4yfoiga5bn9/YSfPahw+73xP3XL1Th49Msr5aaeL0FrLJ7/jbAjw4rxi6MmxBEG/YXPH3Hjc1q4IQ8soSk0UOgLnsvTawwH+5XfeyB0/f/mSx3dEgvzJv3olg+dn+PP7X+Krz5zhbQe20LFIB1SoqFNqWp1SIiIiIiINoaLUOnPMfRJ5QX/tRakdPU5XwWCDw84LRalQvTulnCejEw3KFfqX589yYizOf7zpYvw+U8iZmT/Cd2o8TsjvY6A9XLLe73ZKze+sarSJeIau2NwT9Ys3d/DSMopSU4kM0w3sQivmje+Fa+mU6nW6bU7WGM5djYl4mp29MV69q4f7nju79AHr1PB0ivt+fJZ3vmYHF2/u4PjYbNXjioPnZ+hvD/Or1+wmb+Ebzzpfp++8NMyLZ6cJB3wLvu9OjcfZ3h3F55vLv9vWHeXMxMKx2KVMlhnfA9jT30agyu+jn7xogHe9Zgef/t7LjMcz3HzlwoBzT6jQKWWZSmYXLV6JiIiIiMjyqSi1znjjNruXMb7n9xku7G8rjOE0SiLtFDbqXZTyOqUaFXb9vZeG6YwE+MmLNgFORwfA2an5RakE27ojJU+0AXrbQuRt44pmi5lMpAudUgAXbe7g5eHZQjdSNbK5PLPpHDMN2t1uPu/aasmU2tnjdUrVvyg1Hs/QEwty42WbeX5oas0HxS/XPQdPks1bful1u9jdFyOZyXN+urruvsPnZ9i/qZ39mzu4eHMHX3VH+P7qO0fY2hXh3a/dyUvnSnd+PDlvl0qA7d3RQi5bLcqN7y3H//f2A2zuDNPbFqo48hd0vzcTGefnQuN7IiIiIiL1p6LUOnNsdJZYyL+gS6dazg58jS5KOQWHuo/vuYWXRgS1W2t5+PAIr9/bj98tNhWKUpOlT6BPl3miDc7uewBjTR7hm4hnSp6oX7ylg2zecmy0+jFNrxjVrKJUKut009VSlOqOBWkPBxpUlErTEwtx42VbADZkt1Qub/n7x0/wExf2sXegvTACXM0In7WWwXMzhVy6n71iK08cG+drzwzx2NExbrv2Ai7f1kUyky8JTz89Hi90uHm2dkWYTmWZqjEbzAujn98pVauuaJAv3PY6/vrWqwq5UeV4nVLeSK7G90RERERE6k9FqXXm+Gic3X1tGGOWfnAZewfaOTkeL+yQ1wiJTI5QwFco7tSL1w3UiE6poyOznJlMcu3+/sJaX3sYv8+U7ZSaH3IOzu57ACNN3oFvIlFalPKyw16uYUzTG9ubTmaasvtcIVOqhqKUMYadvbFC0Hw9TbqFvd19bVyypYN/eW7j5Up97/Awp8YT/PI1zg54XrdlNcXL89MpplNZ9m92vrfe/kpn7O0//MPTdEWD/OLVu7h4i7vz49kpAOLpLCMz6QUF3G3dzs/OUI0jfBPxDG0hf8VCUrUu2tzBq3f1VHxMKOD8/vJ21OxcYTFMREREREQWUlFqnTk2Orusnfc8+za1Yy2F3bcaIZHO1r1LCuaeFE40oFPqkcERAK7dN1eU8vsMmzvCDBVlSiUzOUZmUuWLUu1OUWq0iUUpay2TiUxJ+LN3HeM1fJ28rpVMzhZ2xlup4ekUozPlu8aWM74HsLMnWvdOKWttSWHvxsu28MTxMYarHGtbL772zBBd0SA3HHC6wbZ3Rwn4DCeq6JQ6fM4pcHqdUnv623jlji4SmRy3/sRu2sIBLtrcgTFw6Kzz2NOL7FK5rdvpQDwzWVtxcTKRobsoO63RQn7nd9iI+z3cEdH4noiIiIhIvakotY7k8paTY/Fl7bzn8Z5UNnKEL5HJNaQoFfT7aA8HmEjUv+jz0OERdvREF2R1be6KcK6oU2qxnffAyZSC5o7vJTN50tl8SadUT8wrSlX/dZpKzI3t1Svs/P3/54f8wZefLfu+QlGqxq4Xp1MqXtdurqlkllzeFr5uN162BWvhWy9snG4pay0PHR7m2v39hUJgwO9je0+0qk4pb+e9/Zs6Cmvvfu1OemJBbn39HsDJkNvdG+PQOadTygukX6xTqtZcqclEesWje7UI+ud1Sml8T0RERESk7hpWlDLG3GmMOW+M+XHR2h8ZY04bY55yX3666H0fNMYMGmMOGWNuLFp/jTHmWfd9Hzfu3JoxJmyMudtdf8wYs6fomFuNMYfdl1sb9Tk225mJBJmcXVGn1AX9bRjT6KJUnlidQ849XdFg3cf3srk8j748yrX7+heMRW7tipR0Sp1yn2hvL9Mp1Rtr/vieV6ArDjqPBP2EA76asremi/J96pErlc9bnj09ydmp8oWH1Ao6pZKZPMOLdGAtx4RbvPO6cC7d2sHO3uiGypV66dwM56ZSvLFoPBVgd19bSQbUYg6fn6ErGqS/fa5T6Zeu3sXj/+mthSw1cMbiXjzrFLC8Au78TKlNHRH8PrOMolSmqUUp73uzkCmloHMRERERkbprZKfU54Cbyqx/zFp7pfvydQBjzAHgFuAy95hPGGO8qsYngduB/e6Ld87bgHFr7T7gY8BH3XP1Ah8CXgdcDXzIGFM5PGSd8Doa9vQvv1MqEvSzsyfGkRryhmqVSGeJNKBTCtyiVJ3H9545Pcl0MluSJ+XZ3Bnh7GSy0JlzeqL8SBI4nSfdsSBjs00sSsXL70jWEwvVNOZY3B01XWMAdTmnJxLE07lFu66WXZTq9Xbgq66gMXh+ml/6zA8qfk7e16nH/RoaY7jxwBa+Pzhal6/FWvDQ4WEArttfutvc7t5YYUfPSgbdnfeKi7bGmAX5Tpds6eDYyCzJTI6TY3HCAd+CTRn8PsOWzsiyMqVWuvNeLbzvzRF1SomIiIiINEzDilLW2u8BY1U+/B3AF621KWvtUWAQuNoYsxXotNY+ap2qwOeBm4uOuct9/UvA9W4X1Y3A/dbaMWvtOHA/5Ytj684xN/tlzwrG96DxO/AlMjmiDeqU6o4Fmahzp9Qjh0cwBl6/d2FRamtXxCmuuN1Dp8YTBP2GTR2RsufqawsVOiuaYW5HstKsne5YsLbxveJOqTqM7x1yu2WmFvlvlc55Qee1fZ/scotSXsfaUp44Ns73j4zyzKnJRR8zXuiUmis63Hj5FtK5PA8eGq7p+lbb+ekkP/Pxh3juTOnn+92Xhtm3qb0wOufZ3RdjKpktdIstZvD8TCHkvJKLt3SSt87jvQ0Bym3KsLUrUijwVmt+oH+jeaOlXqaUilIiIiIiIvW3GplS7zfGPOOO93kdTNuBk0WPOeWubXdfn79ecoy1NgtMAn0VzrWAMeZ2Y8xBY8zB4eG1/+Tz+MgskaCPTR3hpR9cwb5N7bw8Mksu35hd1hLp3Loa33tocITLtnUWMqGKbe50ik/n3BG+U+MJtnVHF91ZsK893NSg88nEwoIKOF+nWop3JZ1SdRjfO3RuunDecvlPy9l9D+byiaoNO/cKTpWKsHPdZnP//V+9q4f+9tC6G+H7/uAoz52Z4i8fHCysJTM5Hj86xhvndUkBhXy6YxXCzkdnUozNpgu7OlYytwPfNCfH42Wz18DJlSoei12KF+jfzB3wCuN77s9zu4LORURERETqrtlFqU8Ce4ErgSHgz9z1cs/wbYX15R5Tumjtp621V1lrrxoYWPiEba05Nhpnd28bvkUKItXaN9BOOpuvutukVvF0rmHje92xYF1335tNZfnRiXHesG9hlxTA1i53+/pCUSpednTP43RKrZXxvVqCzue+pvUIOvc6pbJ5SyKTW/D+VNZZq3V8Lxry098erioHCSiMelYqSnmFq56iopTfZ3jbgS1858XzJMtc/1r11MkJAL7547OFwt3jR8dIZfNcd9HC73Evn+54hbBz72u3f3PHoo8pPl8o4OPQuWlOjScW5El5nKJUgnyVhfFCoH+0ebvveaOJozMpOsKBRQvRIiIiIiKyfE0tSllrz1lrc9baPPAZnMwncLqZdhY9dAdwxl3fUWa95BhjTADowhkXXOxc697x0dkFu8Mtx95NTndEo3Klkg3afQ+cMbWpRKZuu689fnSMTM5y3b7yRcktbqfU2am5Tqnt3RWKUu2h5mZKucWk+U/Way3eTSezhQLRTB1ylF5yO6WgdGc/z3J33wMnOLvaTKlaOqXmh2jfeNlmZtM5vn9kpOZrXC1PnZxg70AbPmP43PePAU6eVMjv45oL+hY8fmdvDGPgeIVOqcNeUWrT0p1SAb+PfQPtHDw2xkQ8U6FTKkImZxmpctR1YpGOwEbyfh5m07mmdmiJiIiIiLSSphal3Iwoz88D3s589wK3uDvqXYATaP64tXYImDbGXOPmRb0H+ErRMd7Oeu8Evu3mTt0H3GCM6XHHA29w19a1fN5yfCy+opBzz74Bp+OhUblSiUxjx/fSuXzZ7pvleHhwhFDAx1V7ymfhb+p0RiXPTiZJZnIMT6cWfaIN0NsWZjyebtho5HwT8QyhgI9IsPRHucvN3qq2eDedyrC1yynArbRTKpPLc2R4ptBRVi4svDC+F6z9V9Cu3hgnq+zy8wpOgxUKsBPxNJ2RhZ0wr9/bT0c4wDd/vDojfPc+fYbf+sLBqruJUtkcz5+Z4q2XbuZnXrmVu584yVQyw0OHR3jtBT1lc94iQT9bOiOFTRTA2Y2yeGe8wfMztIX8he+PpVyypYMfuR1bOxcrSrkdiGeqDDv3RnabuftecYh7h0b3REREREQaomFFKWPM3wOPAhcbY04ZY24D/psx5lljzDPAm4HfBbDWPgfcAzwPfBN4n7XWqzq8F/hrnPDzI8A33PXPAn3GmEHg94APuOcaAz4CPOG+fNhdW9eGppKks/m6dEp1xYL0t4cbVpRq9PgeULcRvocPj3D1nt5FrzcS9NPbFmJoMll4ol5pfK+/PYS11BQyvhKTiTRd0eCCMOmeWIh0tvri3VQiS19biHDAx8wKM6WOjcySyVleu6fXOXeFotSyOqV6YgxNJsm4YemVeN8nw9OpRbPIxuMZesrkiYUCPt58ySa+9cJ5slV8rHr73kvD3PfcOR48dL6qx784NE06l+fKnd3cdu0FzKSy/O8HDvPi2emyeVKe3X0xThR1St3x9Re49qPf5i8fHCSftxw+P82+eTvvVXLxlg68WuhiPytbu50C15kqw84LY6pNLEoV550p5FxEREREpDEa9udfa+0vlln+bIXH3wHcUWb9IHB5mfUk8K5FznUncGfVF7sOHHe3bb9ghTvvefZtamtYUSrZ4E4pcDon5u8kVqvz00kOnZvm5leVzcEv2NIZ4dxUklPjXlGqUqeUU9wYnUnT376yQPpqTMQzZZ+oe2sT8Qyx0NI/5tPJDN2xEB2RAFMr7JTyQs6v2tPDP/3odNnzpXN5fMYZ96rVzt4oubxlaCLJriWKtBOJNNGgn0Qmx+D5GV6ze2FH3Hg8XRJyXuzGy7Zw79NnOHh8nGsuXDj+1kjeGOinv/cy11+6ecnHe3lSV+zsZlt3lKsv6OUzDx0F4LpKRaneNh540Sl8nZ5I8Hc/OEFfe5j/ft8hfnRigkNnp/nJizZVfd0XbZnLnlqsKOWNwNZalOpq4vhecadUZ1SdUiIiIiIijbAau+/JMni7Y+2uw/gewN6Bdo4Mz9Ytm8mTyeXJ5GzDMqWKiy0r9cigkxV03f7yIeeeLV0RhiaLi1KVgs6dQtToTHVZOSs1Ec+UzdnxiizVdmxNJ7N0RAJ0RIIr7pQ6dHYav89w5c5uoDRE3ZPK5msOOfd4I2HVjPCNxzOF6ziySBF2MpGhZ5Fix5suHiAU8K3KLnyjMymMgceOjvHsqcklH//0yQk2dYQLY3a/ce0FAPS3h7l06+Ih5bv7Y4zMpJhNZfmLbw9isfzTv309f/SzB/jOofOMzKTZv3npPCnPJW5RKhbyl93REpzicjTor3p8b2oVxvf8PlMY6VSnlIiIiIhIY6gotU4cH50lFPCxtbO6XJel7NvUzmQiw8hMfcfMvHGxcvk19eB1Siw2ilWLhw6P0BMLcmBrZ8XHbenyOqXiBHyGzRX+G/S1u51STQo7n0hk6CqzI5lXqJqssng3lczQGQ3SHg6sOOj80Nlp9vTFGHA7xcp2SmXzyxrdAyecGyjsLrcYay2T8Qyv2NFFKOBbNFdqPJ5edCysLRzgjfv7+ZfnztW9gLuU0dk0b710M+3hAJ956OUlH//UyQmu2NldGLO7/tLNXLKlg5+6fEvF0bvdvU6h++HBEf7h4Eluee0udvTE+LU3XMDdv3UNr9/bx1svrb5TaktnhM5IgB090UU/rjGGbd0Rhiar7JQqBJ03b/c9mBsvVaaUiIiIiEhjqCi1ThwbnWVXbwxfnbYl3+fupFXvET7vfI168jg3vreyoo+1lkcGR3j9vv4lv6ZbOyOMzaZ5eXiWrd2RilvD9xXG95rTKTUZTy/SKeV2lFVZvJsqdEoFVhx0/tK5aS7e0lHYsaxc0Hkqmye8zG66rV3Of4OlOqXi6RzpXJ6+thAX9i8+rjoxm6n4/XrDZVs4PZHguTNTy7re5RqdSbO7N8Ytr93J154d4nSFUbfJeIaXR2YLXWHgdPrc+/5r+aOfu6zix/Fy6v7o3ufw+Qzve/O+wvtes7uX//Ob17Bv0+KdVvMZY3jTxZu4+oLeio/b1h2taXzP7zO0NajYvZig3+2U0u57IiIiIiINoaLUOnF8NM6eOoScewpFqQq7ki3HJx48Qlc0yE2Xb6nreT1e8WCl43uD52c4N5Xiun2VR/cANrvjUE+eGGdHd+X/Bt2xED4zlwfUaBOJ8plSPYuM7z15fJw//ufnSrp+kpkc6WyezojbKbWC8b1EOsfxsTgXbe4gHPAR8vuYStS3Uyrg97GtO8LJscoFDa8g1x0LsnegvWxRKpPLM53KFr5e5bz10s34DE0d4YunsyQyOfraw/y6O4b3Nw8fXfTxT5+aACgpSoET1l6piApzRamhySS/8rrdbKlyl71KPv6Lr+K/3vyKio/Z1hXl9CLje9Za/ulHpzh4bMzpeHO/z6sNW6+XUMApgml8T0RERESkMVSUWgestRwbnWV3nULOwRmxaQv5F83ZWY7nz0zxrRfO8a/fcAHt4caMu7SF/Ph9ZsXjew+7eVJvqKIo5WX0DE+nKuZJgdOd0hMLMdKAotR0MsOffP0Fxt1zp7I54ulc2U6prkWyt772zBB/88ixksKT1xnVGQnQvsJOqcHzM1jr5AoZY9zOq3KdUrmS3c1qtbMnxoklxve8r1N3LMTeTe2cHI+TnLcbofd91NO2eNGhty3E1Rf08s0fN68oNeqO1fa1hdjeHeVnXrGVLz5xsuxOhuDkSRkDr9jRVfPH6ogE6WsLEQn6eO+b9q7oumuxrTvKyEyKVHbhDpE/Pj3F7979NO/8q0e5/s+/y/ePjDY15NwTKnRKaXxPRERERKQRVJRaB85NpUhm8uypU8g5OCM2eze1c6SOnVJ/+eAgHeEAv/aGPXU753zGGLqjwarH0hbz8OER9vTFCvlElWwpypCqtPOep689xFids7oA/uLBQT71vZf5xx+dBuYKKl1lunwiQT/RoH9B8e7slNNdNDw9N17oFY06IkE6I8GyRaRqeTvvXbTZGffqjAYXz5RaYVHq1BLje97n3h0Nsm9TO9bCy8OzJY+ZcDvJlgrQvvGyLRw+P8PLde4sXIzXaedllN3+xguZSWX55HeOlH38Uycn2DvQvuyOnl+5Zjf/6WcOMNDR+B0jPVu7nZ+rs5MLu6X++ZkzBP2Gj9x8OT2xEEdHZgvF4Wbyvkc71CklIiIiItIQKkqtA8dGnSfS9RzfA9i3yEjTcgyen+brPx7iPa/f3fAdsrpiwRV1SmVyeX7w8mhVXVJAyTjT9iU6pcDprBmdrW+m1MmxOH/zyDEAHnzxPDAXYr5YSHd3LFjoFvJ4BYDzRUUpr2jUGQ0UxveWG+p96OwUoYCv0NXXEQmU3X0vncuvqFNqV1+MkZk08fTiXV3e6GJPW4h9A+XHVcfdr2Gl8T1wcqUA7nvuXFXXl887mWXL/Tp63z/e7nWXb+/iX716B3/90MscHSktrFlrefrUBFfs6F7WxwL43bddxK9es3vZxy/H9m7nZ2n+Dnz5vOWrT5/hjfsH+NVrdvPl976eB//Dm/jY/3NlU68PIOiOmGp8T0RERESkMVSUWgeOF4pS9euUAti7qZ2hyeSKMoQ8f/ngESIBP7dde2Edrqyyrmiw6l3lynnq5ASz6RzX7a+uKNURCRYClpca3wPoaw/Xffe9/37fIQzws1ds47Gjo8yksiWZSeV0leko84pSi3VKdUQC5K0TEr4ch87NsH9TeyHHaLHOq5V2Sh3Y5uyYWCnnaaKoaHfhQBvGLAz294p2SxWltndHecX2rqpzpb57eJhf/uvH+OGJ8SUfm8svLFx543v97XOdS39w08WEA34+8tXnSx57ajzByEyaK3d1V3Vta4XX+TQ/7PyHJ8Y5M5nkZ6/YVli7oL+NTXXaebQW3veoxvdERERERBpDRal14NhonKDf1H18Za/bPbLSXKljI7N85anT/Mo1uwqdHY3kjO8tv+jz8OERfAZ+4sLqilIw1y1VVVGqLVQoKtTDUycnuPfpM/zmdRfyS1fvIpOzPHx4pKjoUv5r3hMLFcbTwCl+nHOLUedLilJOUbLDzZQCll2ofOnsNBdvntuprTMaaMj43k/uH+CSLR18/IFBsrl82ccURvNiQSJBPzt7Ygu+15cq7BW76fItPHVyouy42XyD55yPc3y08ojhM6cmuPS/fJOX3LFHj1fULP552tQZ4d9fv49vv3ieb78417FVCDlfQafUatjmdkoNTZYWpf756TOEAz7eemDzalxWCXVKiYiIiIg0lopS68Dx0Vl29sQILHO3ssV4O/CtNFfqk985QsDv4zff2PguKXA7pVYwvvfw4Aiv2NFdU3Dylq4Ifp8pyZdaTF9bmMlEhswixZJaWGu542vP098e4t+8aS9X7emhIxLgwRfPF4ouixVUumPBkqDz0ZlUoSunuFPKG6/rjAQL2TnLyZWaTGQ4O5Xkoi1zRamOcPlOqdQKdt8D8PkMv/e2izg6Mss/uRlb803EM8RCfsLuDmr7Ni0cV13qa1jsxsucIsn9zy/dLfWyO2I3tEQB6+4nTpLO5nn+zFTJ+thsmkjQR8zt0PP82usv4MKBNj78z88zeH6GO772PH/4j8/SEQlwydYO1pNI0E9vW6hkB75sLs/Xnh3i+ks3NWyzhFrMZUqt/rWIiIiIiGxEKkqtA8dG4oVt2+tpd1+MgM+sKFfq1HicL//wFL/42p1s6mjOeE13LLRgV7lqTSczPHVygmv39dV03MWbO7loc0dVhcFeN5x6fp7TcvzL8+d44tg4v/u2i2gPBwj6fbxx/wAPHjpf+BosVlzrjoVKxveKCyTDi3RKdbiFgOXswDc645yzuHDXGQ0wlah/pxTA2w5s5vLtnXz824fLFgDH45mSsbx9m9o5OjJbMi43Hs8Q8JmqCiD7NnVw4UBbVblSx9yi1PzRtGLprFOAATgzr1toZCZFX1sYY0zJeijg47+8/QDHRuO89c+/y52PHOONFw3w9795TaGrZz3Z1h3hRyfGSbjjoo8dHWNkJs3PvnLbEkc2R1hB5yIiIiIiDbX+nsW0GGstx0ZnC8HR9RT0+9jT37aiotSnvvsyxsBv/WTztpLvigaZTmbLZvEs5Qcvj5HLW67dN1DTcX/wUxfzD//mJ6p6bL87cjVShxG+u75/jF29Md591c7C2psuHuD8dIrvHxnB7zOFQtJ8TqdUuhC27RWlwgEf56fnClRTyQzGQFtobnxvOUWp4uKWpyMSJJHJLSgaOUHnpV1AtTLG6ZY6OZbgy0+eWvD+yUS6JHR/30A76Vyek2NzI3UT8TTdsdCC4s9ibrxsC4++PFoyFlnO0SqKUg8PDhcKi/MfNzabXnQU9k0Xb+J33rqff/eWfTz8B2/mL37p1Vy+vauq619rfuPaCzl0bppb/+ZxppMZ/vnpM7SHA7z5kk2rfWmA8zsyGvSvuIAqIiIiIiLl6U57jRueSRFP57igv/5FKYC9A20LdiSr1rmpJHcfPMk7X7OzkA/TDF6hodyubkt5ZHCEaNDPq3d313RcOOCvepzIKyaMrbBTamgywaMvj/ILr95e0qH1poudJ+zfOzxCVzS4aEGlOxokk7OF0PJzU04h6sC2zgWdUh3hAD6fKRSUlpMp5RWlir9OnYsUuerRKQXw5os3ceXObv73twdJZ0sLX+PxDD1tc0Wpve64anERdiKeoaeGMc63HdhMLm95eHBk0cfE01nOul/rSuN7X3nqDN2xIPs2tTM0bwe60Zk0fe2L57P9zlsv4vdvuJitXc37uWuEm1+1nf91y6v44fFxfukzj/GNH5/lhgObiQRXVrCsl5Dfp5BzEREREZEGUlFqjfOCkhsxvgfOSNOJ0fiy8o8+9d2XyeUt721ilxTM5f/M31muGg8dHubqC3pX3KVTSZ+7Y9robGqJR1b2lafOYC3cfOX2kvWBjjBX7Ogil7d0RxcvqHija97XaWgySdBvuGRLByMzRZlSyUxhPMkrKM0so1NqJjW3i5+nc5ECYiqbq0tRyuuWOj2R4CtPlWZLTcTTJSHwXobaoaJQ8fF4uqo8Kc8rt3fRGQnw0EuLF6WOjTg/s/3toUU7peLpLP/y3Dl++hVb2d0b43QNnVIbzc9dsY1Pv+c1vHRumslEpmTXvdW2d1Mbl27tXO3LEBERERHZsFSUWuO8bJo9DRjfA+eJejZvOT46W9NxIzMp/s/jx7n5yu3salDBbDFep1StYedDkwmODM9y7b7qd91bjn63w2WlO/D93x+d5tW7utlTpkvOG2+qFNbuvc/Ltjo7mWBzZ4RNHRFGZ9OFXeumEtlCh5RXUJpaRtD51CLje7CwU2qlQefFrtvfT3s4wHPzwsIn4pmSglNXNMjegTYeOzo27zHVF38Cfh+v39vPw4MjhbHI+Y65P0s/sbefqWS2bNfZ/c+fI5HJ8Y4rtrG1O1LSUWWtZWQmRb9b3GwFb7lkM3/7G6/jN669gGv3N/bnsxb/742X8Llfv3q1L0NEREREZMNSUWqNOz4ax+8zbO9pzJjOvgFnx65ac6X++qGjpLJ5/u2bm9slBUWdUkvk+sz38GGnu6XRT3o7I0H8PrOgU+q/3/ciD7ywdEg2wPNnpnjx7DQ//6rtZd//FrcoVU2nlFe8OzuVZGtXhIGOMNbCqFusmk5mCh1NhU6pZYzvzZQpSnnje/OLXOlsnnCwPr9+jDHs6IlyoigrylrLRCKzoAvquv0DPPbyKMmMM9I4Hk/XNL4HcN1F/ZyeSBR22JvPy5N6/V4nTH+oTLfUvU+dYWtXhNfu6WVbd5TJRIZZ92seT+dIZfMt0ynlee2eXv6/tx9Yl4HtIiIiIiKyPLr7X+OOjc6yoyfasCdqFw44XTi1FKUm4mm+8Ogx3v7KbewdaG/IdVXSFS0ttlTr4cER+ttDXLy5oxGXVeDzGXrbQiWZUslMjk9+5wh/9d0jVZ3j/z51moDP8PZFdiG7fFsXW7sibKmQKeQVZMbjXqdU0u2Ucjpwzk85RbPpZLZQPPL7DG0hf0ln02wqy7s/9ShPHBujEu+YtnC5Tqm5/1bWWifovI7f07t6YyUB5tMpJwi/Z14X1HX7+0ll8zx5fByovVMK4Do3JN8rcs53dGSWTR3hwrjg/NG88dk0331pmJ+7Yhs+n2Gb+99wyN2Bz+uw62uxopSIiIiIiLQeFaXWuEbtvOdpCwfY1hXhyHD143t3PnKM2XSO961ClxQsb3zPWssjgyO8YV8/Pl91O62tRF9bqGT3vZeHZ8lb+OGJiSWvO5e3fOWp07zp4k30LFKY8PkMX37v6/nAT12y6Hm8LqqJeAZrLUOTc51SAMMzzshYcaYUQHskUJIp9eLZaR47OsYHvvzMgjDxYjOpDNGgv6SA6oVETyXmzpfNW6ylrjua7eqNcWIsXhipm3R3teua10l2zYV9BP2G7x0eJuF2JNWSKQWwqy/Grt4YDx0eLvv+YyOzXNDfVgj/nx92/vUfD5HNW37uSqfg6D3ujBt27nXYVQo6FxERERER2QhUlFrDrLUcH4lzQYMzm/Zuaq+6U2oqmeFzjxzlxss2c8mW1QkA7ioqtlTrxbPTjMykeUOD86Q8fe2lnVKHzzvh2rm8XbTDxvPokVHOTaX4hVeXH93zbOuOLii6FPMypSYTGSYTGVLZvNMp1RkBKOzAV9wpBc4I33Rq7mt7atzpQDoyPMvfPHJ00Y83ncyWjO5BUdB5UadUyi1s1bUo1Rcjlc0XPievO2x+p1RbOMCrdvXw8OGRRR9Tjev29/PokdGyGwQcdYtSmzvC+MzC8b3vD46yvTvKATdAe2uX899jYadU62RKiYiIiIhIa1JRag0bm00znco2tFMKnLDzI8Mz5PPlg5uLfeHR40wls7z/zfsbek2VhAI+2kL+mjqlHhl086SaVJTqbQszWrTD3eFzM/h9hs5IgAcPna947D/+6BQdkUAhN2q5wgE/sZCf8dl0oVtna1e0EMR+fiqFtZbpeZ1SHZFgyfieNxZ33f5+/tcDhzk7r/PHM53M0j6vKNUeCmDMXAg6UMhzqucOiDt7nMKtlyvlFSzLdUG9cX8/z52Z4siwU4itNVMKnK/FbDrHj05MlKxPJjKMzqa5oL+NgN/Hpo4IpydKv14vDE1x+fZOjHE69rZ0RTCGwuO8YmarZUqJiIiIiEjrUVFqDTs26jzB3tPf4E6pgXbi6RxDU+WLDZ54OstfP/Qyb754gFfs6GroNS2lKxqsqVPqocMjXDgwN1LVaH1toZLd9146N82evhhvvGiA7740XLEA+PDhEd566WYiwZUXbbqjQSYSmUIhaUtXhHDAT1c0yPBMitl0jrydv2NeoCTo/ORYgv72MHfc/AqyecsdX3+h7MeaTmVLilvgjBm2hwNMFRUQvWvZ3Fm/TqCdvc7PyEm3q8vrgiqXF3XtficT6mvPDAFzGWW1+Im9/fgMPDxvhK+wW6a7Y+K27kihAwogkc5xdHS2pMsw6PexqSNc6KjyAug1viciIiIiIhudilJr2HF3a/lmdEoBHFlihO/vfnCC8XiG979l9bqkPF2xUNWdUqlsjsePjnFdk7qkwClKTaeypLJOV9Dh8zPs39TBmy7exPB0iueHpsoel89bRmfTbK9T8aw7FmIinuHs1FxRCmBTR5jzU6lCAHlntLhTKlDSKXViLM7O3ii7+mK89yf38s9Pn+H7RxaOIE4nM3SEAwvWOxfpvNrRU79i6w53d8oTo05hx/veKNcp9YrtXXRFg3zjx2cB6GmrvVOqKxrkip3dPDRY+nU45v7MXugWpbZ2R0sypQ6dm8ZauHRr6ejrtu4oZwrjeymiQT+x0MKvpYiIiIiIyEaiotQadmw0js/MPeFuFK8oVSlXKpnJ8anvvcwb9vXxmt09Db2eanRFA0wm0ks/EPjh8QkSmVzT8qQA+tqdLqCx2TTJTI7jo7NctLmdn7zI6dL57kvlQ7InExlyeVu3LpnuWJCJuDO+ZwyFnfcGOsIMz6QKAeQd8zKlioPOT47HC+Nx733TXrZ0Rrjz4WMLPtZMmUwp79zFmVJeN9OuOmalRYJ+tnRGCuN747NuUapM5pbfZ7h2X3+hcLWcTCmA6/b18/TJiUKoOjiB9sbMdW5t745yZiJRCGB/wS1GHphflOqKMlQ0vqfRPRERERERaQUqSjVBLm8LT0prcXx0lm3d0bpm75TT1xaiOxZkcHjxotTdT5xkZCa1qllSxbqjoarH9x4ZHMHvM1yzt6/BVzXHKyqNzqQLO+/t39zBQEeYy7d38p1FcqXmdl6rz2hbTyzkju8lGGgPF3bG29QRZnh6rlNqYaaUs57N5RmaTLLLLbJEgn4u2tLBcFFelmc6maW9XKdUNFgyvndiLE5XNEhnpPYOpUp29kYLBa+JRJqOcICAv/yvuGv3zxUoK4XFV3LdRQPkLTz68ly31LHRWbZ3Rwujl1u7IqSy+UJO1AtDU7SF/AsKzVu7IpyZdIpXI7PpQu6XiIiIiIjIRqaiVIM99vIoV9/xLV48O13zscdGZtnT4NE9AGMM+wYW34Evlc3xV989wmv39HDNhb0Nv55qdMeCVY/vPTQ4wpU7u+teBKmkz+10GZ1NF3beu2hzBwBvvngTTx4fL+mw8YwUdl6rT1Giy+2UOjuVKozugdMpdX46Wehgmr/73mw6Ry5vGZpMkstbdvbOFVG6o0Em4wu71GbKZEp55y4d30sUilz1tLM3VhgNnIhn6K4wlucF3keD/mVnd125s5uuaJAvPnGysObtvOfZ2uV83c64XVAvDk1zydZOfD5Tcq5t3VGSmTzj8Qxjsyl1SolIwxhjjhljnjXGPGWMOeiu9Rpj7jfGHHb/7Sl6/AeNMYPGmEPGmBuL1l/jnmfQGPNx4+7eYIwJG2PudtcfM8bsafonKSIiIuuGilINduFAO2PxNN9082tqcWw03vCQc8/egXZeXqRT6h9/eJqhySTvf8v+wo5hq63LDfBeymQ8w7OnJpo6ugfF43spXjo3jd9nCv8t33Sx02Hz0ODCEb6xOodcd7uB8EMTCbZ0lhalkpl8oVhS2inlFKhm09nCONzOovynnliQ8XkFtVzeukWp8plSJeN7bkZVve3qjXF2Kkkyk2Minqa7QoD5zt4YF/S3LWvnPU/Q7+P9b97Hdw4N8+CL57HWLihKedlgXhfUC2enuHRrx4Jzbet2/tucmUgwOpOuW6eciMgi3mytvdJae5X79geAB6y1+4EH3LcxxhwAbgEuA24CPmGM8Sr5nwRuB/a7Lze567cB49bafcDHgI824fMRERGRdUpFqQYb6Ajz2j29NRelJuJpJhOZpnRKgZMrNTKTZmJeB0wml+cT3xnkih1dvHF/cws7lXTFgqSzeZKZXMXHPfryCHkL1zX52r1Ol9GZNIfPzbCnL1YYw7xyZw9d0SDfObSwKDXqjsXVq1OmJxYim7ccH42XdEpt6nBeP+IWIjvn7b4Hzjie13m0s6izqTsWYirpZF95vN36FsuU8jql8nnLqfFESZGrXnb1xrAWTk8kGI9nyoacF7v9jRfyr16zY0Uf89bX7+HC/jY+8tXnOTeVYjqZLfmZ3eoWm4YmEpwaTzCdzJbsvOfxdoU8M5FgdDZdt045EZEqvQO4y339LuDmovUvWmtT1tqjwCBwtTFmK9BprX3UOvkEn593jHeuLwHXm7XyFy0RERFZc1SUaoKbLtvCoXPTi3YilXNs1CkGNHrnPc9iYef3PnWGk2MJ/t0a6pKCuRygpXKlHh4coS3k58qd3U24qjmdkQBBv3HH92YKo3vgBG1ft7+fRwYX7mA36nZK9S4zfHu+Lrcwk87lF4zvARwZdnaLK959rz3svD6TzHJyPI7fZ9hadGx3LIi1lOREVSpKdUadjKp83nJuOkk6ly8pctWLd86TY3EmExm6l/ga/uLVu/j9Gy5e0ccMBXz857cf4OWRWf7o3ucASjql+tpChAI+zkwmCyO883feg7kxv8PnZ0hn8xrfE5FGssC/GGOeNMbc7q5tttYOAbj/bnLXtwMni4495a5td1+fv15yjLU2C0wCC0IdjTG3G2MOGmMODg+X3/xDRERENj4VpZrgpsu3APDN56rvljrubi2/p447lFVSriiVy1v+8sFBLt3ayfWXblrs0FXhjWZNLLED38OHR7jmwr5CwHezGGPobQtxZiLB8dFZ9rtfX8++Te0MTSZJZ/Ml66MzabpjwUUDumtVvLPc1pJOKaco9fLwDEG/IRyY+3hznVIZTo4l2NYdKbke75zjRV11XjC6V9Aq1hkJkrfOOODJsQRAQ4pSu4qKUuPx9IpG82rx5ks28aaLBwo/38VFKWMM27oinJlIFHbeu2TLwvE9r3j149OTztsa3xORxnmDtfbVwE8B7zPGvLHCY8v9NcpWWK90TOmCtZ+21l5lrb1qYGBgqWsWERGRDUpFqSbY1h3lip3dNY3wHRuJl2wt32jOLn++wjgXwNefHeLlkVn+3Vv2rakuKaAwmlUuLNxzcizOsdF40/OkPH1tYZ44OlbYea+Yl+80fxe70dlUXUe3ikfYNncu7JQ6PZGgIxIs+e/b7hWlUk6m1PxRO6/7qjjTayZZeXwPnHFAL6OqEUHnA+1hwgEfx0bdTqll7qq3HP/57QcI+AwBnymzs160UJTa3RejrcwOhT63G+1ZryilTikRaRBr7Rn33/PAPwFXA+fckTzcf70tYk8BO4sO3wGccdd3lFkvOcYYEwC6gLFGfC4iIiKy/qko1SQ3XbaFZ05NcnoiUdXjj43OsrUzsuydwWrl9xkuLNqBL5+3/MW3B9m3qZ2bLtvSlGuoRWF8r0LYuTce1+w8KU9fe4gz///27jy+7rLM+/jnmz1pmnRPSxda2aFqkQqIPEJBtnlccADF0YGXMqI+4DbDDKjzDOgMPur4iDoo80JBcF9HQUdZHhYRRTZlaUFka6FSWrpIm5Y0TXs9f/zuX3OyN21yzknO9/165ZVz7t/vd3Kf+6SnyZXruu4Xs0bi+/cKSuUBoufT8dxIN7kuDMzkJWKQrV9ddRURPftJQff9TR1drNzQNyiVZ0r9pUem1ODlewAbO7bx7Pos2Jo39h5JVVVi7pQmlj33IhEMWb43kvaZ3sxHTtifkw6Z2SfLba9Jjax6sYNHV23koH76Se08r7WRlRuy94eRanRvZlZI0gRJE/PbwInAUuB64Ox02tnAden29cCZaUe9BWQNze9JJX6bJB2Z+kWd1eua/LFOB25NfafMzMzM+nBQqkh2lvDtYrbU8nWbi9ZPKrfvjGaeSJlSNz+6msdWb+L8Jfv22b6+HORBqRcHCUrd+cRa2lrqd5YmFlue7VJdpR4lXdAdlFqzsVdQaoSbXBcGZgp335O0M1uqcOc96C7BW7Oxg7Xtncyb2jsolR3fsLl77TcN0egcuhunz2pp2Nn0faTNndzI0j9nZXJDNTofaect2Zcvv+NVfcb3mtTA6o0drFi/pd9+UrlZBYE695Qys1HSBtwp6UHgHuC/I+IG4NPACZIeB05I94mIZcAPgEeAG4DzIiLfYeT9wNfImp8/CfwyjV8FTJX0BPD3pJ38zMzMzPrT9zdIGxULpk3gwJkTuWHpKs45esGQ569Yt4WTipyhtO/0Zn7+0HO81Lmd/7j1cfae2sQbXjGrqHPYVa1DlO/t2BH89sl1HHvA9JKVHuYZT/OnNlFX0zP+29aSHXu+V1Bq/ebOEc2SyYN3rY21NNb1DARNm1ifyvd6vg3k9/PG3L3L0br7eRUEpVJPqd4BLsh6SkHWGP3ZDVuYM4olqfOmNHFb2tVwchEzpQYzq7WRfKPCg2b17SeVmz2pe52nTnBPKTMbeRHxFPDKfsbXAccPcM2lwKX9jN8HLOxnvAM4Y48na2ZmZhXBmVJFdPLCmdy3YgNrNnUMet6LL21j/ebOojU5z+0zYwIRcPVvnmbpnzdy3rH7jljD7ZE2sb6G6ioN2Oj8kVUbWb+5k6NL1E8KurNdepfu5cdqq8Xqjd09pbq272DDlk6mjGBAoq6miub6mh5NznN5s/OWXoGkprpqJHjkuSzjqHdfs4kNNVSp//K95n76JRWW7z2zfsuo9JPKFc61tciZUgMpLFUcNFMqlVc21VX3CSCamZmZmZmNR+UZcRinTlk4iwi4adnqQc97Zl3WDLoU5XsAX7zlcWZPauTUQ2cPcUXpSKK1sXbA8r07Uz+pUgalpqWMp95NziGb/4yJWVlXbsOWbUR0XzdSWhtrmdlPUKq7fK9nIEkSzfU1PL4my5TqHUSqqsrW/i9bejY6r1IWUOktf/y1mzpZvXFrnx5VI6lwruWSKbVXyoCaWF/TJ+usUF6+535SZmZmZmZWKRyUKqL925pZMG3CkH2llq/bDMD8acXNlFowbQJVgs6uHbzv2H36lJyVm96BkdyKdZu56s6nOWSvFma0jHxD7V2VZzztN0BPq5mtPYNS6zdnmUcjXbp13pJ9Ofs18/uMzxigpxRk2VPbtgeNtdX99ria3FTHhh6ZUttorq/pt1QyD0o9uirLvJo3deDAzJ4qzJQq5u57g8mz1A6cNXHQUtK8fG8kM+XMzMzMzMzKWXlHHcYZSZy8cCZ3PbWuR+lTb8vXZkGp0Sxz6k99TTXzpjQxY2I9Zxw2Z+gLSqy/TKnVGzt451V3s237Dr7wtkWlmVjy6vmT+etDZ/O6/ab3e7ytpb5HT6l17Vkp30g3uf6bI+ax5MAZfcbzTKmWxr4ld3kZ3twpjf0GUlqbeq79pq1d/Qa3IPu+qq+pYlleDjiKmVJ5UErqLhsstYkNtcxqbeDQeZMHPS8PXo1ko3szMzMzM7Ny5kbnRXbKwplccfuT3PzIas5YPLffc5av20JbSz1NdcV/ef7t1JfTUFtFQ23597Rpbaztka3zly2dnHXVPaxv7+Tb7zmy37K5YprUVMfnBwmMtbU0cMef1u68vy5lSo10+d5ApjcPnCmVZzcNFECa3FTXozfapo6ufnfey7U01u7c2bF3j6qR1Fxfw9QJdXTtCKrLaNfI685/bZ/eXb1NbKiltbF25+tiZmZmZmY23jlTqshePruV2ZMauXHZwCV8K9ZtZn6R+0nljt5vGovnTynJ1x6uSU3d5XtbOrt41zX38vTazVx51mIWzZ1U2sntgraWBtq3dtG+NWsSPlqZUgPJSxv7CyY150GpAQJIk5pq2bC55+57gwalGmrYviOor6ka9aDL3ClNTC6TJue5GRMbdinQe8U7X8V5S/YtwozMzMzMzMxKz0GpIpPESYfM5I7H1+4MRvS2fN2WkgWlxpJJqXxva9d23vvN+3nw2b/wpbcfymtL2Nx8OGamoFDeV2rd5k6qlGVYFcOBMyfylkNnc9Q+U/scy7OnBgxKNdb1KN9rH6R8r/Dx5kxupGqUM5je+Mq9OOXls0b1a4yWo/aZxrwi77ppZmZmZmZWKg5KlcDJC2fS2bWDW/+4ps+x9q1drG3fyt5FbnI+FrU21rKxYxsf+f4D/PrxtXz6tFdw8sKZpZ7WLpvRkmUMFQalJjfVFa3srKG2msvetog5/ZTo7ewpNcBucZObamnf2kVn1w4gK9/Lr+lP3t+pGH3Szjl6AReefOCofx0zMzMzMzPbMw5KlcBhe09mWnM9N/azC9+KfOc9Z0oNqbWpjgj4xcPP8/G/Ooi3DtCjq1z1yZRq38rUIvWTGsrEXSjfA3ZmS7UP0VNqqMczMzMzMzOzyuOgVAlUV4mTDmnjtsfW0LFte49jK9ZtAWBvl/AMKd897rwl+/Ce172sxLMZvradQamsl9T6zZ1MnVAeTa6nNddRU6VBglJZ8CzfRXJTR9fOPlT9yZt8F3tHSTMzMzMzMytfDkqVyMkLZ7Klczt3/OmFHuNPr80ypfZ2ptSQTjy4je+85wguOPGAUk9lt0yor2FifQ3Pv5hnSnUypUwypd5++Dx+9P6jBizJm5yCUhu2ZD29OrfvGHR3uZbG7HH6KxU0MzMzMzOzyuSgVIkc+bKptDbWckOvEr4V6zYzrbl+0P48lmmoreaofaYhFacH02iY0VLPmk1ZUGpt+1amFWnnvaFMbKgddAfDvHzvL1s62dTRla5xppSZmZmZmZntOgelSqS2uorXH9TGzY+u3tksGrKd9xa4yXnFmNnawPMvdtDZtYONHV1MKZPyvaF0B6W27QxKDRZIPXhWC7MnNTLf39tmZmZmZmaWjFpQStLVktZIWlowNkXSzZIeT58nFxz7qKQnJD0m6aSC8cMkPZyOfUkpLUZSvaTvp/G7Jc0vuObs9DUel3T2aD3HPXXKwpls6ujirqfW7RxbsW6zS/cqSNvEBlZv3MqG1JupXBqdD2VnT6mXOmnfmSk1cPnekgNn8JuLjqOpzhmAZmZmZmZmlhnNTKlrgJN7jV0E3BIR+wG3pPtIOhg4EzgkXfMVSdXpmiuAc4H90kf+mOcAGyJiX+Ay4DPpsaYAFwNHAIcDFxcGv8rJ0ftNY0JdNTcsXQXAls4uVm/cynw3Oa8Yba0NrNnUwQubsmbn08ZIUGpCXTW11WLDlm1s6sh24HPJqZmZmZmZmQ3HqAWlIuIOYH2v4TcD16bb1wKnFox/LyK2RsTTwBPA4ZJmAS0RcVdEBPCNXtfkj/Uj4PiURXUScHNErI+IDcDN9A2OlYWG2mqWHDiDm5atZvuO4Jn1+c57zpSqFG0T69m2PXjyhXaAMVO+J4nWxrqsp9TWoXtKmZmZmZmZmfVW7J5SbRGxCiB9npHGZwPPFpy3Mo3NTrd7j/e4JiK6gBeBqYM8Vh+SzpV0n6T7Xnjhhf5OGXWnLJzFus2d3Lt8PcvTznvzHZSqGDNbGwB45LmNwNgp3wOY3FTbo6fUYLvvmZmZmZmZmfVWLo3O+9s+LQYZ391reg5GXBkRiyNi8fTp03dpoiPt2AOmU19TxQ1Ln2f5uixTap7L9yrGjJYUlFqVglJlsvverpjcVMeGLZ205+V7zpQyMzMzMzOzYSh2UGp1KskjfV6TxlcCcwvOmwM8l8bn9DPe4xpJNUArWbngQI9VlibU1/C6/adzw9LnefqFzUyZUEdrozNOKsXMFJRa9txGaqo0prKNWntlSrmnlJmZmZmZmQ1HsYNS1wP5bnhnA9cVjJ+ZdtRbQNbQ/J5U4rdJ0pGpX9RZva7JH+t04NbUd+pG4ERJk1OD8xPTWNk6ZeFMnt/Ywc2PrnaT8wozfWI9Eqzf3MnkCXVUVfWX6Feedpbvbe2ivqaKuppySbw0MzMzMzOzsWDUUhskfRc4FpgmaSXZjnifBn4g6RzgGeAMgIhYJukHwCNAF3BeRGxPD/V+sp38GoFfpg+Aq4BvSnqCLEPqzPRY6yX9K3BvOu+TEdG74XpZOf7ANmqqxPrNnRy7f2nKCK00aqurmDqhnrXtW8dU6R7ApFS+t6mji4ljKMPLzMzMzMzMysOoBaUi4u0DHDp+gPMvBS7tZ/w+YGE/4x2koFY/x64Grt7lyZZYa1MtR+07jTv+9IJ33qtAbS1ZUGpa89jYeS83qamWrV07eGHTVu+8Z2ZmZmZmZsPmepsyccrCmQDMn+byvUqT95WaMsYypSY3ZfNduWGLg1JmZmZmZmY2bP5Nsky88ZV78djzmzjG5XsVJ9+Bb2rz2ApKTUoN+Z9dv4VF8yaVdjJmZmZmZmY25jgoVSaa62u45E2HlHoaVgJ5ptRY7CkFsLlzu3feMzMzMzMzs2Fz+Z5ZibW1ZL2kpo7BnlI5Nzo3MzMzMzOz4XJQyqzE2lrHZqZU3lMKcKaUmZmZmZmZDZuDUmYl9ur5U3jb4rkcsWBqqacyLIWZUi1udG5mZmZmZmbD5N8kzUqsub6Gz5z+ilJPY9gaaqtpqK2iY9sOmh2UMjMzMzMzs2FyppSZ7ba8hM89pczMzMzMzGy4HJQys93W2pgFoyY6U8rMzMzMzMyGyUEpM9tteaaUG52bmZmZmZnZcDkoZWa7LW927vI9MzMzMzMzGy4Hpcxst03a2VPKmVJmZmZmZmY2PA5Kmdlum9zknlJmZmZmZma2exyUMrPdNmdyE4211UxqrCv1VMzMzMzMzGyMcXqDme22MxbP4ZgDptNYV13qqZiZmZmZmdkY40wpM9tttdVVzJ7UWOppmJmZmZmZ2RjkoJSZmZmZmZmZmRWdg1JmZmZmZmZmZlZ0DkqZmZmZmZmZmVnROShlZmZmZmZmZmZF56CUmZmZmZmZmZkVnYNSZmZmZmZmZmZWdA5KmZmZmZmZmZlZ0TkoZWZmZmZmZmZmReeglJmZmZmZmZmZFZ2DUmZmZmZmZmZmVnQOSpmZmZmZmZmZWdE5KGVmZmZmZmZmZkXnoJSZmZmZjRhJJ0t6TNITki4q9XzMzMysfDkoZWZmZmYjQlI18GXgFOBg4O2SDi7trMzMzKxcOShlZmZmZiPlcOCJiHgqIjqB7wFvLvGczMzMrEwpIko9h7Ig6QVgRannMQKmAWtLPYky4bXo5rXo5rXo5rXIeB26VcJa7B0R00s9ifFK0unAyRHxd+n+3wJHRMT5vc47Fzg33T0AeKyoEx09lfBvaKzxa1Ke/LqUH78m5Wm8vC4D/vxVU+yZlKvx8gOqpPsiYnGp51EOvBbdvBbdvBbdvBYZr0M3r4WNAPUz1ucvoBFxJXDl6E+nuPxvqPz4NSlPfl3Kj1+T8lQJr4vL98zMzMxspKwE5hbcnwM8V6K5mJmZWZlzUMrMzMzMRsq9wH6SFkiqA84Eri/xnMzMzKxMuXxv/Bl3qfB7wGvRzWvRzWvRzWuR8Tp081rYHomILknnAzcC1cDVEbGsxNMqJv8bKj9+TcqTX5fy49ekPI3718WNzs3MzMzMzMzMrOhcvmdmZmZmZmZmZkXnoJSZmZmZmZmZmRWdg1JjgKSrJa2RtLRgbJGk30l6QNJ9kg5P47WSrpX0sKRHJX204JrD0vgTkr4kqb9tm8vWAOvwSkl3pef1M0ktBcc+mp7rY5JOKhgf0+sAw1sLSSdIuj+N3y/puIJrKmotCo7Pk9Qu6YKCsYpbC0mvSMeWpeMNabyi1mKcv2/OlXRbel7LJH0ojU+RdLOkx9PnyQXXjNv3TjMzMzMrLw5KjQ3XACf3Gvss8ImIWAT8S7oPcAZQHxEvBw4D3itpfjp2BXAusF/66P2Y5e4a+s75a8BF6fn+BPhHAEkHk+34c0i65iuSqtM1Y30dYBhrAawF3pjGzwa+WXBNpa1F7jLgl73GKmotJNUA3wLeFxGHAMcC29I1FbUWjO/3zS7gHyLiIOBI4Lz0/ngRcEtE7Afcku5Xwnun2YiSdKCkC1Og9ovp9kGlnpdZuUn/Vo6X1Nxr3P+XlIikwyW9Ot0+WNLfS/qrUs/Lukn6RqnnUAwOSo0BEXEHsL73MJBnPLQCzxWMT0i/cDYCncBGSbOAloi4K7Lu9t8ATh3tuY+kAdbhAOCOdPtm4LR0+83A9yJia0Q8DTwBHD4e1gGGtxYR8YeIyL8/lgENkuorcS0AJJ0KPEW2FvlYJa7FicBDEfFgunZdRGyv0LUYz++bqyLi9+n2JuBRYDbZe+S16bRr6X5e4/q902wkSboQ+B4g4B7g3nT7u5IuKuXcrC9J7yr1HCqVpA8C1wEfAJZKenPB4U+VZlaVTdLFwJeAKyT9H+ByoBm4SNLHSzq5CiXp+l4fPwP+Or9f6vmNpppST8B224eBGyV9jiy4eFQa/xHZLxWrgCbgIxGxXtJiYGXB9SvJfjEZ65YCbyL7j+4MYG4anw38ruC8/PluY3yuAwy8FoVOA/4QEVslzabC1kLSBOBC4ATggoLzK24tgP2BkHQjMJ0sEPFZKnMtKuJ9M2V/HQrcDbRFxCrIAleSZqTTKvG902x3nQMcEhHbCgclfZ7sDx+fLsmsbCCfAL5e6klUqPcAh0VEe/q/6EeS5kfEF8kCuVZ8pwOLgHrgeWBORGyU9O9kPydcWsK5Vao5wCNkmf1B9m9jMfB/SzmpYnCm1Nj1frJfnOYCHwGuSuOHA9uBvYAFwD9Iehn9v+FHMSY6yt5NVo5yPzCRLMMBBn6+43UdYOC1AEDSIcBngPfmQ/08xnhfi08Al0VEe6/zK3EtaoCjgXekz2+RdDyVuRbj/n0zlUv8GPhwRGwc7NR+xsb7e6fZ7tpB9r7R26x0zIpM0kMDfDwMtJV6fhWsOv/ZKyKWk7UMOCUFcB2UKo2uiNgeEVuAJ/OfDSLiJfz+VSqLgfuBjwMvRsTtwEsR8auI+FVJZzbKnCk1dp0NfCjd/iFZRBXgb4Ab0l/t1kj6Ddk3+K/Joq+5OXSX/I1ZEfFHsjIkJO0P/M90aCU9M4Xy57uScbgOMOhaIGkOWQ+dsyLiyTRciWtxBHC6pM8Ck4AdkjrIflmvtLVYCfwqItamY78AXkXWZ6rS1mJcv29KqiX7Hv92RPxXGl4taVbKkpoFrEnjFffeabYHPgzcIulx4Nk0Ng/YFzi/VJOqcG3AScCGXuMCflv86VjyvKRFEfEAQMqYegNwNfDyks6scnVKakpBqcPyQUmtOChVEhGxA7hM0g/T59VUSLzGmVJj13PAMen2ccDj6fYzwHHKTCBrbPvHVKaxSdKRaceks8hKWMa0vOREUhXwz8B/pkPXA2em3kkLyJry3jNe1wEGXgtJk4D/Bj4aEb/Jz6/EtYiI/xER8yNiPvAF4FMRcXklrgVwI/AKSU2pl9IxwCMVuhbj9n0zzfsq4NGI+HzBoevJ/rhB+nxdwXhFvXea7a6IuIGsFPoTZO+pNwGXAAekY1Z8PweaI2JFr4/lwO2lnVpFO4usRGyniOiKiLOA15VmShXvdSkglQdDcrV0/3xgJRARKyPiDLJNmb5V6vkUg7J+pVbOJH2XLM11GrAauBh4DPgiWfS0A/hfEXF/KtH4OnAw2V+Fvh4R/54eZzHZjlSNZN/kH4gx9A0wwDo0A+elU/6LLPAS6fyPk5XrdJGVrPwyjY/pdYDhrYWkfwY+SnfgEuDEiFhTaWvR67pLgPaI+Fy6X3FrIemdZN8bAfwiIv4pjVfUWozz982jyTK+Hqb7L58fI+sX8QOyrI5ngDMiYn26Zty+d5qZmZlZeXFQyszMzMzMzMzMis7le2ZmZmZmZmZmVnQOSpmZmZmZmY0TktokfUfSU5Lul3SXpLeUel5mZv1xUMrMzMzMzGwcSJtR/BS4IyJeFhGHAWfScwfVoR6jepSmt0fSxizDPmZm5c1BKTMzMzMzs/HhOKAzIvIdZkk7EP4HgKT5kn4t6ffp46g0fqyk2yR9h2xzDCT9NGVaLZN0bv54ks6R9CdJt0v6qqTL0/h0ST+WdG/6eG3vyUmqlvQ5SQ9LekjSB9L4v6Rrlkq6MgXXSF/jU5J+BXyo12Ndks69CfiGpL0l3ZIe9xZJ89J5A41fI+mK9LyfknSMpKslPSrpmhF7RcxsUI4om5mZmZmZjQ+HAL8f5Pga4ISI6JC0H/BdYHE6djiwMCKeTvffHRHrJTUC90r6MVAP/G/gVcAm4FbgwXT+F4HLIuLOFPi5ETio19c/F1gAHBoRXZKmpPHLI+KTAJK+CbwB+Fk6Nikijhng+RwGHB0RL0n6GfCNiLhW0ruBLwGnApcPMA4wmSyQ96b09V4L/F16vosi4oFB1tLMRoAzpcysrClzp6RTCsbeKumGEfway9Nf7B5In9+8C9d8UtLr0+3bJS1Otz82UvMyMzMz2xOSvizpQUn3pqFa4KuSHgZ+CBxccPo9BQEpgA9KehD4HTAX2I8scPWriFgfEdvSY+ReD1wu6QHgeqBF0sReU3o98J8R0QUQEevT+BJJd6d5HUcWXMt9f5CneH1EvJRuvwb4Trr9TeDoIcYBfhbZdvQPA6sj4uGI2AEsA+YP8nXNbIQ4U8rMylpEhKT3AT+UdBtQDVwKnLw7jyepOiK293NoSUSslXQAcBNw3RDz+pcBDn0M+NTuzM3MzMxsDy0DTsvvRMR5kqYB96WhjwCrgVeSJSh0FFy7Ob8h6ViyANJrImKLpNuBBkCDfO2qdP5Lg5wjIHoMSA3AV4DFEfGspEvS1+ozr34Mdix2YXxr+ryj4HZ+378rmxWBM6XMrOxFxFKylOoLgYuBbwEfT70H/pBnNg2nT8IgWoANBY+3ND8g6YL0g1Leh+D0wgslfRpoTBlX3x6J525mZmY2DLcCDZLeXzDWVHC7FViVsoH+luyPff1pBTakgNSBwJFp/B7gGEmTlTUXP63gmpuA8/M7khb187g3Ae9L15LK9/IA1FpJzcDp/Vy3K35L1tQd4B3AnUOMm1kZcPTXzMaKT5D1SOgEfg7cGhHvljQJuEfS/2N4fRJ6uy011XwZ8NbdmWBEXCTp/IhYtDvXm5mZme2JlGF+KnCZpH8CXiDLJrownfIV4MeSzgBuY+BMoxvIgkcPAY+RlfAREX+W9CngbuA54BHgxXTNB4Evp2tqgDuA9/V63K8B+wMPSdoGfDUiLpf0VbI/HC4H7mX3fBC4WtI/puf9riHGzawMKCuhNTMrf5I+CbSTBY0agK50aApwEtkPR5cDi4DtwP4R0ZRS0C+OiCUDPO5yspTxtZL2AW4BFgLTgJ9HxMJ03gVAc0RcknZl+XlE/CiltF8QEfdJao+I5pF+7mZmZmblQFJzRLSnbKefAFdHxE9KPS8zG5ucKWVmY8mO9CHgtIh4rPBgKq0bsk/CYCLiSUmryRp/PkfPMueG/q8yMzMzqxiXpM1eGsjK8X5a2umY2VjmnlJmNhbdCHwgldsh6dA0vqt9EgYkaQbZVsUryAJcMyRNlVRPtj3xULZJqh3u1zUzMzMbCyLigohYFBEHRsQHw6U3ZrYHnCllZmPRvwJfIOtHILL+A29g1/sk9Oc2SdvJtkq+KCJWw86SwbuBp4E/7sLjXJnm9fuIeMcwvr6ZmZmZmVlFcU8pMzMzMzMzMzMrOpfvmZmZmZmZmZlZ0TkoZWZmZmZmZmZmReeglJmZmZmZmZmZFZ2DUmZmZmZmZmZmVnQOSpmZmZmZmZmZWdE5KGVmZmZmZmZmZkXnoJSZmZmZmZmZmRXd/wfTMk79Rj1BFAAAAABJRU5ErkJggg==\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {
+ "needs_background": "light"
+ },
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "fig, axes = plt.subplots(nrows=1, ncols=2)\n",
+ "fig=(20,7)\n",
+ "data.groupby('YearBuilt').mean()['SalePrice'].plot(ylabel='Sale Price',xlabel='Year Built',ax=axes[0],figsize=fig)\n",
+ "data.groupby('GarageCars').mean()['SalePrice'].plot(kind='bar',ylabel='Sale Price',xlabel='Garage car room',ax=axes[1],figsize=fig)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### The cell below scales the numeric features using a min_max_scaler (scales them between 0 and 1)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 186,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "numeric_columns = train_x.select_dtypes(include='number').columns\n",
+ "# scaler = sklearn.preprocessing.MinMaxScaler()\n",
+ "# scaler = sklearn.preprocessing.KBinsDiscretizer(n_bins=10, encode='ordinal')\n",
+ "scaler = sklearn.preprocessing.StandardScaler()\n",
+ "# train on concatenation of train and validation\n",
+ "scaler.fit(pd.concat((train_x[numeric_columns], valid_x[numeric_columns])))\n",
+ "# apply on all data\n",
+ "train_x[numeric_columns] = scaler.transform(train_x[numeric_columns])\n",
+ "valid_x[numeric_columns] = scaler.transform(valid_x[numeric_columns])\n",
+ "test_x[numeric_columns] = scaler.transform(test_x[numeric_columns])\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### The cell below one-hot encodes the data"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 187,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def get_dummies_column_keys(dataFrames):\n",
+ " '''returns a column list of get dummies for concatenated dataframes'''\n",
+ " return pd.get_dummies(pd.concat(dataFrames)).columns.tolist()\n",
+ "\n",
+ "def get_dummies_using_keys(dataFrame, column_keys):\n",
+ " '''returns get dummies result with columns matching column_keys'''\n",
+ " result = pd.get_dummies(dataFrame)\n",
+ " result = result.reindex(columns=column_keys).fillna(0.00)\n",
+ " return result\n",
+ "\n",
+ "# get the keys for the concatenation of all datasets \n",
+ "column_keys = get_dummies_column_keys((train_x, valid_x, test_x))\n",
+ "train_x = get_dummies_using_keys(train_x, column_keys)\n",
+ "valid_x = get_dummies_using_keys(valid_x, column_keys)\n",
+ "test_x = get_dummies_using_keys(test_x, column_keys)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### The cell below appends a '1' to all rows by adding a 'Bias' feature with the value of 1 for all rows
This is to simulate a bias without adding any bias functionality to the Linear Regression algorithm "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 188,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "train_x['Bias'] = 1\n",
+ "valid_x['Bias'] = 1\n",
+ "test_x['Bias'] = 1"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### This last cell gets the numpy arrays that we will work on from the panda dataframes"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 189,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# store the dataframes in df_* variables\n",
+ "df_train_x, df_train_y = train_x, train_y\n",
+ "df_valid_x, df_valid_y = valid_x, valid_y\n",
+ "df_test_x, df_test_y = test_x, test_y\n",
+ "\n",
+ "# store the numpy arrays in the regular variables\n",
+ "train_x, train_y = train_x.values, train_y.values\n",
+ "valid_x, valid_y = valid_x.values, valid_y.values\n",
+ "test_x, test_y = test_x.values, test_y.values"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Proceed with any data analysis in the next cell\n",
+ "\n",
+ "## (Optional but encouraged)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# This is the main task of the exercise\n",
+ "\n",
+ "## You need to implement the following functions which
implement Linear Regression and all it's required functions"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### The first major function is the 'predict' function
Remember the Linear Regression equation is\n",
+ "$$\\hat{Y} = w_0 + w_1 x_1 + w_2 x_2 + ... + w_m x_m$$\n",
+ "\n",
+ "### hint: try to implement a 'vectorized' version by using a matrix multiplication
which will massively increase performance"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 190,
+ "metadata": {
+ "scrolled": true
+ },
+ "outputs": [],
+ "source": [
+ "\n",
+ "def predict(w, x):\n",
+ " '''\n",
+ " Return the linear regression prediction from the weights and input features\n",
+ " Args:\n",
+ " w: The weight vector, shape: (features, 1)\n",
+ " x: The input data, shape: (num_samples, features)\n",
+ " Returns:\n",
+ " The prediction of the model, shape: (num_samples, 1)\n",
+ " '''\n",
+ " ### YOUR CODE HERE\n",
+ "# raise NotImplementedError\n",
+ " return x.dot(w)\n",
+ "\n",
+ "def init_weights(x):\n",
+ " '''\n",
+ " Initialize the weights vector, the data is passed in to know how many weights you need\n",
+ " Args:\n",
+ " x: The input data, shape: (num_samples, features)\n",
+ " Returns:\n",
+ " The initial weight vector of random values, shape: (features, 1)\n",
+ " '''\n",
+ " ### YOUR CODE HERE\n",
+ "# raise NotImplementedError\n",
+ "# rv = np.zeros((x.shape[1], 1)) \n",
+ " return np.random.randn(x.shape[1], 1)/np.sqrt(x.shape[1])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Next you have to implement the Mean Squared Error Loss calculation following the following formula \n",
+ "$$LOSS(W)=\\frac{1}{2n} \\sum_{i=1}^{n} (\\hat{y}_i-y_i)^2$$"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 191,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def loss(y_hat, y):\n",
+ " '''\n",
+ " Calculate the Mean Squared Error based on the y_hat (the predicted value) and y (the true values)\n",
+ " Args:\n",
+ " y_hat: the predicted values of the input, shape: (num_samples, 1)\n",
+ " y: the true values of the input, shape: (num_samples, 1)\n",
+ " Returns:\n",
+ " Mean Squared Error, shape: Scaler\n",
+ " '''\n",
+ " ### YOUR CODE HERE\n",
+ "# raise NotImplementedError\n",
+ " n = y.shape[0]\n",
+ " delta = y_hat - y\n",
+ " return ((delta.T @ delta) / (2*n))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Next you have to implement the gradient calculation for the weights accoarding to the formula\n",
+ "\n",
+ "$$\\frac{\\partial LOSS(W)}{\\partial W_j} \n",
+ "= \\sum_{i=1}^{n} (2*(\\hat{y}_i-y_i)*x_{i,j} + \\eta * \\frac{\\lvert{\\partial W_j}\\lvert}{\\partial W_j})$$"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 192,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def get_gradient(w, x, y_hat, y, l1_coeff):\n",
+ " '''\n",
+ " Get the gradient of the weights using the parameters passed \n",
+ " (Note: not all parameters passed have to be used)\n",
+ " Args:\n",
+ " w: The weight vector, shape: (features, 1)\n",
+ " x: The input data, shape: (num_samples, features)\n",
+ " y_hat: the predicted values of the input, shape: (num_samples, 1)\n",
+ " y: the true values of the input, shape: (num_samples, 1)\n",
+ " l1_coeff: L1 regularization coefficient\n",
+ " Returns:\n",
+ " The gradients of the weight vector, shape: (features, 1)\n",
+ " '''\n",
+ " ### YOUR CODE HERE\n",
+ "# raise NotImplementedError\n",
+ "# https://towardsdatascience.com/intuitions-on-l1-and-l2-regularisation-235f2db4c261\n",
+ " n = y.shape[0]\n",
+ " delta = y_hat - y\n",
+ " dW = ( (2 * x.T.dot(delta) ) + np.sign(w) * l1_coeff ) / n\n",
+ " return dW"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Next you have to implement the gradient descent update for the weights accoarding to the formula\n",
+ "\n",
+ "$$w_j:=w_j - \\alpha * \\frac{\\partial LOSS(W)}{\\partial W_j} $$"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 193,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def get_updated_weights(w, w_gradients, learning_rate):\n",
+ " '''\n",
+ " Calculate the new value of the weights after applying the gradient descent weight update rule \n",
+ " Args:\n",
+ " w: The weight vector, shape: (features, 1)\n",
+ " w_gradients: The gradients of the weight vector, shape: (features, 1)\n",
+ " learning_rate: The learning rate of the algorithm, shape: Scaler\n",
+ " Returns:\n",
+ " The updated value of the weights, shape: (features, 1)\n",
+ " '''\n",
+ " ### YOUR CODE HERE\n",
+ "# raise NotImplementedError\n",
+ " return w - learning_rate * w_gradients"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Finally implement the training loop, this should simply be
a loop that calls the functions you implemented above"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 194,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def train(epochs, x, y, learning_rate=1e-3, initial_weights=None, l1_coeff=0, initial_b=0):\n",
+ " '''\n",
+ " The main train loop for the algorithm. This performs the gradient update step 'epochs' times.\n",
+ " Args:\n",
+ " x: The input data, shape: (num_samples, features)\n",
+ " y: the true values of the input, shape: (num_samples, 1)\n",
+ " learning_rate: The learning rate of the algorithm, shape: Scaler\n",
+ " initial_weights: The initial weight to start training, this should be passed to continue training\n",
+ " Returns:\n",
+ " The final weight values after applying 'epochs' number of updates on 'initial_weights', shape: (features, 1)\n",
+ " '''\n",
+ " if initial_weights is None:\n",
+ " weight = init_weights(x)\n",
+ " else:\n",
+ " weight = initial_weights\n",
+ " \n",
+ " for epoch in range(epochs):\n",
+ " ### YOUR CODE HERE\n",
+ "# raise NotImplementedError\n",
+ " y_hat = predict(weight, train_x).reshape(-1, 1)\n",
+ " \n",
+ " delta = y_hat - y\n",
+ "\n",
+ " dW = get_gradient(weight, x, y_hat, y, l1_coeff)\n",
+ "# dW = ( -(2 * x.T.dot(-delta) ) + np.sign(weight) * l1_coeff ) / x.shape[0]\n",
+ " weight = get_updated_weights(weight, dW, learning_rate)\n",
+ "# weight = weight - learning_rate * dW\n",
+ " \n",
+ " delta = delta.reshape(-1)\n",
+ " meanSquareError = delta.dot(delta)/x.shape[1] # Capture MSE - and?\n",
+ "\n",
+ " return weight\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### The cell below will run your code\n",
+ "\n",
+ "### After 200,000 epochs our results were the following: \n",
+ "### Train loss:1.9036887e+08
Valid loss: 4.7176443e+08
Test loss: 1.08308357e+09"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 195,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "epoch: 10000 Train loss: [[2.4804421e+08]] Valid loss: [[3.34900642e+08]]\n",
+ "epoch: 20000 Train loss: [[2.27315727e+08]] Valid loss: [[3.5796312e+08]]\n",
+ "epoch: 30000 Train loss: [[2.16517742e+08]] Valid loss: [[3.72254317e+08]]\n",
+ "epoch: 40000 Train loss: [[2.09689841e+08]] Valid loss: [[3.83888202e+08]]\n",
+ "epoch: 50000 Train loss: [[2.05042711e+08]] Valid loss: [[3.94172634e+08]]\n",
+ "epoch: 60000 Train loss: [[2.01730763e+08]] Valid loss: [[4.03437126e+08]]\n",
+ "epoch: 70000 Train loss: [[1.99288268e+08]] Valid loss: [[4.11812646e+08]]\n",
+ "epoch: 80000 Train loss: [[1.97437495e+08]] Valid loss: [[4.19391221e+08]]\n",
+ "epoch: 90000 Train loss: [[1.96003669e+08]] Valid loss: [[4.26255866e+08]]\n",
+ "epoch: 100000 Train loss: [[1.94872149e+08]] Valid loss: [[4.32483734e+08]]\n",
+ "epoch: 110000 Train loss: [[1.93965135e+08]] Valid loss: [[4.3814508e+08]]\n",
+ "epoch: 120000 Train loss: [[1.93228301e+08]] Valid loss: [[4.43302553e+08]]\n",
+ "epoch: 130000 Train loss: [[1.92622764e+08]] Valid loss: [[4.48011256e+08]]\n",
+ "epoch: 140000 Train loss: [[1.92120095e+08]] Valid loss: [[4.5231931e+08]]\n",
+ "epoch: 150000 Train loss: [[1.91699114e+08]] Valid loss: [[4.56268633e+08]]\n",
+ "epoch: 160000 Train loss: [[1.91343773e+08]] Valid loss: [[4.59895749e+08]]\n",
+ "epoch: 170000 Train loss: [[1.9104173e+08]] Valid loss: [[4.63232563e+08]]\n",
+ "epoch: 180000 Train loss: [[1.90783367e+08]] Valid loss: [[4.6630703e+08]]\n",
+ "epoch: 190000 Train loss: [[1.90561097e+08]] Valid loss: [[4.69143745e+08]]\n",
+ "epoch: 200000 Train loss: [[1.9036887e+08]] Valid loss: [[4.7176443e+08]]\n",
+ "Final Model -- Test loss: [[1.08308357e+09]]\n",
+ "CPU times: user 8min 55s, sys: 48.1 s, total: 9min 44s\n",
+ "Wall time: 1min 6s\n"
+ ]
+ }
+ ],
+ "source": [
+ "%%time\n",
+ "w = None\n",
+ "weights=[]\n",
+ "l1_coeff = .0001\n",
+ "for i in range(20):\n",
+ " w = train(10000, train_x, train_y, learning_rate=5e-3, l1_coeff=l1_coeff, initial_weights=w)\n",
+ " weights.append(w)\n",
+ " print('epoch:', (i+1)*10000, end=' ')\n",
+ " print('Train loss:', loss(predict(w, train_x), train_y), end=' ')\n",
+ " print('Valid loss:', loss(predict(w, valid_x), valid_y))\n",
+ "print('Final Model -- Test loss:', loss(predict(w, test_x), test_y))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### The cell below shows us how the prediction improves while training over the span of 200,000 epochs ( By steps of 10000 epochs)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 196,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {
+ "needs_background": "light"
+ },
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "import IPython\n",
+ "for w in weights: \n",
+ " IPython.display.clear_output(wait=True)\n",
+ " pd.DataFrame({'Actual': train_y.flatten(), 'Predicted': predict(w,train_x).flatten()}).head(15).plot(kind='bar',figsize=(10,8))\n",
+ " plt.show()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "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.7.4"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/002/solution/readme.md b/002/solution/readme.md
index 2feed1ff..9277759c 100644
--- a/002/solution/readme.md
+++ b/002/solution/readme.md
@@ -6,3 +6,8 @@
[](https://colab.research.google.com/github/gimseng/99-ML-Learning-Projects/blob/master/002/solution/linear_regression.ipynb)
[](https://nbviewer.jupyter.org/github/gimseng/99-ML-Learning-Projects/blob/master/002/solution/linear_regression.ipynb)
+`lasso_regression.ipynb`
+
+[](https://colab.research.google.com/github/gimseng/99-ML-Learning-Projects/blob/master/002/solution/lasso_regression.ipynb)
+[](https://nbviewer.jupyter.org/github/gimseng/99-ML-Learning-Projects/blob/master/002/solution/lasso_regression.ipynb)
+