2649 lines
146 KiB
Plaintext
2649 lines
146 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "6d2db899",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"<!-- HTML file automatically generated from DocOnce source (https://github.com/doconce/doconce/)\n",
|
|
"doconce format html chapter13.do.txt -->"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "f4908a97",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"# Recurrent neural networks: Overarching view\n",
|
|
"\n",
|
|
"Till now our focus has been, including convolutional neural networks\n",
|
|
"as well, on feedforward neural networks. The output or the activations\n",
|
|
"flow only in one direction, from the input layer to the output layer.\n",
|
|
"\n",
|
|
"A recurrent neural network (RNN) looks very much like a feedforward\n",
|
|
"neural network, except that it also has connections pointing\n",
|
|
"backward. \n",
|
|
"\n",
|
|
"RNNs are used to analyze time series data such as stock prices, and\n",
|
|
"tell you when to buy or sell. In autonomous driving systems, they can\n",
|
|
"anticipate car trajectories and help avoid accidents. More generally,\n",
|
|
"they can work on sequences of arbitrary lengths, rather than on\n",
|
|
"fixed-sized inputs like all the nets we have discussed so far. For\n",
|
|
"example, they can take sentences, documents, or audio samples as\n",
|
|
"input, making them extremely useful for natural language processing\n",
|
|
"systems such as automatic translation and speech-to-text.\n",
|
|
"\n",
|
|
"More to text to be added"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "43cb4913",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"## A simple example"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"id": "cf6b9dab",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAi8AAAGdCAYAAADaPpOnAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/YYfK9AAAACXBIWXMAAA9hAAAPYQGoP6dpAACTeUlEQVR4nO2dd5wU9f3/X7O714C7ox53J+1AmiCINEEREAXBEo0mVuz+gg2VGCIaEzVRTEIMMRaiUdFg+yZgi0hApUpvSpGiHtyJd8BR7o5ydef3x7J7M7NTPp8pOzO77+fjcbA785mZz8585vN5f97tI4iiKIIgCIIgCMInBNyuAEEQBEEQBA8kvBAEQRAE4StIeCEIgiAIwleQ8EIQBEEQhK8g4YUgCIIgCF9BwgtBEARBEL6ChBeCIAiCIHwFCS8EQRAEQfiKkNsVsJtwOIwff/wR2dnZEATB7eoQBEEQBMGAKIqorq5GYWEhAgF93UrSCS8//vgjOnbs6HY1CIIgCIIwQWlpKTp06KBbJumEl+zsbACRH5+Tk+NybQiCIAiCYKGqqgodO3aMjeN6JJ3wEjUV5eTkkPBCEARBED6DxeWDHHYJgiAIgvAVJLwQBEEQBOErSHghCIIgCMJXkPBCEARBEISvIOGFIAiCIAhfQcILQRAEQRC+goQXgiAIgiB8BQkvBEEQBEH4CkeFl5deegn9+vWLJYwbNmwYPv30U91jli5dioEDByIzMxNdu3bFrFmznKwiQRAEQRA+w1HhpUOHDnjmmWewfv16rF+/HhdccAF+8pOfYNu2barli4uLMWHCBIwYMQKbNm3CI488gsmTJ2Pu3LlOVpMgCIIgCB8hiKIoJvKCrVu3xp///Gfcfvvtcft+/etf46OPPsI333wT2zZp0iR89dVXWLVqFdP5q6qqkJubi8rKSloegCAIgiB8As/4nTCfl8bGRrz77rs4fvw4hg0bplpm1apVGDt2rGzbuHHjsH79etTX16seU1tbi6qqKtkfQRAEQRDJi+PCy5YtW9CiRQtkZGRg0qRJeP/993HGGWeoli0vL0f79u1l29q3b4+GhgZUVFSoHjN9+nTk5ubG/jp27Gj7byCIZOHDzfvwxY79KK44jgVby5FgxStBEIQtOL6qdM+ePbF582YcPXoUc+fOxc0334ylS5dqCjDK1SSjnavWKpPTpk3DlClTYt+jS2oTBBGhtqERGaEgyitrcP+7m2X7Xr91MEb3zHOnYgRBECZxXPOSnp6O008/HYMGDcL06dPRv39//O1vf1Mtm5+fj/Lyctm2AwcOIBQKoU2bNqrHZGRkxKKZon8EQUT444Id6PmbBdhcehRHTtTF7f+q9GjiK0UQBGGRhOd5EUURtbW1qvuGDRuGRYsWybYtXLgQgwYNQlpaWiKqRxBJxUtLvgMATJ//DQIa2kuCMEN1TT3eXlOCQ8fU+3OCcBJHhZdHHnkEy5cvx549e7BlyxY8+uijWLJkCW644QYAEZPPTTfdFCs/adIk7N27F1OmTME333yD1157Da+++ioeeughJ6uZFHxVehTllTVuV4PwKOmhAAIqsgu5vBBmeXjeFjzy/hbcOnud21UhUhBHfV7279+PiRMnoqysDLm5uejXrx8WLFiAiy66CABQVlaGkpKSWPmioiLMnz8fDz74IF544QUUFhbiueeew1VXXeVkNT3Jkx9vx+4D1Zh96xAE1UYdCXsqjuMnL3wZ+fzMJYmoHuEz0oLq8xSSXQizzN9SBgD4+odKl2tCpCKOCi+vvvqq7v7Zs2fHbRs5ciQ2btzoUI38QWNYxGtfFgMAvv7hKAZ0aqVbvuTwidjnimO1aNsiw9H6Ed5nw94jmPqfr2Lf04MBNJKahbCRgCBQmyJcg9Y28iAVEhtyiwxj+bJFZlOZLTQLIgBc9/JqfHfweOx7WiiAhkYaaAj7MFAIE4SjkPDiQaS+Kyw+ltLJz/G6BsrdQaCuMSz7nhYU0BimdkHYhwCSXgj3IOHFg5RJhJfGMPDtgWOoqlHPMByhaVD68ttDGPSHz/DpKXs0QQARs1FDOBy/gwRdwiwkuxAuQsKLB5Hm49iyrxIXPrsUI/+0WLO8dPx5Z20JDh2vw11vpbbfECEnLahuNiLRheChvjGMu9/agNlfFpPZiHAVxzPsEvyEJdLI/7ZFkvYdOaGteaEBiDAiPRQgsxFhmflbyjB/SznmbylHs/Sg29UhUhjSvHgQqSbl8PH4rKh65QlCjbRgAA0kvBAWOVHXGPtMSQ8JNyHhxeNs2HvEsEyYpBfCgHQGh11y9CaMkOacItGFcBMSXjyI3hCiNsDQmEMYkRYMoL4x3mE32nae+3w3zpn+OcoqTya4ZoRXCIdFvLD4W6z8tkKzTEjq6ELSC+Ei5PPiEb7+4Si2/ViFksMn0FzDlnz3Wxuw72gN5t01XDYDEsnrhTAgzcDn5dlFuwAAf120C3+6un+iqkV4iAXbyvHn/+0EoJ2pW9rvkNmIcBMSXjyAKIq4/PkvDcvN3xJx3t2yrxJndWwpOYFDFSN8SV1DvIYlFBBUfV6Ugi9p8VKXUkmmbi3kwouTtSEIfchs5AF4/SgbFfk6aLwhojz+0Tac8dsFcdsFgZLUpTpvrtqDn89apZkzSiqYVEqiG8NhEYt3HsDh43VysxFBuAgJLx6A11FS6bpgdHhDYxi1DY36hQhfcqCqBje9thaLtu8HAMxeuUddwyKKqj4vROrw2w+3Ye2ew/jH0u9U9wsSM9DFf1sW+/zW2hLc+vo6XPb3FQgGmoaMIwoBhyASCQkvHoD3tVfOoI18Xsb+dRkGPLkINfUkwCQbj3+8Dct2HcSdb643LKumeVEKvuTGkPwcPq6heZE8e2mW7/lfR7J17zt6EhqLk6P/kwux7UdaV41IHCS8eABePwNlaLTR8d9XHMeJukbsLK/mrBnhdSqqjfMAAZE2QnleCACaWtiAhklI2t9INS9Sqmsa8Pv/brdeOYJghIQXD8CbpyVe88IGmQ2SD9ZIMxEis89LOCziwfc24+Vl6uYFwt/UNoSxYGs5vlSERGtFD0n7Jz2Xl5CGYEMQTkCtzYfEa17YBiW1KBTC37DKvVqaF7XDl39bgfc37cPT83dYqxzhSfZUHMekORtwwz/XyLYHNSQTqdCr194y02g4IRIHtTYP4KTZ6NsDx2Kf60jzknSwNh0REcdtIwQIOFHbYKlOhLf54Yh6IsKgpual6bNee8sI0VpHROIg4cUDsKj+27bIiH2OizbSOf7CZ5fGPterrCpMJA96GjizPi8lh06QuTHJ0HLc13LWlk6W9EzcmWkkvCQTXg/wIOHFA7BoXqQDU1yeF8YxSW0Qqm8M44cjxsmpCG8ibRe1OmZBLZ8XtbYjHcTO//Ni3PTqWkt1JLyFVjthMRvpzbPIbJQ8bP+xCr0eW4DffrjV7apoQq3NA7DIHtIyvHleoqgJL9e+vBrn/XExVn9/iO0khKeQPvraeh3hxUK00SpqG0mLKHPGNTYb6WletIQfwn889/luAMCbq/a6XBNtSHjxACzRRtIyjUqfF8brqM24oqtW/9+6UsazEF6lxiARIZPPC40/KYW0K9F69lIBR6+rUgYE/GXhToyesQSHj7OF8xPewQ/9AAkvHoBFcxKWefybizbS812gWZM/kT56PRu1KGqYjWhxiZRG+vRZzEZ6Ey2l8PL3L75FccVxvLTkW0t1JBIPCS8EG0w+L02fTed50fGJIOHF/+j6vIj6++VQW0gVWMxGUk2vXl+j1b7Kq2pN1Y1wD8EHfQAJLx6AZfYrMxsphReNw5UaGb1oI63smoS3kT5RXc0LNAYXWh4gqfmq9Cguf36Fpk+b9PFr+rzoaH2laAkvFdUkvPgOH/QDIbcrQDCajXSd5tRPoHTQ1MvzopXjgfA4krag55Ab0bywhD5SO0gmrntlNU7UNeLal1er7mfJnisLNtLzedHoXw4eI+HFb/ihFyDNiwdgMfvINS+K4zVOoNTQkM9L8iF9wnoDiwhRNxqJSE5O1OkLrNI2o2k2kvm8aJ+rTkM4PkTCi+8QfDCZJeHFA7BEG8l8XhijjZSn1VsegISXZMAOzQuZjpIF1ucdhSVJnZ6JW8tsRMuS+A8/dAEkvHgANrORRPOi0KBoCT/K7SS8JBfhsIiDEn8CvXZ0oLpGdXChWKPk5dH3jROMiTomocc+2ApRFBUZdrXPRUJK8uCHCQwJLx6AxWFXWkLZgWgNWkofCDIbJRe/mLMBZZU1se96reidtaVYW3w4brvSAVMQ/DHrIoz5z4YfDMtI+x5l+/nX6r3YUV4tM1OzOuyWHm7K2k0Csv/wQx9AwosX4FweIG5hRo1jlD4vJ3WiUchh138s2r5f9t1Ig8ceKk2kCnLNS3wDqqlvlJuNdNpYtNwPR05gxJ8W21ZHwl2iGjivQcKLB2Bz2G36HB8qrRVtJB+sqmu0VwumUGn/48UOhvA2cn8WdViT1EVPsH7PERtqRriJ1Hn7X6v3Ytf+Y6iuqXexRvE4KrxMnz4dgwcPRnZ2NvLy8nDFFVdg586duscsWbIEgiDE/e3YscPJqroK75jz5Xdsa82UHa2RfdcVXkh2IU7hh0gDwh5YotVYNS9i7H+2PFSEh1F0AZPf2YQzH1+I9XviTc9u4ajwsnTpUtxzzz1YvXo1Fi1ahIaGBowdOxbHjx83PHbnzp0oKyuL/XXv3t3JqroKS7SRlGW7DioSR6mX+8kLX8q+KyVn6YwqRNKL77FjjGBpBS8s/hYPvrdZ1gYJfyLvO9Sfp7SMXl+lpfmjJSj8z8791QAiSz54BUeT1C1YsED2/fXXX0deXh42bNiA888/X/fYvLw8tGzZ0sHaeQczr3ZYFBGAgB3lVXjgvc1Mx1QpNC9SsxKZjfyPmRmumWP+/L+I9vTngzpiWLc2/CcgvINBAjoR8kmOXnMhESV50FoewEtK2YT6vFRWVgIAWrdubVh2wIABKCgowJgxY7B4cXI7f5nxVYjmevnpiyuZj6k6Kde8NEiWCyCHXe8iiiIe/2gb/rVqj345E8MH6xFqWpaT9dpmSMIfDJ3+WSwySKstyM1G+rmEiORAazjY9mMVxvxlSVywgBskTHgRRRFTpkzBeeedh759+2qWKygowMsvv4y5c+di3rx56NmzJ8aMGYNly5aplq+trUVVVZXsz29YmTEbZdCUUlWjI7yQ5sWzbNh7BLNX7sFjH27TL2iiHVWerMe8jfKQWrWWUB+Oj1RS2UT4jJr6MJ75NOJPaN3nxdjsRPgDrdHgYHUtvjt4HHe+uT6h9VEjYWsb3Xvvvfj666+xYsUK3XI9e/ZEz549Y9+HDRuG0tJSzJgxQ9XUNH36dDzxxBO219fr8PrJAJGFGTfsPYKBnVtFvkvNRqR58SzHatk0HGbGiP9s+EGWD0SrGSgj3ABzbZDwHtHnqCV8sC4PoAW1Ev/hh+EgIZqX++67Dx999BEWL16MDh06cB9/zjnnYPfu3ar7pk2bhsrKythfaWmp1eomHDNjgNpgwsKGvU3e4lLNC3Uw3kUqWL63rsTRa2nZutVWJP9ixwF8VXrU0foQzmM0UMkWZjRYgkL6P+FftPoBL+Go8CKKIu69917MmzcPX3zxBYqKikydZ9OmTSgoKFDdl5GRgZycHNmf3zAzgzUb6CG9lDTjLuUI8S5S4eXXc7dolnPyETaoZGd+d11pXEQb4T+iofEs7UdvyQGj40sPn4hpERfvOICLnl2KLT9UMteTSBx+0Lw4aja655578Pbbb+PDDz9EdnY2ysvLAQC5ubnIysoCENGc7Nu3D2+++SYAYObMmejSpQv69OmDuro6zJkzB3PnzsXcuXOdrKqrmIo2Mim9SI+SLmFPJgDvwuqOxOqwGwoIcUtHSFHruMxq+gjvE33cjj1hEfju4DGM+ctSZGeEsOWJcbh19joAwG1vrMO6Ry906sqESVJeeHnppZcAAKNGjZJtf/3113HLLbcAAMrKylBS0qQKr6urw0MPPYR9+/YhKysLffr0wSeffIIJEyY4WdWE8cWO/fiqtBIPXNhdMuMxo3kxKbyIkdVmP9t+APe8vVFyPlOnIxIAaxg7a5MI6ggvWp1WPTWQpMVKPyRF7/jluw4CAKoV/lvHGf25iETjfenFUeGF5WWYPXu27PvUqVMxdepUh2rkPrfNjnhpn1GYg3F98gGYm/E0mhVeIGL6/B2YvXKPfDuNTZ6F1Zma9RGGAgJqjYvJaFTxeSGSA73WxdMvaBUVdXSC3h8iUxOWLmdHeRV65bvnpkFrG7lEuXQ14AQlF4se9+/18U7NZDbyLkHFW6qZyZTxGYaUJ2Tgy+8qsHUf+Sd4neKK4/ju4DGuY6KKPSe7AK1z01IU3oTlqdz4z7WO10OPhIVKE3KkA80THxvk71ChMSxiUwn/AmhWBz4i8Sg7+NqGMDLTgnHleDQveqg1hWnzIo7Ce565hPEqRKKpawhj9IwlAIAdv7+Y+biY2cii14tetBH1Lv6CRaasOMarv7UX0ry4hPRlXr67gvv4sCjiSo7surHravQiWtsPVNXgvXUlOMmRDI+wF2X249p6a9nhQkHtnkmA/kBD6xl5F+naZay5gYDIM99ZXo0d5dWWrm8mSZ2yJe49dBzvri2RRUISiccPodKkefEpZrObaqcAV99+1ayVKD18Elv3VeH3V2hnRiacQ+nzcuREHQJq0w5GuSKkenATeibEOhpUPMnB6lrc/sa62Pc0DtNgXWMY42aqZzDnQXNiBB3NrmKMHPnnJQAi2cD/3/ndLNeJ0ObhuV+jurYBz183IE676wdrHmlefArPzEoKSwpwKaWHTwIAPvvG/bUsUhVlRzJqxhKc+fjCuHKsan+jpSD0ZsokvHiTxz/ehq9N5kypqbdHq2pGJ6fVEtd8f1hjD2EHdQ1hvLuuFJ98XYa315Zg6n++ko0pPpBdSPPiFlZdTKSZcnnQUsca+bz4oTEnK6xthSdU2uBMmnvqG0h48SJlR0/KN3D1L/a83eQ25x8aJKr7aOLBVs3TMW18b7eqxA1pXmzk/9aX4rcfbk2IX4DhIn0aPL/4WxxX8V+hfsd9vj1wDNe+vAorv5P7QLFqVFgHDz3ZRRAE0rwkATzOt06bCPQmRlJzxfLdB52tCBGjviH+mfx4tCkC1g9RYCS82MjU/3yNN1ftxdJdxi+h14QFo1BpPzRmv3PXnA1Y/f1hXP/KGtl2Zs0L43WMfV6096l1eoT3sG/+xHMiUfKvfKt2qHTk/4pjtZj4qruht6mE2iREOqnxQ3dPwosDVEm8/rXwWmgyBZG4T3lVjep2ux+NUcZevVl7XSNFnfkBnrxNeq2BR9FmpkuLXtvtsNtUQ819gDUZplcgnxcH8FsjAOI7u8qT9XhDkYWXcBhOZ+q4w1mT1Flx2CXNiy/gEV70+qs6Dh8nvStqCcRaGl0fdqG+Qu25StsBhUqnKH4UXpR9y2MfbMVHX/0Y++7Hn5Qs2G020tO8CIL+ecjnxZsonxlPKgU9TRtPvhXtBJgsxzJfhrABdc1L02c/9PdkNnIA1lWAvYRyprZuD4UqJhrt/jtxDruR8/ANZl4zgaYiykfAo3mxy0HbzlBpwlnUfV6kmhfvQ8KLA7CsAuy1/l7p86LUHvlBEvc7Vmaup0oylTIMlNY1G6kJL0yXJRKIXWuVcZmNdC559IS6H2C0X4nvX6jDcZJ6lYVWpX78fujvyWzkAMp07gDwVelR/HtD04KIVtcRsRtlx+OHxpsqbCo5ylTuxSXfMZUzMmvqOuyqCS9MVyUSidkM3ErsStOv3Ta12iK1Kicxctj1Q3QpCS82Ic3topYE7CcvfJnI6nCjnKnFaV5oJuQ4at31iboGPDX/G6bjWTOs6vVLAvTzvNSqal5E0EzZW3CZjXT28WleRNn/LPhgjExK1JJN+s1Xk8xGNtEoeWFZ2oDXVO3KDueEIpGdz9q1L1FrE1UnzS0DoYfRrEovbH57WVXcNo81ZQLy/sgKbvm8fPbNAfx10S7yp3IIozwvfliAlYQXm2iUPGwWCdZrTUPaVjeVHKG8Cx7BLt8FKVYcdp/7fDfTNf5vfSme/Hg7DT4JIj7aKPE+L2Y6Nb2u8m+f78aGvUf4T0oYoubzIp3U+OGtJbORTTQYmI28jtTPYeZn8QOU/36R/1DzNXGjE+G9ppp8MvU/XwMARvVsh/N7tLNeKYILHtnF3WgjQbcOh4/XmTgrYYSRz4sf5hykebGJRokk60ezkbSzs8tJj+BDrU0kWn0rCOAehfQcfI+eNM42TdiAovG4E21kf1v1g+OoH1FPUtf02QmNr92Q8GITUhuzWrSREu9FGzXVR014UXYiy3cfRMmhE47XKxnZe+g47pqzAf9c/r2hcOJEH2LkfM3bcfmgn0s6auob8eHmfZqaiUY3zEYmINnEHVR9XnxmMSCzkU002BWbaIGczBCqasw5eEqrb9Rhrd9zOLaI2p5nLjF1vVTmqpdWouJYHT7dWo6t+yox89oBANQVHm4IuXZekXxe7ONAVQ3aZWdAEAT8acFOvPZlMXq2z8b/Hjw/rizfbdcurBZdZnQWnkv7a7hMHtQnqE2fSfOSQkhnOsrHrjYLcqJtWPG1kQ6SdWrOXJLPrHlHCHUqjjXNlj/Y/KNOSftm0FKMZru8bdMH/Zzvef3LYgx5+nP89ZQ/2idbIu1m5/5q1fI80Ua8SQnNnEeLqEZXq02ScOMMRqHSfninSXixiQbJgK+UWk/U2R/uqoYV4cXQ54V6EedR83lxwmyk8yzXFB/CI+9v4Trfh5v3ae7zQyfoB574eDsA7WivuGgjN/K8OKAlJLOS/eyvqsGy3RVx24OyaCPvv7hkNrIJWWeheO7KnClOYSXJkLT+Ttu5CXXUOwwHBgQdSXTrvvg8LkY8PG8Lrh3SyUqVCE6MZBMec52edo8r2kgEnvh4G17/cg/zMUTiOWf656rtR+6wm7j6mIU0LzbRoGM2qq3X7gBKD9vn9GpFeBENNC80AXIHvwd+KQUyURTJDyYB8LQbPS1NbQP7xKsxLHILLtEui5pE4tC61wKZjVITmc+L4sGrdQ6iKGLrvkqM+NNi2+pgyefFINqIcB61DsMJnxe3EEUR17y8Gte+vJoEGIfhaTfLVUwIUXi0sA0m2qrRfIvMRolDPvn1/vtJZiObkPq8xM02NY75+Ct9Z01erLzo0n5HrcP67uBxHDpWizYtMsxfhNBFrZ044fWfyAFBWv2jJ+qxtvgwAODgsVrkZWcmriJJjrKZ2CUc8kQbsSKtm1HYPq2pljiCElWGB4JnDSHNiwk27D2CP/9vh0ylKh1kWDoSUbR/Vm3NYTdSF1EUNcOt/7hgh+nzE+ZIJs1LMNjUPsmvylnsajZOCC/3vr3J9nMS1hHIYTf5ueqllQCAZukh3DP6dAD6Pi9abph2LZ4WxZLPy6n/l+w8qFkmmhDLDw3bj6gJuXa3ESCxWUtlfuwG2j2Cnfg+xv0Mu6x8sqUs9pnMQt5BOn74Yc5EmhcLfHfwWOxzo0TPpuw4tFS4ds+qrSRIjNZxz6HjNtWG4EXaGqLZi/2wuqseosYXnigWQs4/l3+P2np9R1q7hF4nNC9SDLssEm4ShnT88INLGgkvFpDaY6U+L8ppkVpDEEVzDm56WAqVPtVHtW6erlnGDw06WTj/zxFHbrvbCJDY8UAquFM4vj384ZNvZKZdtXtp36rSzqZ5oLWLvEPAZ2YjEl4sIH3vpDMdFoddEaLts2o78ryEAtpNQq221TX1eG9dCY6eoNVfreKFhRntYMsPlarbpb/E6Rl9KvH4x9vitnnZ50UKiS7eQSDNSxPTp0/H4MGDkZ2djby8PFxxxRXYuXOn4XFLly7FwIEDkZmZia5du2LWrFlOVtM00hdPL1RaqyHYbjaytDxAhLpG45mW9Pc8PHcLfj13C/7fmxtMX5vQxgmfF6e57PkVsc/S2ks1L3q5jwg+3l5TErfNyz4vMoxCpZ29OiFBvjyA9/sdR4WXpUuX4p577sHq1auxaNEiNDQ0YOzYsTh+XNuvori4GBMmTMCIESOwadMmPPLII5g8eTLmzp3rZFVNIZVUGzSEl9qGRny+Y3/csaJo/8AUtPA0o41Vr7NSa9BR57u1ew6bvzihiRtrGzmFtPnUGPhsEHzE5Zayy2zksG+SUVMks1Li8FuGXUejjRYsWCD7/vrrryMvLw8bNmzA+efHr4QKALNmzUKnTp0wc+ZMAEDv3r2xfv16zJgxA1dddZWT1eVG6vMS1og2mj5/B2av3KN6vO2h0pbMRpH/yRfBOzQ0hvHZN/GCr1USOhzIoo2avtQ47EuRasQnxrTnvE5ryKLCiQ8m+kmBXsZkqebeD48joT4vlZURW3jr1q01y6xatQpjx46VbRs3bhzWr1+P+vr6uPK1tbWoqqqS/bmBVPMiVdm+tWavankR9jtjWpmlROustqJ0FD806GTileXFmLM63iRglYSGSkNdqK8hs5Gj2KXVTZTmxQ8Oon6nqqYeZz2xSHO/qDHR8CoJE15EUcSUKVNw3nnnoW/fvprlysvL0b59e9m29u3bo6GhARUV8Wmsp0+fjtzc3Nhfx44dba+7FjKHXR2fF1VE+x12rS0PEPmfNC/ewe4MzG4gfRekQj2ZjZzFrr4lUUkSfTBW+p7FOw7gpM57t7n0KD7bHtH0+uF5JEx4uffee/H111/jnXfeMSyrnBlGpUC1GeO0adNQWVkZ+ystLbWnwgxo+bxI55h6jcBLeV7CTD4v5s9PEEqfF1EUseWHSq7F/wg2nFhWwgkM1zZKTDVSgnodrToAvL9pH+54cz2KK477QhOWkAy79913Hz766CMsW7YMHTp00C2bn5+P8vJy2bYDBw4gFAqhTZs2ceUzMjKQkeHWejtNr5Y0SR1LvyHC/g7GjlWl9aKNvN+cCRYSmudF8lkWbdQQxuyVe/DEx9txQa88vHbL4ATWKvnxg8Ml0OQ3qL3ScQIrk+Q0MJoASw+foLWNRFHEvffei3nz5uGLL75AUVGR4THDhg3DokVyu9zChQsxaNAgpKWlOVVVU0hfrPoGddu+Vh9iNkldfo72YnZ25Hkxks4J/+OFaKP6xnDMkf2LHQfcqZDH2VlejR+OnGAqq3xr/ZIfiISTxFHP2CZE+MMHyVHh5Z577sGcOXPw9ttvIzs7G+Xl5SgvL8fJkydjZaZNm4abbrop9n3SpEnYu3cvpkyZgm+++QavvfYaXn31VTz00ENOVtUy9RqaFy3Hp7AomjIb6ZmGdPLLGUI+L97Due7D/bWNRNGaj1ayU3GsFuNmLsN5f1xs6ngnzUZ/vaa/7ef0w2Dpd1g1L/9eX+oLFwFHhZeXXnoJlZWVGDVqFAoKCmJ/7733XqxMWVkZSkqaIiqKioowf/58LFmyBGeddRZ+//vf47nnnvNcmDQgHwLqG7TXNlIjLJrzedGLFLFD86KXUdMPHujJhJ/u99Z9lbju5dXYVHJEtl06KMlWXoe10P5kp+Rwk8aFpR0cOlYr++5kckPBAeFX02xEXi+20cCoVf/v12U4VttgXNBlHPV5YXnpZs+eHbdt5MiR2LhxowM1sh9RFPHYh1uxbFdTJBSb2cik5kVH3LTF54U0L0mPEzLDDf9cg8qT9bjyxZWy7aIY0SK0bZGhWKRRJM2LDumSjJP1jSIEQd+X7kC1XHhx0mokCDCsD/u5Tvm8WD8VYUA9hyMLSzRgyOX3l9Y24mSxxD4fEASs+u4Q5qwu4Z4phUXRlGpXT0CxFCqNqM+L+Wgjv9jZCfupPBmfgwkAfvvhVgz6w2dYsLU8TvMSCpLwokVGqKlrNhON5bTWzspESUr0LGuLD6nvpyZiidqGRnxTVoWSQyeYNS8Am/D7+S9HWqiZdRISbZQslB4+gVtnr4t9F4RI4h8zmHXY1es0rHQoUS2QFc1LQ1hEOs2mbcNHViNNok18xsKdmHXjwNj2iM8LzZ20SAtKhRf+d9LJ/CyCYJ8xJ9plPT1/h/p+m66Tqlz5wkpsL4skbr20XwHzcX7oeqj34OCHIyfjtqn1EVpOisrjjDqYy/sX4vwe7WTb9OQTS2sbnfpfT/NixPcVx3DcB7bSVMeNASEjFJBpA0SIrqud/YIZ4cVRsxHs1Yj4ybfLb0QFFwBYuvMg+4E+eCYkvHCgfMkEqD9jFs95lmijszu1xJu3DYm7phZ2+LzoaYOMftfFM5dj9IwlputAyHEqAsMNVXx6KCD3BRPJYVcP+VIKjdwCp5MCgSDY50hrl+8MYQzPUg9+8AAg4YUDteepNsAwJaljEF7UIot0zUaWfF4i6NWJ5XcpHQcJ8zjVqbsRwZERCsRHG5HmRROp8GFmcURHzUYQbFPfCRB8kw3Y7/Bo1Vnaj9uRYCS8cKB8xwRBUJVQWfoNFrORmpzilM9L9Mclai0TIrVIDwXjzKnksKuN9C2sbWjkXkzT8WgjG89FXU5i4LnPfhgHSHjhQKllmb1yj2qEDWu0kZlcDLo+L5bWNor8z1In7zfr5MCp++yGtSZe80Kh0npIX0NzPi/Opji0sw3p1ZX6GjbWFh/G1n2Vtp3PyTxBdkHRRhyoCaMPvLc5bhvLYw+LxgV5+wdLPi8w1rxE27MP2nVSkEyOjOmhgLzdiO7nifA2ErNRQ5i7L3A22shOnxdBtz9JolfAMQ5U1+Dn/1gFANjzzCW2nJMl7UWr5u4u10OaFw6YBxNGnxdDVIQRPfUxr2pZXp/I/7rCC82DkgI3NC//21oucwYnnxd9lCtw8+KsH4lgWxsSYKR5oT7HiP2Vcj/DrfsqcdnfV2DF7gqNI4wxSuMx7+7hyM50V3ghzQsH7LILm9nIqJRa/6DXZ1gKlT5VGd2OhPoRR7jkueXo1q5F3HbHzEYuONo1hEW8vWZv7LsoighRnhdN5D4vHguVFmxMUidQn2M3d7yxHuVVNbjx1TWmz2GkuevfoaXpc9sF9R4csM4Cfj13i6H90Wznotdn2LG2EU8WRsIetv1YhY+++tHtaliCRYmy8rumLKq0MKM+Mp8XM5oXhx0ubXPYBaCXtZ56I2Puf3eT7PvhE3WWz2kkvHjh1SXhhQOeWcA1p2yQWrCoddVkEV3hxYYWRWGLHsI51YvtsAjOsmgjkPCih3SiVNMQ5jbTOO2wa2cboj7HPI1hEd9XHJdtsyN/kp7DbiggWHJRsAsSXjjgeceO1+nPlphcXlR6CD2Vv5VGy5akjkgkfrrfvE2PNC/6SPsHM1oUR1eVtnV5AP08L8nktO4EDQq1lWjTgqd6bc4r7y0JLxzY+RqZ1bzoYcfCjLodJfUjCcWpjtuJrodlOKPlAYxZvOMA/vDf7TLzrZnIISfH/EiotH0LM1KXYx5l27j07ytwzIYlWvQmsWlWnCtthBx2ObBzMAmLxudTddjV6TNsyFGnO2Nbu+cwrRxNqMLS9qRNR6l5ufm1tTirY0s8eFEPB2rnH6ILv0qzob66ohj1nL5oTr6ngmBfxFpkeQB+6eVfq/bg46/K8M9bBiHH5agXN1EKGdt+rNIoyQdpXpIMO/uDapOrUetGG6kuJ8B2XlaH3Y+/9rdjKWHfrFl+TuMySod3qeZl6a6D+Nvnu+2ulm/ZX9UU/rrvaPyCsEY4azayV3unr3lR3/nYh9uwds9hvLLsextr4j+cCrAw8nnxAiS8cGFfQ9lUctRQGFIdEDjWNgoGBOTnZDLVJ1oVI3NWscI5jHAOH/nrMpqNpJ9FBClUWpMF28otHe+o5gX2OWzyrm20v6oGr39ZHPt+wsC3MNlR+rzYd17va17IbMSBnZOZypP1hsmn1B12tbG2tlHkP8P1llxejCuVSDZfRUWCXVrbyEEcte7aqXnhzPPy83+swt5DJ6SHpzROZVLWE35J8+JD7G4mRlkM1d5M/Twv5usSrYmZxSIJZ3Aqu6gTz5DJbKTwebEr0RkRj/NrG9nosKuX50XxM6SCC+Gc2UhvbAp5xGHXG7XwCXZ3CMZaDrZtUayo86K/zchWTsNN4kg6zQtDtFGqhcbur6pxZPbsbIZd+5YHAGhhRrOEwyK+/Nb8EgBmIc2LD/FCv6pXhXQLEnEs2shAkqfJsv9x6xFKW9axmgY8v/jbuDK8UTV+ZuV3FRj69Oe4441IhJGt0YyO+rzY14aCAX2fl99+uFW/LincH32weR8enrcl4dclnxcfkuhMkGqqWb0qZKRZEF7AqHlJ5d4iSXDrGUrfnw82q0et1TeGkR5KjTnVayv2AAAW7zyIVd8dsnUgdjzayMZQaT05q6yyBgeqapCnEXiQyv3Rp1utOXWbhYQXwhDeJpIRCpq+Fsuq0oRzDO/WBifqGrG59Ghsmxc0fXbC8nuk+U2SHamm5bpXVtt6bmd9XgQux/0rzirUFFYBY41TvU6f5I1h1B2y0sz391bwiqN9akxxbCLRg4napEKvChkWZqyswksKT3SYqKqpx7R5W7Dm+0PGhSUEAwJm/Ky/Q7WS45rZiOEFSiWzkZPaEUcz7HJqXsb1ydfcJ4oW/XNSuD9yS3jxSooDb9TCJzgV/WEXSnU7z3stMjrs1jWEsZpzYE4l/rxgJ95ZW4JrXuabSQcDQly0mFvOq7++uBe++OVIrmNYNHYsvyaVNC9OKjmd1KDy+rzoCToR4UW/rqnmxM1KVro7wksamY38hxc0L3oozUY8x4uIOPkZ/caZn1EWVD32HDKXxC8oxCf+cqy5GbSLZulBtG6eznVKlneDzEZynHSqddQ/T+DzNTEqa6WuqZx3yi3hxSs+L6R54SDhwovai6lTCSuOjqJonxo7lWdK0p/+/95cz3wvggHvdMNZ6UHuQYGl7bCZjVJDeKlrCDuqHXHc54Wjeejl8xFhPGHS25/KZuxU93khzQsHiY824itvRXgJi6Jtnakopm6nIjUtLty+H8UVx9G1XQvD4yJmI4XmxaHmZiSYNEsPQuBsSvaZjZJf8D1R14ChT3+O6hrrq/9qwfouRxZG5Ds3r8+L3kRdFK2ZuFK0mwFAPi/eqIVP8Hq3aslhF/bZyRMt5HkJ5U9nvafBQPxs1q0Mu83Sg7qDwmOXnmHqumQ2irC2+LCjggvA7k/TpU1z7nNHfF7YxAZBMNK8AH/9bBd3HaTnT1UyXTIbecRqRMILFx4Yk3WT1HnEbETR1k2w3gtV4cWl+5iVFtL1UzArJLMIY6kgvCQijw2LiS4zLYDnrh3AfW6eDLuGWlgRWLLzIHcdCPccZ4MekRhJeOEgERqFi85oH/vMnaTOUqco2uZASJqXJqT3Yuu+Smz5oVL1uIjw4ozDbv8OuSjIbUryZdT1ZKYFHFkAlE3zkvxtx0ombFZYNH7/vGkwOrVpxn1uI22KlICh5oXFV0qnLiltOHIHryQGdPQtWrZsGS677DIUFhZCEAR88MEHuuWXLFlySqqX/+3YscPJajLjdLf69eNjcdOwzrHv/EnqrPi8MCwUyUgKyy5xnXF0EKmpb8Slf1+By55foXpcUIjvhu26j7ynUdMCSREEoEUGv7scmY0ipCVAeGF5lQMCkGbC+VIA+/o2ASHel0vKuj1HDM+hJ+B4ZBx1Bbe6WY+sy+is8HL8+HH0798fzz//PNdxO3fuRFlZWeyve/fuDtWQD6cH5ZzMNJlKTj1JnXYlrGXYJc2LHSh/evT7ibpG3eNCQf1O3s46KS/TqXUzjJVo/AKCfgbVgAB8cM9w7nqwtAsSXuyB5V4LgmAq7FUQ2H+DnUsJqJ7fuVN7Hre6Wa+sBu9otNH48eMxfvx47uPy8vLQsmVL+ytkkUQkqQtIOhNelaglnxfY6fOSusKLkui9aDAYlAMMfgQBg3VgtFC2W2W7EiFiUJdWWLh9P1NdBEHA6XnZJuphTF1D8redRISaLt9tvNpwQABCpiJHBKQx9jW8YdVq6HYnHhlI3cCtpKleEV48ogCSM2DAABQUFGDMmDFYvHixbtna2lpUVVXJ/pwiEY6oVpYbt7o8QINN/gap7LCr/OlR4aW2QV94CanmeZGfzSlfE1GUCzRGZiPz9TBuGA3h5Ne8eIWASlZnLe4a1S32WRCAdEYBjMc/RguaDKmTiNvyuUqmbY/ILt4SXgoKCvDyyy9j7ty5mDdvHnr27IkxY8Zg2bJlmsdMnz4dubm5sb+OHTs6V8EEtBaZ5oUhR92FvZvU/WqaF1bnqrAo2tZJpHKSOqX0EhXkjISXgJrDrhhfxgxKYVItqkm6LSDoa/3M9l1seV6SX3jxyusR4MiUmybTCPOZjawLL5H/1foVj4yjrpCIZqTmXO4VzYunktT17NkTPXv2jH0fNmwYSktLMWPGDJx//vmqx0ybNg1TpkyJfa+qqnJMgElEY5H5vDCUb5TMVNUaGrMgIQJ7Dp1gK2tAamte5D9ejGleDHxeVPO8yDGrlDNqA8r9ASPNi8kpj11LCPgd76yRxt6gpInJBEFgF17Art3RIto+X/tyT/z5vTGOukMCXha1+0t5Xhg555xzsHu39no6GRkZyMnJkf05hZNrkUQJGmhelEgjhKysOSEC+PybiM9Dj/bGGWH1IDVvE9FoIybNi2Lb4eN18jI29dTGQlJ8XbrntZDtd4pUEHy98nrwdBdSPx1ezYvV0Nro7frjp/FRp6kaKv3Gyj0JyY+j9q57RfPieeFl06ZNKCgocLsaABLTsRoJIMqOr12LjNhnK52EKIo4VhvJ+nnu6W1NnweICC+VJ+px6+tr8dKS7yydy2/E53mJ/F/H4POSqGgj5Yw7YjaS+LwoFon8xciueODCHk1HO9h56WmJRFHEP5d/jw17Dzt2/UTgHeEl8hynXNQD4/vm65YNKSZVrP51kVBp83UEgP9tLUd9YxitmqfF7fPIOJpQNpcexe8+2obPdxxw/Fpq45FX8rw4ajY6duwYvv3229j34uJibN68Ga1bt0anTp0wbdo07Nu3D2+++SYAYObMmejSpQv69OmDuro6zJkzB3PnzsXcuXOdrCYzidAoyBuLkQsnkJ+bidm3DkZOVvyLzUNY4rBrNQOoKAK/nvs1Fu88iMU7D8qc/ZId5fNpMhvpCy9Bjqyl/HVSRBvFaV5EWUsTBHnLa5Eekg1ATnZdohjRcH5fcQzd2rWQdZTvb9qHP3zyDQBgzzOXOFgLZ/GK2SgqvEweE0lF0eXhTzTLhiSaFgECc34YO3xe/rJoF0QALbPSsb+q1tK5koHyypqEXcvLZiNHhZf169dj9OjRse9R35Sbb74Zs2fPRllZGUpKSmL76+rq8NBDD2Hfvn3IyspCnz598Mknn2DChAlOVpOZRAgv0hed9Z0f1TPP8nVFNC3MmGZx4a2wKGJHuXNRX15GqTkIixEn1A827dM9LhgIOKYCF0X92b7SYVcZbSRCvt9MYjNWRIh4ev43+OeKYkwe0x1TLuqBf63agx+OnETlyXrHrpuK8MgU0mfOleeF8zpafPzVj2jVPN36iZKARCo+vGw2clR4GTVqlK4aePbs2bLvU6dOxdSpU52skiUSbTaKfvr9T/rgsQ+3AXAukkcUmyI9rCbRCovGZpJUISyKeHnZ93jfUHiB4UrOZh+9kdCt6vOi6KCk383lBmEjLAL/XFEMAHju892YclGPWNvPy87QO9Q3eM1sxILSfMCc58Ugwy4rjaKIjq2aYW2x3GTojWE0sSRSeFBbx8gji0p73+fFSyRC8yK3LUc+TxzWxfT5WO2TIpqcf9NCFkMbwyLqUiDkVQ1lC2kURXy6tczwODWHXbtQ1kl5nUieF2ld4s8h3e9kkjW9V+xANZkM7IRL8yKLNmJfn8muDLvhsIjmGfEZxD2iBEgoiUzPryYopYTPS7KRiBmT2VweVhFFsUl4sShaiymseYlfHkBkajdZaUHHOgXj64uyays7LKVDr5MLC3rFH8RJ/Kh5UQqszD4vnNfRYs+hE9izam/8+T0ykCaSRP5mNW2wV3xeSPPCQUJCpTnzvCi59dwupq4bybAbNRtZDW0UU2J1YDXiMuyG2cyNOZlphp2C2YFdaWpU9n2NYVFWRqkqFiHK6hZyUHhR3quV3xqnufcbXhHQeAYhuTmbPc+L0cKMBD+JvJte9nkh4YUDu9b+0UOq9DDTRn53WR/Zd1YfGRFN0UZSe/bonu2467B010GcrG9Kyna8tgEPz/0aF89c5ktH3o0lRzD5nU0oqzzJfWxYFJmeQXZmyDmHXcV35XUawqJMaFBXFTd9dnRtHsW9+t1H25y7lkt4RfPCM4MPKcxG7HlerIdKE3Jc93kh4cV/JGZtI75HYleVImajU5oXSR3MnP+3H8oHnCc+3oZ315ViR3k17pqz0Uo1XeGnL67ER1/9iIf+/ZVxYZVoIxayM9OcC5U2WB6gMSxfGkLV50VykFWzoh7K+5WIFZhTFdNJ6gT2dAp2JKkzOr8Woiiiqib5ItQSKTyoXcojsgsJLzzwRvpsLDnCfQ2ppNuoMvI5NWsLi/Y57Cr5+Ksmh9WKY/51utxTYbx8glqeFxZH75yskGGnIMq0I4anjGF0/QZFO1PVvEg+O+uwK6+Lk2HZieaVZd/jxn+uQU29/lIRiYLL5yXObMTu8+IketrKSXM2oN/jC7H9R/9pe/VIpCaLzEZJAm+00T1v8WsZpJNatVm7k/byqJ+KdLZrh7AknaU1T09uH3Hl/WoURWafFx6zUctm7DkvlHXKVSQ0VGpelGGxoijvsJwUKJT3ykn/mkTz1PxvsOLbCry3vtTtqgDgm0ErF4zlWR5AbRJmF3q/4X/bIsudzF5Z7Nj13SCRDrtqGXa9YgZMnp4hAfC+g2YibqSNxY7QbJ6G3mBTnhcl0lTizVTCHf0Cy63cXiaf5YVFtueYnRli6hSeuLwPLu9faJjOXYlU6P3FyG4Y2aPJl6lR4fOitvaRzOfFQbOR8k4lk+Ylih81L8pAAh6H3YawNyIPk2W1+8RqXtS2eeOdJOGFA15hwkyuEyPhxcn3r96maCMlqaJ5Wb/ncNwsUxRFJsehFhkhpk7h5uFd8Nx1A7gETGWn3SIjhDduGyLbJtO8qJqNJEnqHDYbSdtfMvq8eGUxQZ4xSLlgbLN0tkmIAIc1L4zlPvrqRwx5+nNs2MtvyvcaiUynoTb59Up4evL1DA7CGypdb0Z4MfB5UXJayyzNfbydpJrZyA5kwouPNS9KPvm6DJP+tQHVp5wCv1BZKC0sshn6QsEA12DCU5bl+nJ/Gn1VsZMChSjKB8qQV3TUSYjyOX82ZSR+Na4nQ1mBXXgRhDifKjtheQ8ECJj8ziYcrK7F7W+sw4HqxK0N5ASJeiW0Fgn2yitJwgsHvO+gmVwn0gbDomW5dnBH7mtoUadiNrKj25EmNfOz5kXZUd7z9kYs2FaOv38RWXxU7WVvDLNr7IxmNNKz8AimLNcXZdFG8eeWnoFHoBjWtQ1z2ch1RJlZKpl8XryGUng5Pa8F7hl9ukbZps+CAGQxvseCAHRr18J0HQ3PzzlBO3qiHkOe+tznTryJkR60XnMyG/kQXrORGXWpYKB5kbabEd3bGnbuPHZeu5LUSQkFBIXPi4+FF41O48ejkfwvasJHOMyWYZe7LjyaF4br6zZVUZS1RdZ1bQDg8rMKmctG6yHtNJ3M5usWa4oPuV0FAFaS1AHNmTUvEQfxtY+O4awdG2bH0Q8266815mXs0nwM7NxKd7/WZIo0Lz4k0f5easJSa8nKqnbXJ+pgbKdZIBgQZB1fMjpg1p66b2ovdaMoOhIhxnMXWa5uJJhLEzTy5HnhbaOiKNe2OJoQzyUqjtW5XQUAfL4L8mgjAVmMwkt0lp6XnclXORvxcq4St+jfIRc3DeusW0bN9w0gnxdfkmivebWOvyBX28fFKk75vDSXaFva+HhZe613Nho9oqZObQyLsK3ZSP1SOKY/LAKEXhkRQKPEBOqkQBEWRZlZysnIplSHy2FXUGpemt7p/BxtwSRRw9x/v/4RE19dg0MqeaTeXRcfmu4Vp2kz2DIVYnj4WkXIbORDausTK7yoLUfQXqejUINHSq4Pq/i8WFTviDDnuOwnopoXNZ8X1uUBtLjjvCLV7Xz9B5/PixrStui0E20oyTV1XoErVFon2ujtO4fK9klxepYebZb3vr0Jy3dXYOAfPsOJugbc/dYG/eM8sr6UGkaBIXZo3AUYP5sTdeoh/V6x5HqkGv7ATOizlJbN0jCmV57mgKREzeelT2GOpTroEX0pbB0wRLnjsp9TLRyoqsWtr6/FYkVUUVR4UesLlDlUeLnxnM4YdMo2feWA02LbeWaOVn1eRFHeFp0ckMJhEUFJ+9OKeCCsw6NBC8g0L3KzkbRfVJoanJ6kqwkhV7+0CvO3lOse94+l3+OkxuDsJu+sLUG/JxZiw97DsW0LtpZj/Z7DklJ25P8yrxUjs5EPsap5ubx/IV69ZXBchlMtpLPhl244G5NGdsMlZxYwX69ru+bcdQTsNRuFRVGWrM/HsgtO1jdi8c6DuHX2Olm0Qq2O2eiJj7ejvEo/NPPPV/fT3BcQBLx6y2D8/boBePzypkU3efoPFkdz3iUEnEKEfABMxjwvXoFHgxaveWkyG0ln6EpzptMmBrVmq0wUqcXC7foCjhtMm7cFx2obcN/bmwAAew8dx6Q5G3D1rFWxMnZMAAOCYFqwJLORD6ltsCapRx85q7+CdLwYf2YBHh7fKy5Ntxr/ve88/OSsQrw8cZCpeto5YIiQZxr2s+ZFyoTnlsc+R31etBzc9PjtpWfgZ4O0w92j0RqX9S+UzXbtdtg1KtNo0nGHVz0vivJ2T3lenINHq6XsErRSOiifl9NPz0p34hUNghrR37XvKP9K9iwIMO/345VX0r9xqy5QayLdv5Toy8LaaZhdHqDvabn427UDTB0L2Gs2EkVR5vPiZVuzWfTMRkZkpOkLitqJouw2GxloXkzkLFIjOyOE6toG3XrIsv2Sz4tjsDpDXzngNNlAH/34wIXd8f3B4zi7U8vYvoSbjZKvOwEg+V2q69tZJ7Lat7ljvaJ5IeGFAzNrFUmJPnPWGbodabXVHDHvHFGEV5ZrL1Zmt+alNgk1L1KaQqXtf6m1zskz+9Fyxg0FJNlPDZ6LHetsAcYdpgj9mTxhH6y39rFLz0DVyfrY96gg88CFPeLPGad5cfb5WWmXXm5ZepM8exx2zT8Zj8guZDbiwbLm5VRzYTcbOTPSF+osKQDY6yQpiskfbRQzGzkw0GpOjnk0Lxrbz+rYEkBkrSO9tiZCxIjukYUc2+dkMF9XDaN7JCo0L2aoqW/EH/67HWu+90YyOK/CajYJCPFJ6rRQPl+vDHR+Qzd1gU3hRn7XvJDwwoFln5eY5oWtvB0+ko9c0hsAMKSodVM9DI6x20lSGo2QLCu7StFLUmcVrY7Cjkjp568/GzcN64wP7hluGG1U2DIL6x69EEt/NZrnynEYCy9Avw65se9m3oGXl32Pf64oxjUvr+Y/2CH83O4FQWDytQPUhBenHXb9e1/10E14bcP5Bcm/vHhFGUrCCwdWNS/Rh846Q2eNStLj0n6F2PjYRfj9T/rGthl1KNLq2dE31CdJtJEWUfOeEx21dpZL9nOERRFDiiJrDEnTuufnZuLJn/TF6XnZTM+5XXYGMtP4FtZUntfoHoVF0XBldSOKK45zH+M0CQrWcoRgQFAkqdN+hsr26vRAZ6V/8ogCQRV9zYv18+v5vFzWX39Jj0Suaq0H+bxwYN3nhc1s9Jef9ceX31bgqrM7WLpelNbN03H0RFNKcqOXVjrA2OFgK01w5pQpzAuYUade3Cff1Dm58rwA+MMVfXF6uxb4icZaQ4l6Lkb+XkqfFzPV8kbXKsfP7T4gyM2XfJoXhyp1CkvRRp5sKREqjtXi75/vxoBOrRw5v57PS76BadgrUVqkeeHAquYlquI06sCvGtgBz15zFtI5FsAzQnquRDe9sMXBKFGIoog9FcdNqaLDYZFrlpmVFsSO31+MNi30Owotnxeeaw3v1ga5WWm4/8Lu6NJWPfeP3m+285EZ1TuSkVhybS83GA78LbwIzEEGccKLRm8zrk97y/UCklfzAgB/WbQLH0oWkIy+C3ZMKPV+u5F21SOKFxJeeIgmI7OKG2o32eq8CX5rpemuvdyFv7D4W4yasQRPffIN97Fjnl2qupyDFgHBuJOIlNOINmJoQ1ed3QG/vKgH/nx1f8OyiTJrGNZblHfOpny9PdK5SknwsmiWiMvVIsjboV4zZ9G8XNKvAJPHdLdUx1hdPN2jWKfk8InYZ73waV7O6thSU4MyvFtb3WPJYdeHWNe8RP43k8zMKlLNS6Jns9JZp5cnoDMW7gIA/HOFdhi5FsUVx2VZd41gVb1aiWDq2DoL943pjlYMi2E6HZoZxajjU5qN/KyxkHL0pDdWkmZBKWAGFQ67em1F2Vy12rldJpskaR6aSN+XcEzzYo1fjeuJyWO6az6BYd3a4F+3D8GKX6s755PmxWc0hkXbUqS7sV6LVHjh8d2xo3OQ37bk7W14ZFLWolrnZLkWzwCRKM2LUdsPh+VDox2CdnHFcby2ojgW0u4G4/66zLVr86LUvAQEQeFErX1svNkoHgH2KX+t+bx4H6nZOKZ4sfhK3DP6dGSmBXWfwYju7dChVTPVfV7xeSGHXUasOusCTS+sG2YjafhzotapUSPZZ0rMMDYBKw67diSyA6yp5pVHMliNZHUxc2XlvRk9YwkA4PDxOjw0rqeJM5rn+4PH8M8Vxaiq0c4q7DWkmuHFD41CQBFtpLfqsbK9aj1v28a/JO9Q1DUviUkYqQWZjXxGZloAG35zoaVzuGk2ks6m6m0QxMxipa9ZvvsgXjNh0vEirC1Aq62wCCZc4dQJahJGHZ/SYddOs9E62cq8iWHiq2vx9pqShF/XCtIlGYpOOXhLH5veI1GuVK02SxcEK/ld5VjSvHhjDNZl+e6K2Ge75TTWZzD3ruGy72Q28hmCIBhGhrBiVw64HI48MNJOpL4xjEcm9AIAPPPTM+2pDCNmZw3llTWY+OpaPPnf7di9v9rmWiUeVtWrVjGW2Q+PejdRjo8sSeqkNTGjJLRyz+xkw94jji2s5yRqArPMYVenrcStbaRRzjazUXIrXmREf6ttv5nxGQzs3Aqzbjw79j0lNC/Lli3DZZddhsLCQgiCgA8++MDwmKVLl2LgwIHIzMxE165dMWvWLCeraDv/mTRMc1/MbGTx4f/t2rMwqHMrPHbJGaaOrw+L+H/nd8NXvxuLa4d00i1rd+dg1mK1fm/TrPlEnXu+C3bB2gQ0HR7t1rw4NAgoQ2KNBCpRFGVmIz0ThRrVNfWay1EwrkNoG7/414bEXtAm1Mza6aEAJpyZjxHd26JTa3VfCCBeONXq6+wwwwNWhW5vDMKs2OWwG4Xv1zeV9ojs4qzwcvz4cfTv3x/PP/88U/ni4mJMmDABI0aMwKZNm/DII49g8uTJmDt3rpPVtBWtmWWn1s1imQuVqlVefnLWafjPXcORn5tp6vio2ciODL68mBWGGn0Sbs1KIt5/PoddvZSe5uuQl52J31/RlN3ZSOsoQql5Yb/4sdoGnPn4Qny4+UfV/YlOSlZncTkRt9AyVb54w0D86/ahugJoXP+nUlSA9aVWovgpz0vliXqctDDxanLYtcvnhf0GSIt6RfPiqMPu+PHjMX78eObys2bNQqdOnTBz5kwAQO/evbF+/XrMmDEDV111lUO1tBe1B9suOwPLpo7WLZNIXHXYtUH0SIbEZVY99lmO57mEQ7ILACA7o6mbMfL3ik9Sx34do1B1K7d8+qffoLqmAU9fqW1mPVbbgBaS3+qVqAxerERDKn+z1plq6+3SvCSO+sYwXltRjMy0IG4e3oXr2KqaevR/ciEy0wLY8fv4MfFAVQ0OHqvVPYebmhdpWbfHryieijZatWoVxo4dK9s2btw4vPrqq6ivr0daWrymoLa2FrW1TQ+9qoo914YTqD1XZYftRqi0lDo3V3kWgU++LsPm0iOYNr43c+SVbEBzqGqJxGoLYHLY5ThfZppzSljZrM1Bn5c0A42mWWGiMSziH0u/BwBMOr8bOrWJN5t8sWM/bpu9Hv/v/K7Yuq/yVBIwU5dznWbpfOtXSVFLcKdazibHv31HTnKbFqPwPp5315Vi+qc7AESyoEsFVSN2lEX89GokQtvK7yoQCgQwpKg1hjz9ueE57ExSB1iJNrLn+lbxlMNueXk52reX28jbt2+PhoYGVFRUqB4zffp05Obmxv46duyYiKpqoiaVKh+2G9FGUniijayauJSIAO55eyNeWV6M/1tfynycXxLdsWK1CbA57LKf78GLeqB3QQ5+/5M+FmqlVY+mirAlqZM+a/aHbbQautlOV2qyrGtUV/v/7qNtACIrWq/87hBeXPKdbdqFRPO3awfgtJZZ+Os1xpmZlbD6vAzu0gpXD7S+dttHX/2IRz/YYurYe97eyNW+KiXrw1mN2Kyqqcf1r6zBz/+xitn/x26NM48ZVfoOe0Wj6CnhBYi/MdEHpnXDpk2bhsrKythfaSn7gJgolLNNt1fl5DEbTTizAGd3aokR3fVTRrMifQEfnrcF1TX1jMepn8O/WDUbGZfhUe/mZWfi0/tHYOKwLszHtMtmi76T1sJwYUbFs+XxeTEWXszdc2kdeCb5J11MimeFMwpz8OXDF+DKAdaFC808RYKAGT/rjzG98ixf45215vr8+kYRew+dMC54CivaX2W7PnysSRBibeOxaCMX8rzIzUa2XN4ynhJe8vPzUV5eLtt24MABhEIhtGnTRvWYjIwM5OTkyP7cRNVspEy37SOzUXowgHl3n4tHJvS25drK124XQ9hzdU09fvnvrzTP4UesTl6khxdqOG6P6W3X4nfqd/yT+85jOl46gBlF/IiicnkApksAMNYSSl+7Y7UNMo2KHjJn8WRofA6ivD9q7byzitmNFbtTO/CsRybF6gTKjOk+5vNil9mIp2yqOezyMmzYMHz88ceybQsXLsSgQYNU/V28iLrZSFuVGhASl5o9SoOJF8euBqt88Roa1X98Q2MYi7bvx5Ci1vj3hh90z+EkPPZ0nnpZvpuS57HkV6Oxa381Lv37CgDA0l+NQmZaEO1zzEWjKdH6XXmM5+fp+MKiKJtZ8mhejNtoZP/B6loMfuoz9CnMwSeTRxiet1E0rg8JNRH0ntfbdw7F/7aW4+5Rp5s+f16OPbm2zCBqfGZBaTmQmoqYNS/R/13w2JX7rdl0fYs4KrwcO3YM3377bex7cXExNm/ejNatW6NTp06YNm0a9u3bhzfffBMAMGnSJDz//POYMmUK7rzzTqxatQqvvvoq3nnnHSeraStq/Wecz4tkQ1owYHnBR154BrXoe2JXYj3li6o1+5219DvMWLgLvfKzMeHMAnmdEjhS8AyePGWt+7w0fU4PBWRtKi87E1kWnC7tRlpXpiR1JqONjNpF9NKLdxwAAGxjXEhTKsAqn3FZ5Uks312hmVsm1ejQKkv2veupDL1AZLVioxWLjUh0uLsUO7M9S/t81tPaHW3Eg/S+p4TmZf369Rg9uilEeMqUKQCAm2++GbNnz0ZZWRlKSppSZxcVFWH+/Pl48MEH8cILL6CwsBDPPfecb8KkAfWXS/mwpXb/RAovb942BO9v2odfXsS/vottmhfFdy3/m3fXRezYO8qrMb6vXHhJpKaKR6285nv29PNWO2Hl8dLIB7sjh1h9W7Rhd/YLx5mNODRfBvtjbZjz1jfomI2ufmmVL7Po2s3rtw7Gou378dtLz8DJ+jB276/GoC6t7F9LKgHjZnVNPfYeOoG+p+XKtpsVqpWIoijLc8N8Kpv7PbN9UEoIL6NGjdKdDc2ePTtu28iRI7Fx40YHa2U/LTJCOFYbWXhNbWIZ54Ef0N7nJOf3aIfze7QzdaxtDVbRHLQ0L0eONzm0KR3UEpXKHuBb8+f7iuPMZe3UvABAx9bN8Oer+6FtdoZt0QCzbhyIz7/Zz53TQolM82JQNRFysxGf5kV/P4vsIopi3P2Tal6igkz41CrzJLhEGN0zD6N7Rpxv/37dAMeuk4iB88Jnl2J/VS3evG2IrL+ULxgqb2wvL/sOnVo3w8V9CyCKIv7wyTfIzgzhgQt7xB0bFiPOwmrn1SPaDO1LUsdTuOljSggvqUJuVlpMeFE3GykTN0k1L95oCEbYJWQpX3otdftxvUyUjO9uY1jEXxbuxNCubTDSpNBmp6pYitW7qdbOfjbI3jQBF/fNx8V98y2fR+7jxWc24rv/RmajyLW1hLvnv9iNd9aWYq4ie7VU+9Z4Spq95uVV2FHu/zW2/EYiesv9VZG8YZ9uLZcLL9JCki9f/3AUT8+P5H/Z88wl2FhyFK+eWkA2KrzIDhVFhc8LW72ifacLLi8yAT4r3RtOL96ohc9p2azJmVitY1Q6OEmLGIV3egW7wruVYxFLxIfyGOURx2obsGBrWVzq7Xkbf8CLS77Dza+tNVHTU/VzSnhJQIbdRNKqmbZDvVTwNU5SJxdvbRUeT11aqwozFu7CvqMnMfOzXbLt0jYanTGv23ME1TUN9tXNAWbdOFBzn18mTUrsbvbGWYeakOWakmyvUGTGXSFbCTq+/YZF+fII/JoXpuIyfnYqp84DF3aPbePpQ6Tvbb8OLfkr4ACkebGBCWcWYNuPVcjJVL+dytwW0tmn3UngnMIu61ZctBGL8KL4rhzQ7n9nEz7fcQBXnFWImdc2qax/OGJdpW82e6fTeK3VdG+fjbXF6j4/0o7PqB1FfF7M5VUx6tSbNC/65ZQCtdR0yBpe7QXUtGY922djVM92tmvpEoWbJgstnxdlu3t1xfeyfcoq/2v1Xvz+v9tj31nbVNN7wd8Gf39FX0wZ2wP5kmANnls5sns7TB7THcO7tfHMhNsbtfA5Q4pa4/27h2PxQ6NUO1DlbFOmefFK3JkBdmUFVpqNjF7crLRgXO+gvMefn4oe+UCxIJ8dw4xTg5XlPC8e0bz0LojkVbrt3CLNMtK2Y2R+/OirH7Fuz5HYdx77vrHDrrnzSLVvfo8qatU8DdMm9MbpeS3croop7G71PO+RtF08fiqjshpVEo1c9BjpdaSCC8CeNDSWpM5kl1SQmyXPlMtxbCAgYMpFPXBOV/V8a25AmhcbEAAM6NQKAHBEkkI6SrzPSxNekWKNsGuw5NW8iCruueze+dYFD6cm2lZvZxcLib7s5J07h+LbA8cwqEtrzTJS+Zx35myn5iV6ZWkd1Bx09UybWnmJ3KJru+b4/iC7o7jX4H4PEiizK9uBVOO7YFs5WIgI34KuEM4rvJhB7T57ZP5jGhJebEDaCNQamFJrIf3qF7ORXQ67yve00SCcR+nAGTkH48uus6+uIYzahkZkZ+onP3Qqp4zVUOlBXVpj+k/PRJEkj4YbtGyWriu4APL2zy+8qN9/VaHDQKxV29sYFuPeQeV5pHVwc0V2NfzRe9iHq5EuJh49yyGNjAKx/Xle/N16SHixAWknqta3Kd83Qebz4m3NS6fWkRm+XWajbT9Wyr4ba15UBiVW73ydcmOeXYLSwyex+bcXoWWzdM1yXtW8AMB1QzpZP0kCkAq+vEKw1jNU8yUwkjM/3PwjurZtgaJ2TQJfQ1hESJnPT0/zwhM7nwC8Yj5MFG7+WjPZlVnmPqxBATGPFxN9ktpkye9Nx9sjp08wagPKDlv6TbmEvFf4v18Mw/SfnokhRZFZtWBTSymrrJF9N/QpUdnNmudFr1zp4Ygz7xoNJ9MoXg2V9hNSny/eDlNzwFDbxvCo/vrZLtm9Z/FpkpZpDIu+Xhg0PU5S8xeJFNbiohwZHruybbD0VazLtTRpXvjbn6rZiPss3oKEFxuQvlBqDStOeJGUd1vtr8WQotaymb1dmhcl9QwqUzOdCGs5o4HIMeFF437+yu6MpB5AKqDztiOt+19y+AS27quUL5rI2KlLTQ9qmr84h11FqLTHLEdMXHV2BxS1bY4nL+/jdlUskUhtQbz50MQ5GI5h93kRmc+pRO22+V1rR8KLDUibgJpWWdlIWjdvMlM8OqE3rh7YAW/fMdSh2tmDU5mADX1eVIYk9qROxkx+Z7OuAOPUJDtdw1x4z2jzi9Z5FZ4kdUq0nvXoGUtw6d9X4KlPvoltY31W0iqohcIr24NUrX/0RB1uf2Md24UMaNM8HXkWl15gvZvXDemIxQ+NQhePTpZYcVNRzSIcm+kv2EOlo/Xgx++CihokvNiAzGFXTfOiaDe5WWn4z6Rh+PCec9GqeTpm/Kw/hp9ubcEyp3Gq7RtpTNUcdo20JZUn6zH7y2JUVNfqlgMiS9NHM2pqXd8JWmjkBEpGeJLUKTF61q99WWyqTlFYZr1SAeeFxd9iyc6Dlq7pFP+97zy3q5AAvGc2EjU+6x0jhVXzYkXjl3yiCzns2oLUGUo1z4vKyG8UoZFI1j16IQY/9ZluGafMRkb2XjWHXaN3+Jf/txmffXOAuQ56piGnzEbShRSTnSBHkjolTpholD4sSvTMRkdO1NtWD7t/mnIhwWTE1WAjhr7ASZ+X2PIAPva5shPSvNiA9IXqpJJ/Iz3k7dvMsmqwUyGK9UbRRqIY18sr312l0zOP4ALod4iOCS8Mmhct05LfkLYd/mgj9vvPWlQe+hw/cMTleXFwsEhCbT4nTTcgGtmoRyJDpeO0KMrvKu1CS/Oi180xa14sBLqp3Ta/C0HJ0Tt6iJzMNCyfOlq2LTPN3x7+gH1rGymJZiytqqnX7AyMOg2ruXL0cq449Xq3SDcWXvy6/owSueaF7zfx5IRjddiVCi8smhePRUfLYL2dhS2znK2IDZQcPmFYxkuh0kwBAbH/tQsz+7zENC9MxWWQzwuhirJddGzdDG0kTrkZHte8sMIyax7Rnc93p6ExjLXFh9Hv8YWaKbfjVbFyQg4useDU7IRF85KWLO3GgsOuI5oXzrWKnNS8mKVbu+b4/U+MI4c+mXwe3r5zqC+EFxbsHoP12td/NvyAj7/6UVJWcazKObT88/SaEPfyAI5NqfxFcvSOLqOeAKhpWzJoXgA2fwXefr6+UcQfF0SWk39j1V7V8xk5zlmNhNLrDJwKi2XxefHL0hFGBINSsxHfsTxmO9aSjUaaF8U1nVqc04pg/PkvR2HisC6G5foU5mJ4N28HA0TpzrDekt1mI6MncN87m2Kflc1AXVOsPtHSa8fMPi+i/P9UJzl6R5dRm/hLx9OMtOS4zdKO49WbB9l23qqTTU6Q/15fGrc/3vasMBtZFF4O6kQluemwmyw+L9Y0L8Zljhyvw9Z9lczCgLQcS54Xry0JIMXqMhNuM6BTy9jn124ZjF752Qm9Pt/rbRw4oDXRskPzUl1bj1IG01qqkBy9o8uodSBHJVEJGT7PahlFOvAUtsxCz/bWOxpRFFFV03SvfvWfr1XKyL8r33WrmpfLn/8SCzUWWnMzVLpHe3+u/KtEtjAj57Ni6dcH/H4RLv37CmzYeyRun5pA02hkNlI67HpYePE7d47oit9c0huLHjwfHVs3w12juumWt9tstOr7Q3jsg604UddgWFbp+8STBFNvEsTavq5/ZQ1G/GmxrxfitJPUidd0ELUXqk7SQ2YmieYlqEjzrvZC8tpjRQBVJ/U7jjhVrNJh14YIlhkLd6qWdUPzMu/u4XhrdQl+PT45su1KNS+8Yw+PyUYt/4ra4Y2GmheF2cjDenq/+2GmhwK4Y0TX2Hc9x9JbhnexXdP02AdbAQDN0oOYNqG3btl4kxBL6LT8fzV4NXvLdnszz1CiSY5R1WWMXic/aF5+d9kZAIDJF2hneJXKCAIEXD2wAwCgfwfz+SVEEThZ32hYRo8gZ1SO2vnKFWsusV7bLHrCy9mdWuEvP++PvOxMZy6eYKxoxngEB7WyqgK2gc+LEic1L343+9iN1t346rdj8fjlfVRN9Hbwj2XfG5od1UxC4bCIbT9WaZeJ/a+nefFwOJuHIeHFBoxmP37QvNx6bhHWPDIGD17UQ7OM1IFUEIA7RnTF23cOxVt3nhPbzjvYm5nVKo/hjTZSu2JVjbr2x85Z9wvXnx37nEoZdq2E2fPcfbVHpSZ4GCWpU+JlzYuSqJ9UZ5V8U34mmivLSWFv0fb9qtvDYRGvrSjG1z9Uxu2b+dku/O3z3bHvWlpiPfmkgScfABEjdXpQR9F/oby6crSS9jn6M32Z8ILIjNpqJIOZ19ZqtBFXBIuN/UpBy6b725whz0uyYCU7M1+0kZqWRe2cTZ+ZktQ5FW3kwDnfv2c4Xlz8HR7y6QKfWk0lut1JM9meQ+q+JB9s3ocn/7s9brsoAs998W3cNtl3xf9q8LYvf4wmzpM6PaiDGL1QdUkiWaeF5D4vavAO9qaWmVccw+/zwl7Wzlm3tJ7ZKaR5kQqXvHeT71mpbVMxJRktD+Ajh12lj0ifwly8cMPZGqW9j5ZmJfoznZwHaqUm2Lm/WnW7qrCs/H5qg14/YpRlPA6/OzrZhPftGT7AqCnVGvh0+AX5y63+q3kddmsbjO9NfKi0HCc1L3aOW9LLpuraRryyoJpmRAs15161BHPGGXb947CbbGiNy9FIRyczxZ6oU++LtML71ZqFcqK1+vtDqtulNDLmeYlCokuE1OlBHcTohaptSA6HrHSFz4sdVByrMywTHyptLdqIBzsz7Eodk5unkvAiSDUvfPez9PBJ5rJqQkajitbTOEmdonxyvL6+JtqCnFogFgD+/L+dqu+7Vvei1pKVsvZ972zCZf0LbY02cpLrh3ZyuwrMkObFBozGzglnFiSmIg6j9HmRclbHlgCAawZ35DqnXoK4KHEDngi8v+kHXPHClyivrDGRO4THj8I+pAOl1xfrtBPp88lwMPGe2higGgotGuxXfLcaDZKVJBm2E4HWmxydIFrN6WTEjIW74rZpCUxqgs51r6xWLasnn6gJ0EOLWmsf4CBPXdHXleuaIXWmfw6i5wHfPD2IorbNE1gb55AuFKjUNr37/87BtweOoU9hDh587yvmcx49wa95ESHiwfciyez+8Em8Ix3v+fSwMzX8wM6tMLSoNXrYkNzPr+RkpTl2bvWEdNajjaz4vGSEAshICximAzBLspkQtM1Gp/53IfhBS7Ou1iq2l1WpbNXXOKoJ0HrviZMuL35awJGEFxvQe975ucmRqwMAQpJZs7IPyUwLou9p/Ple6hmcmd9aUyL7Lh2jjtU2MHXg6/ccxvG6Rozs0c41n5e0YADv/WKYfSf0OF1UwnWdXOdLXfMSrzWRPn8jzQzAt7K1kpysNE3hh1xp2IkOqm5EbtqxnpJeP6IWKu2keSxZIOHFYZxc8TjRyHxebJrzmcvz0vR5yc6D6H/KZAUAG0uOqB5z9axVAIC1j47hShpop89LqnRHuVlpmHXjQJxRmBO3z8nxWj2ySKWcpAGpP1+Fw64FCTYnMyRbKkQJjVFK9G+I3QszsqBl6eTpGnQddlUaqZ55jJpMBBJebEDvfXLaRptI5GYje87JuqKqFKUK9qvSo7HPP31xpe6xFdV1OK1lFvO17NS8pMpAJQjAsG5tVPfZKQwqYde8SOtjfF4rDpXZmWmoNFj+wgrJ1qaMfo8b/ammKYVLeNHep9a+9MxjTph2Hp3QW/Od9SrJoxZwET2HvBBn6novo5UHwQpmBgYr45/Wmkya17JRV+Ane7JT2OlDpITZ50VSTn35APn3OgvRgtmZIUdzkyQb0lt15YDT4va7YU7RDJXm6Bv0+hy1PjDR5rE7z+9qyuzvJiS82EAznWypSaV5CdkfKm1KeLF4TZ7jPRTFmBSIAC7uk+/MuRmjjcIy4UXlPIrvLLmItAgFBEe1IyEHo7fcRq3vdMMKrxkqbZfmRcXnhcc8dvt5RewVSSIS0hRefPFFFBUVITMzEwMHDsTy5cs1yy5ZsgSCIMT97dixIxFVNUWGTtirX5YGYEGe58UmnxdTmhfzEgWv5oUSlLFzw6kcEQ+N1U5NHxaB568fgCUPjbL9+qp5XtSEl7C+5kW5SKeVPE2CIOgORFbfohYZyRWGLe1X1PpONyaDWtfkmwRpl1bbpyeTKmvTtV1yRLPy4rjPy3vvvYcHHngAL774Is4991z84x//wPjx47F9+3Z06qSdEGfnzp3IyWly+GvXrp3TVTWNnn0yqTQvUp8Xm86ZaLMR6/FvrdmL/h1aOuqjkWz84Yq+uGtUN3Ropb0ooCiKCAUDjiwcyCq8/PfrMll9lGwvq8KeiuPocirFQY2FMOeAoJeh1bpRMtnWyJLeKVXNiwtmI81QaZvWSFMXXpJn3HAKxzUvzz77LG6//Xbccccd6N27N2bOnImOHTvipZde0j0uLy8P+fn5sb9g0J8zjGSKNlKuKu0WljQvEJiOf/T9rbj07ysonJUDQRB0BRegqRN3wv+H1WxUJtGsaMnOn24tj322liHbWbNRMi8zEY0ibJ+TEdvmJU02T9egJ6aqCdg8QpqTK217GUdH1rq6OmzYsAFjx46VbR87dixWrtSPChkwYAAKCgowZswYLF68WLNcbW0tqqqqZH9eIpkk6DQHQqXNYNUPhed48nmxFzsdoJWoO9/qX09LnV9V0xTebEV4CQj6gr5014IHRnCfP9mWmZDeq+yMELY9MQ7Lp14Q2+ZGf6rVhqwuGqq3T09IU7anVI0DcFR4qaioQGNjI9q3by/b3r59e5SXl6seU1BQgJdffhlz587FvHnz0LNnT4wZMwbLli1TLT99+nTk5ubG/jp25EtP7zRemilYxYlQaTNYd9glnxe3cFIYVDu3UXZcrd1VJyXCiwWzkaBjNlLSKz8HQzjTwiez8CIIAppnhGRLabgRsafVB/D0I3rdiOp6Srp5XgTFd3O0bp5u8khvkJCWr2xwoihqNsKePXuiZ88mh79hw4ahtLQUM2bMwPnnnx9Xftq0aZgyZUrse1VVlScEmJzMEKpqGjCmd3vjwj4hGcxGAN8ASj4v9uLkmi2qPi8Gz0/r+VbVNOVmsaZ50XbYtaNlJZ3DrmQo9sq8TzMVFZfmhc9sxBMSbrYddW7THE9d2RdtfCrEOCq8tG3bFsFgME7LcuDAgThtjB7nnHMO5syZo7ovIyMDGRkZqvvc5LMpI7Gp9CguTFbhxad2VkGwz9GOYGftI2NQeuQkBnRq5dg11J6V0fPTinarlGherDjsCoLOzNiGtpVsmhcpbjjnqqGV54fL50VPeOHUvCg1Plb6qBuGdjZ/sMs4ajZKT0/HwIEDsWjRItn2RYsWYfjw4czn2bRpEwoK/LUyc15OJsb1yU8qnxfpujRu9itWTTl22aoJdvJyMjGws3OCC8CepE6K1u5jNvm8CLrSi3UzSL8O/kosZojMbOReNaT89bP4laYB9n6kpr4Rh49rLxGhdh69cUPZpp30I/MyjovtU6ZMwcSJEzFo0CAMGzYML7/8MkpKSjBp0iQAEbPPvn378OabbwIAZs6ciS5duqBPnz6oq6vDnDlzMHfuXMydO9fpqpqiV37qrBDcIrOpubjZr1jKsMt5PPm8+Ac1QcTo+bE8X0vCi+kj2RjYuTWev34AOrdOjlwf2RJNklc0L1qwCg3X/GMVvvqhUnO/aqi0zm9XtvNUnWA5Lrxcc801OHToEJ588kmUlZWhb9++mD9/Pjp3jqirysrKUFLStGpwXV0dHnroIezbtw9ZWVno06cPPvnkE0yYMMHpqnLRtW1zPHbZGTi7o7OzSS8hta+7mere6rtKSeqSE5ZU/6z7pQOnlQy7gqBvYO2Vn419R0+aPj8AXNqv0NLxXqJlsyb/C4/LLsyTID3BBVAXPvQ0L8riqeqXlxCD6d133427775bdd/s2bNl36dOnYqpU6cmoFbWCAYEjO6Z53Y1EkqLjLTYZ99qXjgz7BL+wVy0kfp+6cBZW++c5uWZq/rhb5/vwnVD1BN2PjKhF56e793s4nYjjYDx+mtqV/XU/K7SdbK2K8t7/T45RfJkUEswXp8VOIE0IZavfV4SeC0icajNQI3NRurbpfoSa0nq9LWU7bIz8IcrzkSfQnXflVbN0jHloh6Wru8ncrOaJkhSp2ketj0xzq7q6GKXxkNNwNZbckbZplNV80LCi0m6tWvhdhUSTrbM58U56cXpl5Hn/GFr4xaRQNQEFTt8XhotNAI9f12WVhgM+DWuzxxSc8nRE3WmzpGoCCy7uim1NigNjlAS77CbmiRvnJ1DzLt7ON5dW4KpF/dyuyoJR9YpONijGvspmH9deQ8lzYt/UDcb6R+j2ZYk7dvI9OQkAcHZ5QW8TLMkDgOXota89MxGyiabql1UarQOGzm7Uyuc7WCuCi8jNRs1GI0KFjB6F628rCLn8SnaL/gSc5oX9e1SecGK8GJV7tDL95GsvHD92Vi++yCuOOs0t6uii5OaFz2zUVyeF8nnDq2yUNsQxsHqWnsq52FIeCGYadM8HT3bZ0MQIrZ4pzBej8bKuXnTepP44hfUHpVZs5F0s1GWXiOsaE6CguBqZJ8bXNKvAJf0835eL7vyq6gLLxxmI1HER/eei7dWl+CX43qgXYsMFE2bb0vdvAwJLwQzgYCAT+8fARH8M8JIZlu2soaaFwudhgiRy48lVXMo+BE7NS9SgcWK35NVwSOoMgFPMVnGs9g1r1F12E3jMxv169AS/a5uaU+FfAI57BJcBAKCqazBXGt1mMzNwXpuJ/O8dG7TjLdKhE2orm1k0udFOqBY1rxYMB6p+bx4PXlbqmCXP9zy3RVx2zL1NC/KaKMUNW6T8EIkBJ7+dvnug7r7rZhytDqcti3U18fivdS/bhvKWyXCJsyYjWYt/Q7fHzwW16aix4miaNnnRavtq7Xj7nnyKMaAIMQJPzwTgWTn7E4tXbu2k1pZ3TwvcaHSztXDy5DwQiQEntnn7W+s191v5V3V0ryENLRJvIJSgN4o11BfmFH/+dU3irjgL0vjjo0KLIk2G/56vDyKMRiI17yQ7BJhZI92mHf3ua5d30l/ON08LwptYorKLiS8EImhzsboJKt9Bs9CaLyDF6n03UPNvMPa7JRHRoUXy2HSnM0hJzMNv5QkpQsEhLg6nNO1jbU6JQluD9pOCraZuj4vpHkBSHghfMjzi781fayW5kWagE8Kr107mVYR9xuqPi+Mz0/LbKT1/G89twtf5TiQyr9BQUCdJMPv7ecV4dmf93fs2n7C7UhAJ3NA6UUbxS/MmJrSCwkvREohari3aaVn5+0XSPHiHqo+L4zTY17NC6uGzYyzrjRCKRCQay0fmdAbbTT8s1INt8dsZ4UX7aE5zmHX7RvhEiS8ECmFKMa/7M3SgzijMEe1PLfmhaQXT8H6/JTFojKLluaGVcEmCPzh0lLtXUAQUC/RvJBmrwm3o2yclBn0NC9kNopAwguRUoiQq13PPb0N5t41HDkaZiPejoF8XrwFq1+CUsiJaV4arWleAP61jdIlyV2CAcFWf7Fkwu11x5wUGkJB7fZVcUy+5lOKyi4kvBCphSiKsU6na7vmeOuOc9C7IAdZ6eozHV7NSyqmc/cyrGYjJTHhRUvzwviczbSGNInJIKDweSGaiGpe/nR1P1eu76TZiEc4Js0LQaQAEc1L5G2Xdg/NNISXAyprhHRt21zz/CS7eAvzZqNTDruaPi+WqqVLBmlemIg+s58P6ogbz+mU8Os7KbzwKHDdNp+5BQkvREoR8XmJfJbObrSWoH91RXHcNr0sumQ28hasihflAGCoeWF12BX4nbjTZZoXkOZFQdd2kcnDZf0LY9usZDE2i9M5gB6/7AymcqR5IYgU4G+f78aCrWUA5JEkWRrCixp6DpjkUGkfCx883/I5zGpejKKNeJxweYWXtKDcbFRLwouM9+8+F2/fMRTXD0m8tkWK05qXnw/uyFQ2RWUXWpiRSC2W7TqIZac+f19xPLZdy+dFDb2xiBQv9tEiw3r3xJpkLi5UWjQKlWa7vhmNgFTzEgwIqCezkYzcrDQMP72t29Uw7U/FQkAQwPrUjUKl1z16ofUKeRDSvBCOopevwEvwaV6091GotH3YMTQwCy9a0UYax/M8Zy0BRmvMUQovZDbyJk6ajQREJlSTLzgdd43qplvWSAHULjs58wKR5oVwlGbpQV+ovXmEFz3dC5mN7MOOmS2raj8ua2lsbSOL0UYmmkOaJEyWoo28i5PJ4aJmySljewIAXlrynXY9UtRwRMIL4ShZaUEcQb3b1TAkk8dspDMgqflCdGydhWsHu2ufT1WY1yZS+rzEzEbqxe1QsGkNOhkKh10yG3kTpzUvrJDDLkHYxCs3DUJOZgiv3TJI5kvy/87v6mKt9OEyG3Gee/nUC3DP6NM5j0ptmqcHkZ+bafk8zGsbKQSJaAI0q8sDACaijYJNbTEYENDcBt8fwn68EyqdmpDwQtjORWe0x1e/G4sLerVHt3YtYtsfmdAbY89o72LNtJFGeBjB07E4uYBfMnHbuUWxzw9e2AMbf3sR1zPRgnltI03Ni/U8L7zCblpIbjaa/tMzcVbHlph140DOMxFO4qzwwt5qaGFGgrCR6Mv31JVnYnzffMy5fajLNTKmY+sspnI8ESS/u6yP2eqkDE9feSZ+c0nv2PdQUNBd24UHVouL5sKMNuR5CXEKYcrlATq3aY4P7jkXF/fN5zoPEWH6T8905LyekRm8Uo8EQ8IL4SjtsjPw0o0DcV5390MbjXj252cxlaOAInv4733n4fnrB+D6oZ1kDrB2OkI2Mi6Ao3bNhsawLXleQhpqGpZoI0p6aA1BAK4b0gmXSxLa2YVXNB569Ujm5kPCC0GcgvU9T+YOIZH0PS0Xl/aLH1TsHBNYfV7UZJRL/77CBrORIBNGWJBqXgLUQ9vCJf0KbD+n0xl2WdFr4lqCczJArwbhOhf3ycfAzq0AAC2bpblWD1ahxI1U5KmEnWMCu9ko/qo7yqtx6Fj82lYAn0aEdwCxw9eHiBB9Tk68sV7RvIzs2S5u26wbz0bbFul48zbvm+vNQm8JkVDU+vxgQMAfrugLAAi5OtVkll70d5Ns4xmYc8VoFCuvqlHdzpxh14TPS1CS54U51JtQJXonecx8rDiZ54WFXvnZ+M+kYRjRPV54ubhvAdY9eiGGdWvjQs0SAwkvREJR01rIF69zr0PQ6t/6d8iVRUkZdYPkp2ANN8xGWqXKKzWEFw5tijTpHAvNJGH7uVnuaSKTgeir6IzmxYGTctC6eToGdWmtud8Jgc1LkPBCuE4wIMSEGq2x5n8PWF+kzwitV71ls3SEJAOQUaeQxGbmhGBnxlCzodJRyrSEF9ZoI/BrE0PBAD7/5UgsfPB8NEunHC9WcNLE67bZyO3ru01ChJcXX3wRRUVFyMzMxMCBA7F8+XLd8kuXLsXAgQORmZmJrl27YtasWYmoJuESAUGIDfhaL2Sr5mnoU5jjaD20hJK0YEA2WBl1h+QTYw1pE7A6eeRNUqf0T7FqNgLkPizSZIh6NevWrgV6tM9mvwihTlTzYsMr2Stf/jzc1ry4fX23cVx4ee+99/DAAw/g0UcfxaZNmzBixAiMHz8eJSUlquWLi4sxYcIEjBgxAps2bcIjjzyCyZMnY+7cuU5XlXCJgCDEOhet9zEoCK7Z/zNCAdmaRYYdIckutmHVBMe+MKP69Y4cr1Mtz6qSFwS52WjL42OZjiPsocnnxfq5ChQZn932eXFyVWs/4Ljw8uyzz+L222/HHXfcgd69e2PmzJno2LEjXnrpJdXys2bNQqdOnTBz5kz07t0bd9xxB2677TbMmDHD6aoSLhGRC/TNRoIgOK4m1erf0kMB2SrCxj4vtlUpJZE+Zau3klV4ibUtxQWPnFAXXnhWlZaaHHmddwlrNPm8WH8p4xbvdFl4YdUqJiuOvkl1dXXYsGEDxo6VzzbGjh2LlStXqh6zatWquPLjxo3D+vXrUV8fv8BfbW0tqqqqZH+EvwgGjM1GApyPvNAaj0IBQaF50e8IyWxkEUkbsGw24ta8yLcfOaG+qCirG4sAweUIutQmYKPHrjKNA2P+Q8dIccWLs8JLRUUFGhsb0b69fD2b9u3bo7y8XPWY8vJy1fINDQ2oqKiIKz99+nTk5ubG/jp27GjfDyASgiAITQKBzgvpuPCi0cOFgoLcYdfgPKR5sYZc82LtZvLOjgUIeOWmQbHvdizMyBttRNiHnXf+UckSFoD7mhe3zVZuk5ApgXKmKoqi7uxVrbzadgCYNm0aKisrY3+lpaU21JhIJMFAUyej9zo6nbxLW/Mid9g1zvNCg5UVbHXY5dS8CEJkYdF/TNRfBJHP54U0L24RfU5W38jXbxmMvGylz4vFk1rEbeHJbRx9q9q2bYtgMBinZTlw4ECcdiVKfn6+avlQKIQ2beIT7mRkZCAnJ0f2R/gLmcOuxguZlR7EX685K3GVkhAMCLhrVDdkpQVx67ldDLUBJLtYQxoqbT3aiO+a0ctlZ+qHKPNo1zT9XFJ77EkItiWpUzncbeHBbbOV2zgqvKSnp2PgwIFYtGiRbPuiRYswfPhw1WOGDRsWV37hwoUYNGgQ0tIoYZPfUetDIqHSkR3KifInk8/D/MkjkJkWRN/TcnGZjQusPfvz/oZ1AyI+Lx1aNcOWx8fid5f1MRxQSXYxx+l5LQBAtt6RVFCcdePZ3OfkzfMSHeRyMvX7Gp48L2lkR3ScoNY9tsnlRe14t31O3Bae3MbxDEhTpkzBxIkTMWjQIAwbNgwvv/wySkpKMGnSJAARs8++ffvw5ptvAgAmTZqE559/HlOmTMGdd96JVatW4dVXX8U777zjdFUJl5AOBMoEZX0Kc2Xf7VpoLCMUwE/P7sBUNpquPTqDNoyUJtWLKT6ZfB6OHK9HviQkVfq4L+7Lv7jemuJDTOWirS56ucw0+bwuGJCH6nOtbaTQvFzYOw+ffXMANw/vzHwOQp+7R3XDwm3l+Nkguc+jXW+i2jvttvDg9vXdxnHh5ZprrsGhQ4fw5JNPoqysDH379sX8+fPRuXPkxS0rK5PlfCkqKsL8+fPx4IMP4oUXXkBhYSGee+45XHXVVU5XlXCJYAASs5F+WbtS76sJQVrmoDRFtIhRFWiibY6MUBD5uUHZNqvPu57RbhSO+dXh1P/y68YLL2zXFwQhzmH3+evPxsa9RzC4SDu1O8FHXk4mvnz4grjnFl3GwWq3ofa83RYdUn3dq4Tknr777rtx9913q+6bPXt23LaRI0di48aNDteK8ApSs5GR8GKX76Pa2jRaHZxSJW3k8/LXa87CLa+vw2OXnmG6fsQpEiQIKs1GSqEpLSBAmvGFR6jq0CpL9j0zLYjhp7c1VU9CGzXtSMznxWJDUjve7WifFFe8JEZ4IQg9AgFphl39N1LTts2JquZFx+eFpVyUUT3zsOsP45EeoigTq2jd6q5tm+P7iuM2XkmueVEmoVO2O57ULWPPyMfdo7qhf8eWVipImCAWbWSi2xjQqSU2lRzVPN7tDLdkNiIIlwkITTMbIzW/XWYjNSFIa3YWDPIJLwBIcLEJLf+hgpaZtgovygS7yssq/VZ4QqUDAQFTL+5lsYaEGcz2FunBANIlz9wph938nEzN9bOMSHGrEa0qTbhPULIwo2FZmzQvqsKLxqmVPi8UT5Q4tJ6J3VmMYw67UbNRwEDzQk7ZvsB0gl1B0fYcCpW20p+lus8LCS+E6wiCwNy72KZ54ThPnM8LjVsJQ+t52/0MlJoXZftQmg6ZHXZJ0HUZc9KLAPmzU/d5sVCtU1hZOcJtnxu3IeGFcJ1gQGDu5O0KlVaaggDt/i2kNBvZUgOCBa17fU7X+ISVVhAVPi/KZqZsA6R58QdmF2aMmPvizyPFDs2LlXaU4ooXEl4I9wkI7DNZ28xGapEJrNFGNG4lDOW9Xj51NF664Wxc2o8/54se0WylTQ6eSs0LX7g84Q2ir66Z5yXXvMRjVXho2yLdovCS2tILCS9EQlHNsBsQmB0g1UKczaAuBLHleaFVghOJ/Jl0bN0M488scMDnRb48gLJ5KNsLj9mRcA+z7USAIOurnEhS99/7RlgSgkl4IQiXCQXYuxi7EsDxOOwqy9pluiKM0cz67pTPSzRU2uCZswrRJOO4i1mHXeVzU3uOVnxO0oIC8nMzLWleyGGXIFwmMy3I/BLbFyod3/RZfV40F9ojbEcz2sghoSAqRqtl2JVC8qs/aAp95/R5gbyvUXveVmSHaDuz0o5SXHYh4YVwn4xQgHlqZNe6QWryh9a5lQOXMt074RxaOjnps7LjeSg1L/EOuybzvFitGGEJs0nqBEFQHGO/2QiwNhm7f0x3y9f3MyS8EK6TmRZk7lzsMxuxN32ljwv5vCQO7TwvTTw8vrfl6yh9XozMRuTzktwIkLcx9Wgj69fZub/a9LG3ntvFegV8DPXChOtkhFwwG6mcRtNspBzISPOSMLSet3R7XnaG5euEY5qXqDrfyGxEPi9+IDrP4H4Mgly7pna8HXlWzJ6ia7vmKb96PQkvhOtkpgUS7rCrpj3RdNhVCCvpGj4vLTJotY1EIX1WdggvYtyq0vL9vOtbEd6gyYeJ9zh5X+NEtJEVauvDrl3bK5DwQrhORojdbGTXbEPN8qPlX8GqeZl393DL9SLksJiN2tkhvCiuZ5/mhaQcN2m6/ZwOu4LccOREnhcr1DaQ8ELCC+E6mWmBhJuNeDQvcT4vKpqX3156Bnq0z7albkQTWs+kRjLztEV4iS0PELmg0qclTfHMye3JHzQ9xSZJg6ULEQR5Odsz7OrU4SyG1cdrGxrNXztJID034TqZaUHmsnaZjXiS3Sk1LWkUJ5swtITVDq2yMLhLK7Runo7m6XZ0Y/pmI9OaF8v1IqwQ1XxJtSRBQUCDgeAR57Cr8iSraxpsqGET944+HZlpAfx8cEfDsqR5IeGFSDBqnQBftJFdmpf48zAnqVPRvJB1wBm0bmsgIODfk+wz0zWeGgui7SsaKhsd4+IXZqQH7geiT0kqq0SenYHwIgiyZ6z2uN9eU2K5XlLycjJw07AuTMfXkfBCZiPCfTJC7GYju8YMnsFHOXBRnpfEkSifkfpT0ouWM7bpJHXUVNzl1P2XRgYxmY0Yy9kJNRU+SHghXCczLcgRbeSk5kX93CwmA+p4nCFR9zU6k00PNXWJ0tk6aV78SUDFbMTy7A4dr5MJL4l43nqCek/yp4uDhBfCdXg0L46ubaRRVumToyq80GDmCIm6rXWnNC9aWjVlUkN2nxdqF27SZDZqkl5Y+xDBwGxkN3rX+PDec/HFL0c6XwkfQcIL4TqZaUFmB1ppuasHdjB9TZ6FGbPihBeVMunsTscEO2ZmvEO6tOY+Rk3zIkUp1JCs6g+iz0nq4WLG2TohwouOoJuZFkTXdi3QsXUWAKB183TnK+RxyGGXcI3/TBqG9FBAVZDQQjobsrK6s7rmRf18SuFFWodfXtQDG0qO4IqzTjNdF0IbM4NG39Ny0RAOY2PJUeZj6mOaF0afF8a2N7hLK+Y6EPYTfaelYc3sK4JL87wkwmxkXObN24Zi5me7cPeo0x2vj9ch4YVwjUEmZsjSfodH6FHCpXlJ19a8/GJkN83ZOmEdM4OGIBjFksQT1bxkaDxLZXsxqtXyqaOxo7waF/TK46wJYScxzYukQZzdqSUW7zxofKzKeZyE5RJFbZvjb9cOcLwufoB6XcJXBOzSvHA43SoHNGkdrAhQhDHZmebmV7zZT+sMNC/Kp2w0mHVs3QwXndGefKE8glTzMuNn/ZmOkS0PYHeFVKCmwgcJL4SvCMoEB/PNV7lekR7KAUh6WZJdnOVPV/dDr/xsPHed/mzzqSv7xj4LAPeKd1HNi5bwUq+QhsgR1x9E311pc2jTgi0js5MOu2rnc3GpJF9CwgvhK6QvvZXVndU0L+x1EFQ/E/bTtV0LLHjgfFzev1C3XGFuVuyzmUdSa+Cw29AoTwpGj93b/OaS3shMC+CZn54JABC5DYlKbYszD/xPV/eLfSbZhQ8SXoiEYnXlZbtMNlaOpRwf3kO+Do3APRDU1kfWitHSvCgzmlIL8DZ3jOiKbU9cjP6n1gkypdWQtSlbqhXHhDMLYp8b3Vzp0YeQwy6RUB4a1xO7DlTjusGdTB0vNdnYHW3E2nWQqch7yFK5g3/RvFoDh906heaFpBfvI33HzcgFyjblBNI+TCS7ERckvBAJpV12Bt6/+1zTx9uleVETfNT6DrV08X0Kc01fl3AGmTZM4J9p1zboJ6mra5CfkLRv/sLMCtDSJ+zU85aelxQvfJDZiPAVUh8TLRU/C6y5HtRm4q2bp2P1tDH46ndjTV+fsBd5ZIhgQniJmI20fF7qlT4vfKcnXMaMUkOwaDZ6dEJvzfYU9bmTTsDIbMQHCS+Er5AOUlbMRqqaFxXDUUaa+iuSn5uJ3Kw009cn7EUqjJrJ81Jbz5ekjhy1/YUZk4w0osxMdNmd53dFTqZ6HxHVuEiblRntUCrjqPBy5MgRTJw4Ebm5ucjNzcXEiRNx9OhR3WNuueWWU8vRN/2dc845TlaT8BGyPC9WNC8qg49a3xGyEI5NJA6lfwLvYGUUbXTruV1k30l08RdmxALpq29WVtWaX0XPR0KweRztma+//nps3rwZCxYswIIFC7B582ZMnDjR8LiLL74YZWVlsb/58+c7WU3CRzireYmHktD5A+VjMuvzoubjdHpeizgtm3TMmTSyG/oU5vBdkEgo53Vvi/RgAAM78yzXYP3d1/KVUetXyGzEh2MOu9988w0WLFiA1atXY+jQoQCAV155BcOGDcPOnTvRs2dPzWMzMjKQn5/vVNUIHyPY5LCr5vOiNlsn4cUfKBOK8eb10PN5UWsCUjPCred2wcPje6HLw59wXZNIHDmZadjyxFikcWhSrfq8ANqaFzWhhmQXPhzTvKxatQq5ubkxwQUAzjnnHOTm5mLlypW6xy5ZsgR5eXno0aMH7rzzThw4cECzbG1tLaqqqmR/RPJiV7SRWmekXIARsKbdIRKH0mGXd8XxaB4X1gU7pcIRtRB/kBFiX70eUK5tZO4pax2ntp18XvhwTHgpLy9HXl78omR5eXkoLy/XPG78+PF466238MUXX+Avf/kL1q1bhwsuuAC1tbWq5adPnx7zqcnNzUXHjh1t+w2E97BrYUZl0jEgkjb88cvOkG0jzYs/kAq1AQG4/byu+PkgdgGm4dS0V01YNUzlTk0k6RjcpRVznpfObZpp7tPqP9Tc9cKkeuGCW3h5/PHH4xxqlX/r168HoC5diqKoK8Vec801uOSSS9C3b19cdtll+PTTT7Fr1y588om6SnbatGmorKyM/ZWWlvL+JMJH2LUwY62K8AIAt5xbJPtOwos/kD6nUDCAYEDAed3bMR8fTf+vtl5WQNAPvaZ1jpKPt+88h9lspBd1SGYj5+D2ebn33ntx7bXX6pbp0qULvv76a+zfvz9u38GDB9G+fXvm6xUUFKBz587YvXu36v6MjAxkZLAttEX4H8FBzYsaJLz4A7U1r3jWr4pqXtRmxKqaF67aEV5jaFFrrCk+rLk/LRhQTVJ39cAO+M+GH2Rl9VqZ1kRdXXihVsUDt/DStm1btG3b1rDcsGHDUFlZibVr12LIkCEAgDVr1qCyshLDhw9nvt6hQ4dQWlqKgoIC48JE0mOXz0vUQdMI8nnxB9J2EY0Y4mkfuw8cizuP2rmjSJ27KdrVf/xj4kBc9dJKfHfwuGYZQcVs9PSVZ8YJL3oNQGuXuimShBceHPN56d27Ny6++GLceeedWL16NVavXo0777wTl156qSzSqFevXnj//fcBAMeOHcNDDz2EVatWYc+ePViyZAkuu+wytG3bFldeeaVTVSV8hEx4sTBqkOYluVAzJ/I8u2h7UMvro3aa5pIFRpun0yorfqNls3TcMLSzbhlZ93Lqc3oogNNaZsnL6ZxDq48is5F1HH3r3nrrLUyePBljx0bSqF9++eV4/vnnZWV27tyJyspKAEAwGMSWLVvw5ptv4ujRoygoKMDo0aPx3nvvITs728mqEj7BLoddLZ8XJSS8+APpY0oLRTUv/OdRNxvFt4HMtCD+M2kYACArPT5KjfA+aq/2lQNOw0/OKgSgnWH3jduG4MJnlzbt0+kiuPK8kOaFC0eFl9atW2POnDm6ZaSqsqysLPzvf/9zskqEz7Erz0s0HbwRtACfP5CGwEZzeag53xqhdowgqA9Qg7q05j4/4R3U+o+fDeqA4d0ibhFaDrun57XAlIt64NlFuwDo9xE8ZiPyeeGD9J2EJ7lndDeM6hkfam+X5uW87sZ+W0CT8yfhbaQDSFqI32E3iprmhQTY5EQ9GlayH+qfAXnfo9c6tNoO6/IkhDa0cAvhSX41rhcGq8xsZSGxJtcdevGGs3HdkE5MZc3M3onEI182IvLMzDw6dYddtmOn//RMAIjLFUR4k1E9I6H0hbmZsW3SFP1yzYu8EbCGUWu1QbU2RXle+CDNC+ErpJ2IWbliwpnskWsUbeQPZJqXU+oTM8Kt2jEC9PO8RLluSCdc2q8A2RorCRPeokOrZlj36IXIzgyh12MLAABnaKxRFad5kUUiafcRPJoXkl34IOGF8BVqM2wnIYddfyAdC9KieV7MaF408rywtgMSXPxFu+xIjrCvHx+LE7WNaNtCPWeYUtaQtQedpsGT56Vfh1z9yhIySHghfIU8z4vz17MSjk0kDjXNixlfFfVQaYFWjU5ycjLTkKMQPAMBbe0K69IBmhl2Jc1s0YPnY1PpUVzev5C5vgT5vBA+Qy68JEDzQg67vkC+PAB/npem8zR9vvXcLgCAh8b1hCAIeOrKvpbqSPiLDEljEBRdjcxh10SotHR79/bZ+PmgjlyLRhKkeSF8hmx5gARoRcjnxR9Im4KZDLtRpALx7y7rg6njelEelxQlPSQRXhT79LQysnJamhfS6FqGNC+Er7BreQBWyGzkD2QZdq0IL4rnLRVcTm/XwmTtCD8iE14EpdkIkn3a57h5eBfV7TQnsg5pXghfIbUUJSIHCzns+gPV5QFMCJ56lsihXdvgr9f0RzcSYlKCjFCT4KobbaTTzC7tV4iits1xyXMrZNtJ82IdEl4IXyF96c10AEVtm3OVJ+HFH6glLzTz7Iwi2K4c0IH7nIQ/kWte5PtYzUYA0KcwF5PHdEdtQyP+sfT7yPEkvFiGzEaEZ3jgwu4AgIfH99IsIw+V5usAbj+vCG/eNoTrGBJe/IHashFWHXaJ1CZD5vMib0usmpcoUy7qgWnje3MdQ+hDmhfCM9w/pjuuGdwRBblZmmWsrG302KX8mU/JYdcfSNtCdFZrZnZLGZWJKHqaF6uTGpoUWYfeVMIzCIKgK7gAiXHYjaYNB4B+HVo6cg3CXtQ0cmZ8oshBm4gi9XlRIjMbmfGtonZmGdK8EL7CroUZ9fjnTYOweOdBHKiuwZUDTnPkGoS9SNX6QQsOu5TXh4ii6/PClmBXE5JdrEPCC+Er1AYpuwkFA7jojPaOnJtwBulg0Dwj0q1JZ8d/v24AKk/W4zcfbNU9D2leiCh2+rwoIbORdUh4IXwL+aMQUTLTgvjT1f3Q0CiidfP0uP1Du7ZGXnamofBCLi9EFKnmRdnVyKON+CGzkXVIeCF8RViyvG8gIEAQwLTiL5H8/HxQR9n35ulN3VurZvECjRqJWOyT8AfpQe0kdZlpQc19LNC8yzokvBC+QiqnhAICgoKABpJeCBWy0oNY9OD5CASE2GKNRpDZiIiSndk0PCpbRfN07QR2LJgReAg5NM0gfIVM8yIIMtvxAxd2j+WKIQggsuidVkbc568fELeNHHaJKJ3bNMctw7vgvgtOj1s0sZlEq2dGDiHNi3VIeCF8hVTJEgoohZce5GhL6PLmbUNwWssszLl9KC7tVxi3nzQvhJTHL++DX47tGbe9mWyxTgqVdgMSXghf0apZWuxzMCBg2qlsvDcN6wwAzOYBIjU5v0c7fPnwBTive1vV/RQFQrDQLEPq88J/PAkv1iGfF8JXtGmRgdduGYTMtCAEQcDEYV0wskceOrSKJLejwYewArUfggWpM3htQ5j7eKUZiuCHhBfCd1zQS24a6tSmWexzGkWLEBwEAwIaw1I/KhcrQ/iGLEm0Ub0J4YVcq6xDPT2RVJDDJcHDtYPl4dUUBUKwINWctG7RFIbfqlkahhS1xhsaC8Ce1bElgPiwfoIf0rwQSUUaTZ0JDh679Ay8tabE7WoQPqZdi4zY585tmuP/fjFMs+z//WIY9lfVoGPrZpplCDZI80IkFeSzQPCQmRbEkKLWbleD8DG98rOZy6aHAiS42AQJL0RSEaJoI4KTfUdOul0Fwof8/boBuPGcTrh6YAf85KxI2P29o093uVapA5mNiKQijXxeCE72HSXhheDnsv6FuKx/RGiZec1ZePSS3sjLznS5VqkDTVOJpILMRgQvvxrXU/Y/QfAiCAIJLgmGNC9EUkGh0gQvd43shov75qOoTXO3q0IQBCPU0xNJhTL508PjeyE9FCBbNKFJICCgW7sWlDiMIHyEo8LLU089heHDh6NZs2Zo2bIl0zGiKOLxxx9HYWEhsrKyMGrUKGzbts3JahJJzKDOrbD18XF4iEwCBEEQSYOjwktdXR1+9rOf4a677mI+5k9/+hOeffZZPP/881i3bh3y8/Nx0UUXobq62sGaEslKi8wQ0kOkYCQIgkgmHO3Vn3jiCTz44IM488wzmcqLooiZM2fi0UcfxU9/+lP07dsXb7zxBk6cOIG3337byaoSScTbdw6Nfc7OTNMpSRAEQfgRT01Ji4uLUV5ejrFjx8a2ZWRkYOTIkVi5cqXqMbW1taiqqpL9EalN/w4tY5+lq1ATBEEQyYGnoo3Ky8sBAO3byxfea9++Pfbu3at6zPTp0/HEE084XjfCPzTPCOH1WwYjEBDQLN1TTZwgCIKwAW7Ny+OPPw5BEHT/1q9fb6lSysXRRFHUXDBt2rRpqKysjP2VlpZaujaRHIzulYeRPdq5XQ2CIAjCAbinpffeey+uvfZa3TJdunQxVZn8/HwAEQ1MQUFBbPuBAwfitDFRMjIykJGRobqPIAiCIIjkg1t4adu2Ldq2betEXVBUVIT8/HwsWrQIAwYMABCJWFq6dCn++Mc/OnJNgiAIgiD8haMOuyUlJdi8eTNKSkrQ2NiIzZs3Y/PmzTh27FisTK9evfD+++8DiJiLHnjgATz99NN4//33sXXrVtxyyy1o1qwZrr/+eierShAEQRCET3DUm/G3v/0t3njjjdj3qDZl8eLFGDVqFABg586dqKysjJWZOnUqTp48ibvvvhtHjhzB0KFDsXDhQmRnsy87ThAEQRBE8iKIoii6XQk7qaqqQm5uLiorK5GTk+N2dQiCIAiCYIBn/PZUnheCIAiCIAgjSHghCIIgCMJXkPBCEARBEISvIOGFIAiCIAhfQcILQRAEQRC+goQXgiAIgiB8BQkvBEEQBEH4ChJeCIIgCILwFY5m2HWDaM69qqoql2tCEARBEAQr0XGbJXdu0gkv1dXVAICOHTu6XBOCIAiCIHiprq5Gbm6ubpmkWx4gHA7jxx9/RHZ2NgRBsPXcVVVV6NixI0pLS2npAQeh+5w46F4nBrrPiYHuc2Jw6j6Loojq6moUFhYiEND3akk6zUsgEECHDh0cvUZOTg69GAmA7nPioHudGOg+Jwa6z4nBiftspHGJQg67BEEQBEH4ChJeCIIgCILwFSS8cJCRkYHf/e53yMjIcLsqSQ3d58RB9zox0H1ODHSfE4MX7nPSOewSBEEQBJHckOaFIAiCIAhfQcILQRAEQRC+goQXgiAIgiB8BQkvBEEQBEH4ChJeGHnxxRdRVFSEzMxMDBw4EMuXL3e7Sr5i+vTpGDx4MLKzs5GXl4crrrgCO3fulJURRRGPP/44CgsLkZWVhVGjRmHbtm2yMrW1tbjvvvvQtm1bNG/eHJdffjl++OGHRP4UXzF9+nQIgoAHHnggto3us33s27cPN954I9q0aYNmzZrhrLPOwoYNG2L76V5bp6GhAb/5zW9QVFSErKwsdO3aFU8++STC4XCsDN1nfpYtW4bLLrsMhYWFEAQBH3zwgWy/Xff0yJEjmDhxInJzc5Gbm4uJEyfi6NGj1n+ASBjy7rvvimlpaeIrr7wibt++Xbz//vvF5s2bi3v37nW7ar5h3Lhx4uuvvy5u3bpV3Lx5s3jJJZeInTp1Eo8dOxYr88wzz4jZ2dni3LlzxS1btojXXHONWFBQIFZVVcXKTJo0STzttNPERYsWiRs3bhRHjx4t9u/fX2xoaHDjZ3matWvXil26dBH79esn3n///bHtdJ/t4fDhw2Lnzp3FW265RVyzZo1YXFwsfvbZZ+K3334bK0P32jp/+MMfxDZt2oj//e9/xeLiYvHf//632KJFC3HmzJmxMnSf+Zk/f7746KOPinPnzhUBiO+//75sv1339OKLLxb79u0rrly5Uly5cqXYt29f8dJLL7VcfxJeGBgyZIg4adIk2bZevXqJDz/8sEs18j8HDhwQAYhLly4VRVEUw+GwmJ+fLz7zzDOxMjU1NWJubq44a9YsURRF8ejRo2JaWpr47rvvxsrs27dPDAQC4oIFCxL7AzxOdXW12L17d3HRokXiyJEjY8IL3Wf7+PWvfy2ed955mvvpXtvDJZdcIt52222ybT/96U/FG2+8URRFus92oBRe7Lqn27dvFwGIq1evjpVZtWqVCEDcsWOHpTqT2ciAuro6bNiwAWPHjpVtHzt2LFauXOlSrfxPZWUlAKB169YAgOLiYpSXl8vuc0ZGBkaOHBm7zxs2bEB9fb2sTGFhIfr27UvPQsE999yDSy65BBdeeKFsO91n+/joo48waNAg/OxnP0NeXh4GDBiAV155Jbaf7rU9nHfeefj888+xa9cuAMBXX32FFStWYMKECQDoPjuBXfd01apVyM3NxdChQ2NlzjnnHOTm5lq+70m3MKPdVFRUoLGxEe3bt5dtb9++PcrLy12qlb8RRRFTpkzBeeedh759+wJA7F6q3ee9e/fGyqSnp6NVq1ZxZehZNPHuu+9i48aNWLduXdw+us/28f333+Oll17ClClT8Mgjj2Dt2rWYPHkyMjIycNNNN9G9tolf//rXqKysRK9evRAMBtHY2IinnnoK1113HQBq005g1z0tLy9HXl5e3Pnz8vIs33cSXhgRBEH2XRTFuG0EG/feey++/vprrFixIm6fmftMz6KJ0tJS3H///Vi4cCEyMzM1y9F9tk44HMagQYPw9NNPAwAGDBiAbdu24aWXXsJNN90UK0f32hrvvfce5syZg7fffht9+vTB5s2b8cADD6CwsBA333xzrBzdZ/ux456qlbfjvpPZyIC2bdsiGAzGSYkHDhyIk0oJY+677z589NFHWLx4MTp06BDbnp+fDwC69zk/Px91dXU4cuSIZplUZ8OGDThw4AAGDhyIUCiEUCiEpUuX4rnnnkMoFIrdJ7rP1ikoKMAZZ5wh29a7d2+UlJQAoDZtF7/61a/w8MMP49prr8WZZ56JiRMn4sEHH8T06dMB0H12ArvuaX5+Pvbv3x93/oMHD1q+7yS8GJCeno6BAwdi0aJFsu2LFi3C8OHDXaqV/xBFEffeey/mzZuHL774AkVFRbL9RUVFyM/Pl93nuro6LF26NHafBw4ciLS0NFmZsrIybN26lZ7FKcaMGYMtW7Zg8+bNsb9BgwbhhhtuwObNm9G1a1e6zzZx7rnnxoX779q1C507dwZAbdouTpw4gUBAPlQFg8FYqDTdZ/ux654OGzYMlZWVWLt2bazMmjVrUFlZaf2+W3L3TRGiodKvvvqquH37dvGBBx4QmzdvLu7Zs8ftqvmGu+66S8zNzRWXLFkilpWVxf5OnDgRK/PMM8+Iubm54rx588QtW7aI1113nWpoXocOHcTPPvtM3Lhxo3jBBRekdLgjC9JoI1Gk+2wXa9euFUOhkPjUU0+Ju3fvFt966y2xWbNm4pw5c2Jl6F5b5+abbxZPO+20WKj0vHnzxLZt24pTp06NlaH7zE91dbW4adMmcdOmTSIA8dlnnxU3bdoUSwFi1z29+OKLxX79+omrVq0SV61aJZ555pkUKp1IXnjhBbFz585ienq6ePbZZ8dCfAk2AKj+vf7667Ey4XBY/N3vfifm5+eLGRkZ4vnnny9u2bJFdp6TJ0+K9957r9i6dWsxKytLvPTSS8WSkpIE/xp/oRRe6D7bx8cffyz27dtXzMjIEHv16iW+/PLLsv10r61TVVUl3n///WKnTp3EzMxMsWvXruKjjz4q1tbWxsrQfeZn8eLFqn3yzTffLIqifff00KFD4g033CBmZ2eL2dnZ4g033CAeOXLEcv0FURRFa7obgiAIgiCIxEE+LwRBEARB+AoSXgiCIAiC8BUkvBAEQRAE4StIeCEIgiAIwleQ8EIQBEEQhK8g4YUgCIIgCF9BwgtBEARBEL6ChBeCIAiCIHwFCS8EQRAEQfgKEl4IgiAIgvAVJLwQBEEQBOErSHghCIIgCMJX/H9UhkB8Zzg4bwAAAABJRU5ErkJggg==\n",
|
|
"text/plain": [
|
|
"<Figure size 640x480 with 1 Axes>"
|
|
]
|
|
},
|
|
"metadata": {
|
|
"filenames": {
|
|
"image/png": "/Users/mhjensen/Teaching/MachineLearning/doc/LectureNotes/_build/jupyter_execute/chapter13_3_0.png"
|
|
}
|
|
},
|
|
"output_type": "display_data"
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Metal device set to: Apple M1\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Model: \"sequential\"\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"_________________________________________________________________\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
" Layer (type) Output Shape Param # \n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"=================================================================\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
" simple_rnn (SimpleRNN) (None, 32) 1184 \n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
" \n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
" dense (Dense) (None, 8) 264 \n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
" \n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
" dense_1 (Dense) (None, 1) 9 \n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
" \n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"=================================================================\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Total params: 1,457\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Trainable params: 1,457\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Non-trainable params: 0\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"_________________________________________________________________\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 1/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stderr",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"2023-10-02 06:54:48.552042: W tensorflow/core/platform/profile_utils/cpu_utils.cc:128] Failed to get CPU frequency: 0 Hz\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 4s - loss: 1.7644 - 4s/epoch - 84ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 2/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 0s - loss: 0.4353 - 499ms/epoch - 10ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 3/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 0s - loss: 0.4066 - 479ms/epoch - 10ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 4/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 1s - loss: 0.4033 - 740ms/epoch - 15ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 5/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 1s - loss: 0.4014 - 596ms/epoch - 12ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 6/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 1s - loss: 0.3999 - 561ms/epoch - 11ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 7/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 1s - loss: 0.3996 - 515ms/epoch - 10ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 8/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 0s - loss: 0.3975 - 495ms/epoch - 10ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 9/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 0s - loss: 0.3970 - 473ms/epoch - 9ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 10/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 1s - loss: 0.3944 - 687ms/epoch - 14ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 11/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 1s - loss: 0.3948 - 500ms/epoch - 10ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 12/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 1s - loss: 0.3932 - 501ms/epoch - 10ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 13/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 0s - loss: 0.3923 - 499ms/epoch - 10ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 14/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 0s - loss: 0.3919 - 484ms/epoch - 10ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 15/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 1s - loss: 0.3928 - 556ms/epoch - 11ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 16/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 0s - loss: 0.3901 - 497ms/epoch - 10ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 17/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 0s - loss: 0.3907 - 456ms/epoch - 9ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 18/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 0s - loss: 0.3893 - 456ms/epoch - 9ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 19/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 0s - loss: 0.3878 - 458ms/epoch - 9ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 20/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 0s - loss: 0.3877 - 455ms/epoch - 9ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 21/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 0s - loss: 0.3873 - 462ms/epoch - 9ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 22/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 0s - loss: 0.3865 - 468ms/epoch - 9ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 23/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 0s - loss: 0.3856 - 487ms/epoch - 10ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 24/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 0s - loss: 0.3853 - 473ms/epoch - 9ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 25/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 0s - loss: 0.3838 - 492ms/epoch - 10ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 26/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 0s - loss: 0.3840 - 477ms/epoch - 10ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 27/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 1s - loss: 0.3836 - 643ms/epoch - 13ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 28/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 0s - loss: 0.3834 - 484ms/epoch - 10ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 29/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 0s - loss: 0.3838 - 483ms/epoch - 10ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 30/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 0s - loss: 0.3837 - 468ms/epoch - 9ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 31/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 1s - loss: 0.3821 - 671ms/epoch - 13ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 32/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 1s - loss: 0.3816 - 832ms/epoch - 17ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 33/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 1s - loss: 0.3812 - 581ms/epoch - 12ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 34/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 1s - loss: 0.3770 - 515ms/epoch - 10ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 35/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 1s - loss: 0.3814 - 654ms/epoch - 13ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 36/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 1s - loss: 0.3789 - 522ms/epoch - 10ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 37/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 0s - loss: 0.3799 - 488ms/epoch - 10ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 38/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 0s - loss: 0.3795 - 450ms/epoch - 9ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 39/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 1s - loss: 0.3786 - 692ms/epoch - 14ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 40/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 1s - loss: 0.3773 - 741ms/epoch - 15ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 41/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 1s - loss: 0.3765 - 555ms/epoch - 11ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 42/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 0s - loss: 0.3799 - 485ms/epoch - 10ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 43/100\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"50/50 - 1s - loss: 0.3786 - 665ms/epoch - 13ms/step\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Epoch 44/100\n"
|
|
]
|
|
},
|
|
{
|
|
"ename": "KeyboardInterrupt",
|
|
"evalue": "",
|
|
"output_type": "error",
|
|
"traceback": [
|
|
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
|
|
"\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)",
|
|
"Input \u001b[0;32mIn [1]\u001b[0m, in \u001b[0;36m<cell line: 58>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 55\u001b[0m model\u001b[38;5;241m.\u001b[39mcompile(loss\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mmean_squared_error\u001b[39m\u001b[38;5;124m'\u001b[39m, optimizer\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mrmsprop\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[1;32m 56\u001b[0m model\u001b[38;5;241m.\u001b[39msummary()\n\u001b[0;32m---> 58\u001b[0m \u001b[43mmodel\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfit\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtrainX\u001b[49m\u001b[43m,\u001b[49m\u001b[43mtrainY\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mepochs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m100\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mbatch_size\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m16\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mverbose\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m2\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 59\u001b[0m trainPredict \u001b[38;5;241m=\u001b[39m model\u001b[38;5;241m.\u001b[39mpredict(trainX)\n\u001b[1;32m 60\u001b[0m testPredict\u001b[38;5;241m=\u001b[39m model\u001b[38;5;241m.\u001b[39mpredict(testX)\n",
|
|
"File \u001b[0;32m~/miniforge3/envs/myenv/lib/python3.9/site-packages/keras/utils/traceback_utils.py:64\u001b[0m, in \u001b[0;36mfilter_traceback.<locals>.error_handler\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 62\u001b[0m filtered_tb \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m 63\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m---> 64\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 65\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e: \u001b[38;5;66;03m# pylint: disable=broad-except\u001b[39;00m\n\u001b[1;32m 66\u001b[0m filtered_tb \u001b[38;5;241m=\u001b[39m _process_traceback_frames(e\u001b[38;5;241m.\u001b[39m__traceback__)\n",
|
|
"File \u001b[0;32m~/miniforge3/envs/myenv/lib/python3.9/site-packages/keras/engine/training.py:1384\u001b[0m, in \u001b[0;36mModel.fit\u001b[0;34m(self, x, y, batch_size, epochs, verbose, callbacks, validation_split, validation_data, shuffle, class_weight, sample_weight, initial_epoch, steps_per_epoch, validation_steps, validation_batch_size, validation_freq, max_queue_size, workers, use_multiprocessing)\u001b[0m\n\u001b[1;32m 1377\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m tf\u001b[38;5;241m.\u001b[39mprofiler\u001b[38;5;241m.\u001b[39mexperimental\u001b[38;5;241m.\u001b[39mTrace(\n\u001b[1;32m 1378\u001b[0m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mtrain\u001b[39m\u001b[38;5;124m'\u001b[39m,\n\u001b[1;32m 1379\u001b[0m epoch_num\u001b[38;5;241m=\u001b[39mepoch,\n\u001b[1;32m 1380\u001b[0m step_num\u001b[38;5;241m=\u001b[39mstep,\n\u001b[1;32m 1381\u001b[0m batch_size\u001b[38;5;241m=\u001b[39mbatch_size,\n\u001b[1;32m 1382\u001b[0m _r\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m1\u001b[39m):\n\u001b[1;32m 1383\u001b[0m callbacks\u001b[38;5;241m.\u001b[39mon_train_batch_begin(step)\n\u001b[0;32m-> 1384\u001b[0m tmp_logs \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtrain_function\u001b[49m\u001b[43m(\u001b[49m\u001b[43miterator\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1385\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m data_handler\u001b[38;5;241m.\u001b[39mshould_sync:\n\u001b[1;32m 1386\u001b[0m context\u001b[38;5;241m.\u001b[39masync_wait()\n",
|
|
"File \u001b[0;32m~/miniforge3/envs/myenv/lib/python3.9/site-packages/tensorflow/python/util/traceback_utils.py:150\u001b[0m, in \u001b[0;36mfilter_traceback.<locals>.error_handler\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 148\u001b[0m filtered_tb \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m 149\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 150\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 151\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m 152\u001b[0m filtered_tb \u001b[38;5;241m=\u001b[39m _process_traceback_frames(e\u001b[38;5;241m.\u001b[39m__traceback__)\n",
|
|
"File \u001b[0;32m~/miniforge3/envs/myenv/lib/python3.9/site-packages/tensorflow/python/eager/def_function.py:915\u001b[0m, in \u001b[0;36mFunction.__call__\u001b[0;34m(self, *args, **kwds)\u001b[0m\n\u001b[1;32m 912\u001b[0m compiler \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mxla\u001b[39m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_jit_compile \u001b[38;5;28;01melse\u001b[39;00m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mnonXla\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 914\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m OptionalXlaContext(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_jit_compile):\n\u001b[0;32m--> 915\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_call\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwds\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 917\u001b[0m new_tracing_count \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mexperimental_get_tracing_count()\n\u001b[1;32m 918\u001b[0m without_tracing \u001b[38;5;241m=\u001b[39m (tracing_count \u001b[38;5;241m==\u001b[39m new_tracing_count)\n",
|
|
"File \u001b[0;32m~/miniforge3/envs/myenv/lib/python3.9/site-packages/tensorflow/python/eager/def_function.py:947\u001b[0m, in \u001b[0;36mFunction._call\u001b[0;34m(self, *args, **kwds)\u001b[0m\n\u001b[1;32m 944\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_lock\u001b[38;5;241m.\u001b[39mrelease()\n\u001b[1;32m 945\u001b[0m \u001b[38;5;66;03m# In this case we have created variables on the first call, so we run the\u001b[39;00m\n\u001b[1;32m 946\u001b[0m \u001b[38;5;66;03m# defunned version which is guaranteed to never create variables.\u001b[39;00m\n\u001b[0;32m--> 947\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_stateless_fn\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwds\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;66;03m# pylint: disable=not-callable\u001b[39;00m\n\u001b[1;32m 948\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_stateful_fn \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 949\u001b[0m \u001b[38;5;66;03m# Release the lock early so that multiple threads can perform the call\u001b[39;00m\n\u001b[1;32m 950\u001b[0m \u001b[38;5;66;03m# in parallel.\u001b[39;00m\n\u001b[1;32m 951\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_lock\u001b[38;5;241m.\u001b[39mrelease()\n",
|
|
"File \u001b[0;32m~/miniforge3/envs/myenv/lib/python3.9/site-packages/tensorflow/python/eager/function.py:2956\u001b[0m, in \u001b[0;36mFunction.__call__\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 2953\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_lock:\n\u001b[1;32m 2954\u001b[0m (graph_function,\n\u001b[1;32m 2955\u001b[0m filtered_flat_args) \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_maybe_define_function(args, kwargs)\n\u001b[0;32m-> 2956\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mgraph_function\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_call_flat\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 2957\u001b[0m \u001b[43m \u001b[49m\u001b[43mfiltered_flat_args\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcaptured_inputs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mgraph_function\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcaptured_inputs\u001b[49m\u001b[43m)\u001b[49m\n",
|
|
"File \u001b[0;32m~/miniforge3/envs/myenv/lib/python3.9/site-packages/tensorflow/python/eager/function.py:1853\u001b[0m, in \u001b[0;36mConcreteFunction._call_flat\u001b[0;34m(self, args, captured_inputs, cancellation_manager)\u001b[0m\n\u001b[1;32m 1849\u001b[0m possible_gradient_type \u001b[38;5;241m=\u001b[39m gradients_util\u001b[38;5;241m.\u001b[39mPossibleTapeGradientTypes(args)\n\u001b[1;32m 1850\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m (possible_gradient_type \u001b[38;5;241m==\u001b[39m gradients_util\u001b[38;5;241m.\u001b[39mPOSSIBLE_GRADIENT_TYPES_NONE\n\u001b[1;32m 1851\u001b[0m \u001b[38;5;129;01mand\u001b[39;00m executing_eagerly):\n\u001b[1;32m 1852\u001b[0m \u001b[38;5;66;03m# No tape is watching; skip to running the function.\u001b[39;00m\n\u001b[0;32m-> 1853\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_build_call_outputs(\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_inference_function\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcall\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1854\u001b[0m \u001b[43m \u001b[49m\u001b[43mctx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcancellation_manager\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcancellation_manager\u001b[49m\u001b[43m)\u001b[49m)\n\u001b[1;32m 1855\u001b[0m forward_backward \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_select_forward_and_backward_functions(\n\u001b[1;32m 1856\u001b[0m args,\n\u001b[1;32m 1857\u001b[0m possible_gradient_type,\n\u001b[1;32m 1858\u001b[0m executing_eagerly)\n\u001b[1;32m 1859\u001b[0m forward_function, args_with_tangents \u001b[38;5;241m=\u001b[39m forward_backward\u001b[38;5;241m.\u001b[39mforward()\n",
|
|
"File \u001b[0;32m~/miniforge3/envs/myenv/lib/python3.9/site-packages/tensorflow/python/eager/function.py:499\u001b[0m, in \u001b[0;36m_EagerDefinedFunction.call\u001b[0;34m(self, ctx, args, cancellation_manager)\u001b[0m\n\u001b[1;32m 497\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m _InterpolateFunctionError(\u001b[38;5;28mself\u001b[39m):\n\u001b[1;32m 498\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m cancellation_manager \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m--> 499\u001b[0m outputs \u001b[38;5;241m=\u001b[39m \u001b[43mexecute\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mexecute\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 500\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mstr\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msignature\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mname\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 501\u001b[0m \u001b[43m \u001b[49m\u001b[43mnum_outputs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_num_outputs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 502\u001b[0m \u001b[43m \u001b[49m\u001b[43minputs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 503\u001b[0m \u001b[43m \u001b[49m\u001b[43mattrs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mattrs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 504\u001b[0m \u001b[43m \u001b[49m\u001b[43mctx\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mctx\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 505\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 506\u001b[0m outputs \u001b[38;5;241m=\u001b[39m execute\u001b[38;5;241m.\u001b[39mexecute_with_cancellation(\n\u001b[1;32m 507\u001b[0m \u001b[38;5;28mstr\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39msignature\u001b[38;5;241m.\u001b[39mname),\n\u001b[1;32m 508\u001b[0m num_outputs\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_num_outputs,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 511\u001b[0m ctx\u001b[38;5;241m=\u001b[39mctx,\n\u001b[1;32m 512\u001b[0m cancellation_manager\u001b[38;5;241m=\u001b[39mcancellation_manager)\n",
|
|
"File \u001b[0;32m~/miniforge3/envs/myenv/lib/python3.9/site-packages/tensorflow/python/eager/execute.py:54\u001b[0m, in \u001b[0;36mquick_execute\u001b[0;34m(op_name, num_outputs, inputs, attrs, ctx, name)\u001b[0m\n\u001b[1;32m 52\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 53\u001b[0m ctx\u001b[38;5;241m.\u001b[39mensure_initialized()\n\u001b[0;32m---> 54\u001b[0m tensors \u001b[38;5;241m=\u001b[39m \u001b[43mpywrap_tfe\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mTFE_Py_Execute\u001b[49m\u001b[43m(\u001b[49m\u001b[43mctx\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_handle\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdevice_name\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mop_name\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 55\u001b[0m \u001b[43m \u001b[49m\u001b[43minputs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mattrs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnum_outputs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 56\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m core\u001b[38;5;241m.\u001b[39m_NotOkStatusException \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m 57\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m name \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n",
|
|
"\u001b[0;31mKeyboardInterrupt\u001b[0m: "
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"%matplotlib inline\n",
|
|
"\n",
|
|
"# Start importing packages\n",
|
|
"import pandas as pd\n",
|
|
"import numpy as np\n",
|
|
"import matplotlib.pyplot as plt\n",
|
|
"import tensorflow as tf\n",
|
|
"from tensorflow.keras import datasets, layers, models\n",
|
|
"from tensorflow.keras.layers import Input\n",
|
|
"from tensorflow.keras.models import Model, Sequential \n",
|
|
"from tensorflow.keras.layers import Dense, SimpleRNN, LSTM, GRU\n",
|
|
"from tensorflow.keras import optimizers \n",
|
|
"from tensorflow.keras import regularizers \n",
|
|
"from tensorflow.keras.utils import to_categorical \n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"# convert into dataset matrix\n",
|
|
"def convertToMatrix(data, step):\n",
|
|
" X, Y =[], []\n",
|
|
" for i in range(len(data)-step):\n",
|
|
" d=i+step \n",
|
|
" X.append(data[i:d,])\n",
|
|
" Y.append(data[d,])\n",
|
|
" return np.array(X), np.array(Y)\n",
|
|
"\n",
|
|
"step = 4\n",
|
|
"N = 1000 \n",
|
|
"Tp = 800 \n",
|
|
"\n",
|
|
"t=np.arange(0,N)\n",
|
|
"x=np.sin(0.02*t)+2*np.random.rand(N)\n",
|
|
"df = pd.DataFrame(x)\n",
|
|
"df.head()\n",
|
|
"\n",
|
|
"plt.plot(df)\n",
|
|
"plt.show()\n",
|
|
"\n",
|
|
"values=df.values\n",
|
|
"train,test = values[0:Tp,:], values[Tp:N,:]\n",
|
|
"\n",
|
|
"# add step elements into train and test\n",
|
|
"test = np.append(test,np.repeat(test[-1,],step))\n",
|
|
"train = np.append(train,np.repeat(train[-1,],step))\n",
|
|
" \n",
|
|
"trainX,trainY =convertToMatrix(train,step)\n",
|
|
"testX,testY =convertToMatrix(test,step)\n",
|
|
"trainX = np.reshape(trainX, (trainX.shape[0], 1, trainX.shape[1]))\n",
|
|
"testX = np.reshape(testX, (testX.shape[0], 1, testX.shape[1]))\n",
|
|
"\n",
|
|
"model = Sequential()\n",
|
|
"model.add(SimpleRNN(units=32, input_shape=(1,step), activation=\"relu\"))\n",
|
|
"model.add(Dense(8, activation=\"relu\")) \n",
|
|
"model.add(Dense(1))\n",
|
|
"model.compile(loss='mean_squared_error', optimizer='rmsprop')\n",
|
|
"model.summary()\n",
|
|
"\n",
|
|
"model.fit(trainX,trainY, epochs=100, batch_size=16, verbose=2)\n",
|
|
"trainPredict = model.predict(trainX)\n",
|
|
"testPredict= model.predict(testX)\n",
|
|
"predicted=np.concatenate((trainPredict,testPredict),axis=0)\n",
|
|
"\n",
|
|
"trainScore = model.evaluate(trainX, trainY, verbose=0)\n",
|
|
"print(trainScore)\n",
|
|
"\n",
|
|
"index = df.index.values\n",
|
|
"plt.plot(index,df)\n",
|
|
"plt.plot(index,predicted)\n",
|
|
"plt.axvline(df.index[Tp], c=\"r\")\n",
|
|
"plt.show()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "897a47c7",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"## An extrapolation example\n",
|
|
"\n",
|
|
"The following code provides an example of how recurrent neural\n",
|
|
"networks can be used to extrapolate to unknown values of physics data\n",
|
|
"sets. Specifically, the data sets used in this program come from\n",
|
|
"a quantum mechanical many-body calculation of energies as functions of the number of particles."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"id": "6776ae2a",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"\n",
|
|
"# For matrices and calculations\n",
|
|
"import numpy as np\n",
|
|
"# For machine learning (backend for keras)\n",
|
|
"import tensorflow as tf\n",
|
|
"# User-friendly machine learning library\n",
|
|
"# Front end for TensorFlow\n",
|
|
"import tensorflow.keras\n",
|
|
"# Different methods from Keras needed to create an RNN\n",
|
|
"# This is not necessary but it shortened function calls \n",
|
|
"# that need to be used in the code.\n",
|
|
"from tensorflow.keras import datasets, layers, models\n",
|
|
"from tensorflow.keras.layers import Input\n",
|
|
"from tensorflow.keras import regularizers\n",
|
|
"from tensorflow.keras.models import Model, Sequential\n",
|
|
"from tensorflow.keras.layers import Dense, SimpleRNN, LSTM, GRU\n",
|
|
"# For timing the code\n",
|
|
"from timeit import default_timer as timer\n",
|
|
"# For plotting\n",
|
|
"import matplotlib.pyplot as plt\n",
|
|
"\n",
|
|
"\n",
|
|
"# The data set\n",
|
|
"datatype='VaryDimension'\n",
|
|
"X_tot = np.arange(2, 42, 2)\n",
|
|
"y_tot = np.array([-0.03077640549, -0.08336233266, -0.1446729567, -0.2116753732, -0.2830637392, -0.3581341341, -0.436462435, -0.5177783846,\n",
|
|
"\t-0.6019067271, -0.6887363571, -0.7782028952, -0.8702784034, -0.9649652536, -1.062292565, -1.16231451, \n",
|
|
"\t-1.265109911, -1.370782966, -1.479465113, -1.591317992, -1.70653767])"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "35227d36",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"The way the recurrent neural networks are trained in this program\n",
|
|
"differs from how machine learning algorithms are usually trained.\n",
|
|
"Typically a machine learning algorithm is trained by learning the\n",
|
|
"relationship between the x data and the y data. In this program, the\n",
|
|
"recurrent neural network will be trained to recognize the relationship\n",
|
|
"in a sequence of y values. This is type of data formatting is\n",
|
|
"typically used time series forcasting, but it can also be used in any\n",
|
|
"extrapolation (time series forecasting is just a specific type of\n",
|
|
"extrapolation along the time axis). This method of data formatting\n",
|
|
"does not use the x data and assumes that the y data are evenly spaced.\n",
|
|
"\n",
|
|
"For a standard machine learning algorithm, the training data has the\n",
|
|
"form of (x,y) so the machine learning algorithm learns to assiciate a\n",
|
|
"y value with a given x value. This is useful when the test data has x\n",
|
|
"values within the same range as the training data. However, for this\n",
|
|
"application, the x values of the test data are outside of the x values\n",
|
|
"of the training data and the traditional method of training a machine\n",
|
|
"learning algorithm does not work as well. For this reason, the\n",
|
|
"recurrent neural network is trained on sequences of y values of the\n",
|
|
"form ((y1, y2), y3), so that the network is concerned with learning\n",
|
|
"the pattern of the y data and not the relation between the x and y\n",
|
|
"data. As long as the pattern of y data outside of the training region\n",
|
|
"stays relatively stable compared to what was inside the training\n",
|
|
"region, this method of training can produce accurate extrapolations to\n",
|
|
"y values far removed from the training data set."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"id": "7dc577d1",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# FORMAT_DATA\n",
|
|
"def format_data(data, length_of_sequence = 2): \n",
|
|
" \"\"\"\n",
|
|
" Inputs:\n",
|
|
" data(a numpy array): the data that will be the inputs to the recurrent neural\n",
|
|
" network\n",
|
|
" length_of_sequence (an int): the number of elements in one iteration of the\n",
|
|
" sequence patter. For a function approximator use length_of_sequence = 2.\n",
|
|
" Returns:\n",
|
|
" rnn_input (a 3D numpy array): the input data for the recurrent neural network. Its\n",
|
|
" dimensions are length of data - length of sequence, length of sequence, \n",
|
|
" dimnsion of data\n",
|
|
" rnn_output (a numpy array): the training data for the neural network\n",
|
|
" Formats data to be used in a recurrent neural network.\n",
|
|
" \"\"\"\n",
|
|
"\n",
|
|
" X, Y = [], []\n",
|
|
" for i in range(len(data)-length_of_sequence):\n",
|
|
" # Get the next length_of_sequence elements\n",
|
|
" a = data[i:i+length_of_sequence]\n",
|
|
" # Get the element that immediately follows that\n",
|
|
" b = data[i+length_of_sequence]\n",
|
|
" # Reshape so that each data point is contained in its own array\n",
|
|
" a = np.reshape (a, (len(a), 1))\n",
|
|
" X.append(a)\n",
|
|
" Y.append(b)\n",
|
|
" rnn_input = np.array(X)\n",
|
|
" rnn_output = np.array(Y)\n",
|
|
"\n",
|
|
" return rnn_input, rnn_output\n",
|
|
"\n",
|
|
"\n",
|
|
"# ## Defining the Recurrent Neural Network Using Keras\n",
|
|
"# \n",
|
|
"# The following method defines a simple recurrent neural network in keras consisting of one input layer, one hidden layer, and one output layer.\n",
|
|
"\n",
|
|
"def rnn(length_of_sequences, batch_size = None, stateful = False):\n",
|
|
" \"\"\"\n",
|
|
" Inputs:\n",
|
|
" length_of_sequences (an int): the number of y values in \"x data\". This is determined\n",
|
|
" when the data is formatted\n",
|
|
" batch_size (an int): Default value is None. See Keras documentation of SimpleRNN.\n",
|
|
" stateful (a boolean): Default value is False. See Keras documentation of SimpleRNN.\n",
|
|
" Returns:\n",
|
|
" model (a Keras model): The recurrent neural network that is built and compiled by this\n",
|
|
" method\n",
|
|
" Builds and compiles a recurrent neural network with one hidden layer and returns the model.\n",
|
|
" \"\"\"\n",
|
|
" # Number of neurons in the input and output layers\n",
|
|
" in_out_neurons = 1\n",
|
|
" # Number of neurons in the hidden layer\n",
|
|
" hidden_neurons = 200\n",
|
|
" # Define the input layer\n",
|
|
" inp = Input(batch_shape=(batch_size, \n",
|
|
" length_of_sequences, \n",
|
|
" in_out_neurons)) \n",
|
|
" # Define the hidden layer as a simple RNN layer with a set number of neurons and add it to \n",
|
|
" # the network immediately after the input layer\n",
|
|
" rnn = SimpleRNN(hidden_neurons, \n",
|
|
" return_sequences=False,\n",
|
|
" stateful = stateful,\n",
|
|
" name=\"RNN\")(inp)\n",
|
|
" # Define the output layer as a dense neural network layer (standard neural network layer)\n",
|
|
" #and add it to the network immediately after the hidden layer.\n",
|
|
" dens = Dense(in_out_neurons,name=\"dense\")(rnn)\n",
|
|
" # Create the machine learning model starting with the input layer and ending with the \n",
|
|
" # output layer\n",
|
|
" model = Model(inputs=[inp],outputs=[dens])\n",
|
|
" # Compile the machine learning model using the mean squared error function as the loss \n",
|
|
" # function and an Adams optimizer.\n",
|
|
" model.compile(loss=\"mean_squared_error\", optimizer=\"adam\") \n",
|
|
" return model"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "1978b6a1",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"## Predicting New Points With A Trained Recurrent Neural Network"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 4,
|
|
"id": "d4ad417a",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"def test_rnn (x1, y_test, plot_min, plot_max):\n",
|
|
" \"\"\"\n",
|
|
" Inputs:\n",
|
|
" x1 (a list or numpy array): The complete x component of the data set\n",
|
|
" y_test (a list or numpy array): The complete y component of the data set\n",
|
|
" plot_min (an int or float): the smallest x value used in the training data\n",
|
|
" plot_max (an int or float): the largest x valye used in the training data\n",
|
|
" Returns:\n",
|
|
" None.\n",
|
|
" Uses a trained recurrent neural network model to predict future points in the \n",
|
|
" series. Computes the MSE of the predicted data set from the true data set, saves\n",
|
|
" the predicted data set to a csv file, and plots the predicted and true data sets w\n",
|
|
" while also displaying the data range used for training.\n",
|
|
" \"\"\"\n",
|
|
" # Add the training data as the first dim points in the predicted data array as these\n",
|
|
" # are known values.\n",
|
|
" y_pred = y_test[:dim].tolist()\n",
|
|
" # Generate the first input to the trained recurrent neural network using the last two \n",
|
|
" # points of the training data. Based on how the network was trained this means that it\n",
|
|
" # will predict the first point in the data set after the training data. All of the \n",
|
|
" # brackets are necessary for Tensorflow.\n",
|
|
" next_input = np.array([[[y_test[dim-2]], [y_test[dim-1]]]])\n",
|
|
" # Save the very last point in the training data set. This will be used later.\n",
|
|
" last = [y_test[dim-1]]\n",
|
|
"\n",
|
|
" # Iterate until the complete data set is created.\n",
|
|
" for i in range (dim, len(y_test)):\n",
|
|
" # Predict the next point in the data set using the previous two points.\n",
|
|
" next = model.predict(next_input)\n",
|
|
" # Append just the number of the predicted data set\n",
|
|
" y_pred.append(next[0][0])\n",
|
|
" # Create the input that will be used to predict the next data point in the data set.\n",
|
|
" next_input = np.array([[last, next[0]]], dtype=np.float64)\n",
|
|
" last = next\n",
|
|
"\n",
|
|
" # Print the mean squared error between the known data set and the predicted data set.\n",
|
|
" print('MSE: ', np.square(np.subtract(y_test, y_pred)).mean())\n",
|
|
" # Save the predicted data set as a csv file for later use\n",
|
|
" name = datatype + 'Predicted'+str(dim)+'.csv'\n",
|
|
" np.savetxt(name, y_pred, delimiter=',')\n",
|
|
" # Plot the known data set and the predicted data set. The red box represents the region that was used\n",
|
|
" # for the training data.\n",
|
|
" fig, ax = plt.subplots()\n",
|
|
" ax.plot(x1, y_test, label=\"true\", linewidth=3)\n",
|
|
" ax.plot(x1, y_pred, 'g-.',label=\"predicted\", linewidth=4)\n",
|
|
" ax.legend()\n",
|
|
" # Created a red region to represent the points used in the training data.\n",
|
|
" ax.axvspan(plot_min, plot_max, alpha=0.25, color='red')\n",
|
|
" plt.show()\n",
|
|
"\n",
|
|
"# Check to make sure the data set is complete\n",
|
|
"assert len(X_tot) == len(y_tot)\n",
|
|
"\n",
|
|
"# This is the number of points that will be used in as the training data\n",
|
|
"dim=12\n",
|
|
"\n",
|
|
"# Separate the training data from the whole data set\n",
|
|
"X_train = X_tot[:dim]\n",
|
|
"y_train = y_tot[:dim]\n",
|
|
"\n",
|
|
"\n",
|
|
"# Generate the training data for the RNN, using a sequence of 2\n",
|
|
"rnn_input, rnn_training = format_data(y_train, 2)\n",
|
|
"\n",
|
|
"\n",
|
|
"# Create a recurrent neural network in Keras and produce a summary of the \n",
|
|
"# machine learning model\n",
|
|
"model = rnn(length_of_sequences = rnn_input.shape[1])\n",
|
|
"model.summary()\n",
|
|
"\n",
|
|
"# Start the timer. Want to time training+testing\n",
|
|
"start = timer()\n",
|
|
"# Fit the model using the training data genenerated above using 150 training iterations and a 5%\n",
|
|
"# validation split. Setting verbose to True prints information about each training iteration.\n",
|
|
"hist = model.fit(rnn_input, rnn_training, batch_size=None, epochs=150, \n",
|
|
" verbose=True,validation_split=0.05)\n",
|
|
"\n",
|
|
"for label in [\"loss\",\"val_loss\"]:\n",
|
|
" plt.plot(hist.history[label],label=label)\n",
|
|
"\n",
|
|
"plt.ylabel(\"loss\")\n",
|
|
"plt.xlabel(\"epoch\")\n",
|
|
"plt.title(\"The final validation loss: {}\".format(hist.history[\"val_loss\"][-1]))\n",
|
|
"plt.legend()\n",
|
|
"plt.show()\n",
|
|
"\n",
|
|
"# Use the trained neural network to predict more points of the data set\n",
|
|
"test_rnn(X_tot, y_tot, X_tot[0], X_tot[dim-1])\n",
|
|
"# Stop the timer and calculate the total time needed.\n",
|
|
"end = timer()\n",
|
|
"print('Time: ', end-start)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "e2cad4fc",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"Changing the size of the recurrent neural network and its parameters\n",
|
|
"can drastically change the results you get from the model. The below\n",
|
|
"code takes the simple recurrent neural network from above and adds a\n",
|
|
"second hidden layer, changes the number of neurons in the hidden\n",
|
|
"layer, and explicitly declares the activation function of the hidden\n",
|
|
"layers to be a sigmoid function. The loss function and optimizer can\n",
|
|
"also be changed but are kept the same as the above network. These\n",
|
|
"parameters can be tuned to provide the optimal result from the\n",
|
|
"network. For some ideas on how to improve the performance of a\n",
|
|
"[recurrent neural network](https://danijar.com/tips-for-training-recurrent-neural-networks)."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"id": "c39f1516",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"def rnn_2layers(length_of_sequences, batch_size = None, stateful = False):\n",
|
|
" \"\"\"\n",
|
|
" Inputs:\n",
|
|
" length_of_sequences (an int): the number of y values in \"x data\". This is determined\n",
|
|
" when the data is formatted\n",
|
|
" batch_size (an int): Default value is None. See Keras documentation of SimpleRNN.\n",
|
|
" stateful (a boolean): Default value is False. See Keras documentation of SimpleRNN.\n",
|
|
" Returns:\n",
|
|
" model (a Keras model): The recurrent neural network that is built and compiled by this\n",
|
|
" method\n",
|
|
" Builds and compiles a recurrent neural network with two hidden layers and returns the model.\n",
|
|
" \"\"\"\n",
|
|
" # Number of neurons in the input and output layers\n",
|
|
" in_out_neurons = 1\n",
|
|
" # Number of neurons in the hidden layer, increased from the first network\n",
|
|
" hidden_neurons = 500\n",
|
|
" # Define the input layer\n",
|
|
" inp = Input(batch_shape=(batch_size, \n",
|
|
" length_of_sequences, \n",
|
|
" in_out_neurons)) \n",
|
|
" # Create two hidden layers instead of one hidden layer. Explicitly set the activation\n",
|
|
" # function to be the sigmoid function (the default value is hyperbolic tangent)\n",
|
|
" rnn1 = SimpleRNN(hidden_neurons, \n",
|
|
" return_sequences=True, # This needs to be True if another hidden layer is to follow\n",
|
|
" stateful = stateful, activation = 'sigmoid',\n",
|
|
" name=\"RNN1\")(inp)\n",
|
|
" rnn2 = SimpleRNN(hidden_neurons, \n",
|
|
" return_sequences=False, activation = 'sigmoid',\n",
|
|
" stateful = stateful,\n",
|
|
" name=\"RNN2\")(rnn1)\n",
|
|
" # Define the output layer as a dense neural network layer (standard neural network layer)\n",
|
|
" #and add it to the network immediately after the hidden layer.\n",
|
|
" dens = Dense(in_out_neurons,name=\"dense\")(rnn2)\n",
|
|
" # Create the machine learning model starting with the input layer and ending with the \n",
|
|
" # output layer\n",
|
|
" model = Model(inputs=[inp],outputs=[dens])\n",
|
|
" # Compile the machine learning model using the mean squared error function as the loss \n",
|
|
" # function and an Adams optimizer.\n",
|
|
" model.compile(loss=\"mean_squared_error\", optimizer=\"adam\") \n",
|
|
" return model\n",
|
|
"\n",
|
|
"# Check to make sure the data set is complete\n",
|
|
"assert len(X_tot) == len(y_tot)\n",
|
|
"\n",
|
|
"# This is the number of points that will be used in as the training data\n",
|
|
"dim=12\n",
|
|
"\n",
|
|
"# Separate the training data from the whole data set\n",
|
|
"X_train = X_tot[:dim]\n",
|
|
"y_train = y_tot[:dim]\n",
|
|
"\n",
|
|
"\n",
|
|
"# Generate the training data for the RNN, using a sequence of 2\n",
|
|
"rnn_input, rnn_training = format_data(y_train, 2)\n",
|
|
"\n",
|
|
"\n",
|
|
"# Create a recurrent neural network in Keras and produce a summary of the \n",
|
|
"# machine learning model\n",
|
|
"model = rnn_2layers(length_of_sequences = 2)\n",
|
|
"model.summary()\n",
|
|
"\n",
|
|
"# Start the timer. Want to time training+testing\n",
|
|
"start = timer()\n",
|
|
"# Fit the model using the training data genenerated above using 150 training iterations and a 5%\n",
|
|
"# validation split. Setting verbose to True prints information about each training iteration.\n",
|
|
"hist = model.fit(rnn_input, rnn_training, batch_size=None, epochs=150, \n",
|
|
" verbose=True,validation_split=0.05)\n",
|
|
"\n",
|
|
"\n",
|
|
"# This section plots the training loss and the validation loss as a function of training iteration.\n",
|
|
"# This is not required for analyzing the couple cluster data but can help determine if the network is\n",
|
|
"# being overtrained.\n",
|
|
"for label in [\"loss\",\"val_loss\"]:\n",
|
|
" plt.plot(hist.history[label],label=label)\n",
|
|
"\n",
|
|
"plt.ylabel(\"loss\")\n",
|
|
"plt.xlabel(\"epoch\")\n",
|
|
"plt.title(\"The final validation loss: {}\".format(hist.history[\"val_loss\"][-1]))\n",
|
|
"plt.legend()\n",
|
|
"plt.show()\n",
|
|
"\n",
|
|
"# Use the trained neural network to predict more points of the data set\n",
|
|
"test_rnn(X_tot, y_tot, X_tot[0], X_tot[dim-1])\n",
|
|
"# Stop the timer and calculate the total time needed.\n",
|
|
"end = timer()\n",
|
|
"print('Time: ', end-start)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "842c7602",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"## Other Types of Recurrent Neural Networks\n",
|
|
"\n",
|
|
"Besides a simple recurrent neural network layer, there are two other\n",
|
|
"commonly used types of recurrent neural network layers: Long Short\n",
|
|
"Term Memory (LSTM) and Gated Recurrent Unit (GRU). For a short\n",
|
|
"introduction to these layers see <https://medium.com/mindboard/lstm-vs-gru-experimental-comparison-955820c21e8b>\n",
|
|
"and <https://medium.com/mindboard/lstm-vs-gru-experimental-comparison-955820c21e8b>.\n",
|
|
"\n",
|
|
"The first network created below is similar to the previous network,\n",
|
|
"but it replaces the SimpleRNN layers with LSTM layers. The second\n",
|
|
"network below has two hidden layers made up of GRUs, which are\n",
|
|
"preceeded by two dense (feeddorward) neural network layers. These\n",
|
|
"dense layers \"preprocess\" the data before it reaches the recurrent\n",
|
|
"layers. This architecture has been shown to improve the performance\n",
|
|
"of recurrent neural networks (see the link above and also\n",
|
|
"<https://arxiv.org/pdf/1807.02857.pdf>."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 6,
|
|
"id": "6f0e9b62",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"def lstm_2layers(length_of_sequences, batch_size = None, stateful = False):\n",
|
|
" \"\"\"\n",
|
|
" Inputs:\n",
|
|
" length_of_sequences (an int): the number of y values in \"x data\". This is determined\n",
|
|
" when the data is formatted\n",
|
|
" batch_size (an int): Default value is None. See Keras documentation of SimpleRNN.\n",
|
|
" stateful (a boolean): Default value is False. See Keras documentation of SimpleRNN.\n",
|
|
" Returns:\n",
|
|
" model (a Keras model): The recurrent neural network that is built and compiled by this\n",
|
|
" method\n",
|
|
" Builds and compiles a recurrent neural network with two LSTM hidden layers and returns the model.\n",
|
|
" \"\"\"\n",
|
|
" # Number of neurons on the input/output layer and the number of neurons in the hidden layer\n",
|
|
" in_out_neurons = 1\n",
|
|
" hidden_neurons = 250\n",
|
|
" # Input Layer\n",
|
|
" inp = Input(batch_shape=(batch_size, \n",
|
|
" length_of_sequences, \n",
|
|
" in_out_neurons)) \n",
|
|
" # Hidden layers (in this case they are LSTM layers instead if SimpleRNN layers)\n",
|
|
" rnn= LSTM(hidden_neurons, \n",
|
|
" return_sequences=True,\n",
|
|
" stateful = stateful,\n",
|
|
" name=\"RNN\", use_bias=True, activation='tanh')(inp)\n",
|
|
" rnn1 = LSTM(hidden_neurons, \n",
|
|
" return_sequences=False,\n",
|
|
" stateful = stateful,\n",
|
|
" name=\"RNN1\", use_bias=True, activation='tanh')(rnn)\n",
|
|
" # Output layer\n",
|
|
" dens = Dense(in_out_neurons,name=\"dense\")(rnn1)\n",
|
|
" # Define the midel\n",
|
|
" model = Model(inputs=[inp],outputs=[dens])\n",
|
|
" # Compile the model\n",
|
|
" model.compile(loss='mean_squared_error', optimizer='adam') \n",
|
|
" # Return the model\n",
|
|
" return model\n",
|
|
"\n",
|
|
"def dnn2_gru2(length_of_sequences, batch_size = None, stateful = False):\n",
|
|
" \"\"\"\n",
|
|
" Inputs:\n",
|
|
" length_of_sequences (an int): the number of y values in \"x data\". This is determined\n",
|
|
" when the data is formatted\n",
|
|
" batch_size (an int): Default value is None. See Keras documentation of SimpleRNN.\n",
|
|
" stateful (a boolean): Default value is False. See Keras documentation of SimpleRNN.\n",
|
|
" Returns:\n",
|
|
" model (a Keras model): The recurrent neural network that is built and compiled by this\n",
|
|
" method\n",
|
|
" Builds and compiles a recurrent neural network with four hidden layers (two dense followed by\n",
|
|
" two GRU layers) and returns the model.\n",
|
|
" \"\"\" \n",
|
|
" # Number of neurons on the input/output layers and hidden layers\n",
|
|
" in_out_neurons = 1\n",
|
|
" hidden_neurons = 250\n",
|
|
" # Input layer\n",
|
|
" inp = Input(batch_shape=(batch_size, \n",
|
|
" length_of_sequences, \n",
|
|
" in_out_neurons)) \n",
|
|
" # Hidden Dense (feedforward) layers\n",
|
|
" dnn = Dense(hidden_neurons/2, activation='relu', name='dnn')(inp)\n",
|
|
" dnn1 = Dense(hidden_neurons/2, activation='relu', name='dnn1')(dnn)\n",
|
|
" # Hidden GRU layers\n",
|
|
" rnn1 = GRU(hidden_neurons, \n",
|
|
" return_sequences=True,\n",
|
|
" stateful = stateful,\n",
|
|
" name=\"RNN1\", use_bias=True)(dnn1)\n",
|
|
" rnn = GRU(hidden_neurons, \n",
|
|
" return_sequences=False,\n",
|
|
" stateful = stateful,\n",
|
|
" name=\"RNN\", use_bias=True)(rnn1)\n",
|
|
" # Output layer\n",
|
|
" dens = Dense(in_out_neurons,name=\"dense\")(rnn)\n",
|
|
" # Define the model\n",
|
|
" model = Model(inputs=[inp],outputs=[dens])\n",
|
|
" # Compile the mdoel\n",
|
|
" model.compile(loss='mean_squared_error', optimizer='adam') \n",
|
|
" # Return the model\n",
|
|
" return model\n",
|
|
"\n",
|
|
"# Check to make sure the data set is complete\n",
|
|
"assert len(X_tot) == len(y_tot)\n",
|
|
"\n",
|
|
"# This is the number of points that will be used in as the training data\n",
|
|
"dim=12\n",
|
|
"\n",
|
|
"# Separate the training data from the whole data set\n",
|
|
"X_train = X_tot[:dim]\n",
|
|
"y_train = y_tot[:dim]\n",
|
|
"\n",
|
|
"\n",
|
|
"# Generate the training data for the RNN, using a sequence of 2\n",
|
|
"rnn_input, rnn_training = format_data(y_train, 2)\n",
|
|
"\n",
|
|
"\n",
|
|
"# Create a recurrent neural network in Keras and produce a summary of the \n",
|
|
"# machine learning model\n",
|
|
"# Change the method name to reflect which network you want to use\n",
|
|
"model = dnn2_gru2(length_of_sequences = 2)\n",
|
|
"model.summary()\n",
|
|
"\n",
|
|
"# Start the timer. Want to time training+testing\n",
|
|
"start = timer()\n",
|
|
"# Fit the model using the training data genenerated above using 150 training iterations and a 5%\n",
|
|
"# validation split. Setting verbose to True prints information about each training iteration.\n",
|
|
"hist = model.fit(rnn_input, rnn_training, batch_size=None, epochs=150, \n",
|
|
" verbose=True,validation_split=0.05)\n",
|
|
"\n",
|
|
"\n",
|
|
"# This section plots the training loss and the validation loss as a function of training iteration.\n",
|
|
"# This is not required for analyzing the couple cluster data but can help determine if the network is\n",
|
|
"# being overtrained.\n",
|
|
"for label in [\"loss\",\"val_loss\"]:\n",
|
|
" plt.plot(hist.history[label],label=label)\n",
|
|
"\n",
|
|
"plt.ylabel(\"loss\")\n",
|
|
"plt.xlabel(\"epoch\")\n",
|
|
"plt.title(\"The final validation loss: {}\".format(hist.history[\"val_loss\"][-1]))\n",
|
|
"plt.legend()\n",
|
|
"plt.show()\n",
|
|
"\n",
|
|
"# Use the trained neural network to predict more points of the data set\n",
|
|
"test_rnn(X_tot, y_tot, X_tot[0], X_tot[dim-1])\n",
|
|
"# Stop the timer and calculate the total time needed.\n",
|
|
"end = timer()\n",
|
|
"print('Time: ', end-start)\n",
|
|
"\n",
|
|
"\n",
|
|
"# ### Training Recurrent Neural Networks in the Standard Way (i.e. learning the relationship between the X and Y data)\n",
|
|
"# \n",
|
|
"# Finally, comparing the performace of a recurrent neural network using the standard data formatting to the performance of the network with time sequence data formatting shows the benefit of this type of data formatting with extrapolation.\n",
|
|
"\n",
|
|
"# Check to make sure the data set is complete\n",
|
|
"assert len(X_tot) == len(y_tot)\n",
|
|
"\n",
|
|
"# This is the number of points that will be used in as the training data\n",
|
|
"dim=12\n",
|
|
"\n",
|
|
"# Separate the training data from the whole data set\n",
|
|
"X_train = X_tot[:dim]\n",
|
|
"y_train = y_tot[:dim]\n",
|
|
"\n",
|
|
"# Reshape the data for Keras specifications\n",
|
|
"X_train = X_train.reshape((dim, 1))\n",
|
|
"y_train = y_train.reshape((dim, 1))\n",
|
|
"\n",
|
|
"\n",
|
|
"# Create a recurrent neural network in Keras and produce a summary of the \n",
|
|
"# machine learning model\n",
|
|
"# Set the sequence length to 1 for regular data formatting \n",
|
|
"model = rnn(length_of_sequences = 1)\n",
|
|
"model.summary()\n",
|
|
"\n",
|
|
"# Start the timer. Want to time training+testing\n",
|
|
"start = timer()\n",
|
|
"# Fit the model using the training data genenerated above using 150 training iterations and a 5%\n",
|
|
"# validation split. Setting verbose to True prints information about each training iteration.\n",
|
|
"hist = model.fit(X_train, y_train, batch_size=None, epochs=150, \n",
|
|
" verbose=True,validation_split=0.05)\n",
|
|
"\n",
|
|
"\n",
|
|
"# This section plots the training loss and the validation loss as a function of training iteration.\n",
|
|
"# This is not required for analyzing the couple cluster data but can help determine if the network is\n",
|
|
"# being overtrained.\n",
|
|
"for label in [\"loss\",\"val_loss\"]:\n",
|
|
" plt.plot(hist.history[label],label=label)\n",
|
|
"\n",
|
|
"plt.ylabel(\"loss\")\n",
|
|
"plt.xlabel(\"epoch\")\n",
|
|
"plt.title(\"The final validation loss: {}\".format(hist.history[\"val_loss\"][-1]))\n",
|
|
"plt.legend()\n",
|
|
"plt.show()\n",
|
|
"\n",
|
|
"# Use the trained neural network to predict the remaining data points\n",
|
|
"X_pred = X_tot[dim:]\n",
|
|
"X_pred = X_pred.reshape((len(X_pred), 1))\n",
|
|
"y_model = model.predict(X_pred)\n",
|
|
"y_pred = np.concatenate((y_tot[:dim], y_model.flatten()))\n",
|
|
"\n",
|
|
"# Plot the known data set and the predicted data set. The red box represents the region that was used\n",
|
|
"# for the training data.\n",
|
|
"fig, ax = plt.subplots()\n",
|
|
"ax.plot(X_tot, y_tot, label=\"true\", linewidth=3)\n",
|
|
"ax.plot(X_tot, y_pred, 'g-.',label=\"predicted\", linewidth=4)\n",
|
|
"ax.legend()\n",
|
|
"# Created a red region to represent the points used in the training data.\n",
|
|
"ax.axvspan(X_tot[0], X_tot[dim], alpha=0.25, color='red')\n",
|
|
"plt.show()\n",
|
|
"\n",
|
|
"# Stop the timer and calculate the total time needed.\n",
|
|
"end = timer()\n",
|
|
"print('Time: ', end-start)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "0752ba7f",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"# Generative Models\n",
|
|
"\n",
|
|
"**Generative models** describe a class of statistical models that are a contrast\n",
|
|
"to **discriminative models**. Informally we say that generative models can\n",
|
|
"generate new data instances while discriminative models discriminate between\n",
|
|
"different kinds of data instances. A generative model could generate new photos\n",
|
|
"of animals that look like 'real' animals while a discriminative model could tell\n",
|
|
"a dog from a cat. More formally, given a data set $x$ and a set of labels /\n",
|
|
"targets $y$. Generative models capture the joint probability $p(x, y)$, or\n",
|
|
"just $p(x)$ if there are no labels, while discriminative models capture the\n",
|
|
"conditional probability $p(y | x)$. Discriminative models generally try to draw\n",
|
|
"boundaries in the data space (often high dimensional), while generative models\n",
|
|
"try to model how data is placed throughout the space.\n",
|
|
"\n",
|
|
"**Note**: this material is thanks to Linus Ekstrøm."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "784138f8",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"## Generative Adversarial Networks\n",
|
|
"\n",
|
|
"**Generative Adversarial Networks** are a type of unsupervised machine learning\n",
|
|
"algorithm proposed by [Goodfellow et. al](https://arxiv.org/pdf/1406.2661.pdf)\n",
|
|
"in 2014 (short and good article).\n",
|
|
"\n",
|
|
"The simplest formulation of\n",
|
|
"the model is based on a game theoretic approach, *zero sum game*, where we pit\n",
|
|
"two neural networks against one another. We define two rival networks, one\n",
|
|
"generator $g$, and one discriminator $d$. The generator directly produces\n",
|
|
"samples"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "a42f89ee",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"<!-- Equation labels as ordinary links -->\n",
|
|
"<div id=\"_auto1\"></div>\n",
|
|
"\n",
|
|
"$$\n",
|
|
"\\begin{equation}\n",
|
|
" x = g(z; \\theta^{(g)})\n",
|
|
"\\label{_auto1} \\tag{1}\n",
|
|
"\\end{equation}\n",
|
|
"$$"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "abe7212f",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"The discriminator attempts to distinguish between samples drawn from the\n",
|
|
"training data and samples drawn from the generator. In other words, it tries to\n",
|
|
"tell the difference between the fake data produced by $g$ and the actual data\n",
|
|
"samples we want to do prediction on. The discriminator outputs a probability\n",
|
|
"value given by"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "0821eaee",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"<!-- Equation labels as ordinary links -->\n",
|
|
"<div id=\"_auto2\"></div>\n",
|
|
"\n",
|
|
"$$\n",
|
|
"\\begin{equation}\n",
|
|
" d(x; \\theta^{(d)})\n",
|
|
"\\label{_auto2} \\tag{2}\n",
|
|
"\\end{equation}\n",
|
|
"$$"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "55e0ccf6",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"indicating the probability that $x$ is a real training example rather than a\n",
|
|
"fake sample the generator has generated. The simplest way to formulate the\n",
|
|
"learning process in a generative adversarial network is a zero-sum game, in\n",
|
|
"which a function"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "f37ece14",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"<!-- Equation labels as ordinary links -->\n",
|
|
"<div id=\"_auto3\"></div>\n",
|
|
"\n",
|
|
"$$\n",
|
|
"\\begin{equation}\n",
|
|
" v(\\theta^{(g)}, \\theta^{(d)})\n",
|
|
"\\label{_auto3} \\tag{3}\n",
|
|
"\\end{equation}\n",
|
|
"$$"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "d6d6d5fa",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"determines the reward for the discriminator, while the generator gets the\n",
|
|
"conjugate reward"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "3c74b45c",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"<!-- Equation labels as ordinary links -->\n",
|
|
"<div id=\"_auto4\"></div>\n",
|
|
"\n",
|
|
"$$\n",
|
|
"\\begin{equation}\n",
|
|
" -v(\\theta^{(g)}, \\theta^{(d)})\n",
|
|
"\\label{_auto4} \\tag{4}\n",
|
|
"\\end{equation}\n",
|
|
"$$"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "605ac8c9",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"During learning both of the networks maximize their own reward function, so that\n",
|
|
"the generator gets better and better at tricking the discriminator, while the\n",
|
|
"discriminator gets better and better at telling the difference between the fake\n",
|
|
"and real data. The generator and discriminator alternate on which one trains at\n",
|
|
"one time (i.e. for one epoch). In other words, we keep the generator constant\n",
|
|
"and train the discriminator, then we keep the discriminator constant to train\n",
|
|
"the generator and repeat. It is this back and forth dynamic which lets GANs\n",
|
|
"tackle otherwise intractable generative problems. As the generator improves with\n",
|
|
" training, the discriminator's performance gets worse because it cannot easily\n",
|
|
" tell the difference between real and fake. If the generator ends up succeeding\n",
|
|
" perfectly, the the discriminator will do no better than random guessing i.e.\n",
|
|
" 50\\%. This progression in the training poses a problem for the convergence\n",
|
|
" criteria for GANs. The discriminator feedback gets less meaningful over time,\n",
|
|
" if we continue training after this point then the generator is effectively\n",
|
|
" training on junk data which can undo the learning up to that point. Therefore,\n",
|
|
" we stop training when the discriminator starts outputting $1/2$ everywhere.\n",
|
|
"\n",
|
|
"At convergence we have"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "cfec7462",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"<!-- Equation labels as ordinary links -->\n",
|
|
"<div id=\"_auto5\"></div>\n",
|
|
"\n",
|
|
"$$\n",
|
|
"\\begin{equation}\n",
|
|
" g^* = \\underset{g}{\\mathrm{argmin}}\\hspace{2pt}\n",
|
|
" \\underset{d}{\\mathrm{max}}v(\\theta^{(g)}, \\theta^{(d)})\n",
|
|
"\\label{_auto5} \\tag{5}\n",
|
|
"\\end{equation}\n",
|
|
"$$"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "37c65d4c",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"The default choice for $v$ is"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "c868a092",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"<!-- Equation labels as ordinary links -->\n",
|
|
"<div id=\"_auto6\"></div>\n",
|
|
"\n",
|
|
"$$\n",
|
|
"\\begin{equation}\n",
|
|
" v(\\theta^{(g)}, \\theta^{(d)}) = \\mathbb{E}_{x\\sim p_\\mathrm{data}}\\log d(x)\n",
|
|
" + \\mathbb{E}_{x\\sim p_\\mathrm{model}}\n",
|
|
" \\log (1 - d(x))\n",
|
|
"\\label{_auto6} \\tag{6}\n",
|
|
"\\end{equation}\n",
|
|
"$$"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "ad465af3",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"The main motivation for the design of GANs is that the learning process requires\n",
|
|
"neither approximate inference (variational autoencoders for example) nor\n",
|
|
"approximation of a partition function. In the case where"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "27858a4e",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"<!-- Equation labels as ordinary links -->\n",
|
|
"<div id=\"_auto7\"></div>\n",
|
|
"\n",
|
|
"$$\n",
|
|
"\\begin{equation}\n",
|
|
" \\underset{d}{\\mathrm{max}}v(\\theta^{(g)}, \\theta^{(d)})\n",
|
|
"\\label{_auto7} \\tag{7}\n",
|
|
"\\end{equation}\n",
|
|
"$$"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "86006023",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"is convex in $\\theta^{(g)} then the procedure is guaranteed to converge and is\n",
|
|
"asymptotically consistent\n",
|
|
"( [Seth Lloyd on QuGANs](https://arxiv.org/pdf/1804.09139.pdf) ).\n",
|
|
"\n",
|
|
"This is in\n",
|
|
"general not the case and it is possible to get situations where the training\n",
|
|
"process never converges because the generator and discriminator chase one\n",
|
|
"another around in the parameter space indefinitely. A much deeper discussion on\n",
|
|
"the currently open research problem of GAN convergence is available\n",
|
|
"[here](https://www.deeplearningbook.org/contents/generative_models.html). To\n",
|
|
"anyone interested in learning more about GANs it is a highly recommended read.\n",
|
|
"Direct quote: \"In this best-performing formulation, the generator aims to\n",
|
|
"increase the log probability that the discriminator makes a mistake, rather than\n",
|
|
"aiming to decrease the log probability that the discriminator makes the correct\n",
|
|
"prediction.\" [Another interesting read](https://arxiv.org/abs/1701.00160)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "2fee38bd",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"## Writing Our First Generative Adversarial Network\n",
|
|
"Let us now move on to actually implementing a GAN in tensorflow. We will study\n",
|
|
"the performance of our GAN on the MNIST dataset. This code is based on and\n",
|
|
"adapted from the\n",
|
|
"[google tutorial](https://www.tensorflow.org/tutorials/generative/dcgan)\n",
|
|
"\n",
|
|
"First we import our libraries"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 7,
|
|
"id": "004a0b53",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"import os\n",
|
|
"import time\n",
|
|
"import numpy as np\n",
|
|
"import tensorflow as tf\n",
|
|
"import matplotlib.pyplot as plt\n",
|
|
"from tensorflow.keras import layers\n",
|
|
"from tensorflow.keras.utils import plot_model"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "353af161",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"Next we define our hyperparameters and import our data the usual way"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 8,
|
|
"id": "8cbaf16a",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"BUFFER_SIZE = 60000\n",
|
|
"BATCH_SIZE = 256\n",
|
|
"EPOCHS = 30\n",
|
|
"\n",
|
|
"data = tf.keras.datasets.mnist.load_data()\n",
|
|
"(train_images, train_labels), (test_images, test_labels) = data\n",
|
|
"train_images = np.reshape(train_images, (train_images.shape[0],\n",
|
|
" 28,\n",
|
|
" 28,\n",
|
|
" 1)).astype('float32')\n",
|
|
"\n",
|
|
"# we normalize between -1 and 1\n",
|
|
"train_images = (train_images - 127.5) / 127.5\n",
|
|
"training_dataset = tf.data.Dataset.from_tensor_slices(\n",
|
|
" train_images).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "822b8cc7",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"### MNIST and GANs\n",
|
|
"\n",
|
|
"Let's have a quick look"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 9,
|
|
"id": "52b5965c",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"plt.imshow(train_images[0], cmap='Greys')\n",
|
|
"plt.show()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "21c5199c",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"Now we define our two models. This is where the 'magic' happens. There are a\n",
|
|
"huge amount of possible formulations for both models. A lot of engineering and\n",
|
|
"trial and error can be done here to try to produce better performing models. For\n",
|
|
"more advanced GANs this is by far the step where you can 'make or break' a\n",
|
|
"model.\n",
|
|
"\n",
|
|
"We start with the generator. As stated in the introductory text the generator\n",
|
|
"$g$ upsamples from a random sample to the shape of what we want to predict. In\n",
|
|
"our case we are trying to predict MNIST images ($28\\times 28$ pixels)."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 10,
|
|
"id": "356759c7",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"def generator_model():\n",
|
|
" \"\"\"\n",
|
|
" The generator uses upsampling layers tf.keras.layers.Conv2DTranspose() to\n",
|
|
" produce an image from a random seed. We start with a Dense layer taking this\n",
|
|
" random sample as an input and subsequently upsample through multiple\n",
|
|
" convolutional layers.\n",
|
|
" \"\"\"\n",
|
|
"\n",
|
|
" # we define our model\n",
|
|
" model = tf.keras.Sequential()\n",
|
|
"\n",
|
|
"\n",
|
|
" # adding our input layer. Dense means that every neuron is connected and\n",
|
|
" # the input shape is the shape of our random noise. The units need to match\n",
|
|
" # in some sense the upsampling strides to reach our desired output shape.\n",
|
|
" # we are using 100 random numbers as our seed\n",
|
|
" model.add(layers.Dense(units=7*7*BATCH_SIZE,\n",
|
|
" use_bias=False,\n",
|
|
" input_shape=(100, )))\n",
|
|
" # we normalize the output form the Dense layer\n",
|
|
" model.add(layers.BatchNormalization())\n",
|
|
" # and add an activation function to our 'layer'. LeakyReLU avoids vanishing\n",
|
|
" # gradient problem\n",
|
|
" model.add(layers.LeakyReLU())\n",
|
|
" model.add(layers.Reshape((7, 7, BATCH_SIZE)))\n",
|
|
" assert model.output_shape == (None, 7, 7, BATCH_SIZE)\n",
|
|
" # even though we just added four keras layers we think of everything above\n",
|
|
" # as 'one' layer\n",
|
|
"\n",
|
|
" # next we add our upscaling convolutional layers\n",
|
|
" model.add(layers.Conv2DTranspose(filters=128,\n",
|
|
" kernel_size=(5, 5),\n",
|
|
" strides=(1, 1),\n",
|
|
" padding='same',\n",
|
|
" use_bias=False))\n",
|
|
" model.add(layers.BatchNormalization())\n",
|
|
" model.add(layers.LeakyReLU())\n",
|
|
" assert model.output_shape == (None, 7, 7, 128)\n",
|
|
"\n",
|
|
" model.add(layers.Conv2DTranspose(filters=64,\n",
|
|
" kernel_size=(5, 5),\n",
|
|
" strides=(2, 2),\n",
|
|
" padding='same',\n",
|
|
" use_bias=False))\n",
|
|
" model.add(layers.BatchNormalization())\n",
|
|
" model.add(layers.LeakyReLU())\n",
|
|
" assert model.output_shape == (None, 14, 14, 64)\n",
|
|
"\n",
|
|
" model.add(layers.Conv2DTranspose(filters=1,\n",
|
|
" kernel_size=(5, 5),\n",
|
|
" strides=(2, 2),\n",
|
|
" padding='same',\n",
|
|
" use_bias=False,\n",
|
|
" activation='tanh'))\n",
|
|
" assert model.output_shape == (None, 28, 28, 1)\n",
|
|
"\n",
|
|
" return model"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "854bcd6b",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"And there we have our 'simple' generator model. Now we move on to defining our\n",
|
|
"discriminator model $d$, which is a convolutional neural network based image\n",
|
|
"classifier."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 11,
|
|
"id": "41473304",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"def discriminator_model():\n",
|
|
" \"\"\"\n",
|
|
" The discriminator is a convolutional neural network based image classifier\n",
|
|
" \"\"\"\n",
|
|
"\n",
|
|
" # we define our model\n",
|
|
" model = tf.keras.Sequential()\n",
|
|
" model.add(layers.Conv2D(filters=64,\n",
|
|
" kernel_size=(5, 5),\n",
|
|
" strides=(2, 2),\n",
|
|
" padding='same',\n",
|
|
" input_shape=[28, 28, 1]))\n",
|
|
" model.add(layers.LeakyReLU())\n",
|
|
" # adding a dropout layer as you do in conv-nets\n",
|
|
" model.add(layers.Dropout(0.3))\n",
|
|
"\n",
|
|
"\n",
|
|
" model.add(layers.Conv2D(filters=128,\n",
|
|
" kernel_size=(5, 5),\n",
|
|
" strides=(2, 2),\n",
|
|
" padding='same'))\n",
|
|
" model.add(layers.LeakyReLU())\n",
|
|
" # adding a dropout layer as you do in conv-nets\n",
|
|
" model.add(layers.Dropout(0.3))\n",
|
|
"\n",
|
|
" model.add(layers.Flatten())\n",
|
|
" model.add(layers.Dense(1))\n",
|
|
"\n",
|
|
" return model"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "353af567",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"Let us take a look at our models."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 12,
|
|
"id": "f899d4e3",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"generator = generator_model()\n",
|
|
"plot_model(generator, show_shapes=True, rankdir='LR')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 13,
|
|
"id": "87ef384b",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"discriminator = discriminator_model()\n",
|
|
"plot_model(discriminator, show_shapes=True, rankdir='LR')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "b2bef82d",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"Next we need a few helper objects we will use in training"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 14,
|
|
"id": "e397847a",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True)\n",
|
|
"generator_optimizer = tf.keras.optimizers.Adam(1e-4)\n",
|
|
"discriminator_optimizer = tf.keras.optimizers.Adam(1e-4)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "db3396cc",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"The first object, *cross_entropy* is our loss function and the two others are\n",
|
|
"our optimizers. Notice we use the same learning rate for both $g$ and $d$. This\n",
|
|
"is because they need to improve their accuracy at approximately equal speeds to\n",
|
|
"get convergence (not necessarily exactly equal). Now we define our loss\n",
|
|
"functions"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 15,
|
|
"id": "931eaced",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"def generator_loss(fake_output):\n",
|
|
" loss = cross_entropy(tf.ones_like(fake_output), fake_output)\n",
|
|
"\n",
|
|
" return loss"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 16,
|
|
"id": "0c4a44bb",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"def discriminator_loss(real_output, fake_output):\n",
|
|
" real_loss = cross_entropy(tf.ones_like(real_output), real_output)\n",
|
|
" fake_loss = cross_entropy(tf.zeros_liks(fake_output), fake_output)\n",
|
|
" total_loss = real_loss + fake_loss\n",
|
|
"\n",
|
|
" return total_loss"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "fcf8f066",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"Next we define a kind of seed to help us compare the learning process over\n",
|
|
"multiple training epochs."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 17,
|
|
"id": "eea2bbee",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"noise_dimension = 100\n",
|
|
"n_examples_to_generate = 16\n",
|
|
"seed_images = tf.random.normal([n_examples_to_generate, noise_dimension])"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "94a6e341",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"Now we have everything we need to define our training step, which we will apply\n",
|
|
"for every step in our training loop. Notice the @tf.function flag signifying\n",
|
|
"that the function is tensorflow 'compiled'. Removing this flag doubles the\n",
|
|
"computation time."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 18,
|
|
"id": "8d48470b",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"@tf.function\n",
|
|
"def train_step(images):\n",
|
|
" noise = tf.random.normal([BATCH_SIZE, noise_dimension])\n",
|
|
"\n",
|
|
" with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:\n",
|
|
" generated_images = generator(noise, training=True)\n",
|
|
"\n",
|
|
" real_output = discriminator(images, training=True)\n",
|
|
" fake_output = discriminator(generated_images, training=True)\n",
|
|
"\n",
|
|
" gen_loss = generator_loss(fake_output)\n",
|
|
" disc_loss = discriminator_loss(real_output, fake_output)\n",
|
|
"\n",
|
|
" gradients_of_generator = gen_tape.gradient(gen_loss,\n",
|
|
" generator.trainable_variables)\n",
|
|
" gradients_of_discriminator = disc_tape.gradient(disc_loss,\n",
|
|
" discriminator.trainable_variables)\n",
|
|
" generator_optimizer.apply_gradients(zip(gradients_of_generator,\n",
|
|
" generator.trainable_variables))\n",
|
|
" discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator,\n",
|
|
" discriminator.trainable_variables))\n",
|
|
"\n",
|
|
" return gen_loss, disc_loss"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "62015b88",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"Next we define a helper function to produce an output over our training epochs\n",
|
|
"to see the predictive progression of our generator model. **Note**: I am including\n",
|
|
"this code here, but comment it out in the training loop."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 19,
|
|
"id": "b189ed96",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"def generate_and_save_images(model, epoch, test_input):\n",
|
|
" # we're making inferences here\n",
|
|
" predictions = model(test_input, training=False)\n",
|
|
"\n",
|
|
" fig = plt.figure(figsize=(4, 4))\n",
|
|
"\n",
|
|
" for i in range(predictions.shape[0]):\n",
|
|
" plt.subplot(4, 4, i+1)\n",
|
|
" plt.imshow(predictions[i, :, :, 0] * 127.5 + 127.5, cmap='gray')\n",
|
|
" plt.axis('off')\n",
|
|
"\n",
|
|
" plt.savefig(f'./images_from_seed_images/image_at_epoch_{str(epoch).zfill(3)}.png')\n",
|
|
" plt.close()\n",
|
|
" #plt.show()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "ba2c82d5",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"Setting up checkpoints to periodically save our model during training so that\n",
|
|
"everything is not lost even if the program were to somehow terminate while\n",
|
|
"training."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 20,
|
|
"id": "a0e2fc8a",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Setting up checkpoints to save model during training\n",
|
|
"checkpoint_dir = './training_checkpoints'\n",
|
|
"checkpoint_prefix = os.path.join(checkpoint_dir, 'ckpt')\n",
|
|
"checkpoint = tf.train.Checkpoint(generator_optimizer=generator_optimizer,\n",
|
|
" discriminator_optimizer=discriminator_optimizer,\n",
|
|
" generator=generator,\n",
|
|
" discriminator=discriminator)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "4d93a0f7",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"Now we define our training loop"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 21,
|
|
"id": "a1275556",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"def train(dataset, epochs):\n",
|
|
" generator_loss_list = []\n",
|
|
" discriminator_loss_list = []\n",
|
|
"\n",
|
|
" for epoch in range(epochs):\n",
|
|
" start = time.time()\n",
|
|
"\n",
|
|
" for image_batch in dataset:\n",
|
|
" gen_loss, disc_loss = train_step(image_batch)\n",
|
|
" generator_loss_list.append(gen_loss.numpy())\n",
|
|
" discriminator_loss_list.append(disc_loss.numpy())\n",
|
|
"\n",
|
|
" #generate_and_save_images(generator, epoch + 1, seed_images)\n",
|
|
"\n",
|
|
" if (epoch + 1) % 15 == 0:\n",
|
|
" checkpoint.save(file_prefix=checkpoint_prefix)\n",
|
|
"\n",
|
|
" print(f'Time for epoch {epoch} is {time.time() - start}')\n",
|
|
"\n",
|
|
" #generate_and_save_images(generator, epochs, seed_images)\n",
|
|
"\n",
|
|
" loss_file = './data/lossfile.txt'\n",
|
|
" with open(loss_file, 'w') as outfile:\n",
|
|
" outfile.write(str(generator_loss_list))\n",
|
|
" outfile.write('\\n')\n",
|
|
" outfile.write('\\n')\n",
|
|
" outfile.write(str(discriminator_loss_list))\n",
|
|
" outfile.write('\\n')\n",
|
|
" outfile.write('\\n')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "6ff3a75a",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"To train simply call this function. **Warning**: this might take a long time so\n",
|
|
"there is a folder of a pretrained network already included in the repository."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 22,
|
|
"id": "371ed41a",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"train(train_dataset, EPOCHS)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "654399f1",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"Now to avoid having to train and everything, which will take a while depending\n",
|
|
"on your computer setup we now load in the model which produced the above gif."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 23,
|
|
"id": "dec4b560",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"checkpoint.restore(tf.train.latest_checkpoint(checkpoint_dir))\n",
|
|
"restored_generator = checkpoint.generator\n",
|
|
"restored_discriminator = checkpoint.discriminator\n",
|
|
"\n",
|
|
"print(restored_generator)\n",
|
|
"print(restored_discriminator)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "296bfa5c",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"We have successfully loaded in our latest model. Let us now play around a bit\n",
|
|
"and see what kind of things we can learn about this model. Our generator takes\n",
|
|
"an array of 100 numbers. One idea can be to try to systematically change our\n",
|
|
"input. Let us try and see what we get"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 24,
|
|
"id": "eecfbb1f",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"def generate_latent_points(number=100, scale_means=1, scale_stds=1):\n",
|
|
" latent_dim = 100\n",
|
|
" means = scale_means * tf.linspace(-1, 1, num=latent_dim)\n",
|
|
" stds = scale_stds * tf.linspace(-1, 1, num=latent_dim)\n",
|
|
" latent_space_value_range = tf.random.normal([number, latent_dim],\n",
|
|
" means,\n",
|
|
" stds,\n",
|
|
" dtype=tf.float64)\n",
|
|
"\n",
|
|
" return latent_space_value_range\n",
|
|
"\n",
|
|
"def generate_images(latent_points):\n",
|
|
" # notice we set training to false because we are making inferences\n",
|
|
" generated_images = restored_generator.predict(latent_points)\n",
|
|
"\n",
|
|
" return generated_images"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 25,
|
|
"id": "333a593d",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"def plot_result(generated_images, number=100):\n",
|
|
" # obviously this assumes sqrt number is an int\n",
|
|
" fig, axs = plt.subplots(int(np.sqrt(number)), int(np.sqrt(number)),\n",
|
|
" figsize=(10, 10))\n",
|
|
"\n",
|
|
" for i in range(int(np.sqrt(number))):\n",
|
|
" for j in range(int(np.sqrt(number))):\n",
|
|
" axs[i, j].imshow(generated_images[i*j], cmap='Greys')\n",
|
|
" axs[i, j].axis('off')\n",
|
|
"\n",
|
|
" plt.show()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 26,
|
|
"id": "2f5f0154",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"generated_images = generate_images(generate_latent_points())\n",
|
|
"plot_result(generated_images)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "ff581bf2",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"We see that the generator generates images that look like MNIST\n",
|
|
"numbers: $1, 4, 7, 9$. Let's try to tweak it a bit more to see if we are able\n",
|
|
"to generate a similar plot where we generate every MNIST number. Let us now try\n",
|
|
"to 'move' a bit around in the latent space. **Note**: decrease the plot number if\n",
|
|
"these following cells take too long to run on your computer."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 27,
|
|
"id": "d3617ad8",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"plot_number = 225\n",
|
|
"\n",
|
|
"generated_images = generate_images(generate_latent_points(number=plot_number,\n",
|
|
" scale_means=5,\n",
|
|
" scale_stds=1))\n",
|
|
"plot_result(generated_images, number=plot_number)\n",
|
|
"\n",
|
|
"generated_images = generate_images(generate_latent_points(number=plot_number,\n",
|
|
" scale_means=-5,\n",
|
|
" scale_stds=1))\n",
|
|
"plot_result(generated_images, number=plot_number)\n",
|
|
"\n",
|
|
"generated_images = generate_images(generate_latent_points(number=plot_number,\n",
|
|
" scale_means=1,\n",
|
|
" scale_stds=5))\n",
|
|
"plot_result(generated_images, number=plot_number)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "1a074f93",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"Again, we have found something interesting. *Moving* around using our means\n",
|
|
"takes us from digit to digit, while *moving* around using our standard\n",
|
|
"deviations seem to increase the number of different digits! In the last image\n",
|
|
"above, we can barely make out every MNIST digit. Let us make on last plot using\n",
|
|
"this information by upping the standard deviation of our Gaussian noises."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 28,
|
|
"id": "4ef8937d",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"plot_number = 400\n",
|
|
"generated_images = generate_images(generate_latent_points(number=plot_number,\n",
|
|
" scale_means=1,\n",
|
|
" scale_stds=10))\n",
|
|
"plot_result(generated_images, number=plot_number)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "385a2d0a",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"A pretty cool result! We see that our generator indeed has learned a\n",
|
|
"distribution which qualitatively looks a whole lot like the MNIST dataset.\n",
|
|
"\n",
|
|
"Another interesting way to explore the latent space of our generator model is by\n",
|
|
"interpolating between the MNIST digits. This section is largely based on\n",
|
|
"[this excellent blogpost](https://machinelearningmastery.com/how-to-interpolate-and-perform-vector-arithmetic-with-faces-using-a-generative-adversarial-network/)\n",
|
|
"by Jason Brownlee.\n",
|
|
"\n",
|
|
"So let us start by defining a function to interpolate between two points in the\n",
|
|
"latent space."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 29,
|
|
"id": "57de87b8",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"def interpolation(point_1, point_2, n_steps=10):\n",
|
|
" ratios = np.linspace(0, 1, num=n_steps)\n",
|
|
" vectors = []\n",
|
|
" for i, ratio in enumerate(ratios):\n",
|
|
" vectors.append(((1.0 - ratio) * point_1 + ratio * point_2))\n",
|
|
"\n",
|
|
" return tf.stack(vectors)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "cfb76bb6",
|
|
"metadata": {
|
|
"editable": true
|
|
},
|
|
"source": [
|
|
"Now we have all we need to do our interpolation analysis."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 30,
|
|
"id": "e25decef",
|
|
"metadata": {
|
|
"collapsed": false,
|
|
"editable": true
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"plot_number = 100\n",
|
|
"latent_points = generate_latent_points(number=plot_number)\n",
|
|
"results = None\n",
|
|
"for i in range(0, 2*np.sqrt(plot_number), 2):\n",
|
|
" interpolated = interpolation(latent_points[i], latent_points[i+1])\n",
|
|
" generated_images = generate_images(interpolated)\n",
|
|
"\n",
|
|
" if results is None:\n",
|
|
" results = generated_images\n",
|
|
" else:\n",
|
|
" results = tf.stack((results, generated_images))\n",
|
|
"\n",
|
|
"plot_results(results, plot_number)"
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"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.9.10"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 5
|
|
} |