{ "cells": [ { "cell_type": "markdown", "id": "32dc1769", "metadata": {}, "source": [ "# A9 Report\n", "\n", "Predict 13 Z-coordinates from 26 input features (X, Y for each joint)" ] }, { "cell_type": "markdown", "id": "fecdba5a", "metadata": {}, "source": [ "## 1. Data\n", "\n", "| Column Range | Data |\n", "|--------------|------|\n", "| 0 | FrameNo |\n", "| 1-3 | head_x, head_y, head_z |\n", "| 4-6 | left_shoulder_x/y/z |\n", "| 7-9 | left_elbow_x/y/z |\n", "| 10-12 | right_shoulder_x/y/z |\n", "| 13-15 | right_elbow_x/y/z |\n", "| 16-18 | left_hand_x/y/z |\n", "| 19-21 | right_hand_x/y/z |\n", "| 22-24 | left_hip_x/y/z |\n", "| 25-27 | right_hip_x/y/z |\n", "| 28-30 | left_knee_x/y/z |\n", "| 31-33 | right_knee_x/y/z |\n", "| 34-36 | left_foot_x/y/z |\n", "| 37-39 | right_foot_x/y/z |\n", "\n", "**Input**: 13 joints × 2 dimensions (x, y) = **26 features**\n", "\n", "**Output**: 13 joints × 1 dimension (z) = **13 targets**" ] }, { "cell_type": "code", "execution_count": 43, "id": "e1ad3f43", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Loaded 14 experiment results\n" ] } ], "source": [ "import os\n", "import json\n", "import pandas as pd\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "from pathlib import Path\n", "\n", "# Load aggregated results\n", "RESULTS_CSV = Path('results_summary.csv')\n", "if RESULTS_CSV.exists():\n", " df = pd.read_csv(RESULTS_CSV)\n", " print(f\"Loaded {len(df)} experiment results\")\n", "else:\n", " print(\"Run aggregate_results.py first to generate results_summary.csv\")" ] }, { "cell_type": "markdown", "id": "8da8874c", "metadata": {}, "source": [ "## 2. Deep Learning Steps Overview\n", "\n", "| Step | Description | Implementation |\n", "|------|-------------|----------------|\n", "| 1 | Load Data | `load_all_sequences()` in models.py |\n", "| 2 | Define Network | Dense, Conv1D, LSTM, GRU architectures |\n", "| 3 | Compile Model | Optimizer (SGD/Adam/RMSprop), Loss (MSE/MAE) |\n", "| 4 | Split Data | 5-fold cross-validation, 90/10 train/test |\n", "| 5 | Train Model | Early stopping, 100 epochs max |\n", "| 6 | Evaluate | MSE, MAE, R² on test set |\n", "| 7 | Use Model | Load saved .h5 weights for inference |" ] }, { "cell_type": "markdown", "id": "d9f90334", "metadata": {}, "source": [ "## 3. Model Architectures\n", "\n", "### Dense\n", "- Fully-connected layers\n", "- Treats each frame independently\n", "- Configuration: (256, 128, 64) hidden units, ReLU, dropout 0.3\n", "\n", "### Conv1D\n", "- 1D convolutional layers for temporal patterns\n", "- Window size: 30 frames\n", "- Best variant (v3): filters=(128, 256), kernel=3, pool=3, dense=(256, 128, 64)\n", "\n", "### LSTM\n", "- Recurrent network for sequences\n", "- Units: (64, 32), tanh activation\n", "\n", "### GRU\n", "- Gated Recurrent Unit variant\n", "- Units: (64, 32)" ] }, { "cell_type": "markdown", "id": "f04dfdfd", "metadata": {}, "source": [ "## 4. Training & Hyperparameter Results" ] }, { "cell_type": "code", "execution_count": 44, "id": "1a49b682", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "ARCHITECTURE COMPARISON\n", "==================================================\n", " model val_r2_mean val_rmse_mean val_mae_mean\n", "conv1d 0.932681 0.037704 0.027598\n", " dense 0.820046 0.058055 0.042731\n", " lstm 0.768530 0.069834 0.052217\n" ] } ], "source": [ "# Architecture comparison\n", "if 'df' in dir() and len(df) > 0:\n", " arch_df = df[df['experiment'] == 'different_models'][['model', 'val_r2_mean', 'val_rmse_mean', 'val_mae_mean']]\n", " arch_df = arch_df.sort_values('val_r2_mean', ascending=False)\n", " print(\"ARCHITECTURE COMPARISON\")\n", " print(\"=\"*50)\n", " print(arch_df.to_string(index=False))" ] }, { "cell_type": "code", "execution_count": 45, "id": "b5578e0e", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CONV1D VARIANTS\n", "==================================================\n", " model val_r2_mean val_rmse_mean test_r2 test_rmse\n", " conv1d 0.922923 0.040316 0.928322 0.039097\n", "conv1d_v2 0.799991 0.064831 0.826827 0.060770\n" ] } ], "source": [ "# Conv1D variants comparison\n", "if 'df' in dir() and len(df) > 0:\n", " conv_df = df[df['experiment'] == 'conv1d_variants'][['model', 'val_r2_mean', 'val_rmse_mean', 'test_r2', 'test_rmse']]\n", " conv_df = conv_df.sort_values('test_r2', ascending=False)\n", " print(\"CONV1D VARIANTS\")\n", " print(\"=\"*50)\n", " print(conv_df.to_string(index=False))" ] }, { "cell_type": "code", "execution_count": 46, "id": "7b21485a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "OPTIMIZER COMPARISON\n", "==================================================\n", " model val_r2_mean val_rmse_mean test_r2 test_rmse\n", "conv1d_v3_rmsprop_mse 0.946803 0.033498 0.954934 0.031001\n", " conv1d_v3_adam_mse 0.949678 0.032558 0.953484 0.031496\n", " conv1d_v3_sgd_mse 0.950863 0.032199 0.952354 0.031876\n" ] } ], "source": [ "# Optimizer comparison\n", "if 'df' in dir() and len(df) > 0:\n", " opt_df = df[df['experiment'] == 'optimizer'][['model', 'val_r2_mean', 'val_rmse_mean', 'test_r2', 'test_rmse']]\n", " opt_df = opt_df.sort_values('test_r2', ascending=False)\n", " print(\"OPTIMIZER COMPARISON\")\n", " print(\"=\"*50)\n", " print(opt_df.to_string(index=False))" ] }, { "cell_type": "code", "execution_count": 47, "id": "6b55c089", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "LOSS FUNCTION COMPARISON\n", "==================================================\n", " model val_r2_mean val_rmse_mean test_r2 test_rmse\n", "conv1d_v3_rmsprop_mse 0.950038 0.032467 0.954592 0.031119\n", " conv1d_v3_adam_mse 0.948571 0.032956 0.952860 0.031707\n", " conv1d_v3_sgd_mse 0.950748 0.032241 0.952171 0.031937\n", "conv1d_v3_rmsprop_mae 0.943196 0.034623 0.944302 0.034465\n", " conv1d_v3_adam_mae 0.939982 0.035601 0.934766 0.037298\n", " conv1d_v3_sgd_mae 0.939909 0.035625 0.934060 0.037500\n" ] } ], "source": [ "# Loss function comparison\n", "if 'df' in dir() and len(df) > 0:\n", " loss_df = df[df['experiment'] == 'loss_optimizer'][['model', 'val_r2_mean', 'val_rmse_mean', 'test_r2', 'test_rmse']]\n", " loss_df = loss_df.sort_values('test_r2', ascending=False)\n", " print(\"LOSS FUNCTION COMPARISON\")\n", " print(\"=\"*50)\n", " print(loss_df.to_string(index=False))" ] }, { "cell_type": "markdown", "id": "e29b86d1", "metadata": {}, "source": [ "## 5. Evaluation & Model Comparison" ] }, { "cell_type": "code", "execution_count": 48, "id": "5128b8be", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "ALL EXPERIMENTS RANKED BY TEST R²\n", "======================================================================\n", " experiment model val_r2_mean test_r2 test_rmse\n", " optimizer conv1d_v3_rmsprop_mse 0.946803 0.954934 0.031001\n", " loss_optimizer conv1d_v3_rmsprop_mse 0.950038 0.954592 0.031119\n", " optimizer conv1d_v3_adam_mse 0.949678 0.953484 0.031496\n", " loss_optimizer conv1d_v3_adam_mse 0.948571 0.952860 0.031707\n", " optimizer conv1d_v3_sgd_mse 0.950863 0.952354 0.031876\n", " loss_optimizer conv1d_v3_sgd_mse 0.950748 0.952171 0.031937\n", " loss_optimizer conv1d_v3_rmsprop_mae 0.943196 0.944302 0.034465\n", " loss_optimizer conv1d_v3_adam_mae 0.939982 0.934766 0.037298\n", " loss_optimizer conv1d_v3_sgd_mae 0.939909 0.934060 0.037500\n", " conv1d_variants conv1d 0.922923 0.928322 0.039097\n", " conv1d_variants conv1d_v2 0.799991 0.826827 0.060770\n", "different_models conv1d 0.932681 NaN NaN\n", "different_models dense 0.820046 NaN NaN\n", "different_models lstm 0.768530 NaN NaN\n" ] } ], "source": [ "# All results ranked by test R²\n", "if 'df' in dir() and len(df) > 0:\n", " print(\"ALL EXPERIMENTS RANKED BY TEST R²\")\n", " print(\"=\"*70)\n", " ranked = df.sort_values('test_r2', ascending=False, na_position='last')\n", " print(ranked[['experiment', 'model', 'val_r2_mean', 'test_r2', 'test_rmse']].to_string(index=False))" ] }, { "cell_type": "code", "execution_count": 49, "id": "38efd1c2", "metadata": {}, "outputs": [ { "data": { "image/png": "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1ltvyf333y+RkZHmfaxbt052795tXktfu06dOrJnzx7Xc3T/gQceMOPVedXzsGLFimTHvXDhgjz33HNSvHhxcwydx1WrVqVrbAAAAJmBGJcYlxgXAAD4feJ28ODBMm7cOBk6dKhs375d5s2bZ5J5mizVatO8efPKxo0bZeHChSax9+STTyZ7/sqVK00SUP98//335b333jOb0oTlhg0bkiUM9bKxn3/+WTp16uTxGAcMGCCrV6+WJUuWyPLly03y8IcffvDouekdg2VZ8vXXX8vOnTvlzjvvvOrxN23aJE8//bRJAutzli1blux5zz77rKxdu1Y+++wz+eqrr2TNmjWXjV0T4AcPHjRz+PHHH5tEsCZz02P06NEmub1lyxZTOazv7bHHHjPnV8eo78v93GkC/r777jPv9ccff5RmzZqZ5PGBAwdcj9HHawJ4/vz5Zr60Kloft2vXrlTHoInekydPJtsAAEDaQoJCpGGZhmbTfXgPMW5yxLjEuAAAZHV+1ypBq0GnTp0q06ZNk27dupnbtOK0Xr16MmPGDDl//rzMnj3bVFsqfZwm915++WWT3FWa2NXbQ0JCTMKwefPmJhn46KOPSpUqVUxlqyaDNTGstJpUKze1wtcTmmCcOXOmfPDBB9K4cWNzmyaIS5Qo4dHzPR3DiRMnTGWpJh/1vWjy9O67777q8TXRqfOj1a65cuWS0qVLS40aNVzzq2PV17bHPmvWLClWrJjr+b/99pt8+eWXJrmsVa9K369WzaaHtnZo37692R80aJDccccd5v3arR60ilgfY9M50c098bt48WKTYNaErb4vHav+aY9Xq281Ma23jxkz5rIxjB07VkaOHJmucQMAkJWFBCclbuFdxLjEuDZiXAAA4LcVtzt27DCJSjupmPI+TezZSVtVt25dc9mZVpa6J0Y10WnTdgDu1aJa8aqJS/ub/g8//NDc5imtlI2Li0vW7kB70laqVMnjY3gyBk26arWqVhdrqwitlPWkLYAmdzVZq316u3TpYpLC2tNM7d2717SNuO2221yPz507d7Kx6zyHhobKrbfe6rpNE+C6UFp6aGsHm51Ur1q1arLbNBFvV8FqQlwTsZog1tfSS8l0LHbFrbbJSEhIkIoVK5r77E0rn92rl1NWtmgC3N60ihgAACCjEeNeQoxLjAsAAPy04lZ7ql6vsLCwy/qtanLX1rFjR1MBqu0Bzp07Z5J5HTp0kIzkyRiCg4NdFbjVq1c3Ab9WkGqP2CvRYFiPq0lebeMwbNgwGTFihEkAZyT386DnIK3b7HOjSVtt3aD9ifV962dBe/NqktxO7GpCfvPmzckS80oTuKnRHsVp9SkGAACX0y+Uj549avYLRhZ0/XuN60OMewkxLjEuAADw04pbXVBLA1ttbZCSVmL+9NNPptetTXu1avCXnmpXbWnQoEEDU4mqm1aoFipUyOPna+sGTUCuX7/edduxY8dMiwFfjkETnFqN7AmtmG3SpIlZ5E17we7fv1+++eYbU4WrY3dP4molqvvYtbr24sWLJkFq04rm48ePiy/pudTeuroom1bmFilSxIzbpu0etOJWq6c1seu+6WMBAMD1i0+Ml+kbp5tN9+EdxLhpI8YlxgUAIKvyu4rbiIgIU4k6cOBACQ8PN60Qjh49ahbv0lYCw4cPN71vtYJUb3/qqadMOwD7UnxP2cfSas7Jkycnu09v00XR7P0//vjDtCzQqk5NEuqfPXv2NAuU5c+f3yRchwwZYhLI3hqDVtbWrFnTJIk1WfvFF1/InDlz5I033rjqcZcuXWpaIuiCZNrvV5+rAbEmt7UaV+dPx67tHXTsOgYdu11Ro4/TBb90ITF9PU0C9+3b1yuVIlf7hWbRokWmZ7GORfvhuldKa4sEnTNd8GzSpEkmkaufAU3ya1sG7WUMAADgRMS4SYhxiXEBAIAfV9wqTdj179/fXOKvVbbaQkCrLCMjIyU2Nlb+/fdfs2iWXkavvXB1IbL00uf+888/pvdrq1atkt136NAhkxTU7c8//zSX7uv+I4884nrMhAkTpH79+ibJqJWtuniae0/Y6x2DVhX37t3b9OvV5PUnn3xiFkNzH0NatD+sJkAbNWpk5u/NN980PXT1WOqVV14xC4Xp4mU6dj2+Pk5/obDZC5ZpVfCDDz4ovXr1SldV8rXQcWmiuU6dOmZedRGzW265JdljdFyauNXPhyaYdd60erhUqVI+HRsAAMD1IsYlxiXGBQAA7oIsbVQGXIEmiYsXL26qWLWSOJDpQmi6GNuJGSJRkZk9GgAAMlGn1EPEuIQ4GbNmjNmPqR8j4SHhvv93+cQJiYqK8tnrIGvKijFuo5gFEhpBkAsgY8QO5apX4HpjXL9rlQDf+/HHH+XXX3+V2267zXyIRo0aZW5/4IEHmH4AAAD4JWJcAADgb/yyVYK/W7NmjemDm9bmhONr+4dq1aqZVglajaDHLFCggEfP1cXU0nptux0DAAAAAgsxLgAAgHdRcZsJdFExXczMqcfXfr2bN2++5ue3bNlSateunep9YWFh13xcAAAAOBcxLgAAgHeRuM0E2bNnl+joaL89/tXkypXLbAAAIPCEBIVInZJ1XPuAjRgXAADAu0jcAgAAwGMhwSFyT/l7mDEAAADAx+hxCwAAAAAAAAAOQ8UtAAAAPGZZlpy4cMLs586WW4KCgpg9AAAAwAeouAUAAIDH4hPjZcr3U8ym+wAAAAB8g8QtAAAAAAAAADgMiVsAAAAAAAAAcBgStwAAAAAAAADgMCRuAQAAAAAAAMBhQjN7AIAjtT8hEhWV2aMAACD9EuJEto1J2q8SIxISziwCMBYPaipRxLgAAPgNKm4BAAAAAAAAwGGouAUAAAgkQcEi+Wtd2vey4KBgqVWslmsfAAAAgG+QuAUAAAgkwaEixZv77PChwaHSvKLvjg8AAAAgCWUSAAAAAAAAAOAwVNwCAAAEEssSSTibtB8SKRIU5OXDW3I2Pun4kWGREuTl4wMAAABIQsUtAABAIEmMF9k+IWnTfS+LT4yXCd9NMJvuAwAAAPANErcAAAAAAAAA4DAkbgEAAAAAAADAYUjcAgAAAAAAAIDDsDgZkJoFuUUimRoAgJ/7ZWT6n9PJ8sVIADhA65djJTSCIBeAM8UObZ7ZQwAch4pbAAAAAAAAAHAYErcAAAAAAAAA4DC0SgAAAIDHgoOCpXqR6q59AAAAAL5B4hYAAACeB4/BodKqcitmDAAAAPAxyiQAAAAAAAAAwGGouAUAAIDHLMuS+MR4sx8WHCZBQUHMHgAAAOADVNwCAADAY5q0HbNmjNnsBC4AAAAA7yNxCwAAAAAAAAAOQ+IWAAAAAAAAAByGxC0AAAAAAAAAOAyJWwAAAAAAAABwGBK3AAAAAAAAAOAwJG4R0Bo2bCh9+/bN7GEAAAAAXkWcCwBA4CNxC7+wbds2adOmjZQpU0aCgoJkypQpmT0kAACypOCgYLmx4I1m030A14c4FwAApIVoG37h7NmzUq5cORk3bpwUKVIks4cDAECWFRocKu2rtDeb7gO4PsS5AAAgLSRu4ZHExEQZP368REdHS7Zs2aRUqVLy0ksvmfu2bt0qjRo1kuzZs0v+/PmlV69ecvr0addzu3fvLq1atZKJEydK0aJFzWP69Okj8fHx5v6YmBipXbv2Za9ZrVo1GTVqlNmvVauWTJgwQR566CHz+qk5c+aMdO3aVXLmzGleZ9KkSZxdAAAAEOcCAAC/ROIWHhk8eLCpdh06dKhs375d5s2bJ4ULFzbJ0qZNm0revHll48aNsnDhQlmxYoU8+eSTyZ6/cuVK2bNnj/nz/fffl/fee89sqnPnzrJhwwZzv/slYz///LN06tTJ4zM0YMAAWb16tSxZskSWL18uq1atkh9++OGKz7lw4YKcPHky2QYAAICsIxDjXGJcAAACA4lbXNWpU6dk6tSppuK2W7duUr58ealXr5488sgjJrA9f/68zJ49W2666SZTeTtt2jSZM2eOHD582HUMDXj19sqVK8v9998vzZs3l6+//trcV6VKFVNdq8eyzZ0711ThaoWvJ7TCd+bMmaaqt3HjxlK1alUTOF+8ePGKzxs7dqzkzp3btZUsWZJPBAAAVxCXECcjVo0wm+4D/ixQ41xiXAAAAgOJW1zVjh07zLf2Giimdp8Gozly5HDdVrduXdNaYefOna7bNGgNCQlx/aytDI4cOeL6WasR7IDWsiz58MMPzW2e0iqGuLi4ZC0X8uXLJ5UqVbpqhcWJEydc28GDBz1+TQAAAPi3QI1ziXEBAAgMrCiBq9LetdcrLCws2c9BQUEm6LV17NhRBg0aZC75OnfunEmgdujQwednR/vlptUzFwAAAIEtUONcYlwAAAIDFbe4qgoVKpig1r7ky90NN9wgP/30k+kBZlu7dq0EBwdftdrVXYkSJaRBgwbm0jHd7r77bilUqJDHz9fL2jRoXr9+veu2Y8eOyW+//ebxMQAAAJC1EOcCAAAno+IWVxUREWGqBAYOHCjh4eHmErGjR4+ahRX0Mq/hw4ebnmAjRowwtz/11FPSpUsXs6hDetjH0kvBJk+enOw+vU0Xi7D3//jjD9myZYvkzJnT9AfTP3v27GkWbsifP79J+g4ZMsQkkAEAAADiXAAA4G9I3MIjuspuaGioDBs2TA4dOmR6dz3++OMSGRkpsbGx8swzz0itWrXMz23atJFXXnkl3TPbtm1bs0qv9ghr1apVsvv0NWvUqOH6WRdn0E2rdHVVXTVhwgSzeEOLFi0kV65c0r9/f9O3FgAAACDOBQAA/ibI0g75AIyTJ09K7ty55cQMkahIJgUAkAV1unJoGJcQJ2PWjDH7MfVjJDwk3Pf/Lp84IVFRUT57HSDQ2X+XGsUskNAIglwAzhQ7tHlmDwHIEOmJcam4BQAAgMeCg4KlQr4Krn0AAAAAvkHiFgAAAJ4Hj8Gh0vnmzswYAAAA4GOUSQAAAAAAAACAw5C4BQAAAAAAAACHoVUCAAAAPKaLk01YO8HsD6g7wKeLkwEAAABZGYlbAAAApEt8YjwzBgAAAPgYrRIAAAAAAAAAwGFI3AIAAAAAAACAw5C4BQAAAAAAAACHIXELAAAAAAAAAA5D4hYAAAAAAAAAHCY0swcAAAAA/xEkQVImTxnXPgAAAADfIHELpKb9CZGoKOYGAIAUwkLCpHv17swL4IcWD2oqUcS4AAD4DVolAAAAAAAAAIDDkLgFAAAAAAAAAIehVQIAAEAgSYgT2Tklab9SX5GQcK8ePi4hTqZ8n3T8vrf3lXAvHx8AAABAEhK3AAAAgebiWZ8e/my8b48PAAAAgFYJAAAAAAAAAOA49LgFAAAAAAAAAIchcQsAAAAAAAAADkPiFgAAAAAAAAAchsQtAAAAAAAAADhMaGYPAHCk3LkzewQAAH9jWeIIQUEikcUu7Xv78BIkxXIVc+0D8B+tX46V0IjIzB4GAAB+IXZo88weAolbAACAgBIcJhLdy2eHDwsJk163+u74AAAAAJJQcQtcg4TISIkvUMAnlUwBybIk7O+/JeTs2cweCQAAAAAAgF8gcQukgxUUJH/16CHHW7YUCQ8ncevxxFkicXGS57PPpMisWRLklMuJAQAAAAAAHIrELZAOJmnbsaMUypNHtDsY9bae0TSt1toe6djR/Fz03Xf53AGAryTGi/z2etJ+xT5JrRO8KD4hXl7fmHT8PrX6mNYJAAAAALyPxC3goYQcOUylrSZt8zNr6ZZd/5MnjxzROZw/n7YJAODTqxyOX9r39uHFkuPnj7v2AQAAAPhGsI+OCwSc+Pz5TXsE1uG9dmbuwsOT+gMDAAAAAAAgTSRuAU/pQmRBQbRHuA5BbvMIAAAAAACAtJG4BQAAAAAAAACHIXELZAGnzpyRvpMmSekWLSR7vXpS5+GHZeO2ba77u48YIUG1aiXbmj31lOv+C3Fx0mXYMIlq2FAqtmkjK9avT3b8CXPmyFMTJmToewIAAAAAAAhkLE4GZAGPvPii/LJnj8wZOVKKFSwoH3z5pTTp00e2L1ggxQsVMo9pdscdMmvYMNdzsoWHu/bfXrxYNv/6q6ybOVO+/O476TR0qByOjZWgoCDZ98cfMuPTT2XT++9nynsDAAAAAAAIRFTcAgHu3Pnz8snKlTL+6aflzltukeiSJWVEr17mzzc++SRZorZIgQKuLW9UlOu+Hfv2Scv69aVK+fLSp107OXrsmPx9PGlF8SfGjZOXn3xSonLmzJT3BwBIQfuIRxRM2nzQU1y7vReMLGg2Or8DAAAAvkPFLeAFcYnxad4XLEESGhzq0WP1F+Cwqzw2PDgsXWO7mJAgCQkJEuFWQauyZ8sm327Z4vp51ebNUuieeyRvrlzSqFYtefHxxyV/njzmvmoVK8qcL74wSeDY77+XogUKSIE8eWTul19KRLZs0vquu9I1JgCAD+m/ExX7+OzwYSFh0uc23x0fAAAAQBISt8hUDRs2lOrVq8uUKVP8+kyM2T0rzfsq5CgpnYvf6/p5wp45Em9dTPWxZbIXle4lW7h+nrLvQzmbcD7ZY0ZU7JWuseXKkUPuqFpVRs+cKTeULSuF8+WTD2NjZd3WrRJdooR5TLM6deTBu+6SssWLy57ff5eY6dPl3meekXXvvishISHycMuW8vOuXXJjhw5SIHduWTB2rBw7eVKGvfWWrHrzTXnhjTdk/vLlUr5ECXl36FBX+wUAAICsKlDiXAAAkHlolQCv2LZtm7Rp00bKlClj+p5mRoA6Y8YMqV+/vuTNm9dsTZo0kQ0bNmT4OJxozqhRYlmWFL/vPslWt668+tFH0vGeeyQ4OOl/AQ/dc4+0bNBAqkZHS6uGDWXpK6/Ixu3bTRWuCgsNldcHDZJ9S5bIxtmzpV716tJ/yhR5ukMH+XHnTvl01Sr5ad48uf2mm+TpiRMz+d0CAAB4D3EuAADILFTcwivOnj0r5cqVk3bt2km/fv0yZVZXrVolHTt2lDp16khERIS8/PLLcs8995hgu3jx4j597ZjoHldsleBuQPkuaT42Za/AvmU7emF0YiphV7/9tpw5d05OnjljWh10GDxYyqUxL+VKlDCtEHb//rs0vu22y+5fuWmTbNu7V9554QUZ8Oqrcl/dupIje3Zp36SJTFu40CtjBgBcI22zs/vtpP3oXkmtE7woPiFe3t6cdPxet/YyrROAQJbV41wAAJB5qLgNEImJiTJ+/HiJjo6WbNmySalSpeSll14y923dulUaNWok2bNnl/z580uvXr3k9OnTrud2795dWrVqJRMnTpSiRYuax/Tp00fi45P6q8bExEjt2rUve81q1arJqFGjzH6tWrVkwoQJ8tBDD5nXT82ZM2eka9eukjNnTvM6kyZN8vj9eTKGuXPnSu/evc0laZUrV5Z33nnHzMvXX38tvqZ9Z9Pa3PvbXu2xYR489npoclWTttrmQHvVPnDnnak+7vfDh+WfEyekaP78l913/sIF6TN+vLwVE2PaKGj/3PiLSa0f9E/9GQCQiSxL5PzRpE33vX14seTo2aNm033A14hzMzfOBQAAmYfEbYAYPHiwjBs3ToYOHSrbt2+XefPmSeHChU2ytGnTpqZ1wMaNG2XhwoWyYsUKefLJJ5M9f+XKlbJnzx7z5/vvvy/vvfee2VTnzp1NywG936bf7v/888/SqVMnj8c4YMAAWb16tSxZskSWL19uKgd++OEHj557LWPQ6ghNPufLly/N4164cEFOnjyZbAtEsevWybLvvpN9f/whX61fL3c9/rhULlNGerRsKafPnpUBU6fK91u3yv5Dh+TrDRvkgeeek+iSJaXpHXdcdiztlXtfnTpSo1Il83PdatVk0cqVpgeuVtvqzwAAAN5CnJv+ODerxLgAAAQ6ErcB4NSpUzJ16lRTcdutWzcpX7681KtXTx555BGTwD1//rzMnj1bbrrpJlN5O23aNJkzZ44cPnzYdQxN7Ort+g3+/fffL82bN3d9g1+lShVT2arHsum3/loBqxW+ntAK35kzZ5qq3saNG0vVqlVNgvji/1VqXs21jGHQoEFSrFgx0+s2LWPHjpXcuXO7tpIlS0ogOnH6tKmSrdyunXQdPtz0qI197TXTuzYkOFh+3r1bWvbvLxXbtJGeL74ot1auLGvefluyhYcnO84vu3fLghUrZORjj7lua9u4sTSvW1fqP/qoSd5O7d8/E94hAAAIRMS51xbnZpUYFwCAQEfiNgDs2LHDfKuuCdHU7tOEZ44cOVy31a1b11xatXPnzmSJUb3s3aatDI4cOZKs4tVOmuoiVx9++KG5zVNaKRsXF5es3YFWCFT6v6pNT6RnDFp9PH/+fFm8eLHpA3alCo4TJ064toMHD0ogan/33bLn00/lwnffyZ/Llsm0gQMld86c5r7sEREmiXtk+XKJW7dO9n/2mbw9ZIgUTqVNwk3R0bJr0SLTcsGmC5xNf/55ObFqlWx4/31TqQsAAOANxLnXFudmlRgXAIBAR+I2AGjv2usVFpa8d2pQUJBJ7tp0MQRN9Gprg++++84Efx06dJCM5OkYtKpXA1ptx3DzzTdf8ZjajzcqKirZBgAAAGcgzr22OJcYFwCAwEDiNgBUqFDBBLWpLU5www03yE8//WR63drWrl1rqiTTU+1aokQJadCggWlPoNvdd98thQoV8vj52r5Bk8Pr16933Xbs2DH57bffvDoGbRcxevRoWbZsmdSsWdPjYwMAAMB5iHMvIc4FACDrSb6EPfySXiKlfa4GDhwo4eHhphXC0aNHzeJd2kpg+PDhpvftiBEjzO1PPfWUdOnSxSxelh72sbTlweTJk5Pdp7fpomj2/h9//CFbtmyRnDlzmh60+mfPnj3NAmX58+c3CdchQ4aYBLK3xvDyyy/LsGHDTDuFMmXKyF9//WVu19fWDQCALCEoSCQ8z6V9bx9egiRPRB7XPuBLxLlJiHMBAMiaqLgNEEOHDpX+/fubxKVW2WoLAe1RGxkZKbGxsfLvv/9KrVq1pG3btqYXri5Ell763H/++cesYtuqVatk9x06dEhq1Khhtj///NNcxqX7ukCabcKECVK/fn1p0aKFWUhBF1C79dZbvTaGN954wyR09THao9fedCwAAGQZwWEilfsmbbrvZWEhYdL39r5m033A14hziXMBAMiqgixd5QmAcfLkSbPy7gkRSdnt9nzp0rLvzTelbIECkvZyZ7iS8yKy7++/pezjj0vE//7HZAEILIRUvvt3+cQJ+tADXvi71ChmgYRGRDKXAAB4IHZoc8nsGJeKWwAAAAAAAABwGHrcwhHWrFkj9957b5r3nz59OkPHAwCA30qMF9k7K2m/XA+vt0uIT4iXWVuSjt+jeg/aJQBXQZwLAACuFYlbOELNmjXNYmYAAMALLRvOHrq072WWWHLo1CHXPoArI84FAADXisQtHCF79uwSHR2d2cMAAAAAvIo4FwAAXCt63AIAAAAAAACAw5C4BbKAU2fOSN9Jk6R0ixaSvV49qfPww7Jx2zbX/ZZlybA335SizZqZ+5v07i27Dhxw3X8hLk66DBsmUQ0bSsU2bWTF+vXJjj9hzhx5asKEDH1PAAAAAAAAgYzELZAFPPLii/LV+vUyZ+RI2frhh3LP7bdLkz595I8jR8z942fPllc/+kjeHDxY1s+aJTmyZ5emTz0l5y9cMPe/vXixbP71V1k3c6b0atVKOg0dapK9at8ff8iMTz+Vl554IlPfIwAAAAAAQCAhcQsEuHPnz8snK1fK+KefljtvuUWiS5aUEb16mT/f+OQTk4Cd8uGH8sLDD8sDDRrIzRUqyOyRI+XQ33/Lp6tXm2Ps2LdPWtavL1XKl5c+7drJ0WPH5O/jx819T4wbJy8/+aRE5cyZye8UAAAAAAAgcJC4BbzBir/CdtG7j02niwkJkpCQIBHh4cluz54tm3y7ZYupmP3rn3+kyW23ue7LnTOn1K5SRdb9/LP5uVrFivLtTz+ZJHDs999L0QIFpECePDL3yy8lIls2aX3XXekeFwDAh0IjkzYfiQyLNBsAAAAA3wn14bGBrOOfWWnfF15SJOreSz//O+fyBK0trKhI7haXfj72oUji+eSPKdArXUPLlSOH3FG1qoyeOVNuKFtWCufLJx/Gxsq6rVslukQJk7RVhfPnT/Y8/dm+7+GWLeXnXbvkxg4dpEDu3LJg7Fg5dvKkDHvrLVn15pvywhtvyPzly6V8iRLy7tChUrxQoXSNEQDgRSHhIjcO9NmUhoeEy8C6vjs+AAAAgCRU3AJZwJxRo0xLhOL33SfZ6tY1/Ww73nOPBAd79r+AsNBQeX3QINm3ZIlsnD1b6lWvLv2nTJGnO3SQH3fulE9XrZKf5s2T22+6SZ6eONHn7wcAAAAAACDQUXELeEP+Hle4Myj5j/m6eP7YvB3FG7QSdvXbb8uZc+fk5JkzptVBh8GDpVzx4lLk/yptD//zj7ndpj9Xr1gx1eOt3LRJtu3dK++88IIMePVVua9uXbOgWfsmTWTawoVeGTMAAAAAAEBWRuIWSM2JEyJRUclvO39eZN8+kbJlRSIi/HLecvzfduzYMYnduFHGjx8vZR94QIoUKSJf//WXVO/c2Tzu5MmTsn77dnli4ECRmjWTHeP8+fPSp2tXmTt3roTUqCEJBQqIpZW7NWtKfGioJOiDUjznsjn89Ve/nUMAcLzEeJH9c5P2y3QWCQ7z6uHjE+Jl7tak43eu2lnCQrx7fAC+s3hQU4lKGeMCAADHInELZAGxsbGmVUKlSpVk9+7dMmDAAKlcubL06NFDgoKCpG/fvvLiiy9KhQoVpGzZsjJ06FApVqyYtGrV6rJjjR49Wu677z6pUaOG+blu3brmeHqsadOmmZ8BAJnIskRO77+07+3DiyX7j+937QMAAADwDRK3QBZw4sQJGTx4sPz++++SL18+adOmjbz00ksSFpZUJTVw4EA5c+aM9OrVS44fPy716tWTZcuWSUSKqthffvlFFixYIFu2bHHd1rZtW1m1apXUr1/fJIbnzZuX4e8PAAAAAAAg0JC4BbKA9u3bmy0tWnU7atQos13JTTfdJLt27Up2my5wNn36dLMBAAAAAADAOzxbUh4AAAAAAAAAkGFI3AIAAAAAAACAw5C4BQAAAAAAAACHocctAABAoAlOWnzSV8J8fHwAAAAAJG4BAAACS0i4yE1DfHb48JBwGXKn744PAAAAIAmtEoB0siyLObtGzB0AAAAAAIBnSNwCHgoLS7os9OzZs8zZNbLnzp5LAAAAAAAApI4et4CHQkJCJE+ePHLkyBHzc2RkpAQFBTF/HlbaatJW507nUOcSAOAjiRdF/vdR0n7pDiLB3g33LiZelI9+STp+h5s6SKiXjw8AAAAgCZE2kA5FihQxf9rJW6SPJm3tOQQA+IiVKHJq16V9L0u0EmXXv7tc+wAAAAB8g8QtkA5aYVu0aFEpVKiQxMfHM3fpoO0RqLQFAAAAAADwDIlb4BpoApIkJAAAAAAAAHyFxckAAAAAAAAAwGFI3AIAAAAAAACAw5C4BQAAAAAAAACHocct4MayLPPnyZMnmRcAgH9KiBM5fSFpX/89Cwn36uHjEuLkwpkLrn8vw718fHf2v8f2v88Arg0xLgAAzpGeGDfIIhIGXPbu3Svly5dnRgAAcJCDBw9KiRIlMnsYgN8ixgUAwD9jXCpuATf58uUzfx44cEBy587N3GTSN08lS5Y0/wOLioriHHAOsiT+HmQ+zoEz5l//PQ4KCpJixYpl8ogA/0aMm378O8B8+RKfL+aLz1fW/vtoWZacOnXKoxiXxC3gJjg4qe2zJm1JGmYunX/OAecgq+PvQebjHGQu/j0GvIMY99rx7wDz5Ut8vpgvPl9Z9+9jbg+LBVmcDAAAAAAAAAAchsQtAAAAAAAAADgMiVvATbZs2WT48OHmT2QOzkHm4xxkPs5B5uMcMP9AIOH/acwZnzFn4e8k88XnyzmyOTwPFGRpR1wAAAAAAAAAgGNQcQsAAAAAAAAADkPiFgAAAAAAAAAchsQtAAAAAAAAADgMiVtkOa+//rqUKVNGIiIipHbt2rJhw4YrPn7hwoVSuXJl8/iqVavKF198kWFjDVTpOQczZsyQ+vXrS968ec3WpEmTq54zePccuJs/f74EBQVJq1atmOYMPgfHjx+XPn36SNGiRU3j/IoVK/L/owyc/ylTpkilSpUke/bsUrJkSenXr5+cP3/+eoaQpf33v/+VFi1aSLFixcz/Uz799NOrPmfVqlVyyy23mM9/dHS0vPfeexkyViCrxLS69MmwYcPMvzP6/zqNuXbt2iWBwtvztWjRIrnnnnskf/785v9jW7ZskUDizfmKj4+XQYMGmdtz5Mhh/t/ftWtXOXToUAa8k4zh7c/XiBEjzP06X/bvQOvXr5dA4cvfyR9//HHzd1Jjt0Dh7fnq3r27mSP3rVmzZhJIXvfBZ2zHjh3SsmVLyZ07t/m7WatWLTlw4ID4nC5OBmQV8+fPt8LDw613333X2rZtm/Xoo49aefLksQ4fPpzq49euXWuFhIRY48ePt7Zv32698MILVlhYmLV169YMH3tWPQedOnWyXn/9devHH3+0duzYYXXv3t3KnTu39fvvv2f42LPqObDt27fPKl68uFW/fn3rgQceyLDxBqL0noMLFy5YNWvWtO677z7r22+/Nedi1apV1pYtWzJ87Flx/ufOnWtly5bN/KlzHxsbaxUtWtTq169fho89UHzxxRfWkCFDrEWLFukiudbixYuv+Pi9e/dakZGR1rPPPmv+PX7ttdfMv8/Lli3LsDEDgR7Tjhs3zsRYn376qfXTTz9ZLVu2tMqWLWudO3fO8ne+mK/Zs2dbI0eOtGbMmGH+P6axaqDw9nwdP37catKkifXRRx9Zv/76q7Vu3Trrtttus2699VYrEPji86Uxx1dffWXt2bPH+uWXX6yePXtaUVFR1pEjRyx/58vfyTWuqFatmlWsWDFr8uTJViDwxXx169bNatasmfXnn3+6tn///dcKFPN9MGe7d++28uXLZw0YMMD64YcfzM9Lliy56u/Q3kDiFlmKBgh9+vRx/ZyQkGD+pz527NhUH9++fXurefPmyW6rXbu29dhjj/l8rIEqvecgpYsXL1q5cuWy3n//fR+OMrBdyznQea9Tp471zjvvmH/oSdxm7Dl44403rHLlyllxcXHX+cq4lvnXxzZq1CjZbZpArFu3LhPqBZ4kbgcOHGhVqVIl2W0dOnSwmjZtyjlAluTtmDYxMdEqUqSINWHCBNf9mmzTL60+/PBDy9/58ncA/UIv0BK3GfE704YNG8y8/e9//7P8XUbM14kTJ8x8rVixwvJ3vpovLezRIhNNdJcuXTpgEre+mK9A/33uNh/Mmcad//nPf6zMQKsEZBlxcXGyefNmc5mJLTg42Py8bt26VJ+jt7s/XjVt2jTNx8P75yCls2fPmsut8uXLx3Rn4DkYNWqUFCpUSHr27Mm8Z8I5+Oyzz+SOO+4wrRIKFy4sN910k4wZM0YSEhI4Hxkw/3Xq1DHPsS+x2rt3r7l86r777mP+Mwj/HgO+jWn37dsnf/31V7LH6KWgenmpv8e9/A7gzPk6ceKEuTw7T5484s8yYr70Nd5++23zd7JatWriz3w1X4mJidKlSxcZMGCAVKlSRQKFLz9f2oJKf7/TVmBPPPGE/PPPPxII4nwwZ/r5+vzzz02rOr1d503/ffSk1Zc3kLhFlvH333+bJIcmPdzpzxqopkZvT8/j4f1zkJL2x9K+WCn/xwrfnYNvv/1WZs6cafoNI3POgSYKP/74Y/M8TRgOHTpUJk2aJC+++CKnJAPmv1OnTubLi3r16klYWJiUL19eGjZsKDExMcx/Bknr3+OTJ0/KuXPnOA/IUnwR09p/BmLcy+8Azpsv7RGvMX3Hjh0lKipK/Jkv52vp0qWSM2dO03Nz8uTJ8tVXX0mBAgXEn/lqvl5++WUJDQ2Vp59+WgKJr+ZL+9nOnj1bvv76azN3q1evlnvvvTcgikL+9sGcHTlyRE6fPi3jxo0zc7d8+XJp3bq1PPjgg2bufC3U568AAF6i/6PUxbH020ENYOB7p06dMt9ea9LW3wNFf6bf8uo3u1ptERISIrfeeqv88ccfMmHCBBk+fHhmDy/g6f9ztMJ5+vTp5tv13bt3yzPPPCOjR482SXQAAOAZvXKuffv2ZjG8N954g2m7grvuussseqeJKI3Fdd50gTKNCXGJVldOnTpVfvjhB1PFjat76KGHXPu6ENfNN99sChM05m3cuDFTmMrvYuqBBx4wCxSr6tWry3fffSdvvvmmNGjQQHyJiltkGZp00oTH4cOHk92uPxcpUiTV5+jt6Xk8vH8ObBMnTjSJW/12S/9hQcacgz179sj+/fvN6u/6LbZu+u2sXrqv+3o/fHsOlK7wrZfm6PNsN9xwg/kWWC8Hgm/nX5Oz+gXGI488YoJb/YZdE7ljx451BXLwrbT+PdZKrezZszP9yFJ8EdPafwZi3MvvAM6ZLztp+7///c9Uj/p7ta2v50tXrY+Ojpbbb7/dXP2msbf+6c98MV9r1qwxFZGlSpVy/b6in7H+/ftLmTJlxJ9l1P+/ypUrZ15LixP8XQEfzJkeUz9XN954Y7LH6O9jBw4cEF8jcYssIzw83FSp6eUANv2FW3/W3pGp0dvdH680yEjr8fD+OVDjx483lW3Lli2TmjVrMs0ZeA4qV64sW7duNd/221vLli1dFQAlS5bkfPj4HKi6deuaQMo9Sfjbb7+ZhK4eD76df+2trb2x3NlJ9KS1teBr/HsM+DamLVu2rPkF1f0x2opEq/v8Pe7ldwBnzJedtN21a5esWLFC8ufPL4EgIz9fetwLFy6IP/PFfOmX6z///HOy31e0tZ72u42NjRV/llGfr99//930uNXfLfxduA/mTI9Zq1Yt2blzZ7LH6O9jpUuXFp/LlCXRgEwyf/58szrue++9Z23fvt3q1auXlSdPHuuvv/4y93fp0sV6/vnnXY9fu3atFRoaak2cONHasWOHNXz4cCssLMzaunUr5zCDzsG4ceOs8PBw6+OPP7b+/PNP13bq1CnOQQadg5QCfRVSJ56DAwcOWLly5bKefPJJa+fOndbSpUutQoUKWS+++GImvousM//6/36df11Zfe/evdby5cut8uXLmxVocW30/+G6ArtuGo6+8sorZt9eXVznX8+DTec9MjLSGjBggPn3+PXXX7dCQkKsZcuWcQqQJfkiptWYS4+xZMkS6+effzb/1pctW9Y6d+6c5e98MV///POP+f/W559/bv4/pq+hP2uc6u+8PV9xcXFWy5YtrRIlSlhbtmxJFtNfuHDB8nfenq/Tp09bgwcPttatW2ft37/f2rRpk9WjRw/zGr/88ovl7zLid/LSpUtbkydPtgKBt+dLY7DnnnvOfL727dtnrVixwrrlllusChUqWOfPn7cCwXwffMYWLVpkbnv77betXbt2Wa+99pqJRdesWePz90PiFlmO/gUrVaqUSQbedttt1vfff++6r0GDBiYp5W7BggVWxYoVzeOrVKligjNk3DnQf3Q1GE656f9MkTHnICUSt5lzDr777jurdu3aJggpV66c9dJLL1kXL1700miynvTMf3x8vDVixAiTrI2IiLBKlixp9e7d2zp27Fgmjd7/rVy5MtX/t9vzrn/qeUj5nOrVq5tzpn8HZs2alUmjBwIzpk1MTLSGDh1qFS5c2Pxb07hxY/NlYaDw9nzp/4MCOUb15nxpcii1udJN/98eCLw5X/plSevWra1ixYqZ+4sWLWoS3xs2bLACha9/Jw+kxK235+vs2bPWPffcYxUsWNAkInWuHn30UVdSM1C85oPP2MyZM63o6Gjz+0C1atWsTz/9NEPeS5D+x/d1vQAAAAAAAAAAT9HjFgAAAAAAAAAchsQtAAAAAAAAADgMiVsAAAAAAAAAcBgStwAAAAAAAADgMCRuAQAAAAAAAMBhSNwCAAAAAAAAgMOQuAUAAAAAAAAAhyFxCwAAAAAAAAAOQ+IWAAAAAAAAAByGxC0AINMEBQVdcRsxYsR1HfvTTz9N1xiioqKkVq1asmTJEtf9p0+flmbNmsldd90lN9xwg7z33nvXPCYAAAD4H3+IWZXGqXq/xqwpLVy40NxXpkwZ120JCQkybtw4qVy5smTPnl3y5csntWvXlnfeecf1mO7du6f6njU+BuB7oRnwGgAApOrPP/907X/00UcybNgw2blzp+u2nDlzZsjMzZo1ywSfJ0+elOnTp0vbtm3lhx9+kKpVq0pkZKQsXbpUQkND5dtvv5X+/fubABYAAABZgz/ErLYcOXLIkSNHZN26dXLHHXe4bp85c6aUKlUq2fFGjhwpb731lkybNk1q1qxpjrtp0yY5duxYssfpa+pru8uWLZvP3ieAS6i4BQBkmiJFiri23Llzm2/v3W+bP3++qRiIiIgwlQAaoNri4uLkySeflKJFi5r7S5cuLWPHjjX32ZUErVu3vqyyIDV58uQxr1exYkUZPXq0XLx4UVauXGnuCw4ONklbDYA1SJ86dapP5wQAAADO4g8xq03j1k6dOsm7777ruu3333+XVatWmdvdffbZZ9K7d29p166dlC1bVqpVqyY9e/aU55577rIkrfv71S1v3rzXMaMAPEXFLQDAkebOnWsSpVoBUKNGDfnxxx/l0UcfNVUE3bp1k1dffdUEmwsWLDDVAwcPHjSb2rhxoxQqVMhVlRASEuLRa2rwq9UIKjw83HX7999/L0OGDDGvedNNN/noHQMAAMDfOClmtT388MPSsGFDU3CgV49pCwU9fuHChZM9ThOw33zzjUneFixY0CvzAcC7SNwCABxp+PDhMmnSJHnwwQfNz1oFsH37dnM5lwbBBw4ckAoVKki9evVMhYJWL9jswNOuSriajh07mkD53LlzkpiYaKod2rdvb+7TStv69etLpUqV5JFHHjHHXLZsmc/eNwAAAPyHU2JWd5pALleunHz88cfSpUsXk7h95ZVXZO/evckep7dpuwV97SpVqkidOnXkgQcekHvvvTfZ47RtWMp2EDExMWYD4FskbgEAjnPmzBnZs2ePuVRLKxbcqwv08jSlfWbvvvtuk1DVCoL7779f7rnnnmt6vcmTJ0uTJk1MMNuvXz9TGaGLMyitgoiPj/fSOwMAAECgcFLMmlrVrVbyapWvjvO+++4zVcHubrzxRvnll19k8+bNsnbtWvnvf/8rLVq0MGN2X6BMF+l94403kj03rdcF4F0kbgEAjnP69Gnz54wZM8zKtu7sS8huueUW2bdvn3z55ZeyYsUKU22ggaxWFqSXVhlER0ebTQNcDWy1UkKTtgAAAIC/xaydO3eWgQMHyogRI0zVrfa+TY2u51CrVi2z9e3bVz744APzeG0TptXDSts+6GsCyHgsTgYAcBztv1WsWDFTTWAHp/ZmB5AqKipKOnToYIJlXeH3k08+kX///dfcFxYWJgkJCel+7dtuu01uvfVWeemll7z6ngAAABBYnByzakVsy5YtZfXq1ab61lNahau0ShdA5qPiFgDgSCNHjpSnn37aXGaml5VduHBBNm3aJMeOHZNnn33W9OTS1Xm1h5dWCixcuNBUIWiPMKU9v77++mupW7euWQk3PSvfarWBru6rVQrFixf34bsEAACAP3NyzKq9badPny758+dP9fna31ZfV3vb6pi0Mnjw4MFSsWJFqVy5sutx+p7++uuvZM/VCt4CBQqkY6YAXAsqbgEAjqQLgWlvLb0MrGrVqtKgQQMTfNrVC7ly5ZLx48dLzZo1zaVd+/fvly+++MIExEoXifjqq6+kZMmSJlBODw269XWougUAAIC/xqzZs2dPM2mrmjZtKv/v//0/09dWk7W6mJombJcvX56stYIuzKvJZ/dNF1sD4HtBlmVZGfA6AAAAAAAAAAAPUXELAAAAAAAAAA5D4hYAAAAAAAAAHIbELQAAAAAAAAA4DIlbAAAAAAAAAHAYErcAAAAAAAAA4DAkbgEAAAAAAADAYUjcAgAAAAAAAIDDkLgFAAAAAAAAAIchcQsAAAAAAAAADkPiFgAAAAAAAAAchsQtAAAAAAAAADgMiVsAAAAAAAAAEGf5/7qqxTA6Te/zAAAAAElFTkSuQmCC", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Visualization\n", "if 'df' in dir() and len(df) > 0:\n", " fig, axes = plt.subplots(1, 2, figsize=(14, 5))\n", " \n", " # Plot 1: R² comparison\n", " plot_df = df[df['test_r2'].notna()].sort_values('test_r2', ascending=True)\n", " colors = ['green' if r > 0.95 else 'orange' if r > 0.90 else 'red' for r in plot_df['test_r2']]\n", " axes[0].barh(plot_df['model'], plot_df['test_r2'], color=colors)\n", " axes[0].set_xlabel('Test R²')\n", " axes[0].set_title('Model Performance (Test R²)')\n", " axes[0].axvline(0.95, color='green', linestyle='--', alpha=0.5, label='95%')\n", " axes[0].axvline(0.90, color='orange', linestyle='--', alpha=0.5, label='90%')\n", " axes[0].legend()\n", " \n", " # Plot 2: RMSE comparison\n", " axes[1].barh(plot_df['model'], plot_df['test_rmse'], color='steelblue')\n", " axes[1].set_xlabel('Test RMSE')\n", " axes[1].set_title('Model Error (Test RMSE)')\n", " \n", " plt.tight_layout()\n", " plt.show()" ] }, { "cell_type": "markdown", "id": "be01d215", "metadata": {}, "source": [ "## 6. Champion Model\n", "\n", "### Conv1D_v3 + RMSprop + MSE\n", "\n", "| Parameter | Value |\n", "|-----------|-------|\n", "| Architecture | Conv1D |\n", "| Filters | (128, 256) |\n", "| Kernel size | 3 |\n", "| Pool size | 3 |\n", "| Dense layers | (256, 128, 64) |\n", "| Activation | ReLU |\n", "| Dropout | 0.2 |\n", "| Optimizer | RMSprop (lr=0.001) |\n", "| Loss | MSE |\n", "| Window size | 30 frames |\n", "| Batch size | 32 |\n", "| Epochs | 100 (early stopping) |\n", "\n", "### Performance\n", "\n", "| Metric | Validation | Test |\n", "|--------|------------|------|\n", "| R² | 0.9468 ± 0.0049 | **0.9549** |\n", "| RMSE | 0.0335 ± 0.0018 | **0.0310** |\n", "| MAE | 0.0241 ± 0.0013 | 0.0229 |\n", "\n", "1. **Conv1D captures temporal patterns** - The 30-frame windows allow the model to learn motion dynamics\n", "2. **Deeper architecture (v3)** - More filters and dense layers provide sufficient capacity\n", "3. **RMSprop optimizer** - Adaptive learning rate works well with this data distribution\n", "4. **MSE loss** - Better than MAE for regression with continuous targets" ] }, { "cell_type": "markdown", "id": "bbf697d8", "metadata": {}, "source": [ "## 7. Dead Ends\n", "\n", "### What Did NOT Work\n", "\n", "| Configuration | R² | Issue |\n", "|---------------|------|-------|\n", "| LSTM | 0.77 | Overfitting, slow training |\n", "| GRU | 0.74 | Similar to LSTM, underperformed |\n", "| Dense MLP | 0.82 | Cannot capture temporal patterns |\n", "| Conv1D_v2 (high dropout) | 0.83 | Too much regularization |\n", "| MAE loss | ~0.93-0.94 | Lower R² than MSE |" ] }, { "cell_type": "markdown", "id": "e68fd0e3", "metadata": {}, "source": [ "## 8. Usage Guide\n", "\n", "### Loading the Best Model" ] }, { "cell_type": "code", "execution_count": 50, "id": "82cdb3ff", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Model loaded from cv_results_loss_optimizer/conv1d_v3_rmsprop_mse_fold4_best.h5\n" ] }, { "data": { "text/html": [ "
Model: \"Conv1DModel\"\n",
       "
\n" ], "text/plain": [ "\u001b[1mModel: \"Conv1DModel\"\u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓\n",
       "┃ Layer (type)                     Output Shape                  Param # ┃\n",
       "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩\n",
       "│ xy_seq_input (InputLayer)       │ (None, 30, 26)         │             0 │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ conv_1 (Conv1D)                 │ (None, 30, 128)        │        10,112 │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ pool_1 (MaxPooling1D)           │ (None, 10, 128)        │             0 │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ drop_conv_1 (Dropout)           │ (None, 10, 128)        │             0 │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ conv_2 (Conv1D)                 │ (None, 10, 256)        │        98,560 │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ pool_2 (MaxPooling1D)           │ (None, 4, 256)         │             0 │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ drop_conv_2 (Dropout)           │ (None, 4, 256)         │             0 │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ gap (GlobalAveragePooling1D)    │ (None, 256)            │             0 │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ fc_1 (Dense)                    │ (None, 256)            │        65,792 │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ drop_fc_1 (Dropout)             │ (None, 256)            │             0 │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ fc_2 (Dense)                    │ (None, 128)            │        32,896 │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ drop_fc_2 (Dropout)             │ (None, 128)            │             0 │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ fc_3 (Dense)                    │ (None, 64)             │         8,256 │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ drop_fc_3 (Dropout)             │ (None, 64)             │             0 │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ z_output (Dense)                │ (None, 13)             │           845 │\n",
       "└─────────────────────────────────┴────────────────────────┴───────────────┘\n",
       "
\n" ], "text/plain": [ "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓\n", "┃\u001b[1m \u001b[0m\u001b[1mLayer (type) \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mOutput Shape \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m Param #\u001b[0m\u001b[1m \u001b[0m┃\n", "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩\n", "│ xy_seq_input (\u001b[38;5;33mInputLayer\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m30\u001b[0m, \u001b[38;5;34m26\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ conv_1 (\u001b[38;5;33mConv1D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m30\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m10,112\u001b[0m │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ pool_1 (\u001b[38;5;33mMaxPooling1D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m10\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ drop_conv_1 (\u001b[38;5;33mDropout\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m10\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ conv_2 (\u001b[38;5;33mConv1D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m10\u001b[0m, \u001b[38;5;34m256\u001b[0m) │ \u001b[38;5;34m98,560\u001b[0m │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ pool_2 (\u001b[38;5;33mMaxPooling1D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m4\u001b[0m, \u001b[38;5;34m256\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ drop_conv_2 (\u001b[38;5;33mDropout\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m4\u001b[0m, \u001b[38;5;34m256\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ gap (\u001b[38;5;33mGlobalAveragePooling1D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m256\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ fc_1 (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m256\u001b[0m) │ \u001b[38;5;34m65,792\u001b[0m │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ drop_fc_1 (\u001b[38;5;33mDropout\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m256\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ fc_2 (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m32,896\u001b[0m │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ drop_fc_2 (\u001b[38;5;33mDropout\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ fc_3 (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m64\u001b[0m) │ \u001b[38;5;34m8,256\u001b[0m │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ drop_fc_3 (\u001b[38;5;33mDropout\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m64\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", "├─────────────────────────────────┼────────────────────────┼───────────────┤\n", "│ z_output (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m13\u001b[0m) │ \u001b[38;5;34m845\u001b[0m │\n", "└─────────────────────────────────┴────────────────────────┴───────────────┘\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
 Total params: 216,461 (845.55 KB)\n",
       "
\n" ], "text/plain": [ "\u001b[1m Total params: \u001b[0m\u001b[38;5;34m216,461\u001b[0m (845.55 KB)\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
 Trainable params: 216,461 (845.55 KB)\n",
       "
\n" ], "text/plain": [ "\u001b[1m Trainable params: \u001b[0m\u001b[38;5;34m216,461\u001b[0m (845.55 KB)\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
 Non-trainable params: 0 (0.00 B)\n",
       "
\n" ], "text/plain": [ "\u001b[1m Non-trainable params: \u001b[0m\u001b[38;5;34m0\u001b[0m (0.00 B)\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Example: Load and use the best model\n", "import tensorflow as tf\n", "from tensorflow import keras\n", "\n", "# Best model path\n", "BEST_MODEL_PATH = 'cv_results_loss_optimizer/conv1d_v3_rmsprop_mse_fold4_best.h5'\n", "\n", "if os.path.exists(BEST_MODEL_PATH):\n", " # Rebuild the model architecture and load weights (avoids Keras version mismatch)\n", " from models import build_conv1d_model\n", " model = build_conv1d_model(\n", " filters=(128, 256),\n", " kernel_size=3,\n", " pool_size=3,\n", " dense_units=(256, 128, 64),\n", " dropout_rate=0.2\n", " )\n", " model.load_weights(BEST_MODEL_PATH)\n", " model.compile(optimizer='rmsprop', loss='mse', metrics=['mae'])\n", " \n", " print(f\"Model loaded from {BEST_MODEL_PATH}\")\n", " model.summary()\n", "else:\n", " print(f\"Model not found at {BEST_MODEL_PATH}\")" ] }, { "cell_type": "code", "execution_count": 51, "id": "15765a85", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Input shape: (1, 30, 26)\n", "Output shape: (1, 13)\n", "Predicted Z values: [ 0.05649754 0.03968733 -0.03916807 0.05462698 0.00482728]...\n" ] } ], "source": [ "# Example inference\n", "def predict_z_coordinates(model, xy_sequence):\n", " \"\"\"\n", " Predict Z coordinates from X/Y sequence.\n", " \n", " Args:\n", " model: Loaded Keras model\n", " xy_sequence: numpy array of shape (batch, 30, 26)\n", " 30 = window size, 26 = 13 joints × 2 (x, y)\n", " \n", " Returns:\n", " z_predictions: numpy array of shape (batch, 13)\n", " 13 z-coordinates for each joint\n", " \"\"\"\n", " return model.predict(xy_sequence, verbose=0)\n", "\n", "# Example with dummy data\n", "if 'model' in dir():\n", " dummy_input = np.random.randn(1, 30, 26).astype(np.float32)\n", " z_pred = predict_z_coordinates(model, dummy_input)\n", " print(f\"Input shape: {dummy_input.shape}\")\n", " print(f\"Output shape: {z_pred.shape}\")\n", " print(f\"Predicted Z values: {z_pred[0][:5]}...\")" ] }, { "cell_type": "markdown", "id": "6fb36696", "metadata": {}, "source": [ "### Retraining the Model\n", "\n", "```bash\n", "# Run the cross-validation training script\n", "cd A9\n", "python cv_training.py\n", "```\n", "\n", "Or modify `all_models_config.py` to change architecture parameters." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.0" } }, "nbformat": 4, "nbformat_minor": 5 }