Skip to content

Instantly share code, notes, and snippets.

@andreh7
Last active March 10, 2021 15:15
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save andreh7/701e93ec95c9822b9e307ae44eea448e to your computer and use it in GitHub Desktop.
Save andreh7/701e93ec95c9822b9e307ae44eea448e to your computer and use it in GitHub Desktop.
Learning a function with a variable number of inputs with PyTorch
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Learning a function with a variable number of inputs with PyTorch\n",
"\n",
"This notebook is licensed under the Creative Commons cc by-sa 3.0 License.\n",
"\n",
"See the full article here: https://medium.com/@andre.holzner/learning-a-function-with-a-variable-number-of-inputs-with-pytorch-c487e35d4dba"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np\n",
"np.random.seed(1773)\n",
"\n",
"import matplotlib.pyplot as plt\n",
"import math\n",
"%matplotlib inline\n",
"\n",
"plt.rcParams['figure.figsize'] = (8.0, 8.0)\n",
"\n",
"from math import sqrt"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# range of variances to generate\n",
"varianceRange = [ 0.5, 1.5 ]\n",
"\n",
"# total number of points to generate\n",
"numRows = 10000\n",
"\n",
"# minimum and maximum number of points to draw from each distribution \n",
"# (both inclusive)\n",
"numPointsRange = [ 10, 20 ]\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# generate true variances of Gaussians\n",
"# convert to float32 to avoid incompatible data types during training\n",
"trueVariances = np.random.uniform(\n",
" varianceRange[0], varianceRange[1], numRows).astype('float32')\n",
"\n",
"trueSigmas = np.sqrt(trueVariances)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"scrolled": false
},
"outputs": [
{
"data": {
"image/png": "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\nSPJDuz7QwTZ5Odp/muS+7n5Z1l9h8NbdHeW+cE+SG5/n/tckuW7x50SSt+3CmPabe/L8x/DjSf5G\nd//VJP8ynhx1Iffk+Y/hl37n35LkV3djQPuBUO+szVxK9R8m+dnu/lySdPezuzzG6TZzDDvJVy/e\n/5ok/2sXx7cvdPf7k3z2eXa5OcnP97pHklxZVdfszuj2h0sdw+7+jS/9Hid5JOvXj2CDTfwcJskP\nJPmFJP4uXBDqnXWhS6kePm+fr0vydVX161X1SFU97782V9BmjuE/T/L3qupMkl/J+i86l2czx5nN\nuyPJf97rQew3VXU4yd+NFZ3nEOq9dyjry42vTnJbkn9XVVfu6Yj2n9uS3NPdR5LclOTfV5WfbfZE\nVf3NrIf6jXs9ln3op5K8sbv/ZK8HMsmeXet7RWzmUqpnkjza3X+U5ONV9dGsh/u3dmeI423mGN6R\nxXmv7v7Nqnph1i/wb+ls81z2dxtU1TcleXuS13T3/97r8exDx5O8u6qS9d/hm6rqXHf/0t4Oa2+Z\ndeyszVxK9ZeyPptOVV2V9aXwp3ZzkMNt5hh+MskNSVJVfyXJC5Oc3dVR7n8PJvn7i2d/vzLJF7r7\nmb0e1H5SVUeTvDfJd3f3R/d6PPtRd1/b3ce6+1iS+5N836pHOjGj3lEXu5RqVf2LJKe6+8HFfX+7\nqp5I8sdJ/rF/if+ZTR7DH836KYMfzvoTy76nXXLvOarqXVn/B+FVi3P5dyX5iiTp7n+b9XP7NyU5\nneSLSV6/NyOdaxPH8J8leUmSty5mhOf8b1DPtYljyAW4hCgADGbpGwAGE2oAGEyoAWAwoQaAwYQa\nAAYTagAYTKgBYLD/D2KPOnSsEPB2AAAAAElFTkSuQmCC\n",
"text/plain": [
"<matplotlib.figure.Figure at 0x10e5e0090>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plt.hist(trueVariances, bins = 100);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### draw points from the distributions"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# determine how many points should be drawn from each Gaussian\n",
"numPoints = np.random.randint(numPointsRange[0], numPointsRange[1] + 1, size = numRows)\n",
"\n",
"# draw a set of points from the Gaussian\n",
"xvalues = []\n",
"\n",
"for row in range(numRows):\n",
" thisNumPoints = numPoints[row]\n",
"\n",
" # draw points from this Gaussian\n",
" xv = np.random.normal(loc = 0, scale = trueSigmas[row], size = (thisNumPoints,1))\n",
" \n",
" # convert to float32 to avoid problems with incompatible data types during training\n",
" xvalues.append(xv.astype('float32'))\n",
" \n",
"# calculate ML estimators for each point\n",
"mlEstimators = np.array([ np.var(xv, ddof = 0) for xv in xvalues], dtype = 'float32')\n",
"\n",
"# calculate unbiased estimators for each point\n",
"ubEstimators = np.array([ np.var(xv, ddof = 1) for xv in xvalues], dtype = 'float32')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### plot a single example Gaussian"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def plotExample(row, variances, xvalues, variancePredictions = None):\n",
" # draw a single case\n",
" \n",
" xrange = np.linspace(-4,+4,101)\n",
" \n",
" from scipy.stats import norm\n",
" \n",
" #----------\n",
" # plot the Gaussian\n",
" #----------\n",
"\n",
" pdf = norm(loc = 0, scale = sqrt(variances[row])).pdf\n",
" \n",
" yvalues = pdf(xrange)\n",
" plt.plot(xrange, yvalues)\n",
"\n",
" #----------\n",
"\n",
" ymin = min(yvalues); ymax = max(yvalues)\n",
" \n",
" ytrue = 0.6 * ymax + 0.4 * ymin\n",
" yestU = 0.5 * ymax + 0.5 * ymin\n",
" yestML = 0.4 * ymax + 0.6 * ymin\n",
" ypred = 0.3 * ymax + 0.7 * ymin\n",
" \n",
" #----------\n",
" # plot points on Gaussian\n",
" #----------\n",
"\n",
" yvalues = pdf(xvalues[row])\n",
" plt.plot(xvalues[row], yvalues, 'o')\n",
" \n",
" #----------\n",
" # plot true sigma\n",
" #----------\n",
" sigma = sqrt(variances[row])\n",
" plt.plot([ - sigma, + sigma ], [ytrue, ytrue], label = 'true (%.2f)' % sigma)\n",
" \n",
" #----------\n",
" # plot ML and unbiased estimator of the variance\n",
" #----------\n",
" \n",
" for label, ddof, ypos in (\n",
" ('unbiased est.', 1, yestU),\n",
" ('ML est.', 0, yestML),\n",
" ):\n",
" \n",
" sigma = sqrt(np.var(xvalues[row], ddof = ddof))\n",
" plt.plot([ - sigma, + sigma ], [ypos, ypos], label = label + ' (%.2f)' % sigma)\n",
" \n",
"\n",
" #----------\n",
" # plot prediction\n",
" #----------\n",
" if variancePredictions is not None:\n",
" sigma = sqrt(variancePredictions[row])\n",
" \n",
" plt.plot([ - sigma, + sigma ], [ypred, ypred], label = 'predicted (%.2f)' % sigma)\n",
"\n",
" \n",
" \n",
" plt.grid()\n",
"\n",
" plt.legend()\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAe0AAAHVCAYAAADcnaM7AAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzs3Xl8VuWd///XlZ0kJEA2loBJ2LcIElkFg4i7iEuptlqt\nY21nau1Xpx1prVRp7dhlauvojOOoVX+jpVi1CmpVkMiOLGLYBEIIkBDIHhKyJ9fvjywNkJCE3Mm5\nl/fz8eBh7nOfc+7PZULenHOd67qMtRYRERFxf35OFyAiIiKdo9AWERHxEAptERERD6HQFhER8RAK\nbREREQ+h0BYREfEQCm0REREPodAWERHxEAptERERDxHgdAFni46OtgkJCS495+nTpwkLC3PpOZ3g\nLe0AtcVdeUtbvKUdoLa4K1e3Zfv27QXW2piO9nO70E5ISGDbtm0uPWdaWhqpqakuPacTvKUdoLa4\nK29pi7e0A9QWd+XqthhjjnRmP90eFxER8RAKbREREQ+h0BYREfEQbtenLSLiyWpra8nOzqaqqsol\n54uMjGTfvn0uOZfT1BYICQkhPj6ewMDAC/pchbaIiAtlZ2fTt29fEhISMMZ0+3xlZWX07dvXBZU5\nz9fbYq2lsLCQ7OxsEhMTL+hzO3V73BhzjTFmvzEmwxizuI33v2eM2WWM2WmMWW+MGde0PcEYU9m0\nfacx5vkLqlJExENUVVURFRXlksAW72KMISoqqlt3YTq80jbG+APPAfOBbGCrMeY9a+3eVru9Ya19\nvmn/BcDvgWua3jtkrZ10wRWKiHgYBba0p7s/G5250p4KZFhrM621NcAy4KbWO1hrT7V6GQbYblUl\nIiIi5zDWnj9fjTG3AddYa+9ren0XMM1a+8BZ+30feBgIAq6w1h40xiQAe4ADwCngZ9badW18xv3A\n/QBxcXFTli1b1s1mnam8vJzw8HCXntMJ3tIOUFvclbe0xcl2REZGMmLECJedr76+Hn9//07vX1JS\nwptvvsl3vvMdl9VwtpUrV7J7924WL17Mhg0bWLx4Mbt37+ZPf/oTCxcubPOYpUuX8sYbb1BaWkpu\nbm7L9sWLF7NuXWMsVFRUUFBQwLFjxygoKOA73/kO77zzTo+1ozu6+n1pLSMjg9LS0jO2zZ07d7u1\nNqXDg6215/0D3Aa82Or1XcCz59n/G8CrTV8HA1FNX08BjgER5/u8KVOmWFdbs2aNy8/pBG9ph7Vq\ni7vylrY42Y69e/e69HynTp3q0v6HDx+248ePb/O92tpaV5RkZ8yYYfPz81s+78svv7R33XWXffPN\nN9s9ZtOmTfbAgQM2LCys3X2eeeYZ++1vf7vl9T333GPXr1/vkppdravfl9ba+hkBttkO8tha26mn\nx3OAoa1exzdta88y4L+b/kFQDVQ3fb3dGHMIGAW4dp5SERE39OvPf81XRV916xxnX9GNGTCGR6Y+\n0u7+ixcv5tChQ0yaNIn58+dz/fXX89hjj9G/f3+++uorPv74Y2644QZ2794NwO9+9zvKy8t5/PHH\nOXToEN///vfJz88nNDSU//3f/2XMmDFnnP/AgQMEBwcTHR0NNE49DeDnd/7e1unTp1NWVnbeff78\n5z/zxBNPtLxeuHAhr7/+OrNmzTrvcb6kM6G9FRhpjEmkMaxvp/FquoUxZqS19mDTy+uBg03bY4Ai\na229MSYJGAlkuqp4ERE501NPPcXu3bvZuXMn0DhH9o4dO9i9ezeJiYlkZWW1e+z999/P888/z8iR\nI9myZQv/8i//wqeffnrGPhs2bOCSSy5xed1Hjhzh8OHDXHHFFS3bUlJS+NnPfubyz/JkHYa2tbbO\nGPMA8BHgD7xsrd1jjFlK4+X8e8ADxpgrgVqgGLi76fA5wFJjTC3QAHzPWlvUEw0REXE357si7ixX\njG2eOnVqh+OCy8vL2bhxI1/72tdatlVXV5+zX25uLjExHS5G1WXLli3jtttuO+OuQmxsLMePH3f5\nZ3myTk2uYq39APjgrG1LWn39w3aOewt4qzsFiohI97ReQjIgIICGhoaW181jhhsaGujXr1/LFXp7\n+vTpc85DVK6wbNkynnvuuTO2VVVV0adPH5d/lifT3OMiIl6kb9++5+07jouLIy8vj8LCQqqrq1m5\nciUAERERJCYm8uabbwKNDyl/+eWX5xw/duxYMjIyXFrzV199RXFxMTNmzDhj+4EDB5gwYYJLP8vT\nKbRFfF36cnh6Ajzej+mb7mt8LR4rKiqKWbNmMWHCBH784x+f835gYCBLlixh6tSpzJ8//4wHzV5/\n/XVeeuklLr74YsaPH8+77757zvFz5szhiy++aB4txNatW4mPj+fNN9/ku9/9LuPHj2/Zd9Kkf8yr\n9W//9m+MGTOGiooK4uPjefzxx1veW7ZsGbfffvs5E4+sWbOG66+//oL/X3gjzT0u4svSl8OKB6G2\nEoCQ6vzG1wDJixwsTLrjjTfeOON1amrqGa8ffPBBHnzwwXOOS0xM5O9///t5zx0aGsqVV17J6tWr\nufLKK7n00kvJzs5uc9/Wt9p/85vf8Nhjj7XZP986wFt777332vyHgy/TlbaIj8ouruD0h0taArtF\nbSXlHywhPbuEhgZNbijn+ulPf0pFRUWPfkZ+fj4PP/ww/fv379HP8TQKbREfU1Vbzy9W7mX2b9bQ\npyK3zX1CK0+w4NkNfOPFzRwr6tlfzuJ54uLiWLBgQY9+RkxMTLuzq/kyhbaID0nPLuGG/1zPS+sP\n842pw6jrO6TN/RoihvDEgvHszjnFtX9cx1+2Hm3pwxQR5yi0RXxAfYPl6U8OcPN/baS8qo7X7p3K\nkzdPJOiqxyHwrCE1gX0ImP9z7p6ZwIc/nM2EIRE88tYu7nt1GwXl547bFZHeo9AW8XLWWn6xci9/\nXH2QBRcP5qP/N4c5o5omx0heBDc+A5FDAUNVcEzj66aH0IYOCOWN+6bz8xvHsT6jgDtf3EJZVa1z\njRHxcQptES/3wtpMXtmYxX2XJfL01ycRGRp45g7Ji+Ch3fB4CZtnvHjOU+N+foZvz0rkf7+VQkZe\nOd/7v+3U1DUgIr1PoS3ixd7dmcO/f/gVNyQP4qfXje3WueaMiuGpW5PZkFHIv/31Sz1Z7kVeeeUV\nHnjggTbfmzlzZo99blZWlksmT/nDH/7Q6afZ//CHP/Daa68B8OabbzJ+/Hj8/PzYtq39dazuvfde\nYmNjz6n1scceIzk5mUmTJnHVVVe1TLm6cuVKlixZ0tapuk2hLeKlNmQU8KM3v2Ra4gD+Y9HF+PmZ\njg/qwG1T4vnRVaP4287jrHj9jy2TsvD0BE3K4qU2btzodAkd6mxo19XV8fLLL/ONbzSueTVhwgTe\nfvtt5syZc97j7rnnnjbHr//4xz8mPT2dnTt3csMNN7B06VIArr/+elasWNEjw+I0uYqIF8rML+d7\n/992EqPDeOFbKQQH+Hd8UCd9f+4IYg+/x/yMp8DUNG4sPaZJWdpw4le/onpf95bmrKuvp6jVIhrB\nY8cw8Kc/bXf/rKysdpfeTE1NZdq0aaxZs4aSkhJeeuklZs+eDcCxY8dITU0lJyeHO++8k5///OcA\nhIeHU15eTnl5OTfddBPFxcXU1tbyy1/+kptuuonTp0+zaNEisrOzqa+v57HHHuPrX/8627dv5+GH\nH6a8vJzo6GheeeUVwsPD2b59O/feey8AV111Vbvt+O1vf8vy5cuprq7m5ptv5oknnmjzs06ePMnx\n48eZO3cu0dHRrFmzpt1zfvrpp1xyySUEBDRG39ixnbv7NGfOnDZXR4uIiGj5+vTp0y0zuhljSE1N\nZeXKlSxa5Nq/DwptES/T0GB55K10/PwMr3x7KpF9Ajs+qAuMMXzt1J8wzYHdrLYSVi9VaLu5uro6\nPv/8cz744AOeeOIJVq1aBcDnn3/O7t27CQ0N5dJLL+X6668nJSWl5biQkBDeeecdIiIiKCgoYPr0\n6SxYsIC///3vDB48mPfffx+A0tJSamtr+cEPfsC7775LTEwMf/nLX3j00Uf54x//yLe//W2effZZ\n5syZ0+Y0qwAff/wxBw8e5PPPP8day4IFC1i7di35+fnnfFZkZCS///3vWbNmTcsa3+3ZsGEDU6ZM\nccX/xhaPPvoor732GpGRkWf8gyElJYV169YptEXk/N74/Chbs4r5zW3JDO7XMyskmdK2p62kve0+\n6nxXxJ3liqU5W7vlllsAmDJlyhlXj/PnzycqKqpln/Xr158R2tZafvrTn7J27Vr8/PzIycnh5MmT\nTJw4kX/913/lkUce4YYbbmD27Nns3r2b3bt3M3/+fADq6+sZNGgQJSUllJSUtNyOvuuuu/jwww/P\nqfHjjz/m448/ZvLkyUDjsqEHDx5k9uzZ53xWV+Tm5nb66rqznnzySZ588kn+/d//nWeffZYnnngC\n6LllRRXaIl4kt7SSpz78ilkjovjalPie+6DI+MZb4m1tF0e1t/Rms+DgYAD8/f2pq6tr2X72Yh1n\nv3799dfJz89n+/btBAYGkpCQQFVVFaNGjWLHjh188MEH/OxnP2PevHncfPPNjB8/nk2bNp1xjmPH\n2viZaYO1lp/85Cd897vfPee9sz+rKw989enT55z/H67yzW9+k+uuu64ltHtqWVE9iCbiJay1PPa3\n3dQ1NPCrmyee80vXpeYtOWdSFhvQp3G7OKq9pTc78sknn1BUVERlZSV/+9vfmDVr1hnvl5aWEhsb\nS2BgIGvWrOHIkSMAHD9+nNDQUO68805+/OMfs2PHDkaPHk1+fn5LaNfW1rJnzx769etHv379WL9+\nPdD4D4G2XH311bz88suUl5cDkJOTQ15eXpufBR0vR9rM1cuKHjx4sOXrd99994wV03pqWVGFtoiX\neH9XLqv25fHw/FFcFBXWsx/WalIWiyHHRvN67L+qP9sNnG/pzfOZOnUqt956K8nJydx6661n3BqH\nxivJbdu2MXHiRF577bWW8+7atYupU6cyadIknnjiCX72s58RFBTEX//6Vx555BEuvvhiJk2a1PIU\n+p/+9Ce+//3vM2nSpHanxr3qqqv4xje+wYwZM5g4cSK33XYbZWVlbX4WwP33388111zD3LlzAbjv\nvvvaHMJ17bXXsnbt2pbX77zzDvHx8WzatInrr7+eq6++Gmj8h8h1113Xst8dd9zBjBkz2L9/P/Hx\n8bz00ksALF68mAkTJpCcnMzHH3/MH//4x5ZjemxZUWutW/2ZMmWKdbU1a9a4/JxO8JZ2WKu2uFrx\n6Wo75Rcf2xueWWdr6+ov+DwX2pbf/v0re9EjK23a/rwL/mxXcvJ7snfvXpee79SpUy49n5PcoS0L\nFy60Bw4c6PZ5zteWEydO2CuuuKLd99v6GQG22U5kpK60RbzAf392iMLTNfz7LRMJ8O/9v9YPXDGC\nxOgwnnx/ryZdEbf21FNPkZvb9up2rnL06FH+4z/+o0fOrdAW8WTpy6n/j/E8snkG28MeYkLhR46U\nERLoz0PzR3HgZDkrd/XsL0SR7hg9enSHk6l016WXXsqkSZN65NwKbRFPlb4cVjyIf1k2fsYyoO5k\n4wQnDs1MdsPEQYyKC+cPqw5Qr6ttkR6h0BbxVKuXNk5o0lrzBCcO8PMzPHTlKDLzT/PelzmO1CDi\n7RTaIp7KDSc4uXr8QMYNiuCPqw5SV6+VwERcTaEt4qHq+g5p+w0HJzjx8zM8NH8UWYUVvP2FrrZF\nXE2hLeJp0pfD0xPwL8vmnK7jQOcnOLlybCzJ8ZE8s/ogtbradoQxhjvvvLPldV1dHTExMdxwww3A\n+ZfivBBpaWmdXg0sNze3pY7CwkLmzp1LeHj4eespKipi/vz5jBw5kvnz51NcXAxAcXExN998M8nJ\nyUydOrVlkZSamhrmzJlzxoxv3kKhLeJJmh4+o/QYBmhcbbNp5rPIoY0Tnjg8wYkxjVfb2cWVvLlN\nc5E7ISwsjN27d1NZ2fjMwyeffMKQIe3cmXGBroT273//e77zne8AjYuQ/OIXv+B3v/vdeY956qmn\nmDdvHgcPHmTevHk89dRTAPzqV79i0qRJpKen89prr/HDH/4QgKCgIObNm8df/vKXbrTKPWnucRFP\n0tbDZ9jGwH5otyMltSV1VAwXD+3HC2sPcfulQ12ylrcnWrf8AAXHyrt1jvr6evxbLc0ZPTSc2YtG\ndXjcddddx/vvv89tt93Gn//8Z+644w7WrVvX6c9ta2nNQYMG8cwzz/D8888TEBDAuHHjeOqpp3j+\n+efx9/fn//7v//jP//zP8y7k8dZbb/HLX/4SaPzHxWWXXdbh1KLvvvsuaWlpANx9992kpqby61//\nmr1797J48WIAxowZQ1ZWFidPniQuLo6FCxfyk5/8hG9+85udbrMn0JW2iCdxw4fP2mKM4d5ZCWQV\nVvDZwXyny/FJt99+O8uWLaOqqor09HSmTZvW6WObl9b861//2rL+9aOPPgo0XvV+8cUXpKen8/zz\nz5OQkMD3vvc9HnroIXbu3HnewM7KyqJ///4ti5Z01smTJxk0aBAAAwcO5OTJkwBcfPHFvP3220Dj\n0qJHjhwhO7vx78KECRPYunVrlz7HE+hKW8STeNDqWtdOGMQv++7jlQ1ZzB0d63Q5jujMFXFHLnRp\nzuTkZLKysvjzn/98xjzanbF///42l9ZsPu83v/lNFi5cyMKFC7t03pMnTxITE9OlY85mjGlZDGfx\n4sX88Ic/ZNKkSUycOJHJkye33JXw9/cnKCjI5UubOk2hLeIp0pdja04DLb3Yjdzg4bO2BAX4cee0\ni3h61QEy88tJigl3uiSfs2DBAn70ox+RlpZGYWFhp4+z1ra5tCbA+++/z9q1a1mxYgVPPvkku3bt\n6vR5Q0JCLmhpzLi4OHJzcxk0aBC5ubnExjb+IzAiIoI//elPLTUnJiaSlJTUclx1dTUhISFd/jx3\nptvjIp6g6QE0U1l0ZmD3GeAWD5+1545pQwn0N7y26YjTpfike++9l5///OdMnDixS8e1t7RmQ0MD\nx44dY+7cufz617+mtLSU8vLyTi+NOWLECLKysrrcjgULFvDqq68C8Oqrr3LTTTcBUFJSQk1NDQAv\nvvgic+bMISIiAmh8Mj06OprAwMAuf547U2iLeII2H0ADgsLcNrABYvuGcP3EQfx1ezbl1d43/Mbd\nxcfH8+CDD7b53iuvvEJ8fHzLn+a+YKDdpTXr6+u58847W25FP/jgg/Tr148bb7yRd955h0mTJrFu\n3Tree+89liw59+5PWFgYw4cPP+PBs4SEBB5++OGWevbu3Qucubzm4sWL+eSTTxg5ciSrVq1qefhs\n3759TJgwgdGjR/Phhx/2ztKYDtPtcRFP4CEPoLXl7pkJ/G3ncd7ekc23ZiQ4XY5PKC8/94n11NRU\nUlNTAbjnnnu45557znuOSZMmnbH2dLP169efs23UqFGkp6efsW3BggVtnveBBx7glVdeaXmCvL0r\n7xdffLHl66ioKFavXn3OPjNmzODAgQNtHv/GG2+0DA3zJrrSFvEE7T1o5oYPoJ1t8rD+/CB6B1d9\nfCX28X7w9ATHFjUR5918880kJCT06GfU1NSwcOFCRo3q/oOA7kahLeIBqi//GZUEnbnRTR9AO0f6\ncn5Y+SwDbT4G2/j0u4OrkYnz7rvvvh49f1BQEN/61rd69DOcotAW8QDv1M3kkZr7qA4bAhi3mf2s\nU1YvJaD+rCeGHVyNrDdYq6VJpW3d/dlQn7aIB3hzezZlMdcQ9P9+BcbDZhfz4P74CxESEkJhYSFR\nUVEt44lFoDGwCwsLuzUMTaEt4uYOF5xm+5FifnLtGM8MAQ+aEMYVmp/Ezs93zUxwVVVVXjPWWG1p\n/EddfPyF/+wrtEXc3Ns7svEzcPPknlvwoUfNW9LYh916yJqn9MdfgMDAQBITE112vrS0NCZPnuyy\n8zlJbek+9WmLuLGGBsvbO3KYPTKG2AgPvUJJXtTY/x45FIshuyGa7Mt+7Rn98SJuRqEt4sY2ZxaS\nU1LJrVM8/FZy8iJ4aDclP85jbv1/8qeyS52uSMQjKbRF3Nhfd2TTNySAq8bFOV2KS/QPC+KKMbG8\nuzOH2voGp8sR8TgKbRE3dbq6jr/vPsENyYMICfTv+AAPcesl8RSU17D2gJbsFOkqhbaIm/pw9wkq\nauq59RIPvzV+ltTRsQwIC+KtHd455EukJym0RdzUW9uzSYgKZcpF/Z0uxaWCAvxYcPFgVu3No6Si\nxulyRDyKQlvEDWUXV7Aps5BbLon3zLHZHbhtSjw19Q2sSM91uhQRj6LQFnFD7+48Dnjw2OwOjB8c\nwZiBfXlHt8hFukShLeKGVqbnMuWi/gwdEOp0KT3CGMONFw9mx9ESjpe0sU64iLRJoS3iZjLzy9mX\ne4rrJg5yupQedX1T+z7YpVvkIp3VqdA2xlxjjNlvjMkwxixu4/3vGWN2GWN2GmPWG2PGtXrvJ03H\n7TfGXO3K4kW8UXOIXTdxoMOV9KyE6DDGDYrgfYW2SKd1GNrGGH/gOeBaYBxwR+tQbvKGtXaitXYS\n8Bvg903HjgNuB8YD1wD/1XQ+EWnH+7tOcMmwfgyK7ON0KT3u+uRBfKFb5CKd1pkr7alAhrU201pb\nAywDbmq9g7X2VKuXYUDzgqE3AcustdXW2sNARtP5RKQNhwtO+8St8WbX6Ra5SJd0ZpWvIUDrdfWy\ngWln72SM+T7wMBAEXNHq2M1nHXvO47DGmPuB+wHi4uJIS0vrRFmdV15e7vJzOsFb2gFqS3tWHGoc\nt9y/PIu0tKMuOWdXOPF9GdbXj2Ub9jOi3nXt1c+Xe1Jbus9lS3Naa58DnjPGfAP4GXB3F459AXgB\nICUlxaamprqqLKBxCTVXn9MJ3tIOUFva85sv13HJsDBuvXaWS87XVU58X75uM/jtR/sZNWkag/u5\npktAP1/uSW3pvs7cHs8BhrZ6Hd+0rT3LgIUXeKyIz8oqOM1eH7o13qy5vR/uPuFwJSLurzOhvRUY\naYxJNMYE0fhg2XutdzDGjGz18nrgYNPX7wG3G2OCjTGJwEjg8+6XLeJ93m95aty3QjsxOoyxgyLU\nry3SCR2GtrW2DngA+AjYByy31u4xxiw1xixo2u0BY8weY8xOGvu17246dg+wHNgL/B34vrW2vgfa\nIeLxPtiVy+Rh/Vx2i9iTXD9xINuPFJNbqqfIRc6nU33a1toPgA/O2rak1dc/PM+xTwJPXmiBIl4v\nfTl1nzzOilM5nO4zCNKXQvIip6vqVddNHMTvPj7Ah7tOcO9liU6XI+K2NCOaiJPSl8OKBwkoy8HP\nQN+qXFjxYON2H5IUE86YgX11i1ykAwptESetXgq1Z90Srq1s3O5jrp0wiO1Hiykor3a6FBG3pdAW\ncVJpO6tctbfdi105LhZr4dOv8pwuRcRtKbRFnBQZ37XtXmzcoAgGR4awau9Jp0sRcVsKbREnzVtC\ntQk+c1tgH5i3pO39vZgxhnlj41h3sICqWg0yEWmLQlvEQZVjbuWn9d+hJDAOMBA5FG58xueeHm92\n5bg4Kmvr2XiowOlSRNySy6YxFZGu25BRwFs1M1l41/9j9sgYp8tx3PSkAYQF+fPJ3jyuGBPndDki\nbkdX2iIOWrXvJOHBAUxLjHK6FLcQHODP5aNjWL3vJA0NtuMDRHyMQlvEIQ0NllX78rh8dAxBAfqr\n2OzKsXHklVWzK6fU6VJE3I5+U4g45MvsEgrKq5k/VreBW5s7OhY/03gXQkTOpNAWcciqfSfx9zOk\njlZfdmv9w4JISRjAJxr6JXIOhbaIQ1btzePShP70Cw1yuhS3M39sHF+dKONYUYXTpYi4FYW2iAOO\nFlaw/2QZV+rWeJvmjY0FYLVukYucQaEt4oDm/tp5Cu02JcWEkxQTxqp9mtJUpDWFtogD1uzPY3hM\nGInRYU6X4rauHBvHlsOFlFfXOV2KiNtQaIv0soqaOrZkFpE6OtbpUtzabYEbWeP/A8L+PRqenuBz\ny5WKtEUzoon0sk2HCqmpb9BT4+eTvpyRnz+K8WtatrT0WOM64+CzU7yKgK60RXpd2v58+gT6MzVx\ngNOluK/VSzFaZ1zkHAptkV5krSXtQB4zh0cRHODvdDnuS+uMi7RJoS3Siw4XnOZYUSWX69b4+Wmd\ncZE2KbRFelHa/nwAUkfpIbTzmrekcV3x1nx0nXGR1hTaIr0o7UA+SdFhDIsKdboU95a8qHFd8cih\nWAw5Nprqa/+gh9DE5ym0RXpJVW09WzILmTNKt8Y7JXkRPLSb9d88yKzqZ1jfZ67TFYk4TqEt0ks2\nZRZSXaehXl01NXEAfQL9+exAvtOliDhOoS3SSz7bn09wgB/Tk6KcLsWjBAf4M3N4FGn787HWOl2O\niKMU2iK9JG1/HjOGRxESqKFeXZU6OoajRRUcLjjtdCkijlJoi/SCrILTZBVWcLn6sy/I5U1P2+sW\nufg6hbZIT0tfTvSLU8gM/gZ3br5ec2hfgGFRoSRFh7UMmRPxVQptkZ6UvhxWPEh4VS5+BgLLchrn\n0FZwd9nlo2PYnFlIVW2906WIOEahLdKTVi9tnDO7Nc2hfUHmjIyhuq6BbVnFTpci4hiFtkhP0hza\nLjMtaQCB/oZ1GbpFLr5LoS3SkzSHtsuEBgVwybD+rD9Y4HQpIo5RaIv0pHlLqCL4zG2aQ/uCzR4Z\nzZ7jpygsr3a6FBFHKLRFelDpiJtZXPtPnAoeCBiIHNo4p7bm0L4gl41sHDK34VChw5WIOCPA6QJE\nvNmmzAL+Vn8Z37j9x0xNHOB0OR5v4pBIIvsEsv5gPgsuHux0OSK9TlfaIj1o3cECwoL8mTysn9Ol\neAV/P8PM4VGsP1igKU3FJym0RXrQ+owCpidFEeivv2quctnIaI6XVpGpKU3FB+k3iUgPOVZUwZHC\nCi4bGe10KV5l9ojGfm09RS6+SKEt0kPWNYXKbIW2Sw2LCmXYgNCW/78ivkShLdJDNmQUMDAihOEx\n4U6X4nUuGxnN5sxCausbnC5FpFcptEV6QH2DZcOhAi4bGY0xxulyvM7sEdGUV9fx5bESp0sR6VUK\nbRFXS18tgFNrAAAgAElEQVRO/e/Hs6P+ayzN/LoWB+kBM4dH42fQLXLxOQptEVdqWtUrqDwHPwOh\nlbla1asHRIYGMjG+H+szFNriWxTaIq6kVb16zewR0ew8VkJZVa3TpYj0GoW2iCtpVa9eM3NEFPUN\nls8PFzldikivUWiLuJJW9eo1lwzrT3CAHxs1D7n4EIW2iCvNW0KtX8iZ27SqV48ICfQnJaG/Qlt8\nikJbxJWSF/GfYT8gzy8WrerV82YOj2ZfrpbqFN+hVb5EXKisqpbnCi+h4fKv8aOrRztdjtebMTwK\ngM2ZRVyfPMjhakR6XqeutI0x1xhj9htjMowxi9t4/2FjzF5jTLoxZrUx5qJW79UbY3Y2/XnPlcWL\nuJutWUXUN1hmjohyuhSfkDwkkvDgADYe0tAv8Q0dXmkbY/yB54D5QDaw1RjznrV2b6vdvgBSrLUV\nxph/Bn4DfL3pvUpr7SQX1y3iljZkFBIU4Mclw/o7XYpPCPD3Y1riAPVri8/ozJX2VCDDWptpra0B\nlgE3td7BWrvGWlvR9HIzoEdlxSdtPFRIykX9CQn0d7oUnzFjeBSHC05zvKSy451FPJzpaCF5Y8xt\nwDXW2vuaXt8FTLPWPtDO/s8CJ6y1v2x6XQfsBOqAp6y1f2vjmPuB+wHi4uKmLFu27MJb1Iby8nLC\nwz1/0QZvaQd4Z1vKaiw/+LSCW0YGsmB4kNNlXRBP/L4cPVXPko1VfGdiELOGBAKe2Y72qC3uydVt\nmTt37nZrbUpH+7n0QTRjzJ1ACnB5q80XWWtzjDFJwKfGmF3W2kOtj7PWvgC8AJCSkmJTU1NdWRZp\naWm4+pxO8JZ2gHe25YNducAO7px/qcfeHvfE70tDg+UPX66iKDCW1NSLAc9sR3vUFvfkVFs6c3s8\nBxja6nV807YzGGOuBB4FFlhrW8ZfWGtzmv6bCaQBk7tRr4jb2pBRQHhwAMlDIp0uxaf4+RlmJEWx\n8VABHd05FPF0nQntrcBIY0yiMSYIuB044ylwY8xk4H9oDOy8Vtv7G2OCm76OBmYBrR9gE/Eamw4V\nMjVxAAH+mv6gt80YHkVuaRVZhRUd7yziwTr87WKtrQMeAD4C9gHLrbV7jDFLjTELmnb7LRAOvHnW\n0K6xwDZjzJfAGhr7tBXa4nVySyvJLDjNzOEa6uWE5v/vGvol3q5TfdrW2g+AD87atqTV11e2c9xG\nYGJ3ChTxBBszGocczRwe7XAlvikxOoxBkSFszCjkm9Mu6vgAEQ+l+3giLrDxUCH9QwMZM7Cv06X4\nJGMMM4ZHsSmzkIYG9WuL91Joi3STtZbNmYVMT4rCz884XY7Pmjk8mqLTNew/WeZ0KSI9RqEt0k35\nlZackkr1ZztsetIAADZnanY08V4KbZFu+qqoHoDpSQptJ8X3D2XogD4KbfFqCm2RbtpXVE90eBAj\nYr1jpidPNiMpii2Hi2jQeG3xUgptkW6w1vJVYQPTkqIwRv3ZTpueFEVJRS3HyhqcLkWkRyi0Rbrh\nSGEFxdWWGbo17haauyi+KlJoi3dSaIt0w6am/lP1Z7uHwf36cFFUaMtzBiLeRqEt0g2bMwuJDDYM\njwlzuhRpMiMpiv1F9dRrvLZ4IYW2yAWy1rLpUCFj+vupP9uNTE+KoqIO9uWecroUEZdTaItcoMMF\np8krq2ZslL/TpUgrzV0VGvol3kihLXKBmvuzxwxQaLuTgZEhxIUaNh1SaIv3UWiLXKDNmUXERQQT\nF6pb4+5m7AB/Pj9cpH5t8ToKbZEL0NyfPUPjs93SmCh/yqrr2HO81OlSRFxKoS1yAQ7ln6agvFpD\nvdzUmP6Nv9rUry3eRqEtcgGa+7NnaJEQt9QvxI/hMWHq1xavo9AWuQCbMwsZFBnCsAGhTpci7Zie\nFMXWrGLq6jU7mngPhbZIF1lr2dK0frb6s93XjOFRlFfXsfu4xmuL91Boi3TRofxyCsprWtZvFvc0\nNbHx+7NF/driRRTaIl20ObMI0Hzj7i62bwjDY8LYcrjI6VJEXEahLdJFmzMLGRih/mxPMC0piq0a\nry1eRKEt0gXWWjZnFjE9aYD6sz3A9KQoyqrr2Kt+bfESCm2RLsgsaByfPU23xj3C9KZ+bY3XFm+h\n0Bbpgs1aP9ujxEaEkBQdxpbDCm3xDgptkS7YkllEbN9gEqLUn+0ppiUNYIv6tcVLKLRFOqmxP1vj\nsz3N9KQoyqrqtL62eAWFtkgnZRVWkFdWzTSNz/Yo0xK1vrZ4D4W2SCepP9szDYwM4Z8itrLws6vh\n8X7w9ARIX+50WSIXJMDpAkQ8xZbMQqLDg0mKDnO6FOmK9OU8UvtfBNnqxtelx2DFg41fJy9yri6R\nC6ArbZFO0PhsD7Z66T8Cu1ltJaxe6kw9It2g0BbphCOFFZw4VaXx2Z6oNLtr20XcmEJbpBOax/nO\n0ENonicyvmvbRdyYQlukEzZnFhEdHsTwmHCnS5GumrcEAvucuS2wT+N2EQ+j0BbpQPP62VMT1Z/t\nkZIXwY3PcLrPIBqsoSZ8CNz4jB5CE4+k0BbpwLGiSo6XVmmolydLXkTx/TtIqn6dN2a8r8AWj6XQ\nFunA5qb+7OZJOsQzxfcPZUi/PlpfWzyaQlukA1syi+gfGsjIWPVne7ppSQP4/HAR1moecvFMCm2R\nDmw53Nif7een/mxPNz0xisLTNWTklTtdisgFUWiLnEdOSSXZxZW6Ne4lmueN36xb5OKhFNoi57Gl\nab5xLRLiHYYNCGVgREjL91XE0yi0Rc5jS2YRkX0CGTswwulSxAWMMS3ra6tfWzyRQlvkPLYcLuTS\nBPVne5NpiVHkl1VzuOC006WIdJlCW6QdJ09VkVVYwXTdGvcqLf3amerXFs+j0BZpR/P62XoIzbsk\nRYcRHR7cMp+8iCdRaIu0Y8vhIvoGBzBusPqzvUlLv3am+rXF8wQ4XYCIu1p18gXCE3O57+Nl592v\npKSEV//+ai9V1bO8pS0dteNkQBXFfSI5WjSdi6LCerEyke7RlbZIG/LLqimtrCUiRP+u9UYRIYFA\n4+gAEU+i30gibfj8cBHVJ2/k17fNYtLQfufdNy0tjdTU1N4prId5S1s6aoe1lpRfrmLz4UIWXTq0\n9woT6SZdaYu0YcvhQsKC/Jmg/myvZIxhauIAXWmLx+lUaBtjrjHG7DfGZBhjFrfx/sPGmL3GmHRj\nzGpjzEWt3rvbGHOw6c/drixepKdsySxiSsIAAvz171pvNS1xADkllRwrqnC6FJFO6/A3kjHGH3gO\nuBYYB9xhjBl31m5fACnW2mTgr8Bvmo4dAPwcmAZMBX5ujOnvuvJFXCx9OfW/H8+HJTfy7Mm7IH25\n0xVJD5nWtD66luoUT9KZy4ipQIa1NtNaWwMsA25qvYO1do21tvmfq5uB+KavrwY+sdYWWWuLgU+A\na1xTuoiLpS+HFQ/ifyobPwMR1SdgxYMKbi81Oq4v/UIDNQ+5eBTT0ThFY8xtwDXW2vuaXt8FTLPW\nPtDO/s8CJ6y1vzTG/AgIsdb+sum9x4BKa+3vzjrmfuB+gLi4uCnLlp1/iE1XlZeXEx7u+Wshe0s7\nwD3bMn3TfYRU55+zvSo4hs0zXmz3OHdsy4XylrZ0th3P7KjiWFkDv708tBequjDe8j0BteV85s6d\nu91am9LRfi59etwYcyeQAlzeleOstS8ALwCkpKRYVz+96itPxHoSt2xLWkGbm0OqC85bq1u25QJ5\nS1s6245DAYf5xcq9jJo0jcH9+vR8YRfAW74noLa4Qmduj+cArcdExDdtO4Mx5krgUWCBtba6K8eK\nuIXI+K5tF4/XPK+8pjQVT9GZ0N4KjDTGJBpjgoDbgfda72CMmQz8D42BndfqrY+Aq4wx/ZseQLuq\naZuI+5m3hHr/kDO3BfaBeUucqUd63JiBEUSEBGjol3iMDkPbWlsHPEBj2O4Dlltr9xhjlhpjFjTt\n9lsgHHjTGLPTGPNe07FFwC9oDP6twNKmbSLuJ3kRfxu6mBwbjcVA5FC48RlIXuR0ZdJD/P0ax2tv\n1sNo4iE61adtrf0A+OCsbUtafX3leY59GXj5QgsU6U0vn0rhrfjXeeM7050uRXrJ9KQoVu3L40Rp\nFQMjQzo+QMRBmjlCpElpRS17c09pKU4fc1X9WtYHPUjc0wPh6Qka4iduTXOPizTZmlWEtTCt6eEk\n8QHpyxm6YTHGr7LxdemxxrH5oG4RcUu60hZpsuVwIUEBfh0uECJeZPVSTG3lmdtqK2H1UmfqEemA\nQlukyebMIiYP7UdIoL/TpUhvKc3u2nYRhym0RYBTVbXsOV7aMh+1+AiNzRcPo9AWAbZnFdNg/zHZ\nhviIeUsax+K3prH54sYU2iLA5sxCgvz9uGSYFqHzKcmLGsfiRw7FYjhpYjQ2X9yaQlsE2Hy4iIuH\nRqo/2xclL4KHdvPfqduYVvlHCpJu6vgYEYcotMXnlVfXsTunVOOzfdz05vW1NaWpuDGFtvi8bVlF\n1DdYZgxXaPuyiUMiCQ3y1+Ih4tYU2uLzNmUWEuhv1J/t4wL9/UhJGMCmQwptcV8KbfF5mzOLmDS0\nH32C1J/t66YnDeBgXjkF5dUd7yziAIW2+LSyqlp255S29GeKb1O/trg7hbb4tG1Hihv7sxXaQmO/\ndliQv5bqFLel0Baf1jw+e7L6s4V/9GsrtMVdKbTFp6k/W842PSlK/drithTa4rP+0Z+tqUvlH5p/\nHnS1Le5IoS0+a1tWY3+2HkKT1iaoX1vcmEJbfJb6s6Ut/+jX1hPk4n4U2uKzNmcWqj9b2jQ9KYqM\nvHLyy9SvLe5FoS0+qayqll05pUzX1KXShuYpbTWlqbgbhbb4pG1aP1vOY8LgCPVri1tSaItP2qT1\ns+U8Avz9uDRR85CL+1Foi0/anFnIpGH9tH62tGt6UhSH8k+TV1bldCkiLRTa4nNONY/PTtStcWlf\n81BAPUUu7kShLT5n6+EiGizMGB7tdCnixiYMjiA8OED92uJWApwuQKS3bTxUSFCAH5OH9evU/id+\n9Suq933V7vv9S0o48tLLrirPUd7SlvO1I3jsGAb+9KcdniPA34+p6tcWN6MrbfE5mw4VknJRf/Vn\nS4dmDo/icMFpcksrnS5FBNCVtviY4tM17M09xb/OH9XpYzq6KjuclsbFqandrMw9eEtbXNWO5n7t\nTYcKueWS+G6fT6S7dKUtPqV5soyZIzSpinRs3KAIIvsE6ha5uA2FtviUjYcKCQ3yJzm+c/3Z4tv8\n/AwzkqLYqNAWN6HQFp+y8VAhlyYMINBfP/rSOTOGR5FTUsmxogqnSxFRaIvvyCurIiOvvGVeaZHO\nmNn087LxUIHDlYgotMWHNPdLzlRoSxeMiA0nOjxY/driFhTa4jM2ZxbSNySA8YMjnS5FPIgxhhnD\nG/u1rbVOlyM+TqEtPmPjoUKmJUbh72ecLkU8zIykKPLKqsksOO10KeLjFNriE3JKKjlSWKH+bLkg\nzV0qukUuTlNoi09Qf7Z0x0VRoQyKDFFoi+MU2uITNh0qpH9oIKPj+jpdinig5n7tzZmFNDSoX1uc\no9AWr2etZdOhAmYMj8JP/dlygWYkRVF4uoYDeWVOlyI+TKEtXu9IYQXHS6uYkaRb43LhZo5oXMp1\nY4ZukYtzFNri9TY0TYrR/EtX5EIM6deHhKhQTbIijlJoi9fbmFHIwIgQkqLDnC5FPNzMEdFsziyi\nrr7B6VLERym0xas1NFg2Hipg5ogojFF/tnTPrOHRlFfX8WV2qdOliI9SaItX25t7iuKKWi7TrXFx\ngRnDozAGNmboFrk4Q6EtXq25/3GWQltcYEBYEOMGRbBeoS0OUWiLV1ufUciI2HDiIkKcLkW8xGUj\novniaAmVNfVOlyI+SKEtXqumroGth4uYpVnQxIVmjoimpr6BrVlFTpciPkihLV7ri6PFVNbWa6iX\nuNSlCf0J9Dds0C1ycUCnQtsYc40xZr8xJsMYs7iN9+cYY3YYY+qMMbed9V69MWZn05/3XFW4SEc2\nZBTgZ2C6JlURFwoNCuCSYf1bxv+L9KYOQ9sY4w88B1wLjAPuMMaMO2u3o8A9wBttnKLSWjup6c+C\nbtYr0mkbDhUyMb4fkX0CnS5FvMysEdHsOX6K4tM1TpciPqYzV9pTgQxrbaa1tgZYBtzUegdrbZa1\nNh3QjAPiFsqqatl5rITLRugqW1xv1ohorIVNmZrSVHpXQCf2GQIca/U6G5jWhc8IMcZsA+qAp6y1\nfzt7B2PM/cD9AHFxcaSlpXXh9B0rLy93+Tmd4C3tgJ5vy868OuobLGHlOaSlneixzwF9X9xRT7ej\nvsES4g/L16YTWri/xz4HvOd7AmqLK3QmtLvrImttjjEmCfjUGLPLWnuo9Q7W2heAFwBSUlJsamqq\nSwtIS0vD1ed0gre0A3q+LWtX7CU44Aj3LkglJNC/xz4H9H1xR73RjllHt3Iov7zHP8dbviegtrhC\nZ26P5wBDW72Ob9rWKdbanKb/ZgJpwOQu1CdyQTZkFHBpwoAeD2zxXbNGRJNVWEF2cYXTpYgP6Uxo\nbwVGGmMSjTFBwO1Ap54CN8b0N8YEN30dDcwC9l5osSKdkV9Wzf6TZcxUf7b0oOZZ9jT0S3pTh6Ft\nra0DHgA+AvYBy621e4wxS40xCwCMMZcaY7KBrwH/Y4zZ03T4WGCbMeZLYA2NfdoKbelRzb9ENd+4\n9KRRceHE9A1mvdbXll7UqT5ta+0HwAdnbVvS6uutNN42P/u4jcDEbtYo0iVrD+bTPzSQ8YMjnS5F\nvJgxhtkjolmzP4+GBoufn1aRk56nGdHEq1hrWX+wgFkjovHXL1HpYbNHRVNcUcue46ecLkV8hEJb\nvMqBk+XklVUzZ2SM06WID2ju116Xke9wJeIrFNriVdYdbPzledlI9WdLz4vtG8LYQRGsO6CH0aR3\nKLTFq6w9WMCI2HAG9+vjdCniI+aMjGbbkSIqauqcLkV8gEJbvEZVbT1bMguZrats6UWzR8ZQW2/Z\nkqmlOqXnKbTFa2zLKqa6rkH92dKrUhL6Exzgx9qD6teWnqfQFq+x7mA+gf6GaUkDnC5FfEhIoD9T\nEwew7qD6taXnKbTFa6w9WEDKRQMIDeqNKfVF/mHOyBgy8so5XlLpdCni5fTbTbxCflk1+3JP8W/X\njO6xz1i3/AAFx8rP2V5S0kDx9h099rm9yVva0lY7ooeGM3vRqB75vNmjouEDWH+wgEWXDu34AJEL\npCtt8QrNU5eqP1ucMDquLzF9g1mnecilh+lKW7zC2oP5DAgLYtygiB77jPau0hqX6Lukxz63N3lL\nW3q7HcYYZo+MZs1XmtJUepautMXjWWtZd7CAy0ZE65elOGbOyBhNaSo9TqEtHm//yTLyy6o1Plsc\n1TylqYZ+SU9SaIvHS9vf+EtytvqzxUExfYMZPziCz/YrtKXnKLTF46Xtz2PMwL4MjAxxuhTxcZeP\nimH70WJKK2udLkW8lEJbPFpZVS3bsopJHR3rdCkizB0TS32DbRnNIOJqCm3xaBsyCqlrsKSO1q1x\ncd7kof2ICAkgbX+e06WIl1Joi0f77EAefYMDmHJRf6dLESHA34/ZI2P47EA+1lqnyxEvpNAWj2Wt\nJW1/PrNGRBPorx9lcQ+Xj47h5Klq9uWWOV2KeCH9phOPdeBkObmlVbo1Lm4ldVTjz2PaAd0iF9dT\naIvHau43vFyhLW4kNiKEcYMiWoYiiriSQls81pqmoV6DIvs4XYrIGVJHx7D9SDGnqjT0S1xLoS0e\nqXmol66yxR2ljm4a+qU1tsXFFNrikVqGeo3S+GxxP5cM60ffkADdIheXU2iLR/rsQB7hwQGkJGio\nl7ifxqFf0Rr6JS6n0BaP84+hXlEa6iVuK3VULCdOVfHVCQ39EtfRbzzxOP8Y6qVb4+K+mp+30C1y\ncSWFtnicT79qHOql8dnizuKahn6t+UrjtcV1FNricVbvO8n4wREa6iVu78qxsWw7UkTx6RqnSxEv\nodAWj1J0uoYdR4uZNzbO6VJEOjRvbBwNVrOjiesotMWjrPkqjwYL88aoP1vc38QhkcT0DWbVPoW2\nuIZCWzzKp1/lEdM3mIlDIp0uRaRDfn6GK0bHsnZ/PjV1DU6XI15AoS0eo6augc8O5DNvTCx+fsbp\nckQ6Zd7YWMqq69iaVeR0KeIFFNriMT4/XER5dZ36s8WjXDYymqAAP1btO+l0KeIFFNriMVbtO0lw\ngB+XjYh2uhSRTgsNCmDW8ChW78vT7GjSbQpt8QjWWlZ/dZJZI6LpE+TvdDkiXTJvbBxHiyo4lF/u\ndCni4RTa4hEO5pVzrKiSeWP11Lh4nuafWz1FLt2l0BaPsLrpl90VGuolHmhQZB/GDYpgtfq1pZsU\n2uIRNAuaeLorx8ay/UixZkeTblFoi9vTLGjiDZpnR1uzX7fI5cIptMXtNc+CdqX6s8WDTRwSSWzf\nYA39km5RaIvb+2jPCQZFhjBhsGZBE8/l52e4clwcafvzqaqtd7oc8VAKbXFrFTV1fHYgn6vGxWkW\nNPF4V48fSEVNPesPFjhdingohba4tbUH8qmua+DqCQOdLkWk22YkRdE3JICP9pxwuhTxUAFOFyDS\npvTlsHopV5dmszEkiriyXwFfd7oqkW4JCvBj3phYVu07SV19AwH+um6SrtFPjLif9OWw4kEoPYbB\nMpgC/N//YeN2EQ939fiBFFfU8rkWEJELoNAW97N6KdRWnrmttrJxu4iHu3x0DMEBfny8R0+RS9cp\ntMX9lGZ3bbuIBwkNCmDOqBg+2nNCC4hIlym0xf1Exndtu4iHuWb8QHJLq0jPLnW6FPEwnQptY8w1\nxpj9xpgMY8ziNt6fY4zZYYypM8bcdtZ7dxtjDjb9udtVhYsXm7eEev+zpisN7APzljhTj4iLzRsb\ni7+f0VPk0mUdhrYxxh94DrgWGAfcYYwZd9ZuR4F7gDfOOnYA8HNgGjAV+Lkxpn/3yxavlryI94Y9\nQo6NxmIgcijc+AwkL3K6MhGX6BcaxPSkAQpt6bLOXGlPBTKstZnW2hpgGXBT6x2stVnW2nSg4axj\nrwY+sdYWWWuLgU+Aa1xQt3gxay1Pn5zEowl/xjxeAg/tVmCL17lm/EAO5Z8mI6/M6VLEg3RmnPYQ\n4Fir19k0Xjl3RlvHDjl7J2PM/cD9AHFxcaSlpXXy9J1TXl7u8nM6wVvaAedvy9FT9RwtquKKQXUe\n0V5f+b54Ek9oR3hV4zXOf6/YxI3Dg9rdzxPa0llqS/e5xeQq1toXgBcAUlJSbGpqqkvPn5aWhqvP\n6QRvaQecvy2//3g/fiaD7y+cQ0zf4N4t7AL4yvfFk3hKO17L3MD+igb+I3V2u/t4Sls6Q23pvs7c\nHs8BhrZ6Hd+0rTO6c6z4IGstK9NzmZYY5RGBLdId108cxO6cU2QVnHa6FPEQnQntrcBIY0yiMSYI\nuB14r5Pn/wi4yhjTv+kBtKuatom0aV9uGZkFp7nh4kFOlyLS466b2PhzvjL9uMOViKfoMLSttXXA\nAzSG7T5gubV2jzFmqTFmAYAx5lJjTDbwNeB/jDF7mo4tAn5BY/BvBZY2bRNp08r04/j7Ga6doNAW\n7ze4Xx9SLurPyvRcp0sRD9GpPm1r7QfAB2dtW9Lq66003vpu69iXgZe7UaP4iOZb4zOHRzEgrP0H\nc0S8yQ3Jg3h8xV4y8soYEdvX6XLEzWlGNHEbu3JKOVpUwQ3JusoW33HtxEEYg662pVMU2uI2Vqbn\nEuBnuHq81s4W3xEXEcLUhAGsTM/VXOTSIYW2uAVrLe+n5zJ7ZDT9QnVrXHzLDRcPJiOvnP0nNdGK\nnJ9CW9zCF8dKyCmp5IbkwU6XItLrrp0wED8DK7/ULXI5P4W2uIWVX+YS5O/H/PFxTpci0uuiw4OZ\nOTyalenHdYtczkuhLY5raLB8sCuXOaNiiAgJdLocEUdcnzyIrMIK9hw/5XQp4sYU2uK47UeLOXGq\nihs1oYr4sGvGDyTAz+gpcjkvhbY47r2dxwkO8GPeWN0aF9/VPyyIWSOiWfHlcRoadItc2qbQFkfV\n1DWwIv04V40fSHiwW6xfI+KYmycPIaekkq1ZmjhS2qbQFket2Z9HSUUtt0w+Z8VWEZ9z1fg4QoP8\neXuH1lWStim0xVHv7MghOjyI2SOjnS5FxHGhQQFcM2EgH+zKpaq23ulyxA0ptMUxpRW1fPpVHjde\nPJgAf/0oigDcMjmesuo6Vu076XQp4ob0m1Ics3LXcWrqG7hlcptrzYj4pBnDo4iLCOYd3SKXNii0\nxTFv78hhZGw4E4ZEOF2KiNvw9zMsnDyEzw7kU1he7XQ54mYU2uKIvIoGth8p5uZLhmCMcbocEbdy\ny+R46hosK7487nQp4mYU2uKIjcfrMAYWTtJT4yJnGz2wL+MGRfDOF7pFLmdSaEuvs9ay8Xgd0xOj\nGNyvj9PliLilWy4ZwpfZpeSWNzhdirgRhbb0uh1HS8irsNx8ia6yRdqz4OLB+JnGu1IizRTa0uve\n2pFNkF/jcoQi0rbYiBBmj4xhw/E66jWtqTRRaEuvOl1dx3s7j3PpwAD6akUvkfNalDKUoirLuoP5\nTpcibkKhLb3q/V25lFfXMSde84yLdOTKcbF8PWg9E9+cBY/3g6cnQPpyp8sSB+k3p/SqZZ8fZXhM\nGKP663afSEeC977FUr8XCa6radxQegxWPNj4dfIi5woTx+hKW3rNgZNl7Dhawu2XDtPYbJHOWL2U\nYGrO3FZbCauXOlOPOE6hLb1m2efHCPQ33KKnxkU6pzS7a9vF6ym0pVdU1dbz9hfZXDVuIFHhwU6X\nI+IZItuZl7+97eL1FNrSKz7ac4KSilpunzrU6VJEPMe8JdT7nfWP3MA+MG+JM/WI4xTa0iv+svUY\n8f37MGu41s0W6bTkRewf/X2IHIrFkGOjqbj6aT2E5sMU2tLjjhSeZuOhQr6eMhQ/Pz2AJtIVeXGX\nw5dnqj4AABhDSURBVEO7yZ77B6yFPiv/WUO/fJiGfEmPW7b1GH4GvpaiW+MiFyR9OUPXLwa/ysbX\nGvrls3SlLT2qqraeZZ8fZd7YOAZGhjhdjohnWr20cahXaxr65ZMU2tKj3vvyOMUVtXx7ZoLTpYh4\nLg39kiYKbekx1lpe3ZjFqLhwZgyPcrocEc+loV/SRKEtPWb7kWL2HD/Ft2YkaAY0ke6Yt6RxqFcr\ntX4hGvrlgxTa0mP+tDGLviEBmgFNpLuSF8GNz0DkUMBQGBDLkob7qRp7q9OVSS/T0+PSI06UVvH3\n3Sf49swEQoP0YybSbcmLWp4U33+ogD//7xYm7zzOoks1KsOX6EpbesTrW47QYC3fmpHgdCkiXmdG\nUhSj4/ryysYsrNWKeb5EoS0uV1VbzxtbjjJvTCzDokKdLkfE6xhjuHtmAntzT7HtSLHT5UgvUmiL\ny72fnkvh6Rru1jAvkR6zcPJgIkICeGVDltOlSC9SaItLWWt5af1hRsSGc9kIzTMu0lNCgwK4feow\n/r7nBMeKKpwuR3qJQltcau3BAvbmnuL+OUka5iXSw749KwE/Ay+uy3S6FOklCm1xqf9Oy2BgRAgL\nJ2mYl0hPGxTZh4WThvCXbccoLK92uhzpBQptcZkvjhazObOI+2YnEhSgHy2R3vDdy5Oormvg1Y1Z\nTpcivUC/WcVlnv/sEJF9Arl96jCnSxHxGSNi+3LVuDhe3XSE8uo6p8uRHqbQFpfIyCvn470n+daM\niwgP1mQqIr3pe5cPp7SylmWfH3W6FOlhCm1xiRfWHiI4wI97NMxLpNdNHtaf6UkDeHHdYWrqGpwu\nR3qQQlu6Lbe0kne+yOHrKUOJCg92uhwRn/TPqSM4caqKv+3McboU6UEKbem2l9YdpsHCfbOTnC5F\nxGfNGRnNuEERPP/ZIeobNLWpt1JoS7fklVXxf1uOsODiwQwdoClLRZxijOFf5g4nM/80K9OPO12O\n9BCFtnTLf605RG295YfzRjpdiojPu27CIMYM7MsfVh2krl59296oU6FtjLnGGLPfGJNhjFncxvvB\nxpi/NL2/xRiT0LQ9wRhTaYzZ2fTnedeWL046XlLJG1uO8rUp8SREhzldjojP8/MzPDR/FIcLTvP2\nF+rb9kYdhrYxxh94DrgWGAfcYYwZd9Zu/wQUW2tHAE8Dv2713iFr7aSmP99zUd3iBp5dk4HF8sAV\nI5wuRUSaXDUujolDInlm9UE9Se6FOnOlPRXIsNZmWmtrgGXATWftcxPwatPXfwXmGU087dWOFlaw\nfOsx7pg6jPj+6ssWcRfGGP71qlFkF1eyfNsxp8sRFzMdLaBujLkNuMZae1/T67uAadbaB1rts7tp\nn+ym14eAaUA4sAc4AJwCfmatXdfGZ9wP3A8QFxc3ZdmyZS5o2j+Ul5cTHh7u0nM6wZ3a8eKuarbk\n1vGbOX3oH9L1RyPcqS3dpba4H29pB1xYW6y1PLmlisJKy6/n9CHI3z2uoXz9+3I+c+fO3W6tTelo\nv56euioXGGatLTTGTAH+ZowZb6091Xona+0LwAsAKSkpNjU11aVFpKWl4epzOsFd2nEov5yNH33G\nP12WyM3XnN1T0jnu0hZXUFv+//buPDyq+t7j+PubbbISyAIkAUKAAAIJq6DFBWhVFAqKUsGtfdx7\nxVpra7Xe60Vv7dVq666UirdaKWBdrlFxQRGpIpsQ9sWwhdUAgUCI2X/3j0y9lEIJSciZmXxez8Pz\nzExOZj6/JzqfOb/5nXMCT6iMAxo+Fl+nfVz1x0Xs8HXm+nOymj5YA+jv0nj12UXaCXQ86n4H/2PH\n3cbMIoBEYL9zrsI5tx/AOfclsAno3tjQ4q3H52wkOjKcW8/v6nUUETmB73RN4ewuyTw3bxNHdE7y\nkFGf0l4CZJtZlplFAROAvGO2yQN+6L99BTDXOefMLNW/kA0z6wJkA7rwaxBbVniAd1bu5sZzsnT2\nM5EA94uRPdhXWsGUTzd5HUWayElL2zlXDUwCPgDWAa8659aY2YNmNsa/2TQg2cwKgJ8Bfz8s7Dxg\npZnlU7dA7VbnXHFTD0Kah3OOB99eS9sEH7doL1sk4A3o1Iax/dKZOn8zOw9+43UcaQL1+k7bOTcb\nmH3MY/cfdbscGH+c33sdeL2RGSVA5K3YRf72gzw2vi9xupKXSFC4e2RP3l+9h0feW89TE/t7HUca\nSWdEk3r5prKGh99bT05GIuP6Z3gdR0TqKaN1DLec14W8Fbv4cpsmOoOdSlvqZer8zewuKec/Rvci\nLCwwDh8Rkfq55fyutGvl48F31lGri4kENZW2nNSeknKmfLqJUTlpDM5K8jqOiJyiOF8Ev7ioJyu2\nHyRvhS4mEsxU2nJSv31/PTW1jnsu7ul1FBFpoHH9M8jJSOTh99ZTVqlDwIKVSlv+pQUF+3hj+U5u\nOi9Ll94UCWJhYcbkMb3Yc6icx+ds9DqONJBKW06ovKqGX725iszkWG4foUtvigS7gZlJXDWkE9M+\n28LqnSVex5EGUGnLCT099yu27i/jN5flEB0Z7nUcEWkCvxzZk+R4H798faWuuR2EVNpyXOv3HOIP\nn27m8gEdGNotxes4ItJEEmMieWBMb9bsOsT/fL7V6zhyilTa8k9qah33vL6KVjGR3DfqDK/jiEgT\nu7hPe753Rlt+P2cj24vLvI4jp0ClLf9k+qJt5G8/yH+MPoOkuCiv44hIEzMzHhzbhzCDf//f1Zzs\nEs0SOFTa8g+27T/CI++t59zsFC7tpzOfiYSq9NYx/OKiHny6cS9/XbrD6zhSTypt+VZVTS13zMwn\nPMx45PJczHTmM5FQdt3ZnTm7SzKT317D1n1HvI4j9aDSlm89PbeA/O0H+c24HNJbx3gdR0ROs7Aw\n43c/6EtkeBh3zMqnSqvJA55KWwBYurWYZ+Z+xbgBGYzOTfc6jog0k/TWMfzmshxWbD/I0x9/5XUc\nOQmVtnC4vIqfzsono00MD4zp7XUcEWlmo3LTuGJgB575pIAlW3UlsECm0m7hnHPc/9YadpeU88SV\n/UmIjvQ6koh4YPKY3nRoE8uds/I5VF7ldRw5AZV2Czd9USFvLt/J7SO6MTCzjddxRMQj8b4InpjQ\njz0l5dw5M1+X8AxQKu0WbOnWYh54ew3De6Tq3OIiwoBObbj/+734eH0RT+r77YCk0m6h9pSUc+sr\ny8hoHcMTE/oTHqbDu0QErj0rkysGduDJj79iztqvvY4jx1Bpt0AV1TX8ePqXlFVWM/W6QSTG6Hts\nEaljZvz60j7kdkjkzln5FBSVeh1JjqLSbmGcc0zOW8PywoP8bnxfurdL8DqSiASY6MhwplwzkOjI\nMG7+81ItTAsgKu0W5rl5m5ixeDu3De/KxTlpXscRkQCV3jqGZ68aQOH+Mm5+eSkV1TVeRxJU2i3K\nq0u38+gHG7i0Xzp3XdDD6zgiEuCGdEnmsfF9Wbi5mDtn5VOjFeWei/A6gDSPj9d9zb1vrOLc7BR+\ne0VfwrTwTETq4dL+GewrreDX764jJX4ND4zpresSeEil3QJ8ua2Y2/6yjD7prZhyzUCiIjTBIiL1\nd+O5Xdh7uII/zN9M2wQfk3SIqGdU2iFu9c4Srv/TUtISY3jxR2cS59OfXERO3S9H9mRvaQWPfbiR\neF8EPxqa5XWkFknv4CFseeEBrntxMYkxkbx8/WCS431eRxKRIBXmv2TvkYpqJr+9lqoax03ndfE6\nVoujedIQtXhLMde8sIikuChm3XI2HZNivY4kIkEuMjyMZ64awOjcNB6avY5n5uqsac1Ne9ohaEHB\nPm54aSnpraP5y01n0a5VtNeRRCRERIaH8cSV/YgKD+OxDzdSWV3LnRd01+K0ZqLSDjGzV+3mzln5\ndE6O45Ubh5CaoClxEWlaEeFhPDq+L5HhYTw1t4CD31Rx/+heRIRr8vZ0U2mHCOccz83bxKMfbGBg\nZhv+eN0gkuKivI4lIiEqPMz473E5JMZGMnX+ZrbtL+OZq3R539NNH4tCQEV1DT//68pvT5wy/cYh\nKmwROe3CwoxfXXIGD4/L4fOCfVz+/AK2F5d5HSukqbSDXNHhcq59YTGvL9vBzy7ozuNX9iM6Mtzr\nWCLSgkwY3ImXrh/M7pJyLnvucxZvKfY6UshSaQex+Rv3csmTf2PlzoM8NbE/P/luthaDiIgnhnZL\n4c1/G0q8L4IJU7/g6Y+/0mlPTwOVdhCqqqnlkffXc92Li0mKiyJv0jmM6ZvudSwRaeG6tY3n7dvP\nYXRuOr+bs5Frpy2i6FC517FCiko7yGzZd4Qr//AFz8/bxMTBnXjrtnN0eU0RCRgJ0ZE8OaEfv708\nl2WFB7j4yb8xZ+3XXscKGVo9HiSqamp5e1Mlb380H19EGE9P7M/3tXctIgHIzPjBmR3p36k1t89Y\nzk0vL2VUThoXpNR6HS3oqbSDwPLCA9z7xirW76nikpz2TP5+b9rqhCkiEuCy2yWQN+kcps7fxFNz\nC/iEWsrbFHLlmR21/qaBND0ewHaXfMPdr61g3PMLOFhWxR0DfDx39UAVtogEjaiIMCaNyOb9O86l\nY0IY97yxinHPL2DJVq0wbwjtaQegQ+VVTJm3iWmfbcE5uGFoFnd8L5svF37udTQRkQbpkhrPPYOj\n2ZfQjcc+3MD4KV9wYa923D2yJ93axnsdL2iotANIyTdVvLJwG9M+20LxkUou7ZfOXRf20MU+RCQk\nmBnjB3VkVG4aL362hSmfbuaiJ+Yzrn8Gtw7rStdUlffJqLQDwNeHynnxsy1MX1RIaUU153dP5ecX\n9iCnQ6LX0UREmlxsVASTRmQzcXAnnp5bwIzFhby2bAcX9WrPj4d1pW/H1l5HDFgqbY8451i8pZgZ\niwuZvWoP1bW1jMpN55bzutAnQ2UtIqEvOd7H5DG9mTSiGy8t2MpLC7by/po9DO6cxMQhHbm4T5rO\n8HgMlXYzKzpczlvLdzFjSSGb9x4hwRfBxMEduf6cLDKT47yOJyLS7FLifdx1YQ9uOb8rMxcX8srC\nbdw5awWT89ZyWf8Mxg/qQK+0Vlpxjkq7Wew9XMH7a/bw7spdLNpSjHMwMLMNj43vxqicNGKi9ElS\nRCTeF8GN53bh+qFZLNyynxmLt/OXRYX8acFWuqTEMSo3jVG5afRol9BiC1ylfRrU1jrW7DrEpxuL\nmLdhL8sKD1DroGtqHD8Zkc3o3DSydRYzEZHjCgszvtM1he90TaH4SCXvrd7Nuyt38+wnBTw9t4Cs\nlDjO757K+T1SObtLcouaQldpN4GaWseGPYdZuq2YxVuKWbh5P/tKKwHIyUhk0vBujMpNp3u7+Bb7\n6VBEpCGS4qK4ekgmVw/J/HbW8uN1XzNjcd0euC8ijDM7JzE4K4lBndvQv2ObkJ69VGmfIucchcVl\nrN55iFU7S1izq4T87Qc5XF4NQPtW0QztlsKwHqmcm51KSrzP48QiIqEhNcHHtWdlcu1ZmZRX1bBo\nSzHzNhTxxab9PP7RRpyDiDCjd3or+mQkkpORSJ+MRLLbxeOLCI0iV2mfwJGKagqLyygsLmPz3iN8\nVXSYTUWlFBSVcqSyBoDIcKNH+wRG56YzOKsNgzKT6NAmRnvTIiKnWXRkeN0UefdUAErKqlhWeIAl\nW4tZVniAvBW7mL6oEKgr8szkWLLbJtCtbTzd2sbTKTmWTkmxJMdFBdV7dr1K28xGAk8C4cALzrmH\nj/m5D3gZGAjsB650zm31/+xe4AagBviJc+6DJkvfALW1jgNllewrrWRfaQX7Siv4+lA5u0vK2VNS\nzq6ScnYeKPt2evvv2rXykd02gfGDOtKjfQI5IfbpTUQkmCXGRjK8Z1uG92wL1L3Xbz9QxqqdJazd\ndYiColI2Fh1mzrqv/+E633FR4XRMiiUtMZr2iTGkJ0bTLjGa1AQfKXE+UhKiSI7zERURGGf9Pmlp\nm1k48CxwAbADWGJmec65tUdtdgNwwDnXzcwmAI8AV5pZL2AC0BtIBz4ys+7OuZqmHsjxVFTX8ONX\nlrFt9zc8sHQeB8oqKfmmCnec67LHRYWT1jqGtMRozjij3befwjolxdI5JY5W0ZHNEVlERJpAWJiR\nmRxHZnIco3P//4qIFdU1FO4v+3YmtbC4jO3FZewuKWfljhL2H6k87vPF+yJoHRtJ69hI2sRGcV5S\nDcOaaSxHq8+e9mCgwDm3GcDMZgJjgaNLeyww2X/7NeAZq5tvGAvMdM5VAFvMrMD/fF80Tfx/LSo8\njP2lFURHQFZ6K9rERtE6NpLkuChSEnwkx/lITYiibatolbKISAvgiwgnu13CCY/gKa+qoehQBXv9\nM7H7/bOyB8oqKSmr4kBZJQfKqjjOvl+zMHe83c6jNzC7AhjpnLvRf/9aYIhzbtJR26z2b7PDf38T\nMIS6Il/onHvF//g04D3n3GvHvMbNwM0A7dq1Gzhz5symGZ1faWkp8fHBf07bUBkHaCyBKlTGEirj\nAI0lUDX1WIYPH/6lc27QybYLiIVozrmpwFSAQYMGuWHDhjXp88+bN4+mfk4vhMo4QGMJVKEyllAZ\nB2gsgcqrsdTnm/WdQMej7nfwP3bcbcwsAkikbkFafX5XRERE6qE+pb0EyDazLDOLom5hWd4x2+QB\nP/TfvgKY6+rm3fOACWbmM7MsIBtY3DTRRUREWpaTTo8756rNbBLwAXWHfL3onFtjZg8CS51zecA0\n4M/+hWbF1BU7/u1epW7RWjVwW3OtHBcREQk19fpO2zk3G5h9zGP3H3W7HBh/gt99CHioERlFRESE\n+k2Pi4iISABQaYuIiAQJlbaIiEiQUGmLiIgECZW2iIhIkFBpi4iIBAmVtoiISJBQaYuIiAQJlbaI\niEiQUGmLiIgECZW2iIhIkFBpi4iIBAmVtoiISJCwusteBw4z2wtsa+KnTQH2NfFzeiFUxgEaS6AK\nlbGEyjhAYwlUTT2WTOdc6sk2CrjSPh3MbKlzbpDXORorVMYBGkugCpWxhMo4QGMJVF6NRdPjIiIi\nQUKlLSIiEiRaSmlP9TpAEwmVcYDGEqhCZSyhMg7QWAKVJ2NpEd9pi4iIhIKWsqctIiIS9FTaIiIi\nQaLFlbaZ3WVmzsxSvM7SEGb2X2a20szyzexDM0v3OlNDmdmjZrbeP543zay115kayszGm9kaM6s1\ns6A7pMXMRprZBjMrMLN7vM7TUGb2opkVmdlqr7M0lpl1NLNPzGyt/7+tO7zO1BBmFm1mi81shX8c\nD3idqbHMLNzMlpvZO8392i2qtM2sI3AhUOh1lkZ41DmX65zrB7wD3O91oEaYA/RxzuUCG4F7Pc7T\nGKuBccB8r4OcKjMLB54FLgZ6ARPNrJe3qRrsT8BIr0M0kWrgLudcL+As4LYg/btUACOcc32BfsBI\nMzvL40yNdQewzosXblGlDTwO3A0E7eo759yho+7GEdxj+dA5V+2/uxDo4GWexnDOrXPObfA6RwMN\nBgqcc5udc5XATGCsx5kaxDk3Hyj2OkdTcM7tds4t898+TF1JZHib6tS5OqX+u5H+f0H7vmVmHYBR\nwAtevH6LKW0zGwvsdM6t8DpLY5nZQ2a2Hbia4N7TPtr1wHteh2ihMoDtR93fQRCWQygzs85Af2CR\nt0kaxj+dnA8UAXOcc0E5Dr8nqNv5q/XixSO8eNHTxcw+Atof50f3Ab+ibmo84P2rcTjn3nLO3Qfc\nZ2b3ApOA/2zWgKfgZGPxb3MfdVOB05sz26mqz1hEmpqZxQOvAz89ZqYtaDjnaoB+/nUrb5pZH+dc\n0K07MLPRQJFz7kszG+ZFhpAqbefc9473uJnlAFnACjODumnYZWY22Dm3pxkj1suJxnEc04HZBHBp\nn2wsZvYjYDTwXRfgJw04hb9LsNkJdDzqfgf/Y+IxM4ukrrCnO+fe8DpPYznnDprZJ9StOwi60gaG\nAmPM7BIgGmhlZq84565prgAtYnrcObfKOdfWOdfZOdeZuum/AYFY2CdjZtlH3R0LrPcqS2OZ2Ujq\nppnGOOfKvM7Tgi0Bss0sy8yigAlAnseZWjyr28OYBqxzzv3e6zwNZWapfz8yxMxigAsI0vct59y9\nzrkO/h6ZAMxtzsKGFlLaIeZhM1ttZiupm+4PysNA/J4BEoA5/kPYpngdqKHM7DIz2wGcDbxrZh94\nnam+/IsBJwEfULfY6VXn3BpvUzWMmc0AvgB6mNkOM7vB60yNMBS4Fhjh//8j37+HF2zSgE/871lL\nqPtOu9kPlQoVOo2piIhIkNCetoiISJBQaYuIiAQJlbaIiEiQUGmLiIgECZW2iIhIkFBpi4iIBAmV\ntoiISJD4P3+HKDpZ7bvkAAAAAElFTkSuQmCC\n",
"text/plain": [
"<matplotlib.figure.Figure at 0x110e48b50>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plotExample(0, trueVariances, xvalues)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# split into train and test set\n",
"from sklearn.model_selection import train_test_split\n",
"\n",
"(inputTrain, inputTest, \n",
" targetTrain, targetTest,\n",
" indicesTrain, indicesTest) = train_test_split(xvalues, \n",
" trueVariances, \n",
" # mlEstimators,\n",
" # trueMeans,\n",
" \n",
" np.arange(numRows),\n",
" test_size=0.20, random_state=42)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### build the network"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import torch\n",
"import torch.nn as nn\n",
"import torch.nn.functional as F\n",
"import torch.optim as optim\n",
"from torch.autograd import Variable\n",
"\n",
"import operator"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"class Net(nn.Module):\n",
" \n",
" def __init__(self, \n",
" numLayersInputSide = 5, \n",
" widthInputSide = 50,\n",
" numLayersOutputSide = 5,\n",
" widthOutputSide = 50,\n",
" ):\n",
" \n",
" super(Net, self).__init__()\n",
" \n",
" #----------\n",
" # input side layers\n",
" #----------\n",
"\n",
" numInputs = 1\n",
" \n",
" self.inputSideLayers = []\n",
" for i in range(numLayersInputSide):\n",
" layer = nn.Linear(numInputs, widthInputSide)\n",
" self.inputSideLayers.append(layer)\n",
" self.add_module(\"iLayer%d\" % i, layer)\n",
" \n",
" numInputs = widthInputSide\n",
"\n",
" #----------\n",
" # output side layers\n",
" #----------\n",
"\n",
" numInputs = widthInputSide\n",
" numOutputs = widthOutputSide\n",
" \n",
" self.outputSideLayers = []\n",
" for i in range(numLayersOutputSide):\n",
" \n",
" if i == numLayersOutputSide - 1:\n",
" # we want to learn the variance\n",
" numOutputs = 1\n",
" else:\n",
" numOutputs = widthOutputSide\n",
" \n",
" layer = nn.Linear(numInputs, numOutputs)\n",
" self.outputSideLayers.append(layer)\n",
" self.add_module(\"oLayer%d\" % i, layer)\n",
" \n",
" numInputs = numOutputs\n",
" \n",
" #----------------------------------------\n",
" \n",
" def forward(self, points):\n",
" \n",
" # points is a list of list if 2D points\n",
" # the first index is the minibatch index, the second index is the index\n",
" # of the point within the row\n",
" \n",
" # overall output for the entire minibatch\n",
" outputs = []\n",
" \n",
" # loop over minibatch entries\n",
" for thisPoints in points:\n",
"\n",
" # outputs of each point of this minibatch entry\n",
" thisOutputs = [ ]\n",
" \n",
" # thisPoints is a list of 1D tensors\n",
" # stack all input points into a 2D tensor\n",
" # (the second dimension will have size 1)\n",
" h = np.stack(thisPoints)\n",
" \n",
" h = Variable(torch.from_numpy(h))\n",
" \n",
" # forward all input points through the input side network\n",
" for layer in self.inputSideLayers:\n",
" h = layer(h)\n",
" h = F.relu(h)\n",
" \n",
" # average the input side network outputs: sum along first dimension (point index), \n",
" # then divide by number of points\n",
" output = h.sum(0) / len(thisPoints)\n",
" \n",
" # feed through the output side network\n",
" h = output\n",
" for layerIndex, layer in enumerate(self.outputSideLayers):\n",
" \n",
" h = layer(h)\n",
"\n",
" # note: since we want to do regression, we do NOT \n",
" # apply a nonlinearity after the last layer\n",
" \n",
" if layerIndex != len(self.outputSideLayers) - 1:\n",
" h = F.relu(h)\n",
" \n",
" outputs.append(h)\n",
" \n",
" # end of loop over minibatch entries\n",
" \n",
" # convert the list of outputs to a torch 2D tensor\n",
" return torch.cat(outputs, 0) "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### run the training"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"scrolled": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"starting training\n",
"epoch 0 train loss= 1.34403298423 test loss= 1.30903041363\n",
"epoch 1 train loss= 1.27560571581 test loss= 1.24003314972\n",
"epoch 2 train loss= 1.2023630403 test loss= 1.16357779503\n",
"epoch 3 train loss= 1.11909852549 test loss= 1.06560254097\n",
"epoch 4 train loss= 0.97649124451 test loss= 0.840762555599\n",
"epoch 5 train loss= 0.570005471818 test loss= 0.211821332574\n",
"epoch 6 train loss= 0.0923129919684 test loss= 0.0661781206727\n",
"epoch 7 train loss= 0.066865336732 test loss= 0.0652242824435\n",
"epoch 8 train loss= 0.0656737335958 test loss= 0.0645456463099\n",
"epoch 9 train loss= 0.065003182739 test loss= 0.0639254450798\n",
"epoch 10 train loss= 0.0644014740828 test loss= 0.0633487328887\n",
"epoch 11 train loss= 0.0636935958173 test loss= 0.0627726018429\n",
"epoch 12 train loss= 0.0629974650219 test loss= 0.0621417053044\n",
"epoch 13 train loss= 0.0623669900233 test loss= 0.0614781565964\n",
"epoch 14 train loss= 0.0616557433968 test loss= 0.0608172900975\n",
"epoch 15 train loss= 0.0608941228129 test loss= 0.0601832903922\n",
"epoch 16 train loss= 0.0601999830687 test loss= 0.0595041774213\n",
"epoch 17 train loss= 0.0594989401288 test loss= 0.0588910728693\n",
"epoch 18 train loss= 0.0588679306675 test loss= 0.0583146847785\n",
"epoch 19 train loss= 0.0582190626301 test loss= 0.0576493963599\n",
"epoch 20 train loss= 0.0575058246031 test loss= 0.0570088624954\n",
"epoch 21 train loss= 0.0568663724698 test loss= 0.0565118715167\n",
"epoch 22 train loss= 0.0563648797106 test loss= 0.0558930262923\n",
"epoch 23 train loss= 0.0557166954968 test loss= 0.0553726106882\n",
"epoch 24 train loss= 0.0551637914032 test loss= 0.0549017675221\n",
"epoch 25 train loss= 0.0546996988123 test loss= 0.054457090795\n",
"epoch 26 train loss= 0.054277137504 test loss= 0.0544672086835\n",
"epoch 27 train loss= 0.0540064994711 test loss= 0.053742185235\n",
"epoch 28 train loss= 0.0534756283741 test loss= 0.0534984655678\n",
"epoch 29 train loss= 0.0533105168724 test loss= 0.0531700924039\n",
"epoch 30 train loss= 0.0529063508147 test loss= 0.0529465340078\n",
"epoch 31 train loss= 0.0526951954234 test loss= 0.0527386814356\n",
"epoch 32 train loss= 0.052497124183 test loss= 0.0525724999607\n",
"epoch 33 train loss= 0.0523197879083 test loss= 0.0524238795042\n",
"epoch 34 train loss= 0.0522577505326 test loss= 0.0523262880743\n",
"epoch 35 train loss= 0.0521777240792 test loss= 0.0522831045091\n",
"epoch 36 train loss= 0.0521923992783 test loss= 0.0522123351693\n",
"epoch 37 train loss= 0.0519728874788 test loss= 0.0521351844072\n",
"epoch 38 train loss= 0.0519024400273 test loss= 0.0522464327514\n",
"epoch 39 train loss= 0.0518238760997 test loss= 0.0520173236728\n"
]
}
],
"source": [
"# instantiate the model\n",
"model = Net()\n",
"\n",
"allIndices = np.arange(len(targetTrain))\n",
"\n",
"# define the loss function\n",
"lossFunc = nn.MSELoss()\n",
"\n",
"minibatchSize = 32\n",
"\n",
"# number of training epochs\n",
"numEpochs = 40\n",
"\n",
"optimizer = optim.Adam(model.parameters(), lr = 0.0001)\n",
"\n",
"trainLosses = []; testLosses = []\n",
"\n",
"# variable for target values of test set\n",
"testTargetVar = Variable(torch.from_numpy(np.stack(targetTest)))\n",
"\n",
"print \"starting training\"\n",
"for epoch in range(numEpochs):\n",
"\n",
" np.random.shuffle(allIndices)\n",
" \n",
" # put model in training mode\n",
" model.train()\n",
" \n",
" trainLoss = 0\n",
" trainSteps = 0\n",
" \n",
" for indices in np.array_split(allIndices, minibatchSize):\n",
" \n",
" optimizer.zero_grad()\n",
" \n",
" # forward through the network\n",
" output = model.forward([ inputTrain[index] for index in indices])\n",
" \n",
" # build a PyTorch variable with the target value\n",
" # so that we can propagate backwards afterwards\n",
" thisTarget = Variable(\n",
" torch.from_numpy(np.stack([ targetTrain[index] for index in indices ])))\n",
" \n",
" # calculate loss\n",
" loss = lossFunc.forward(output, thisTarget)\n",
" \n",
" # accumulate \n",
" trainLoss += loss.data[0]\n",
"\n",
" # backpropagate \n",
" loss.backward()\n",
" \n",
" # update learning rate \n",
" optimizer.step()\n",
" \n",
" trainSteps += 1\n",
" \n",
" trainLoss /= trainSteps\n",
" trainLosses.append(trainLoss) \n",
" \n",
" # evaluate model on test set\n",
" model.eval()\n",
" \n",
" output = model.forward(inputTest)\n",
" \n",
" # calculate loss on test set\n",
" testLoss = lossFunc.forward(output, testTargetVar).data[0]\n",
"\n",
" testLosses.append(testLoss)\n",
" \n",
" print \"epoch\",epoch,\"train loss=\", trainLoss, \"test loss=\",testLoss"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### plot training progress"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"scrolled": false
},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAfUAAAHjCAYAAAA6x4aXAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzs3XmYVNWd//H3t7am91VR2RE0ICiyCUMwMMa4jWiiyWjU\nJBORMdHoJMaJeSY6ozO/Z0gcJ4mjGWMm/oyJvzBGYzSGRFzomKjIFlTABVTURgSBZmm66e6qOr8/\nblXRS/UCdFFVtz6v5+mnqk7dunUO9ejnnnPvPcecc4iIiEj+C2S7AiIiIjIwFOoiIiI+oVAXERHx\nCYW6iIiITyjURUREfEKhLiIi4hMKdREREZ9QqIuIiPhExkLdzO4zs21mtraP7aaZWdTMLs5UXURE\nRAqBZWpGOTM7HWgCHnDOTehhmyDwFLAfuM8593Bf+62rq3MjR47s8/v37dtHaWnpQdU5l6k9uU3t\nyW1qT25Te/q2atWq7c65o/raLjSg39qBc+45MxvZx2ZfAx4BpvV3vyNHjmTlypV9bldfX8+cOXP6\nu9ucp/bkNrUnt6k9uU3t6ZuZvduf7bJ2Tt3MhgCfBv47W3UQERHxk4wNvwMkeupPpBt+N7NfAXc4\n55aZ2f2J7dIOv5vZAmABwODBg6csWrSoz+9uamqirKzs0CufY9Se3Kb25Da1J7epPX2bO3fuKufc\n1D43dM5l7A8YCazt4b13gE2JvyZgG3BhX/ucMmWK64+lS5f2a7t8ofbkNrUnt6k9uU3t6Ruw0vUj\ndzN2Tr0vzrlRyecdeuq/yVZ9RERyTXt7Ow0NDezfv79TeWVlJa+99lqWajXw1J4DBg0axNChQwmH\nw4f0+YyFupn9EpgD1JlZA/DPQBjAOXdPpr5XRMQvGhoaKC8vZ+TIkZhZqnzv3r2Ul5dnsWYDS+3x\nOOfYsWMHDQ0NjBo1qu8PpJHJq98vPYhtv5SpeoiI5Kv9+/d3C3TxLzOjtraWjz766JD3oRnlRERy\nmAK9sBzu761QFxER8QmFuoiIpLVr1y5+9KMfHdJnzz33XHbt2nVQn/nBD37AAw88AMCXvvQlHn64\nz0lGu6mvr+eFF144qM/87Gc/Y+zYsYwdO5af/exnabfZuXMnZ555JmPHjuXMM8+ksbEx9X2VlZVM\nmjSJSZMmcdtttwHw/vvvM3fuXMaPH89JJ53ED3/4w9S+vvnNb/Lss88edNv6Q6EuIiJp9Rbq0Wi0\n188uXryYqqqqfn9XNBrlvvvu4/Of//xB1bGrgw31nTt3cuutt/LSSy+xfPlybr311lRgd7Rw4ULO\nOOMMNmzYwBlnnMHChQtT782ePZs1a9awZs0abrnlFgBCoRB33HEH69evZ9myZdx9992sX78egK99\n7WudPj+QsnZLm4iI9N+tv13H+g/2ABCLxQgGg4e9z/HHVfDP55/U4/s33XQTb731FpMmTeLMM8/k\nvPPO4+abb6a6uprXX3+dN998kwsvvJD333+f/fv3c/3117NgwQLgwJTeTU1NnHPOOXz84x/nhRde\nYMiQITz22GMUFxd3+q5nn32WyZMnEwp1j6XbbruN3/72t7S0tPBXf/VX/PjHP8bMuPPOO7nnnnsI\nhUKMHz+ehQsXcs899xAMBvnFL37Bf/3XfzF79uxe/w2efPJJzjzzTGpqagA488wz+cMf/sCll3a+\n1vuxxx6jvr4egC9+8YvMmTOH7373uz3u99hjj+XYY48FoLy8nHHjxrF582bGjx/PiBEj2LFjBx9+\n+CHHHHNMr/U7WOqpi4hIWgsXLuT4449nzZo13H777QCsXr2aH/7wh7z55psA3HfffaxatYqVK1dy\n5513smPHjm772bBhA9dccw3r1q2jqqqKRx55pNs2zz//PFOmTElbj2uvvZYVK1awdu1aWlpaeOKJ\nJ1L1+8tf/sIrr7zCPffcw8iRI7n66qv5+te/zpo1a5g9ezYPPvhgami849/FF3sLg27evJlhw4al\nvmvo0KFs3ry5Wx22bt2aCuljjjmGrVu3pt578cUXOeWUUzjnnHNYt25dt89u2rSJv/zlL5x22mmp\nssmTJ/P888+nbe/hUE9dRCQPdOxRZ/O+7unTp3e6h/rOO+/k0UcfBbzzyBs2bKC2trbTZ0aNGsWk\nSZMAmDJlCps2beq23y1btjBu3Li037l06VK+973v0dzczM6dOznppJM4//zzOfnkk7nsssu48MIL\nufDCC9N+9rLLLuOyyy47lKb2yMxSV6lPnjyZd999l7KyMhYvXsyFF17I6tWrU9s2NTVx0UUX8YMf\n/ICKiopU+dFHH80HH3wwoPUC9dRFROQgdFxStL6+nqeffpoXX3yRl19+mVNPPbXb7HcARUVFqefB\nYDDt+fji4uK0n92/fz9f/epXefjhh3n11Ve56qqrUtv97ne/45prrmH16tVMmzYt7X776qkPGTKE\n999/P7V9Q0MDQ4YM6bafwYMHs2XLFsA7ADn66KMBqKioSM3zfu6559Le3p4arWhvb+eiiy7isssu\n4zOf+Uy3dnU9BTEQFOoiIpJWeXk5e/fu7fH93bt3U11dTUlJCa+//jrLli075O8aN24cGzdu7Fae\nDPC6ujqamppSV8TH4/HUFebf/e532b17N01NTd3qfNlll6UuYuv4l9zPWWedxZIlS2hsbKSxsZEl\nS5Zw1llndavHvHnzUlfG/+xnP+OCCy4A4MMPP0yuZ8Ly5cuJx+PU1NTgnOPKK69k3LhxfOMb3+i2\nvzfffJMJE7qtdXbYFOoiIpJWbW0ts2bNYsKECdx4443d3j/77LOJRqOMGzeOm266iRkzZhzyd51z\nzjk899xz3cqrqqq46qqrmDBhAmeddRbTpk0DvIsFL7/8ciZOnMipp57KddddR1VVFeeffz6PPvoo\nkyZN4k9/+lOf31tTU8PNN9/MtGnTmDZtGrfcckvqorn58+ezcuVKwLto8KmnnmLs2LE8/fTT3HTT\nTQA8/PDDTJgwgVNOOYXrrruORYsWYWY8//zz/PznP+fZZ59NjQ4sXrwY8HrwGzduZOrUvhddO2j9\nWfUll/4GepW293bs69d22aZVjHKb2pPb8rU969evT1u+Z8+eI1yTzEq258ILL3Rvvvlmlmtz+Pr6\nfX7961+773znOz2+n+53p5+rtBV0T/3p9Vs5/falrNi0M9tVEREpeAsXLkydt/azaDTKDTfckJF9\nF3SozxpTR21pET98ekO2qyIiUvBOPPFETj/99GxXI+M++9nPHtTEPAejoEO9OBLk708fzZ83bmel\neusiIpLnCjrUAS6bMZza0gg/fEa9dRERyW8FH+olkRALTh/NnzZsZ9W73ef7FRERyRcFH+oAV8wc\nQY166yIikucU6hzorT/35kesfk+9dREROLylV8FbSrW5ubnH9y+++GLefvttwFsAZvv27Qf9Hfff\nf/9BTbfqnOO6665jzJgxnHzyyZ2mdO1o1apVTJw4kTFjxnDdddelJpj5l3/5F4YMGdLt3vOnnnqK\nKVOmMHHiRE4//fROS6t+8pOfTLvyWyYo1BOumJHoretKeBERILOhvm7dOmKxGKNHjz7k/cPBh/rv\nf/97NmzYwIYNG7j33nv5yle+kna7r3zlK/zkJz9JbfuHP/wh9V5ywZg1a9Zw7rnnAt6Md7/97W95\n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\nAElFTkSuQmCC\n",
"text/plain": [
"<matplotlib.figure.Figure at 0x113e4e550>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plt.plot(range(1, len(trainLosses) + 1), trainLosses, label = 'train (last=%.3f)' % trainLosses[-1])\n",
"plt.plot(range(1, len(testLosses) + 1), testLosses, label = 'test (last=%.3f)' % testLosses[-1])\n",
"plt.grid()\n",
"plt.legend()\n",
"plt.xlabel('epoch')\n",
"plt.ylabel('loss');"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### show a distribution of predictions minus true value for the test set"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"model.eval()\n",
"\n",
"predictions = model.forward(inputTest).data.numpy()[:,0]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### compare different ways of estimating the variance"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style>\n",
" .dataframe thead tr:only-child th {\n",
" text-align: right;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: left;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>estimator</th>\n",
" <th>rmse</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>network prediction</td>\n",
" <td>0.228073</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>ML estimator</td>\n",
" <td>0.246142</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>unbiased estimator</td>\n",
" <td>0.241594</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" estimator rmse\n",
"0 network prediction 0.228073\n",
"1 ML estimator 0.246142\n",
"2 unbiased estimator 0.241594"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def absDiffFunc(prediction, trueValues):\n",
" absDiff = prediction - trueValues\n",
" return sqrt(np.mean(absDiff**2))\n",
"\n",
"import pandas as pd\n",
"\n",
"df = pd.DataFrame(dict(estimator = [\n",
" \"network prediction\",\n",
" \"ML estimator\",\n",
" \"unbiased estimator\",\n",
" ], \n",
" \n",
" rmse = [ \n",
" absDiffFunc(predictions, targetTest),\n",
" absDiffFunc(np.sqrt([ np.var(x, ddof = 0) for x in inputTest]), targetTest),\n",
" absDiffFunc(np.sqrt([ np.var(x, ddof = 1) for x in inputTest]), targetTest)\n",
" ]))\n",
"\n",
"df\n",
" "
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.13"
}
},
"nbformat": 4,
"nbformat_minor": 1
}
@nikpocuca
Copy link

nikpocuca commented Aug 15, 2020

Replace

  absDiffFunc(np.sqrt([ np.var(x, ddof = 0) for x in inputTest]), targetTest),
  absDiffFunc(np.sqrt([ np.var(x, ddof = 1) for x in inputTest]), targetTest)

with

      absDiffFunc([ np.var(x, ddof = 0) for x in inputTest], targetTest),
      absDiffFunc([ np.var(x, ddof = 1) for x in inputTest], targetTest)

targetTest is just the variance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment