Files
neural-network/nnetwork.ipynb

412 lines
46 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "0ef81ad0",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Using matplotlib backend: module://matplotlib_inline.backend_inline\n"
]
}
],
"source": [
"%matplotlib\n",
"\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt"
]
},
{
"cell_type": "markdown",
"id": "9b3f1635",
"metadata": {},
"source": [
"# Neural Network"
]
},
{
"cell_type": "markdown",
"id": "478651c8",
"metadata": {},
"source": [
"## What is a *Neuron* (artificial)\n",
"\n",
"> **disclaimer**: I'm no neurologist. This explanation below is only based on online research.\n",
"\n",
"An **artificial neuron** works *similarly* to a **biological neuron** in the way it processes information.\n",
"\n",
"In a brain (like yours), a **biological neuron** receives **electrical signals** from others, processes them, and sends an output signal.\n",
"\n",
"An **artificial neuron** contrary to biological ones, follows these steps:\n",
"1. **Takes inputs** (usually numbers between 0 and 1).\n",
"2. **Multiplies** each by a corresponding **weight** (representing the importance of that input).\n",
"3. **Adds a bias**, which shifts the result up or down.\n",
"4. **Applies an activation function**, which normalizes or squashes the output (commonly: **sigmoid**, **ReLU**, etc.).\n",
"5. **Returns the final output**, often a value between 0 and 1. \n",
"\n",
"---\n",
"\n",
"## Vocabulary / Key Components\n",
"\n",
"| Term | Meaning |\n",
"|----------|---------|\n",
"| **inputs** | List of input values (e.g., 8-bit binary numbers like `01001010`) |\n",
"| **weights** | Values associated with each input, controlling how much influence each input has |\n",
"| **bias** | A constant added to the weighted sum to adjust the output |\n",
"| **activation function** | A function like `sigmoid` that transforms the output into a bounded range |\n",
"\n",
"---\n",
"\n",
"## Minimal Neuron Implementation\n",
"\n",
"### Step 1 Initialization"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7d9d6072",
"metadata": {},
"outputs": [],
"source": [
"import random\n",
"\n",
"# neuron class 1\n",
"class Neuron:\n",
" \"\"\"\n",
" z : linear combination of inputs and weights plus bias (pre-activation)\n",
" y : output of the activation function (sigmoid(z))\n",
" w : list of weights, one for each input\n",
" \"\"\"\n",
" def __init__(self, isize): \n",
" # number of inputs to this neuron\n",
" self.isize = isize\n",
" # importance to each input\n",
" self.weight = [random.uniform(-1, 1) for _ in range(self.isize)]\n",
" # importance of the neuron\n",
" self.bias = random.uniform(-1, 1)"
]
},
{
"cell_type": "markdown",
"id": "6dd28c51",
"metadata": {},
"source": [
"On their own, you can't do much yet, but they form a good starting point to illustrate how a neuron behaves: \n",
"it takes a input size as parameter, generates a corresponding list of random weights, and assigns a random bias."
]
},
{
"cell_type": "markdown",
"id": "0c47647c",
"metadata": {},
"source": [
"## Step 2 Activation Functions"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ee0fdb39",
"metadata": {},
"outputs": [],
"source": [
"# transform all numbers between 0 and 1\n",
"def sigmoid(x):\n",
" return 1 / (1 + np.exp(-x))\n",
"\n",
"# sigmoid's derivation\n",
"def sigmoid_deriv(x): \n",
" y: float = sigmoid(x)\n",
" return y * (1 - y)"
]
},
{
"cell_type": "markdown",
"id": "79e011c2",
"metadata": {},
"source": [
"These functions are called activation functions. Their goal is to transform any raw values (which can be any number) into a more reasonable range, usually between 0 and 1. The most well-known ones are:\n",
"- sigmoid \n",
"- ReLU (Rectified Linear Unit)\n",
"- Tanh\n",
"\n",
"### Sigmoid Graphical Representation"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "74a95160",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAzoAAAGsCAYAAAAVEdLDAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAZJRJREFUeJzt3Qd4VFX6x/HfTHogCb2HDlKkN4FVdKXYuyKoKKvY18Kudde6/7WvuvaKZQXFXhEFFBTpHanSIfSa3mbm/5wzmRBCAklImPb9PM9l7twyc/LOZWbeOee+1+HxeDwCAAAAgBDi9HcDAAAAAKCykegAAAAACDkkOgAAAABCDokOAAAAgJBDogMAAAAg5JDoAAAAAAg5JDoAAAAAQk6kgoDb7dbWrVuVkJAgh8Ph7+YAAAAA8BNzGdC0tDQ1atRITqczuBMdk+QkJyf7uxkAAAAAAsTmzZvVpEmT4E50TE+O749JTEz0a1vy8vL0448/avDgwYqKivJrW0IR8SW+wYzjl/gGM45f4hvMOH7DK76pqam2E8SXIwR1ouMbrmaSnEBIdOLj4207AuGFDjXEl/gGM45f4hvMOH6JbzDj+A3P+DqOckoLxQgAAAAAhBwSHQAAAAAhh0QHAAAAQMgJinN0ylqCOjc397iMUYyMjFR2drZcLleVP1+4iI6OPmJ5QAAAACDsEh2T4Kxfv94mO8ejbneDBg1sBTiu6VN5TJLTokULYgoAAIBKEfSJjkk8tm3bpoiICFtmrqp7BUwylZ6erurVq9MDUYkxNddKMq9jw4YNK+thAQAAEMaCPtHJz89XZmamvTKqKXt3vIbIxcbGkuhUorp169pkh+GAAAAAqAxBf1KE74uxOccDwcv3+pHoAAAAoDIEfaLjw/kyofH6maGIAAAAwLEKmUQHAAAAAHxIdAAAAACEnHInOr/88ovOPfdce/K/GW705ZdfHnWfqVOnqnv37oqJiVHr1q317rvvVrS9YaWs8a1K11xzjS644AI7X6NGjVJfu4cfflhdu3Y9zq0DAAAAKinRycjIUJcuXfTyyy+XaXtzfZuzzz5bp512mhYtWqQ77rhD1113nX744QeFu127dummm25S06ZNbRJors8zZMgQ/fbbb3a9Kbd85pln+rWN//3vfwuTm9WrV2vo0KF+bQ8AAABQJeWlzRfv8nz5fu211+yFIP/zn//Y++3bt9f06dP13HPP2S/14eziiy+2parfe+89tWzZUjt27NCUKVO0Z88eu94kPv6WlJRUOF+vXj2/tgUAAIQHU5zI7ZHcHo88RW49KrLcffB+bl6eUnOlXWk5ioh0Hdyn4LF8tY58j+G9LVhXsFyHLPduV7hPsfsH21lwW2yd77F988W39y0tXoOp5G0PxqTM2xZd6yn9OYorrSZUvitfq/Y71GFPhto0qKFgUeXX0Zk5c6YGDhx4yDKT4JiendLk5OTYySc1NdXe5uXl2akoc9/+Z3C77WTms/K8Jaergn38XJcicvIOq/QWFxVR5upv+/fv16+//qqffvpJAwYMsMvMBU979uxp583fYi6C+tlnnxUOHZsxY4ZuvfVWrVy5UieeeKLuv/9+myzNnz/fDhszQwRPP/10TZgwwa4z2/Xt21fjxo2z2/z9739XSkqK7WF78803C687ZGJ99913a/z48TbWpg0mMe3Vq5dd73tck4CZ4WvGk08+qeeff95ew+jSSy+118Hxtdvnrbfesgmt6dVr3ry5/vrXv9oerJL4XjtzXSSj+OuMyuGLK/GtGsS3ahFf4hvMqur4NZ+deS6PcvJdys5zKzvfpZw8t3Ly3co1k8t7W/x+nsutPLfZ1628/IJbV8Gt26N8l1sus75gPt/lUb7bY5eZyTvvLlzmu3UXzJskw+WWXPY7mqfw1pek+NZ7Exqz/vDkpvwi9cD8aZUaXxQVoZyam3XvmdXkb2X9f1Tlic727dtVv379Q5aZ++YLdVZWluLi4g7b5/HHH9cjjzxy2PIff/zxsIuCRkZG2p6P9PR02ztikpC+z86SP8wcfZLioiPKtK35Yl+9enV98skn6tChgx26VhITIxMrM5133nkaNGiQ7SXbvHmz7rnnnsLhhGa9STqMhx56yMbQxGrkyJG65JJL7OO//vrrNk5XXXWVnnnmmcJk895779XXX39thyOaZOuFF17QGWecoQULFqhmzZqFj5uWlmYvkvrFF1/Y1+fpp5/WSSedZBOkV199Vc2aNStMSj/++GPbjqeeekqdO3fWkiVLdPvtt9v9hw0bdtjfaV+7rCybzBmTJk2q0GuAsiG+VYv4Et9gxvFbtX74cZKyXVJWvpRlbx32vm/KsZPj4LxbynVJufbW4b11e9flub2TR2X7kTUcOAr6K0xEfL89++Z9UbK3BfdLWlZ4v5RlJa07ZH2RhSW9MkX3L2mbI72aR/o9vTxHgaOCK/ekrNeECevkb77vpn5PdCrivvvu0+jRowvvmy/P5gv44MGDlZiYeMi22dnZ9ku/SRpiY2MVmevtEfCHhMQExUeXPaRjxozRDTfcoHfeeccWazjllFPsOTAmMfAxiaD5m02vjEkSzLbm7+zdu7f27dtn969WrZrdxpcE/vvf/7Y9MIY5H8r07vzxxx92eJxhEh/T0/bggw/aJMm0w0ymd8gwz2G2NUmY6QXyPW5CQoJ9HtMb9Je//EW33HKLXd6jRw87HNG8Fr7XxyQ4JpnyJTWdOnXShg0b9L///c+2uTizr/lb+/XrZwtemIQuKiqqwq8FSv8FxHyJIb5Vg/hWLeJLfAON6cHYm5GrXek52pOeq72Zedqfmat9mXnal5mr/fbWOx3IytW+9Gxlu6o2KYmNcio2MkIxkU5F+6YI761vWVSEwy6LMpNd7/DORzgV6XQo0tx3OhXhmy9Ybu6bfc1thNO7zOmQIp1OOZ3m1iGn2ccuL9jO4V3mvZX31rfM6S285F0mu8yuMwmEb1mR+yZyvvvyLS9Ylp+fp8mTJ/P5Fibvv6kFP6z7PdExvS3m3JOizH3zhbik3hzD9D6U1MNhAls8uC6Xy/ufwf4nc6paTJSWP1p15/6Ynpi01DSb1Jjnq+jQNcMM+TIV7MwQtlmzZun777+3vSRmyJepdmb4/i6TqJgEqGiPlulNKbqNrz1mGJtv3sTf7GOq3fmYZXPnzrXbmGFl5uA9+eSTC/cxsTeJlBn6VvRxffMrVqzQjTfeeMjfb4bI/fzzz3aZSZ7Wrl2rUaNGHZLUmGFp5pyf4nHzPbaJnemhK+21RuUhvlWL+BLfYMbx65Wanaet+7O0bX+2th7w3m47kG2TGnMeiJn2ZuTYoVZl5zjkO0NiXKQSYqOUEBup6jGRqhYdqeq++ZgIVStYZkaLmO3jzW3hfKS9NYlNTJQ3sTFTuF5A3SRJBsdv1YoKkO9nZW1DlSc65guwOWekKJMRmuVVwfwHL0+vSkUSnfxo7xtMSV/Yy8v0zpjs2EwPPPCA7YExQ758ic6xvvgmHsUPBrOs6Lk0lc0MjzNMz0+fPn0OWWfOOwIAwN/MeSop+7O0YU+GNu3JLLzdtDfTJjTpOWUbIWLyitrVYlSnerRqV49Wjfho1YqPVs1q0aoZH6Va1bzLqkU5tHD2bzr3jIGqVT3O9qwAqFqRFfkSu2bNmsL7pkfAlI2uVauWLZNshp2ZE97ff/99u9788v/SSy/Zk93NcCdz8r05f+O7776r3L8kRJjzdUq6ds4JJ5ygDz74wBYO8PV2mV6ZY9WqVStFR0fbktbmHBvD9PCYxy6tYISpnDd79myNGDGicJnpkSp6Dpa5ztK6det0xRVXHHMbAQCoqIycfP2xM12rd6Tpjx1pWr0jXet2pytlX9ZRe2NqxEepYVKcGiXFqmGNWDtfLyFGdYtMJqmJNGOwjsJ8tqYsMUlRtB0uBiAAE5158+bZa+L4+M6lufrqq+31Vsy1XzZt2lS43pSWNknNnXfeaa/J0qRJEzs0K9xLS5sKZmbomkn+zJA0c/6Lia05t+X8888/bPvhw4frH//4h66//npbPMDE2JwDYxxLN7U5v8dUQrvrrrsKk1XTBnOS17XXXlviPqaogOlxMtXZ+vfvr7Fjx2rZsmWF5wAZpljBbbfdZoeqmcIGJkEzf585r6jo+VcAAFQGU7HL9MQs2XJAS1P2a8U2k9Skacu+rFL3MUO/mtWOL5iq2dvkmvFqVCNOjWrEVukIEQBVr9z/g0899dTD6ngX5bu4ZPF9Fi5cWP7WhTBTPMEM6zLll835LOaXHlNwwZzXYooHFGfOafrmm29sUmLOwTEn95tiAiYBMsPfjsUTTzxhh7KZamymsppJYMwFXU3FtZKYggmmzaaXzhQRMEUMTLuKXgTWDMEz5waZc45MEmUSKtPmI5UVBwCgrPak52jxlv02sfFNu9MPXpqiqDrVY9S2fnW1rZ9gp1Z1q6lFnWq2RyZcz2kBwoHDc6SsJUCYygqmZ+DAgQMlVl0zw+dMz9GxfuEvC5MQmPaYdlTGOTrHwvSkmPLRJi6lFXYIFr7X0fT4meGNZ511VkCc7BZqTEJtzpkjvsQ3GHH8hnd8TRIze91ezVq3R7PX77FD0IozVb5MItO5cZI6Nk4sTGzMeTL+FujxDXbEN7zim3qE3KAo+mSDiDnvyQwPa9y4sRYvXmyvo3PZZZcFfZIDAEBJVc+m/7FbM9fuscmNOc+muJZ1q6lLkxrq3CTJTh0aJpX5enYAQh+JThAxF181w9XMbcOGDe05PuaaOQAAhAJT9Wzyih2asnKH7b3JL1YtoF2DBJ3UsrZOallLvVvUDoieGgCBi0QniJhzYswEAEAocLs9Wrh5nyYt36kpK3Yc1mtjzqU5uU1dm9z0aVHLlmwGgLIi0QEAAMfV2l3p+mJBir5YmGKvZVP0HJtezWtqYPv6Or19fVswAAAqikQHAAAclypp3yzeapObxVsOFC6vHhOpP7erp9Pb19OpbespKd7/JzoDCA0kOgAAoMqGpk37Y5fGztqoqat2FZ5zY3puTmlTRxd2b6JB7etTQABAlSDRAQAAlSozN1+fLUjRO7+t17pdGYXLOzVO0oXdGuvcLo3sNWwAoCqR6ASRffv26YUXXtD1119vq65VJXPh1/r16+vMM8+s0ucBAIQOc77N+zM36MPZm5SanW+XJcRE6rJeyRrWO1mt6yX4u4kAwgiJTpAw13W9+uqr1aFDhzIlOaeeeqq6du2q559/vtzP9dlnn+mpp57Sb7/9VsHWAgDCybKtB/TK1LWa+Pt2uQqGpzWrHa+R/Zrrkp7J9jwcADjeeOfxk2uuuUbvvfee90WIjFStWrXUuXNnDRs2zK5zOp2HbP/000/bK78+/vjjZXr8zz//vEJXrl2zZo3++c9/auLEiapZs2a59wcAhFf1tGcnrdZ3S7YVLuvXqrb+0r+FTmtXz56LAwD+QqLjR2eccYbeeecduVwu7dixwyYXt99+uz799FN9/fXXNgHyKev1c3JzcxUdHW0Tp4po3bq1VqxYUaF9AQDhM0Tthcl/6NMFW2wPjsMhndu5kW46tZXaN0z0d/MAwDq02wDHVUxMjBo0aKDGjRure/fuuv/++/XVV1/p+++/t+fIGPv379d1112nunXr2h6dP//5z1q8eHHhYzz88MN2iNpbb72lFi1aKDY2tnDo2h133GHnzeP26dPnsOfv0qWLHn300cL75jHat29vH6Ndu3Z65ZVXDtl+8+bNuuyyy1SjRg2bSJ1//vnasGFDlcUHABBYdqfn6JFvlum0p6dq/LzNNskx17yZcNvJemFYN5IcAAEl9Hp0PB4pL7PqHt/t9j5+boRUbHiZouJlf9Y6BiaRMQmIGXpmEpxLL71UcXFxNvlJSkrS66+/rtNPP12rV68u7LUxw83MeTVmn4iIiMMe84orrrBD3tauXatWrVrZZcuWLdOSJUvsfsbYsWP14IMP6qWXXlK3bt20cOFCjRo1StWqVbPnBuXl5WnIkCHq27evfv31V9vb9H//93+2V8o8julFAgCEppx8l96Ytk6vTlurzFyXXXZSy1q6a0g79WjGMGcAgSn0Eh2ThDzWqMoe3qQ2NUpbef9WKfrYr+JselNM8jB9+nTNmTNHO3futL0/xjPPPKMvv/zSDm8z1dd8w9Xef/992+tTko4dO9rkady4cXrggQcKExvTy2OGqhkPPfSQ/vOf/+iiiy6y903v0PLly21iZRKd8ePHy+12214fR0EyZ4bdmd6dqVOnavDgwcf8dwMAAs+c9Xt1/xdLtWZnur3fpUmSTXD6t65d+HkAAIEo9BKdEKmwZj48zBC19PR01a5d+5D1WVlZtnfGp1mzZqUmOUV7dcaMGWMTHfP4H374oUaPHm3XZWRk2Me79tprbS+OT35+vu1FMkxbTM9RQsKhpUGzs7MPaQsAIDTsz8zV4xNW2iFqRp3q0XrgnA46r0sjEhwAQSH0Eh0zfMz0rFQR06uRmpamxISEwyqj2eeuBKYYgOlRMUmOKSVtekyKMz0pPmZ42dGYam733HOPFixYYBMlc77N0KFD7TrzPMabb7552Lk8vqFwZpsePXrYnqDijpZkAQCCh/kx7MuFKfrXt8u1JyPXLjPXwLn3jPZKii9/NU8A8JfQS3RMN3olDB874jk6US7vcxRPdCrBTz/9pKVLl+rOO+9UkyZNtH37dns+TPPmzY/pcc1jDRgwwCYqJtEZNGiQ6tWrZ9eZC4M2atRI69atsz0/JTHFEszwNbOPKYoAAAg9u7Ola96brxlr99r7bepV12MXdVKv5hWr5AkA/hR6iU4QycnJsYlM0fLSpmjAOeecoxEjRtgeI3Py/wUXXGAv4Nm2bVtt3bpV3333nS688EL17NmzXM9nkhhzLo45p+e55547ZN0jjzyi2267zQ5VMwUGTNvmzZunffv22SFuZl9zLR9Tac1UajOJ08aNG20BBFP62twHAASvrxdv01OLI5Tj3qvoSKduP72NRp3c0s4DQDAi0fEjk9iYoWmmx8ZcnNMUDHjhhRfsyf++YXETJkzQP/7xD40cOVK7du2y5ahPOeUU2wtTXpdccoluvfVWOxzNJE9FmQpv8fHxNpm566677HC4Tp06FZaoNut++eUXO/zNFCxIS0uzZbFNBTh6eAAgeGXluvTw18sKzsVxqFfzmnr6ki5qXqcKR0cAwHFAouMn5jo5vmvlHIk5+d8kP2YqibmOjpmKK+28HlM8oDTDhw+3U2lMkvXee+8dtc0AgODwx4403TJugVbvSLcjvwc3duv5a3ooLtZb6RMAghmJDgAAYVhw4JP5W/TgV78rO8+tugkx+s8lJ2rfytmKjGCoGoDQQKIDAEAYSc/J1wNf/q4vFqbY+ye3qaNnL+uqGrFOTVjp79YBQOUh0QEAIExs3pupke/OtRf/jHA6NHpQW900oJWcTofy8vL83TwAqFQkOgAAhIGlWw7YJGd3eo7qJ8bopeHdKRsNIKSR6AAAEOJ+XrlTN49doKw8l9o1SNC7I3urQVKsv5sFAFUqMpROrETwv34OU/YHAFBpxs3epAe++l0ut8eej/PKFd2VEBtFhAGEvKBPdKKiouyXY3ONmbp161b5F2W3220vuGnKNPuudYNjT3LM62deO3NNIQBA5by3PvPjKr3881p7/5IeTfT4RZ0URVU1AGEi6L9VmotfNmnSRFu2bNGGDRuOywdHVlaW4uLi6H2oRCbJMa+jeT0BAMcmN9+tuz9drC8XbbX3bz+9je4Y2IbPLQBhJegTHaN69epq06bNcakYY57jl19+0SmnnGJ7k1A5TCxNkkPVHwA4Npm5+Rr1/jz9tmaPIp0OPXZRJ13WM5mwAgg7IZHoGOZL8vHoDTDPkZ+fr9jYWBIdAEBAyc5z6br35mnG2j2qFh2hV6/soVPa1vV3swDAL0Im0QEAIJzl5Lt0w//mFyY5/7uuj7o3renvZgGA33A2PQAAIXBOzi1jF2ja6l2Ki4rQOyN7k+QACHskOgAABLF8l1u3f7RQk1fsVEykU29f3VO9W9Tyd7MAwO9IdAAACFLm2jh3frxY3/++XdERTr0xoqf6ta7j72YBQEAg0QEAIAi53R7d/ekSfbN4q62uZi4EOoDCAwBQiEQHAIAgY67p9o8vl+qzBVsU4XTopeHdNLBDfX83CwACCokOAABB5oUpa/ThnM1yOqTnhnbVGSc29HeTACDgkOgAABBEvl2yVc9NXm3n/++CTjqvSyN/NwkAAhKJDgAAQWLx5v3628eL7fy1f2qh4X2a+rtJABCwSHQAAAgC2w5kadT785ST79ZpJ9TV/We193eTACCgkegAABDgMnPzbZKzMy1HbetX1wvDutkiBACA0pHoAAAQ4GWkR49frN9TUlWrWrTevrqXEmKj/N0sAAh4JDoAAASwZyet1sRl3guCvn5VDyXXivd3kwAgKJDoAAAQoL5cmKKXfl5j5x+7qJN6Na/l7yYBQNAg0QEAIAAt2bJfd3+2xM7fOKCVLunRxN9NAoCgQqIDAECASc/J118/XKjcfLcGtq+nu4ec4O8mAUDQIdEBACDAPPDl79q4J1ONkmL1n0u7ykmFNQAoNxIdAAACyOcLtuiLhSkyuc1/h3VTUjwV1gCgIkh0AAAIEOt3Z9jeHOOOgW0pPgAAx4BEBwCAAGDOx7ntw4XKyHWpd4tauuW01v5uEgAENRIdAAACwNM/rNTSlAOqER+l/17eVRGclwMAx4REBwAAP5u6aqfe/HW9nX/q4s5qmBTn7yYBQNAj0QEAwI92pmXr758stvMj+jbT4I4NeD0AoBKQ6AAA4Cdut0d/+3ixdqfnql2DBN1/VnteCwCoJCQ6AAD4yXszN+jXP3YrNsqpF4d1U2xUBK8FAFQSEh0AAPxgy75MPf3DKjv/j7Paq039BF4HAKhEJDoAABxnHo9H//zyd2WaUtLNa+mKPs14DQCgkpHoAABwnH29eKumrtql6AinHruok5yUkgaAwEh0Xn75ZTVv3lyxsbHq06eP5syZc8Ttn3/+eZ1wwgmKi4tTcnKy7rzzTmVnZ1e0zQAABK29Gbl65Jvldv6vf26t1vWq+7tJABCSyp3ojB8/XqNHj9ZDDz2kBQsWqEuXLhoyZIh27txZ4vbjxo3Tvffea7dfsWKF3n77bfsY999/f2W0HwCAoPJ/3y23yc4J9RN0w4BW/m4OAISscic6zz77rEaNGqWRI0eqQ4cOeu211xQfH68xY8aUuP2MGTPUv39/DR8+3PYCDR48WMOGDTtqLxAAAKHml9W79PmCFDkc0hMXd1J0JCPIAaCqRJZn49zcXM2fP1/33Xdf4TKn06mBAwdq5syZJe7Tr18/ffDBBzax6d27t9atW6cJEyboqquuKvV5cnJy7OSTmppqb/Py8uzkT77n93c7QhXxJb7BjOOX+B5JZm6+7v9iqZ2/qk9TndiwekB9lnD8Et9gxvEbXvHNK2M7HB5T+qWMtm7dqsaNG9temr59+xYuv/vuuzVt2jTNnj27xP1eeOEF/f3vf7dVZvLz83XjjTfq1VdfLfV5Hn74YT3yyCMlDoMzvUcAAASbLzc49fM2p2pGe3RvV5diuWQOAFRIZmamHS124MABJSYmVk6PTkVMnTpVjz32mF555RVbuGDNmjW6/fbb9a9//UsPPPBAifuYHiNzHlDRHh1TxMAMezvSH3O8MshJkyZp0KBBioqK8mtbQhHxJb7BjOOX+JZmacoBTZvl/THw6aHdNaBtXQUajl/iG8w4fsMrvqkFo72OplyJTp06dRQREaEdO3Ycstzcb9CgQYn7mGTGDFO77rrr7P1OnTopIyND119/vf7xj3/YoW/FxcTE2Kk4E9hACG6gtSUUEV/iG8w4folvUXkut/751Qq5PdL5XRtpYMdGCmQcv8Q3mHH8hkd8o8rYhnKdBRkdHa0ePXpoypQphcvcbre9X3QoW/GupeLJjEmWjHKMmgMAICi989t6Ld+WqhrxUXrgnA7+bg4AhI1yD10zQ8quvvpq9ezZ0xYXMNfIMT00pgqbMWLECHsez+OPP27vn3vuubZSW7du3QqHrpleHrPcl/AAABCKdqXl6IUpa+z8/We1V53qh49WAAAESKIzdOhQ7dq1Sw8++KC2b9+url27auLEiapfv75dv2nTpkN6cP75z3/K4XDY25SUFNWtW9cmOf/+978r9y8BACDAPDtptdJz8tW5SZIu6d7E380BgLBSoWIEt956q51KKz5wyBNERtqLhZoJAIBwsXxrqsbP3WTnzZA1p9Ph7yYBQFjhSmUAAFQycw7q/3233BYgOLtzQ/VqXosYA8BxRqIDAEAlm7xip2as3aPoSKfuPaMd8QUAPyDRAQCgEuXmu/Xv75bb+VEnt1ByLS50DQD+QKIDAEAlen/mBm3Yk6m6CTG66dTWxBYA/IREBwCASrInPUf/nfKHnb9r8AmqHlOhmj8AgEpAogMAQCV5bvJqpWXnq2OjRF3cg3LSAOBPJDoAAFSCVdvTNG72wXLSEZSTBgC/ItEBAKASy0mf0bGBTmpZm5gCgJ+R6AAAcIx+XrVTv/6xW9ERTt13FuWkASAQkOgAAHAMXG6Pnvh+pZ0f2b+5mtWuRjwBIACQ6AAAcAy+XbJVq3ekKzE2UjefRjlpAAgUJDoAAFRQvsut5yd7y0lff0pLJcVFEUsACBAkOgAAVNAXC1O0fneGasZH6Zr+LYgjAAQQEh0AACogN99deHHQm05txcVBASDAkOgAAFABH8/brC37slQ3IUZXndScGAJAgCHRAQCgnLLzXHrppzV2/tbTWisuOoIYAkCAIdEBAKCcxs3epO2p2WqUFKvLeycTPwAIQCQ6AACUQ2Zuvl6Z6u3N+evpbRQTSW8OAAQiEh0AAMrhvRkbtTs9V01rxeuSHk2IHQAEKBIdAADKKC07T6//stbO3zGwjaIi+BgFgEDFOzQAAGU0ZvoG7c/MU6u61XR+18bEDQACGIkOAABlsD8zV2/9us7O3zmorSKcDuIGAAGMRAcAgDJ445d1SsvJV7sGCTrrxIbEDAACHIkOAABHcSAzT+/N2GDnRw9qKye9OQAQ8Eh0AAA4iv/N2qCMXJftzRnUoT7xAoAgQKIDAMARZOW6NOY3b2/OTae2ksPBuTkAEAxIdAAAOILxczdpb4b3ujlnd+LcHAAIFiQ6AACUIs/l1pu/rrfz15/SUpFcNwcAggaJDgAApfhq0Val7M9SneoxuqRHE+IEAEGERAcAgBK43R69Nm2tnb/2Ty0UGxVBnAAgiJDoAABQgh+X79CanelKiI3UlSc1JUYAEGRIdAAAKMbj8ejVgt6cEX2bKSE2ihgBQJAh0QEAoJiZa/do8eb9iol0amT/FsQHAIIQiQ4AAMW8MtXbm3N5r2RbiAAAEHxIdAAAKGLJlv2avma3IpwOXXdyS2IDAEGKRAcAgCJe+dnbm3N+l0ZKrhVPbAAgSJHoAABQwFRZ+2H5djt/46mtiAsABDESHQAACrw+ba08HmlQh/pqWz+BuABAECPRAQBA0s7UbH25KMXG4iZ6cwAg6JHoAAAg6f2ZG5Xn8qhns5rq3rQmMQGAIEeiAwAIe9l5Lo2dvdHG4do/cd0cAAgFJDoAgLD3xcIU7cvMU5OacRrcsUHYxwMAQgGJDgAgrHk8Ho2Zvt7OX9Ovub1+DgAg+JHoAADC2i9/7NYfO9NVPSZSQ3sl+7s5AIBKQqIDAAhrbxf05lzas4kSYqP83RwAQCUh0QEAhK0/dqTpl9W75HBII/tRhAAAQgmJDgAgbI35zdubM7hDfTWtHe/v5gAAKhGJDgAgLO3NyNXnC7wXCL32Ty393RwAQCUj0QEAhKVxszcqJ9+tTo2T1Ks5FwgFgFBDogMACDu5+W69P/PgBUId5iQdAEBIIdEBAISdb5ds1c60HNVPjNFZnRr6uzkAgCpAogMACLsLhPpKSo/o21zRkXwUAkAo4t0dABBWZq/fq2VbUxUb5dQVfZr6uzkAgCpCogMACCu+3pyLuzdRjfhofzcHAFBFSHQAAGFj895MTV6xw86P7M8FQgEglJHoAADCxgezNsrjkU5pW1et61X3d3MAAFWIRAcAEBay81waP2+znR9xUjN/NwcAUMVIdAAAYeHrxVu1PzNPjWvE6bR29fzdHABAFSPRAQCERUnp/xVcIPSqvs0U4eQCoQAQ6iqU6Lz88stq3ry5YmNj1adPH82ZM+eI2+/fv1+33HKLGjZsqJiYGLVt21YTJkyoaJsBACiXRZv3a2nKAXvNnMt6JhM9AAgDkeXdYfz48Ro9erRee+01m+Q8//zzGjJkiFatWqV69Q4fCpCbm6tBgwbZdZ9++qkaN26sjRs3qkaNGpX1NwAAcETvF/TmnNu5kWpVo6Q0AISDcic6zz77rEaNGqWRI0fa+ybh+e677zRmzBjde++9h21vlu/du1czZsxQVFSUXWZ6gwAAOB52p+fouyXb7PzV/ShCAADholyJjumdmT9/vu67777CZU6nUwMHDtTMmTNL3Ofrr79W37597dC1r776SnXr1tXw4cN1zz33KCIiosR9cnJy7OSTmppqb/Py8uzkT77n93c7QhXxJb7BjOM3MOM7btYG5brc6twkUe3rV+P9u5Lji7IhvlWL+IZXfPPK2A6Hx5yhWUZbt261Q89M74xJXnzuvvtuTZs2TbNnzz5sn3bt2mnDhg264oordPPNN2vNmjX29rbbbtNDDz1U4vM8/PDDeuSRRw5bPm7cOMXHx5e1uQCAMOfySI8uiND+XIeubO1Sr7pl/sgDAASozMxM23Fy4MABJSYmVt7QtfJyu932/Jw33njD9uD06NFDKSkpevrpp0tNdEyPkTkPqGiPTnJysgYPHnzEP+Z4ZZCTJk2y5x35huKB+AYLjl/iG8wqcvz+uHyH9s9arJrxUbp3+OmKiSp5JAF4f6hqvP8S32CWF2Dff32jvY6mXIlOnTp1bLKyY8eOQ5ab+w0aNChxH1NpzQSk6DC19u3ba/v27XYoXHT04SeFmspsZirOPE4gBDfQ2hKKiC/xDWYcv4ET33Fzt9jby3s3VfX42CpuWWjg+CW+wYzjNzziG1XGNpSrvLRJSkyPzJQpUw7psTH3iw5lK6p///52uJrZzmf16tU2ASopyQEAoDKs2Zmm39bskblkzhV9mhJUAAgz5b6OjhlS9uabb+q9997TihUrdNNNNykjI6OwCtuIESMOKVZg1puqa7fffrtNcEyFtscee8wWJwAAoKr4LhB6evv6alKT8zsBINyU+xydoUOHateuXXrwwQft8LOuXbtq4sSJql+/vl2/adMmW4nNx5xb88MPP+jOO+9U586dbTEDk/SYqmsAAFSF9Jx8fbYgxc6P6EtJaQAIRxUqRnDrrbfaqSRTp049bJkZ1jZr1qyKPBUAAOX2xYItNtlpWbea+reqQwQBIAyVe+gaAACBzFw14f2CYWtXndRMTnOSDgAg7JDoAABCyuz1e/XHznTFR0fo4h5N/N0cAICfkOgAAELK2Nmb7O35XRsrMdb/ZVABAP5BogMACBm703M08fdtdp6S0gAQ3kh0AAAh4+N5m5Xn8qhrcg2d2DjJ380BAPgRiQ4AICS43R6NKxi2Rm8OAIBEBwAQEn75Y5e27MtSYmykzuncyN/NAQD4GYkOACCkihCYSmtx0RH+bg4AwM9IdAAAQW/bgSxNWbHDzjNsDQBgkOgAAILeR3M2y+2RTmpZS63rJfi7OQCAAECiAwAIankutz6a6ytC0MzfzQEABAgSHQBAUJuyYqd2pOaodrVoDenYwN/NAQAECBIdAEBQGzt7o729rFeyoiP5WAMAePGJAAAIWhv3ZOjXP3bL4ZCG927q7+YAAAIIiQ4AIGiNm+M9N2dA27pKrhXv7+YAAAIIiQ4AICjl5Lv0ybwtdp4iBACA4kh0AABBaeLv27U3I1cNk2J12gl1/d0cAECAIdEBAASlsbO8w9Yu79VUkRF8nAEADsUnAwAg6KzekaY5G/YqwunQ0F7J/m4OACAAkegAAILOuNne3pyB7eupQVKsv5sDAAhAJDoAgKCSlevSZwu8RQiG92nm7+YAAAIUiQ4AIKhM+H270rLzlVwrTie3ruPv5gAAAhSJDgAgqHxUUFLaFCFwOh3+bg4AIECR6AAAgkZKhrRo8wFFOh26rCdFCAAApSPRAQAEjRk7vB9bQzo2UN2EGH83BwAQwEh0AABBITM3X/N2e4eqDe/T1N/NAQAEOBIdAEBQ+G7pdmW7HGpWK159W9b2d3MAAAGORAcAEBQ+nOstQjC0V2OKEAAAjopEBwAQ8H5POaClKamKcHh0UbfG/m4OACAIkOgAAALe2Nmb7G2XWh7Vrhbt7+YAAIIAiQ4AIKCl5+Tr60Updr5/fbe/mwMACBIkOgCAgPbVohRl5LrUsk41tUr0d2sAAMGCRAcAELA8Ho/GFQxbu7xXEzm81aUBADgqEh0AQMBasuWAlm1NVXSkUxd2beTv5gAAggiJDgAgYI2dvdHent2poWrER/m7OQCAIEKiAwAISKnZefpm8TY7P7xPU383BwAQZEh0AAAB6cuFKcrKc6lNverq2aymv5sDAAgyJDoAgIAsQjB2lrcIwRV9mspBFQIAQDmR6AAAAs78jfu0akea4qIidFGPJv5uDgAgCJHoAAACztiCktLndWmkxFiKEAAAyo9EBwAQUPZm5Oq7Jd4iBFecRBECAEDFkOgAAALKp/M3K9flVqfGSercpIa/mwMACFIkOgCAgOF2ezRu9sEiBAAAVBSJDgAgYMxYu0cb9mQqISZS53Vt5O/mAACCGIkOACBgjJ290d5e1L2x4qMj/d0cAEAQI9EBAASEHanZ+nH5Djs/vE8zfzcHABDkSHQAAAFh/NzNcrk96tW8pk5okODv5gAAghyJDgDA7/Jdbn04x1eEgN4cAMCxI9EBAPjd1FW7tO1AtmrGR+mMExv4uzkAgBBAogMACJgiBJf2TFZsVIS/mwMACAEkOgAAv9q8N1NTV++y88N7c+0cAEDlINEBAPiVOTfH45FOblNHzetU49UAAFQKEh0AgN/k5rv18bzNdv6KPvTmAAAqD4kOAMBvfly+XbvTc1UvIUant6/PKwEAqDQkOgAAv3l/prcIweW9khUVwUcSAKDy8KkCAPCLldtTNWf9XkU4HRrOtXMAAJWMRAcA4NfenCEd66tBUiyvAgCgUpHoAACOuwNZefpiQYqdv+qk5rwCAIBKR6IDADjuPpu/RVl5LrWtX10ntazFKwAACIxE5+WXX1bz5s0VGxurPn36aM6cOWXa76OPPpLD4dAFF1xQkacFAIQAt9ujD2Z5h61d1be5/VwAAMDvic748eM1evRoPfTQQ1qwYIG6dOmiIUOGaOfOnUfcb8OGDfr73/+uk08++VjaCwAIctPX7Na63RlKiInURd0a+7s5AIAQVe5E59lnn9WoUaM0cuRIdejQQa+99pri4+M1ZsyYUvdxuVy64oor9Mgjj6hly5bH2mYAQAgUIbi4RxNVi4n0d3MAACGqXJ8wubm5mj9/vu67777CZU6nUwMHDtTMmTNL3e/RRx9VvXr1dO211+rXX3896vPk5OTYySc1NdXe5uXl2cmffM/v73aEKuJLfIMZx+/RbdmXpSkrd9j5y3s2Ltd7KfGtWsSX+AYzjt/wim9eGdtRrkRn9+7dtnemfv1Dr15t7q9cubLEfaZPn663335bixYtKvPzPP7447b3p7gff/zR9h4FgkmTJvm7CSGN+BLfYMbxW7qvNzrl8TjVNsmtVXOnaRXxDTgcv8Q3mHH8hkd8MzMzy7RdlY4ZSEtL01VXXaU333xTderUKfN+psfInAdUtEcnOTlZgwcPVmJiovydQZoXedCgQYqKivJrW0IR8SW+wYzj98hy8lx6+JlfTKR0x1ndNahDPeIbQDh+iW8w4/gNr/imFoz2qtRExyQrERER2rHDO+zAx9xv0KDBYduvXbvWFiE499xzC5e53W7vE0dGatWqVWrVqtVh+8XExNipOBPYQAhuoLUlFBFf4hvMOH5L9tWSHdqXmafGNeI0+MSGioyo2BUOiG/VIr7EN5hx/IZHfKPK2IZyfcpER0erR48emjJlyiGJi7nft2/fw7Zv166dli5daoet+abzzjtPp512mp03vTQAgPDwv5kb7O3wPk0rnOQAAFBW5R66ZoaUXX311erZs6d69+6t559/XhkZGbYKmzFixAg1btzYnmdjrrNz4oknHrJ/jRo17G3x5QCA0LVo834t3nJA0RFOXd6LH7kAAAGY6AwdOlS7du3Sgw8+qO3bt6tr166aOHFiYYGCTZs22UpsAAD4vF/Qm3NO54aqXf3wockAAFS2ChUjuPXWW+1UkqlTpx5x33fffbciTwkACFJ70nP07eJtdn5Ev+b+bg4AIEzQ9QIAqFIfzd2sXJdbnZskqWuyd/gyAABVjUQHAFBl8lxu/W/mRjs/oi+9OQCA44dEBwBQZSYs3abtqdmqUz1G53ZpSKQBAMcNiQ4AoEp4PB6Nmb7ezl91UjPFREYQaQDAcUOiAwCoEvM37vOWlI506oqTmhJlAMBxRaIDAKgSbxf05lzYtbEdugYAwPFEogMAqHSb92bqh2Xb7fxf/tSCCAMAjjsSHQBApXtvxga5PdLJberohAYJRBgAcNyR6AAAKlV6Tr7Gz91s5+nNAQD4C4kOAKBSfTx3s9Jy8tWqbjUNaFOX6AIA/IJEBwBQaVxuj96Z4S1CMLJ/CzmdDqILAPALEh0AQKWZtHyHNu/NUo34KF3cvQmRBQD4DYkOAKDS+C4QOrx3U8VFc4FQAID/kOgAACrF0i0HNGfDXkU6HRrRtzlRBQD4FYkOAKBSjPnN25tzTueGapAUS1QBAH5FogMAOGY7UrP1zeKtdv7aP7UkogAAvyPRAQAcs/dnblC+26PezWupU5MkIgoA8DsSHQDAMUnLztP/Zm6081wgFAAQKEh0AADHZNzsTUrNzlfLutU0uEN9ogkACAgkOgCACsvOc+mtgpLSNw5oxQVCAQABg0QHAFBhny3Yol1pOWqYFKsLujYmkgCAgEGiAwCokHyXW69PW2fnR53cUtGRfKQAAAIHn0oAgAqZ8Pt2bdqbqZrxUbq8dzJRBAAEFBIdAEC5eTwevTp1rZ0f2b+F4qMjiSIAIKCQ6AAAym3qql1asS1V1aIjNKJvMyIIAAg4JDoAgHJ7Zeoaezu8T1PViI8mggCAgEOiAwAol7kb9mruhn2KjnDqupNbEj0AQEAi0QEAlMsrP3t7cy7u0Vj1E2OJHgAgIJHoAADKbPnWVP28apecDumGU1oROQBAwCLRAQCU2avTvJXWzurUUM3rVCNyAICARaIDACiTDbsz9N2SrXb+plPpzQEABDYSHQBAmbz+yzq5PdKAtnXVsVESUQMABDQSHQDAUW3Zl6lP52+28zfTmwMACAIkOgCAo3pxyhrluTzq37q2+rSsTcQAAAGPRAcAcETrd2fo0wVb7PzoQScQLQBAUCDRAQAc0X8nr5bL7dFpJ9RVj2Y1iRYAICiQ6AAASvXHjjR9tdhbaY3eHABAMCHRAQCU6rnJq+XxSGd0bKBOTai0BgAIHiQ6AIAS/Z5yQBOWbpfDId05qC1RAgAEFRIdAECJnpu02t6e27mRTmiQQJQAAEGFRAcAcJiFm/ZpysqdcjqkOwa2IUIAgKBDogMAOMyzBb05F3VvopZ1qxMhAEDQIdEBABxi9ro9+vWP3Yp0OnT76fTmAACCE4kOAKCQx+PRf3709uYM7ZWs5FrxRAcAEJQi/d0AAEDgmL5mt+Zs2KvoSKdu/XNr/zUkJ03aNFtKmSflZdlFTrdbHVLWyvnTPMlZ8DtdrZZS8z95b015OAAACpDoAAAKe3OeKejNuaJPUzVMijt+kck+IG2aJW34Vdrwm7RtseRxHbJJhCQ7kG5nCftXb+BNeJr3l5r9SarThsQHAMIciQ4AwPp68VYt3rxf8dERuunUVlUflfwcafGH0vx3CxIb96HrazSTmvWT4mvbuy63S+vXrVeLli0U4YyQ3PnStiXeXp/07dLvn3ono1o9qd1ZUr/bpNrH4W8BAAQcEh0AgLJyXXry+5U2EjcNaKV6CbFVF5WcdGn+O9LMl6W0bQeXm+FnzfpLzU/29swkNTlkN3denpZNmKBmA89SRFTUwRVmaNuWud6eoI2/eeczdnoTqAXvSx0ukP50p9SwM680AIQREh0AgN78dZ22HshWo6RYjTqlZdVEJHOvNPt1ac7rUtY+77KERlK/W6WOF0mJDSv2uFFxUotTvJOvp2jTTGnmK9IfP0jLPvdOrQdJJ4/29hIBAEIeiQ4AhLkdqdl6depaO3/vWe0VG2XOhqlEGbul6c9J896R8jK8y2q1kv50h9R5qBQZU7nPZx6v5aneaftS73Mv+0JaM8k7Ne0rDbhHanVa5T4vACCgUF4aAMLcUxNXKSvPpe5Na+jczhXsVSmNSTBe7i3NfMmb5DToJF3yjnTrXKn7iMpPcoqzzzdGunWe1OMaKSLa29vzvwukL2462LMEAAg5JDoAEMaWbNmvzxZssfMPnttRjsoq0Zy+S/p4hPTJNVLmHqleR+mKz6QbfpVOvEgyxQSOJ1OQ4Nz/SrcvkXrfIMkhLR4nvXyStGri8W0LAOC4INEBgDAuJ/3oN8vt/EXdGqtrco3KeFDp98+lV/pIy7+SnJHeYWLXT5XaDPR/yWdzHtBZT0l/+UGq3dpbre3DodLnN9C7AwAhhkQHAMLUhKXbNW/jPsVGOXXXGScc+wOm7/T24nw60tuLU/9EadRP0mn3S5HRCihN+0g3Tpf6/VVyOKUlHxX07nzv75YBACoJiQ4AhKHsPJcem7DCzt84oNWxXxx05XfSy32kFV8f7MUZ9bPUsIsClqnWNvj/Cnp32hT07lzu7d0xJbABAEGNRAcAwtDb09crZX+WGibF6oZTjuGCmm639PNj0kfDpay9Uv1OgduLU5rk3tKNv3ovLurr3RkzRNq3wd8tAwAcAxIdAAgzO1Oz9crPa+z8PWe0U1x0BQsD5KRJ46+Upj3pvd/nJm+SE8i9OEfs3fmXdM0EqVo9acfv0hunSeum+btlAIAKItEBgDDzzI+rlJHrssUHzuvSqGIPsmet9NZAadV3UkSMdMGr0plPBE8vTmma9fUWTmjUzdtD9b8LpVmveYssAACCCokOAISR+Rv36ZP5vnLSHeR0VqAK2prJ0punSbtWSgkNpZHfS12HK2QkNfb+TZ0vlzwuaeI90le3SHnZ/m4ZAKCqE52XX35ZzZs3V2xsrPr06aM5c+aUuu2bb76pk08+WTVr1rTTwIEDj7g9AKBq5Oa7de9nS2znxMXdm6h705rlewCz428vSGMvlbIPSE16eXs/mvQIvZfMDGW78DVpyGPe83YWjZXePVtK3ebvlgEAqirRGT9+vEaPHq2HHnpICxYsUJcuXTRkyBDt3LmzxO2nTp2qYcOG6eeff9bMmTOVnJyswYMHKyUlpbxPDQA4Bq9OXas/dqarTvVo/fPs9uXb2ZUnfXmzNOkByeOWul0lXfOdlNAgdF8Tc82fvrdIV34mxdaQUuZJb5wqbVvs75YBAKoi0Xn22Wc1atQojRw5Uh06dNBrr72m+Ph4jRkzpsTtx44dq5tvvlldu3ZVu3bt9NZbb8ntdmvKlCnlfWoAQAWt2ZmmlwsKEDx4bkfVrFaOc2lMqeVxQ6XF4yRHhHTm09J5L0qRMeHxerT6s3T9z1Ld9t4S1O+cTZECAAgCkeXZODc3V/Pnz9d9991XuMzpdNrhaKa3piwyMzOVl5enWrVqlbpNTk6OnXxSU1PtrdnPTP7ke35/tyNUEV/iG8wC9fh1uz2659MlynW5dWrbOjqjfZ2ytzFjtyLGD5Nz20J5ouLlumiMPK0HSvn5Cqv4JiRLI75TxKdXybnxN3nGXiLXea/I0+EChYpAPX5DBfElvsEsL8DeH8raDofHU/ZSMlu3blXjxo01Y8YM9e3bt3D53XffrWnTpmn27NlHfQzTu/PDDz9o2bJl9hyfkjz88MN65JFHDls+btw423sEACi76dsd+mR9hGKcHt3b1aVaZeyIicvZpX5rn1L1nB3Kiaiu2a3+pn3VjuGaOyHA6c5T942vqfH+ufLIoaVNrtT6uoP83SwACCuZmZkaPny4Dhw4oMTExMrp0TlWTzzxhD766CN73k5pSY5heozMeUBFe3R85/Yc6Y85XhnkpEmTNGjQIEVFRfm1LaGI+BLfYBaIx++2A9m6/8XfzEk2uufM9rrypKZl23HHMkV+dJccOTvkSWwi5/BP1Ld2G/lTwMTXfY5cP96viPlvq/OW/6ljs7pyD7jPe05PEAuY+IYo4kt8g1legL0/+EZ7HU25Ep06deooIiJCO3bsOGS5ud+gwZFPSH3mmWdsojN58mR17tz5iNvGxMTYqTgT2EAIbqC1JRQRX+IbzALl+DUd9o9+t0oZOS51a1pDV/dvqYiylJPeMF36cJiUkyrV6yDHlZ8pKrGC19sJyfhGSef8R0psKP38f4r47VlFZO6UzvmvFHFcfz8M0fiGNuJLfINZVIC8P5S1DeUqRhAdHa0ePXocUkjAV1ig6FC24p566in961//0sSJE9WzZ8/yPCUAoIK+/327Jq/YoagIh568uHPZkpzlX0v/u8ib5DTt572eTAAlOQHD9N4MuEs69wVv+emFH0jjr5TysvzdMgBARauumSFl5to47733nlasWKGbbrpJGRkZtgqbMWLEiEOKFTz55JN64IEHbFU2c+2d7du32yk9Pb28Tw0AKKMDmXl68Ktldv6mU1urbf2Eo++0cKz0ydWSK0dqd4501edSXA1ifiQ9rpaGfiBFxkqrv5c+uETKSSNmABCMic7QoUPtMLQHH3zQloxetGiR7ampX7++Xb9p0yZt23bwgmqvvvqqrdZ2ySWXqGHDhoWTeQwAQNV4/PsV2p2eo1Z1q+mW08pQQGD2G9JXNx+8Rs5l73svmomja3e2dNWXUkyitHG69P75UuZeIgcAflahwcS33nqrnUpiCg0UtWHDhoq1DABQIT+v2qmP5m62809c3FkxkRFH3uHX/0hTHvXOn3SzNOSxoD+x/rhr1le6umDYX8p86d1zpBFfStXr+btlABC2yt2jAwAIXDtTs/X3jxfb+Wv6NVev5qVfs0zm6gKTHzmY5JxyN0nOsWjUTRo5QapeX9q5TBpzhrTfm3ACAI4/Eh0ACBHmwqCjP16sPRm5at8wUfee2e5IG0vf3yNNf9Z7f9Cj0p//QU/OsarX3lvAISlZ2rtWeudMac/aY35YAED5kegAQIh449d1mr5mt+KiIvTisG6KjSplyJrbJX19qzTndVM+TDr7Wan/7ce7uaGrdivpLxOl2q2lA5u9yc6O5f5uFQCEHRIdAAgBizbv1zM/rLLzD5/XQa3rVS95w/xc6dO/SIvGSo4I6cLXpV7XHt/GhoOkJt6enXodpfQd0rtnSSkL/N0qAAgrJDoAEOTSsvN024cLle/26OzODXVZz+SSNzTXeDHXeln+peSMki57T+oy9Hg3N3yYQgTXfCs17iFl7ZPeO0/aOMPfrQKAsEGiAwBBzOPx6B9f/K5NezPVpGacHruwkxwlVUwz13YZe6n0xw9SZJw0/COp/bn+aHJ4ia8ljfhKavYnKTfNW5VtzcGLbgMAqg6JDgAEsc8WpOjrxVsV4XTov5d3U1Jc1OEbmd6E/10obfhVik6QrvxMaj3QH80NTzEJ0hWfeGOenyV9eLm04lt/twoAQh6JDgAEqXW70vXgV7/b+dGD2qpHs5qHb5SxW3rvXGnLXCm2hnT1V1Lz/se/seEuOl66fJzU/jzJlSt9PEJa8om/WwUAIY1EBwCCUE6+S3/9cKEyc13q27K2bhzQ6vCNUrd6K35tXypVq+e9xos5XwT+ERkjXfKO1GWY5HFJn4+S5r/LqwEAVYREBwCC8Lyc+z5fqmVbU1UzPkrPX97VDl07xL4N3gtW7l4tJRZUAKvf0V9Nhk9EpHT+K1JPU+nOI31zuzTzZeIDAFWARAcAgszrv6zT5wtSbHLzwrBuqp8Ye+gGO1dKY86U9m+UaraQ/vK9VKe1v5qL4pxO6ez/HLx20Q/3Sz8/bjJYYgUAlYhEBwCCyA/LtuvJiSvt/EPndtDJbeoeusHmudKYIVLaVqluO++FK2s09U9jUTpTGW/gI9Jp//Ten/aENOEuye0magBQSUh0ACBILNt6QHeOX2R/+L/qpGYa0bf5oRv8MVl6/zwpe7/UpJd3uFpCA381F2VJdgbcJZ31jLkjzX1T+uxa70VdAQDHjEQHAILAzrRsjXpvni0+8KfWdWxvziGWfip9OFTKy5Rane69dou5hgsCX+9R0sVveS/iuuxz7+uYk+7vVgFA0CPRAYAAl53n0vXvz9fWA9lqWbeaXr6iuyIjirx9z35D+uw6yZ0vnXiJNOwjKbqaP5uM8up0iTR8vBQVL639SXr/fClzL3EEgGNAogMAAV5h7e5Pl2jR5v32YqBjru518KKgZgzbz49J39/lreDV+3rpojelyGh/NxsV0fp06epvpLiaUso8b9W8A1uIJQBUEIkOAASwl35ao68Xb1Wk06FXr+yu5nUKemrcLum7v0nTnvTeP/V+6cynvBW9ELya9JRGTpQSG0u7V0lvD5F2rfZ3qwAgKPGJCAAB6vMFW/SfSd4vuf+64ET1a1XHuyI3Qxp/pTTvbe9J7KZU8an3eE9uR/CrZ6rl/SDVbiOlbpHeHiRtmO7vVgFA0CHRAYAA9M3irfr7J4vt/HV/aqFhvQtKRKduk945U1o1QYqIkS59R+p1nX8bi8pXI9mb7JjqeaaK3vsXSIs/ItIAUA4kOgAQYCb+vl13jF8kt0e6vFey7j+rvXfFjmXSWwOlbYul+NrSNd9KHS/0d3NRVarV9p6z0+ECyZ0nfXEDFxYFgHIg0QGAADJlxQ799cMFcrk9uqhbYz12YSc5nQ5pzWTv+RpmKJMZ0nTdZCm5t7+bi6oWFSdd8o70pzsPXljUJDz5OcQeAI6CRAcAAsQvq3fppg8WKM/l0TmdG+qpSzp7k5x5Y6Sxl0m5aVLzk6XrJkm1Wvq7uTheTIGJgQ9L574gOSKkJeO9Q9koPw0AR0SiAwABYMba3Rr1/jzlutwa0rG+nhvaVZGmtsCP/5S+vVPyuKQuw6QrP/eWH0b46XG1dOWnUkyitGmGdxjjnrX+bhUABCwSHQDws7kb9urad+cpJ9+t09vV04vDuisq94D04VBpxovejU77h3TBq1wjJ9y1+rO3SEFSsrR3rfTmn6U/Jvm7VQAQkEh0AMCP5m3Yq5HvzFVWnksnt6mjl6/orujdy6Q3TpX++FGKjJUueksacDflo+FVv4N03RSpcU9vRbaxl0pTn5TcbiIEAEWQ6ACAn3y/dJuueGu20nPy1bdlbb05oqdil38qvTVI2rdBqtFUuvZHqfOlvEY4VEJ9aeQEqcdISR5p6mPSR8OkrP1ECgAKkOgAgB+8PX29bh63wA5XG9i+nt6+qrNiJ90nfXG9lJ8ltR4oXT9NatiF1wcli4yRzn1eOv9l7zWVVk+U3jzNW4YcAECiAwDHkykb/cg3y/Svb5fL45GuOqmZXr+gieLHXSjNed270Sl3S8M/luJr8eLg6LpdKV1rzttpKu1d5y1SsPRTIgcg7NGjAwDHSXaeS7eMXaB3fttg7993Zjs92mWvIt4cIG2e5a2mdfmH0p//ITkjeF1Qdo26SddPlVqeJuVlSp9dK024W8rLJooAwhaJDgAcB3szcjX8zVmauGy7oiOceumyjroh51053jtPSt8h1evg/aLa7ixeD1RMtdrSlZ9JJ//Ne9/0EJqqbAxlAxCmSHQAoIqt352hi1+doQWb9isxNlKfXlxT58y5UprxgvdE8u5XS9dOkmq34rXAsTE9gac/WDD0sY60s6CC38yXqcoGIOyQ6ABAFfpm8Vad++J0m+w0TorVlD+tVOcJ50vbl0pxtaShY6XzXpBiqvM6oPK0HSLdPFNqM0Ry5Uo/3C99cKGUupUoAwgbJDoAUEXn49z3+VL99cOFtnz04GSPfmr4kupOf1DKz/ZWVTNfRNufQ/xRNarXk4aPl85+VoqMk9ZNlV7tJy3/iogDCAskOgBQydbuytAFL/+mD+dsksPh0Yud1+v1jNsUs+En7wVAz3xauuJTKaEBsUfVcjikXtdKN/ziLVWetU/6eIT0xU1S5l6iDyCkkegAQCWas8uhC1+dqZXb09S52j7Nb/GGzl39Dzky90j1O3kLDvS53vsFFDhe6raVrp0s/Wm0yX6kxeOkl3pKiz6UrXMOACGIRAcAKkFmbr7u/vx3jV0Toby8XD1eb7K+cvxdtbZOkyKipQH3SKOmSPXaE2/4R2S0NPAh6S8TpbrtJZN8f3mj9P550u41vCoAQk6kvxsAAMFu2upd+ueXS7V5b5Z6OlbqtRofqE7qOu/K5idL5zwn1Wnj72YCXk1P8g5lm/miNO0paf0v0qt95ex3u5zuE4gSgJBBogMAFbQzLVv/9+0Kfb14q5KUrufjP9YF7slSlqT42tLgf0tdLmeYGgKzd8dcb6fjRdKEv0trJivi16d1WkwDOU6sJbU53d8tBIBjRqIDAOXkdnv00dzNeuL7FcrJztQNkT/ojphvFedK867vcoWcQ/5Piq9FbBHYarXwFsZY9oU839+j6hnbpbEXSW3P9A5zY6glgCBGogMA5bBqe5ru/2KpFm7co4siftXd8Z+rnnuX5JI89TpoetKFOumcO+WMiiKuCA6mMMaJFym/2QBtfu96tdjzsxyrv5f++EHqOlw69X4pqbG/WwkA5UYxAgAog/2ZuXp8wgqd/cIvStz8kybG3Kdnol73JjmJTaQLXlX+tT9rb3XOcUCQik3U0uQRyr9hutT+PMnjlhZ+IL3YXZr0kJS1398tBIByoUcHAI4gIydfY6av1xu/rFOb3OX6IGq8TnKu8K6MreE9z6H39VJUrJSXRywR/Gq3kYb+T9o8V5r0oLRphvTb89L8d73Hu7kuT3Q1f7cSAI6KRAcASpCd59K42Zv0yk+r1Tl7jt6O/Ea9Y1Z5V0bESH1ukE4eLcXVJH4ITcm9pJETpNU/SJMflnatkCY9IE1/zpvcm6labX+3EgBKRaIDAEXku9z6bMEWvTRphXql/6Sxkd/qhOgtdp3HGSWHqaJ26r1SUhPihvA4f+eEM6Q2g6RF46Rfn5H2bZCmPSHNeEHqdpXU71apRlN/txQADkOiAwCS0rLz9Mm8Lfrot5Xqn/qdPoz8Xk2id9vYeKIT5Oh5jRwn3SwlNiJeCD/OCKn7Vd7iBMu/8g5l27ZYmvO6NPct6cSLpf63Sw1O9HdLAaAQiQ6AsLZpT6benbFBi+f9qvNck/RpxG9KjMq06zzV6slx0o1y9LxWiqvh76YCgZHwnHiR1PFCad1U7zC29dOkpR97p2b9pR7XeIsZmPPWAMCPSHQAhB2Px6PZ6/dq7C/LFf/HVxoW8ZMedK4tfEd012wpZ/+/ytFlOF/WgNKGtLU6zTttXShNf15a8bW08TfvFHuX1GWY1ONqrsUDwG9IdACEjZ2p2fpqYYqWzv1ZffZP0GMRM5QQlWXXuR2RcrQ/R44eV8vZ4lTJSfV9oEwadZMue086kCItGisteF86sFma/ap3Sj7Jm/C0P1eKSSCoAI4bEh0AIS0zN18//L5N82f/osYpE3WWc5ZGOXcWvvvlJjVXdK+RcppzD6rX83dzgeBlLio64G5vCeq1P3nLUa/6Xto8yzt9e6e3qIEZ9tZmiBRT3d8tBhDiSHQAhJzcfLdmrd2tObN+UcK6bzXYM0MXOncUvuPlO2Plbnumovv8RdHNT/YOwwFQeefxmITGTGnbvRcdNRXb9q6VVnzjnSLjpLaDC5KewVyXB0CVINEBEBL2ZeTql2UbtWXRZCWlTFN/zwKdYpIbk8M4THITo5wWA1Wt2yWKbDuEL1bA8ZDQQDrl795enu1LpWVfeKd9673V28wUFS+1PFVqfbrUeqBUszmvDYBKQaIDIGgLCqzdmab58+coc8UPanlgloY4VijWkVeY3OQ5opWefJpq9LpMkW3PUCRDZQD/ML2mDTt7p9Mf9Jam9iU9+zdKqyZ4J6N2G2/CY6bm/aWoOF41ABVCogMgaBKbdbvStXzpPKWv/lWJO+epi3uZhjq817pRQe2A1Oj6ym3xZ9XqcpaiWp2mmpz8DARe0tOoq3ca+LC0fYn0xyRpzRRp82xpzx/eyRQyiIyVkntLTft6pya9OLcHQJmR6AAISHkut1Zv2aVNy2cre+1vqrVnvjq5V+hcR/rBjUyvjaK0q3YPxXcYohqdzlJi3RM45wYIqp6eLt7JDHHLPiCtmyatmeydUlOk9b94J7t9hLdXyCY+J3kTn4SG/J8HUCISHQB+53J7tG7rTm1ePltZm+Yrdtfvapy9Widoizo63Ac3dEg5jhjtTOwkR7O+qtdxgKJb9FOj6Gr+bD6AyhKbJHU4zzt5PNKuVdKmGdLGmdKmWdKBTd7r9php1ivefarV8yZKpofIJk1dpaQmJD8ASHQAHN/hZ9v2HFDK2t+Vuul3uXauUHzqGjXMXq+W2qo2Ds/BjQsKoR1wJml3ja6KbNFPDTqdppgm3ZQcGc3LBoRDb0+9dt6p51+8yw5s8SY8G2d4b3etkDJ2SmvM0LdJB/eNqyU16CTVbSfVbVtw206qVsdvfw6A448eHQCVbn9ahrZtWqN9KauVs3ONtG+j4tM2qF7OBiVrhxoV7aUpktTsddbSnoT2cjfsopqteqpu2z5KSmysJMo/AzBMT02nS7yTkZsp7VgmbVtUMC2Wdq6QsvZK66d5p6Lia0t1TpDqtJFqtfBWeKvZwjtvepMAhBQSHQDl4nZ7tGffHu1JWae0nRuVvWez3AdSFJm+VdWytqpO3lY18OxW++LJTJGEJl3VtDO2mTKTWiuiXjslNT1R9dv2Vq2khqrF6wGgrKLjpeRe3sknL1vaudyb8Oxa6R3+tnuV/cFFmXu8Q+HMVFxcTW/SU7OZlNjYm1QlNpISC27NBYXNNYIABA0SHQByu9xKTd2v1L07lLF/pzL3bVfO/u1yp+2QI2OnorJ2Ky53j6rn71Vt917VdWSq7pHi5pCyFa1dkQ2UGtdEeYnNFFG7pWo07aT6rTqreo1Gqk4vDYCqEBUrNe7unYoyvT+mmptJfPas9V7LZ+96ad8G7/C3rH3eaeuCkh/XGektfGASHnNeUHXfVP/gsvha3l6j2BpSBF+xAH+r0P/Cl19+WU8//bS2b9+uLl266MUXX1Tv3r1L3f6TTz7RAw88oA0bNqhNmzZ68sknddZZZx1LuwEUS1Qys9KVlbZfmekHlJNxQLmZqcpJ36/8zH1yZe6Xx1QzytqvhP3btWTVG4rJS1W864AS3KlK9KSphiNfNcoS1YJemVRV076IOkqPaaCc+AbyJDZWVK2mSmrcWvWatlNczcZKdhbUfAaAQOj98VV4Ky4n3Xs9H5P4mNvUrd6KbwdSvLdp2yR3vnRgs3cqCzMUziQ95nyh+FqKiElUp+375Zy6SIqv6V1vp0QpJlGKru4tnW1vE+g9AvyR6IwfP16jR4/Wa6+9pj59+uj555/XkCFDtGrVKtWrV++w7WfMmKFhw4bp8ccf1znnnKNx48bpggsu0IIFC3TiiSdWxt8ABAyP2618V77yc3OUm5uj/Nxs5eeZ2zzl52UpLzdbLrOs4NadlyNXXpa9dedlyZObLU9+lpRnpmw5XDly5GfLmZ+piPxsRbqzFOnKUrQ7204xnmzFebJUTVmq7vCoelkbmldy8pLtiVKqM1HpEUnKjKqtnNi6clerI0f1+opOaqC4Wg2VWLeJajVsocS4RCVWcvwAwC9MglG/o3cqiStfSt/hTXjSd3rnM3Z5b+1UMG/ODTI/Khnm1s6vs3fNzz4tzczuyWVrU2RcQeJTTYoyU5w3WTtkPl6KjPFua3qyzK25b9bb5bFShLmNLpg3tzHe28IpqmCKlpxREj9QIZwTnWeffVajRo3SyJEj7X2T8Hz33XcaM2aM7r333sO2/+9//6szzjhDd911l73/r3/9S5MmTdJLL71k9w0mOdmZWjlrorK3rdTy31xyljhWt8h5CaY0ZrGKU6Uqss6jYtsdst+h5z0c9pBFFxR9TE/x/bzrHAWPV9g2j6fIbu7Cx7BtMvNFtjNLC/ct3Mns713nsI9VsF2xedn2FF/utomCZ/s2zf1wvpy+SHhcMsW4PB7Xwec1+xebzPN55832xdd593UUWeebd3jyC25dNh5Ou9xlbwsnuRRRsD7Ck69IT74i5FKkWS5z38znK9rhUpQZOWGGe+s4KUhSfNIVpywzOeOU7aymnMgE5UUlyhWdIFd0onal5qhecmvFJNZRdEIdxdesr+o16yqxVgPFxlVXrKTDf7IAgDBmhqElmfN2Gh99W5MUZe/3ng+Uudeb/GTukStzn9b8Pl+tm9RTRG7awUTITOa+6VXKSZPcBb9EmR+9zGQSquPJXKvIJD4m6TF/t7019818ZJH5CO+t2b7wfsSh9x3OIts5vevssqLzBbeF6x0H79vvWUXuF64ruC1c55DT7VGrnSvlnLPJ2+7C9QUfkvbWUc5bHWG+yGOWOF/CvofsV2TZYUO5S9q2hHWHrS++TkdQjucwS1wu1U39Xdp7glS/nUIy0cnNzdX8+fN13333FS5zOp0aOHCgZs6cWeI+ZrnpASrK9AB9+eWXpT5PTk6OnXxSU1PtbV5enp38Zd+u7eoydaRsp/d2vzUjPBS5JmRQOMKbSa7HpkLKd0QqV1H2Apf5jijlOaLtrcsZLZczSvnOWLmcMXJHxMhV8CucJzLWTo6oeDmi4+WMqaaI6HhFxFZXZEw1RcVVU0y1RMXGJykuIUlx8QmKcUYoRipxGJr5/2N+aOg0aJCioqJKXI+K88WPOFYN4lu1iG8lik7yTjVaHhLflXuaK/nPJb//FsrPkXLTvVNOmhx5md5e/twMqWDeu8xM2QUJUbYcZj87IsAsK5h35XqXu3K9y1y++WxvQmbWH/bjqkvKd5kuKQUTkxLZcUIp/m5JaIqU1M8cx/MzlDfoEX83p8yfs+VKdHbv3i2Xy6X69esfstzcX7lyZYn7mPN4StreLC+NGeb2yCOHB/HHH39UfHy8/CUvK01d1PSYHsNT5Btx0fnK2O/QbXxK2M9x6P3ij+e971unI27j6xey/SxFfqE4uLzoc/i28563UXSdbzK/KhyyrW/eLncW3JplzoPP4TDzRW+Lrjf7OIvs6/1VqHAbx8H1vl+M7H1nhDyKkKPgvvcXKu+8wxFh15tbFdw6ivyS5YyIlMPpm8y6sr/OZZJfkAimmw+i1IJpS7kewiQ7qDrEt2oRX+IbvsdvfMFU+/BV5qPVXGKsvJcZ85iRDPneye299Y1qcKjg1ne/YCSEd13RkRFm5IO5LZhUsNyO7igYVeGbt9uZT2vftt7RI4Wf+gXr7MiLgm8HxdercF/7BxRZXjBSxXNwxErhtw3faJMi+3i3OzhCxftcvn2KJoAHH9933+fgdr4RL0WXHz6Sp3D7Ekb5HJZ0Frt/aBuOpuT2ep/nSLsd+Tk2pezTugkT5G+ZmZll2i4gS4KYHqOivUCmRyc5OVmDBw9WYqJ/zwrIy7vIvkkNKuUXcRxrfL09DsS3ahDfqkV8iW8w4/glvsGM4/f4xbddAHz/9Y32qtREp06dOoqIiNCOHTsOWW7uN2jQoMR9zPLybG/ExMTYqTiTWARKchFIbQlFxJf4BjOOX+IbzDh+iW8w4/gNj/hGlbEN5ar9Gh0drR49emjKlCmFy9xut73ft2/fEvcxy4tub5iMsLTtAQAAAOBYlXvomhlSdvXVV6tnz5722jmmvHRGRkZhFbYRI0aocePG9jwb4/bbb9eAAQP0n//8R2effbY++ugjzZs3T2+88cYxNx4AAAAAKiXRGTp0qHbt2qUHH3zQFhTo2rWrJk6cWFhwYNOmTbYSm0+/fv3stXP++c9/6v7777cXDDUV17iGDgAAAICqUqFiBLfeequdSjJ16tTDll166aV2AgAAAIDjoVzn6AAAAABAMCDRAQAAABBySHQAAAAAhBwSHQAAAAAhh0QHAAAAQMgh0QEAAAAQckh0AAAAAIQcEh0AAAAAIYdEBwAAAEDIiVQQ8Hg89jY1NdXfTVFeXp4yMzNtW6KiovzdnJBDfIlvMOP4Jb7BjOOX+AYzjt/wim9qQU7gyxGCOtFJS0uzt8nJyf5uCgAAAIAAyRGSkpJKXe/wHC0VCgBut1tbt25VQkKCHA6H3zNIk3Bt3rxZiYmJfm1LKCK+xDeYcfwS32DG8Ut8gxnHb3jF1+Px2CSnUaNGcjqdwd2jY/6AJk2aKJCYFzkQXuhQRXyJbzDj+CW+wYzjl/gGM47f8Ilv0hF6cnwoRgAAAAAg5JDoAAAAAAg5JDrlFBMTo4ceesjeovIR36pFfIlvMOP4Jb7BjOOX+AazmCD9/hsUxQgAAAAAoDzo0QEAAAAQckh0AAAAAIQcEh0AAAAAIYdEBwAAAEDIIdEBAAAAEHJIdIr597//rX79+ik+Pl41atQoMWibNm3S2WefbbepV6+e7rrrLuXn5x8x0Hv37tUVV1xhryZrHvfaa69Venq6wt3UqVPlcDhKnObOnVvqfqeeeuph2994443Hte3Bonnz5ofF6oknnjjiPtnZ2brllltUu3ZtVa9eXRdffLF27Nhx3NocLDZs2GD/L7do0UJxcXFq1aqVLb+Zm5t7xP04fkv38ssv22M2NjZWffr00Zw5c44Yy08++UTt2rWz23fq1EkTJkyo4KsZ2h5//HH16tVLCQkJ9nPrggsu0KpVq464z7vvvnvYe4eJMw738MMPHxYrc1weCcfusX+Wmcl8VnH8lt8vv/yic889V40aNbJx/PLLLw9Zb4oyP/jgg2rYsKH9fBs4cKD++OOPSn8Pr2okOsWYLyiXXnqpbrrpphID5nK5bJJjtpsxY4bee+89+2FgDoYjMUnOsmXLNGnSJH377bf2ALv++usV7kxSuW3btkOm6667zn5x7Nmz5xH3HTVq1CH7PfXUU8et3cHm0UcfPSRWf/3rX4+4/Z133qlvvvnGfhBPmzZNW7du1UUXXXTc2hssVq5cKbfbrddff93+/37uuef02muv6f777z/qvhy/hxs/frxGjx5tk8UFCxaoS5cuGjJkiHbu3FliDM178LBhw2yyuXDhQvvl3Uy///57Jby6ocX8PzZfCGfNmmU/h/Ly8jR48GBlZGQccT/z41zR946NGzcetzYHm44dOx4Sq+nTp5e6Lcdu+ZkfP4vG1xzHhvnOVhqO39KZ//vmPdYkJiUx36leeOEF+5k2e/ZsVatWzb4fmx9CK+s9/Lgw19HB4d555x1PUlLSYcsnTJjgcTqdnu3btxcue/XVVz2JiYmenJycEkO5fPlyc60iz9y5cwuXff/99x6Hw+FJSUkh/EXk5uZ66tat63n00UePGJcBAwZ4br/9dmJXBs2aNfM899xzZY7V/v37PVFRUZ5PPvmkcNmKFSvsMTxz5kxifhRPPfWUp0WLFkfchuO3ZL179/bccssthfddLpenUaNGnscff7zE7S+77DLP2WeffciyPn36eG644QaO06PYuXOn/T89bdq0cn8O4nAPPfSQp0uXLmUODcfusTPfAVq1auVxu90lruf4LTvzXvDFF18U3jcxbdCggefpp58+5LtBTEyM58MPP6y09/DjgR6dcpo5c6YdHlG/fv3CZSZbTU1Ntb/olraPGa5WtIfCdAE6nU6bJeOgr7/+Wnv27NHIkSOPGpaxY8eqTp06OvHEE3XfffcpMzOTUJbCDFUzw9C6deump59++ohDLefPn29/7TXHqI8ZgtG0aVN7LOPIDhw4oFq1anH8lpPpJTfHXtHjzrxHmvulHXdmedHtfe/HHKdlO06Nox2rZoh1s2bNlJycrPPPP7/UzznIDusxw4BatmxpR3GYYe6l4dg99veLDz74QH/5y1/ssCuO38q1fv16bd++/ZD316SkJDsUrbT314q8hx8PkX575iBlXviiSY7hu2/WlbaPGRNdVGRkpP2AKW2fcPX222/bLypNmjQ54nbDhw+3H77mQ2XJkiW655577Hjzzz///Li1NVjcdttt6t69uz3ezHAJkxSabv9nn322xO3NMRkdHX3YOWrmOOd4PbI1a9boxRdf1DPPPHPE7Th+D7d7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",
"text/plain": [
"<Figure size 1000x500 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"x = np.linspace(-10, 10, 100)\n",
"\n",
"y = sigmoid(x)\n",
"dy = sigmoid_deriv(x)\n",
"\n",
"plt.figure(figsize=(10, 5))\n",
"plt.plot(x, y, label=\"Sigmoïde\")\n",
"plt.plot(x, dy, label=\"Dérivée\")\n",
"plt.legend()\n",
"plt.grid(True)\n",
"\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"id": "1aff9ee6",
"metadata": {},
"source": [
"## Step 3 - Forward Pass Function"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "7ca39a42",
"metadata": {},
"outputs": [],
"source": [
"import random\n",
"\n",
"# neuron class 2\n",
"class Neuron:\n",
" \"\"\"\n",
" z : linear combination of inputs and weights plus bias (pre-activation)\n",
" y : output of the activation function (sigmoid(z))\n",
" w : list of weights, one for each input\n",
" \"\"\"\n",
" def __init__(self, isize):\n",
" # number of inputs to this neuron\n",
" self.isize = isize\n",
" # importance to each input\n",
" self.weight = [random.uniform(-1, 1) for _ in range(self.isize)]\n",
" # importance of the neuron\n",
" self.bias = random.uniform(-1, 1)\n",
"\n",
" def forward(self, x, activate=True):\n",
" \"\"\"\n",
" x : list of input values to the neuron\n",
" \"\"\"\n",
" # computes the weighted sum of inputs and add the bias\n",
" self.z = sum(w * xi for w, xi in zip(self.weight, x)) + self.bias\n",
" # normalize the output between 0 and 1 if activate\n",
" output = sigmoid(self.z) if activate else self.z\n",
"\n",
" return output"
]
},
{
"cell_type": "markdown",
"id": "3e7b79fa",
"metadata": {},
"source": [
"The `forward()` method simulates how a neuron proccesses its inputs:\n",
"1. **Weighted Sum and Bias** (z variable): \n",
" \n",
" Each input is multiplied by its corresponding weight, then all are summed and the bias added.\n",
" ```z = w1 * x1 + w2 * x2 + .... + bias```\n",
"\n",
"2. **Activation**: \n",
"\n",
" The z output is then passed through an **Activation function** (like sigmoid). This squashes the output between 1 and 0.\n",
" However, it can be disabled with `activate=False`. It's useful for **output neurons** in some tasks.\n",
"\n",
"3. **Returns the output**:\n",
"\n",
" The output has become the neuron's final output\n",
"\n",
"#### Test - Forward Pass"
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "6709c5c7",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Neuron output : 0.41519831999176\n"
]
}
],
"source": [
"# 8 for 8 bits (1 Byte)\n",
"nbits: int = 8\n",
"neuron = Neuron(nbits)\n",
"inputs: list = [1, 0, 1, 0, 0, 1, 1, 0] \n",
"\n",
"output = neuron.forward(inputs)\n",
"print(\"Neuron output :\", output)"
]
},
{
"cell_type": "markdown",
"id": "5593a84a",
"metadata": {},
"source": [
"The test result is a bit random due to the randomly initialized weights and bias in each Neuron. None of the neurons has been trained for this input.\n",
"\n",
"## Step 4 - Backward Pass Function"
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "f6de25ea",
"metadata": {},
"outputs": [],
"source": [
"import random\n",
"\n",
"# neuron class 3\n",
"class Neuron:\n",
" \"\"\"\n",
" z : linear combination of inputs and weights plus bias (pre-activation)\n",
" y : output of the activation function (sigmoid(z))\n",
" w : list of weights, one for each input\n",
" \"\"\"\n",
" def __init__(self, isize):\n",
" # number of inputs to this neuron\n",
" self.isize = isize\n",
" # importance to each input\n",
" self.weight = [random.uniform(-1, 1) for _ in range(self.isize)]\n",
" # importance of the neuron\n",
" self.bias = random.uniform(-1, 1)\n",
"\n",
" def forward(self, x, activate=True):\n",
" \"\"\"\n",
" x : list of input values to the neuron\n",
" \"\"\"\n",
" # computes the weighted sum of inputs and add the bias\n",
" self.z = sum(w * xi for w, xi in zip(self.weight, x)) + self.bias\n",
" # normalize the output between 0 and 1 if activate\n",
" last_output = sigmoid(self.z) if activate else self.z\n",
"\n",
" return last_output\n",
" \n",
" # adjust weight and bias of neuron\n",
" def backward(self, x, dcost_dy, learning_rate):\n",
" \"\"\"\n",
" x : list of input values to the neuron \n",
" dcost_dy : derivate of the cost function `(2 * (output - target))`\n",
" learning_rate : learning factor (adjust the speed of weight/bias change during training)\n",
"\n",
" weight -= learning_rate * dC/dy * dy/dz * dz/dw\n",
" bias -= learning_rate * dC/dy * dy/dz * dz/db\n",
" \"\"\"\n",
" # dy/dz: derivate of the sigmoid activation\n",
" dy_dz = sigmoid_deriv(self.z)\n",
" # dz/dw = x\n",
" dz_dw = x\n",
"\n",
" assert len(dz_dw) >= self.isize, \"too many values for input size\"\n",
"\n",
" # dz/db = 1\n",
" dz_db = 1\n",
"\n",
" for i in range(self.isize):\n",
" # update each weight `weight -= learning_rate * dC/dy * dy/dz * x_i`\n",
" self.weight[i] -= learning_rate * dcost_dy * dy_dz * dz_dw[i]\n",
"\n",
" # update bias: bias -= learning_rate * dC/dy * dy/dz * dz/db\n",
" self.bias -= learning_rate * dcost_dy * dy_dz * dz_db\n",
"\n",
" # return gradient vector len(weight) dimension\n",
" return [dcost_dy * dy_dz * w for w in self.weight]"
]
},
{
"cell_type": "markdown",
"id": "0c9baabf",
"metadata": {},
"source": [
"The `backward()` method train the neuron by adjusting its weights and bias using **the gradient descent**. This is based on erros between the neuron's prediction and the expected output, and gradient of the activation function:\n",
"\n",
"1. **derivates sigmoid, inputs, and lineear combination**:\n",
" \n",
" \n",
"2. **adjust each input weight**:\n",
"\n",
"3. **adjust neuron bias**:\n",
"\n",
"4. **return gradient vector**:\n",
"\n",
"#### Test - Backward Pass"
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "e07b7881",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"5.317753460658206\n"
]
}
],
"source": [
"target = [0, 0, 1, 0, 1] # 5 \n",
"target_normalize = 5/31\n",
"epoch = 200\n",
"\n",
"neuron = Neuron(len(target))\n",
"\n",
"for i in range(epoch):\n",
" output = neuron.forward(target)\n",
" error = 2 * (output - target_normalize)\n",
" neuron.backward(target, error, 0.1)\n",
"\n",
"print(neuron.forward(target)*31)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".env",
"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.14.2"
}
},
"nbformat": 4,
"nbformat_minor": 5
}