{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## General Information" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This notebook is used to train a simple neural network model to predict the chemistry in the barite benchmark (50x50 grid). The training data is stored in the repository using **git large file storage** and can be downloaded after the installation of git lfs using the `git lfs pull` command.\n", "\n", "It is then recommended to create a Python environment using miniconda. The necessary dependencies are contained in `environment.yml` and can be installed using `conda env create -f environment.yml`.\n", "\n", "The data set is divided into a design and result part and consists of the iterations of a reference simulation. The design part of the data set contains the chemical concentrations at time $t$ and the result part at time $t+1$, which are to be learned by the model." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Setup Libraries" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2025-01-22 15:50:06.981475: I tensorflow/core/util/port.cc:153] oneDNN custom operations are on. You may see slightly different numerical results due to floating-point round-off errors from different computation orders. To turn them off, set the environment variable `TF_ENABLE_ONEDNN_OPTS=0`.\n", "2025-01-22 15:50:07.001765: I tensorflow/core/platform/cpu_feature_guard.cc:210] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.\n", "To enable the following instructions: SSE4.1 SSE4.2 AVX AVX2 AVX_VNNI FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Running Keras in version 3.6.0\n" ] } ], "source": [ "import keras\n", "import h5py\n", "import numpy as np\n", "import pandas as pd\n", "import time\n", "import sklearn.model_selection as sk\n", "import matplotlib.pyplot as plt\n", "from sklearn.cluster import KMeans\n", "from sklearn.pipeline import Pipeline, make_pipeline\n", "from sklearn.preprocessing import StandardScaler, MinMaxScaler\n", "from imblearn.over_sampling import SMOTE\n", "from imblearn.under_sampling import RandomUnderSampler\n", "from imblearn.over_sampling import RandomOverSampler\n", "from collections import Counter\n", "import os\n", "from preprocessing import *\n", "from sklearn import set_config\n", "set_config(transform_output = \"pandas\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Define parameters" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "dtype = \"float32\"\n", "activation = \"relu\"\n", "\n", "lr = 0.001\n", "batch_size = 512\n", "epochs = 50 # default 400 epochs\n", "\n", "lr_schedule = keras.optimizers.schedules.ExponentialDecay(\n", " initial_learning_rate=lr,\n", " decay_steps=2000,\n", " decay_rate=0.9,\n", " staircase=True\n", ")\n", "\n", "optimizer_simple = keras.optimizers.Adam(learning_rate=lr_schedule)\n", "optimizer_large = keras.optimizers.Adam(learning_rate=lr_schedule)\n", "\n", "loss = keras.losses.Huber()\n", "\n", "sample_fraction = 0.8" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Setup the model" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Model: \"sequential\"\n",
"\n"
],
"text/plain": [
"\u001b[1mModel: \"sequential\"\u001b[0m\n"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓\n",
"┃ Layer (type) ┃ Output Shape ┃ Param # ┃\n",
"┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩\n",
"│ dense (Dense) │ (None, 128) │ 1,664 │\n",
"├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
"│ dense_1 (Dense) │ (None, 128) │ 16,512 │\n",
"├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
"│ dense_2 (Dense) │ (None, 12) │ 1,548 │\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",
"│ dense (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m1,664\u001b[0m │\n",
"├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
"│ dense_1 (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m16,512\u001b[0m │\n",
"├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
"│ dense_2 (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m12\u001b[0m) │ \u001b[38;5;34m1,548\u001b[0m │\n",
"└─────────────────────────────────┴────────────────────────┴───────────────┘\n"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"Total params: 19,724 (77.05 KB)\n", "\n" ], "text/plain": [ "\u001b[1m Total params: \u001b[0m\u001b[38;5;34m19,724\u001b[0m (77.05 KB)\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
Trainable params: 19,724 (77.05 KB)\n", "\n" ], "text/plain": [ "\u001b[1m Trainable params: \u001b[0m\u001b[38;5;34m19,724\u001b[0m (77.05 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": [ "model_simple = keras.Sequential(\n", " [\n", " keras.Input(shape = (12,), dtype = \"float32\"),\n", " keras.layers.Dense(units = 128, activation = \"relu\", dtype = \"float32\"),\n", " keras.layers.Dense(units = 128, activation = \"relu\", dtype = \"float32\"),\n", " keras.layers.Dense(units = 12, dtype = \"float32\")\n", " ]\n", ")\n", "\n", "model_simple.compile(optimizer=optimizer_simple, loss = loss)\n", "model_simple.summary()" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Model: \"sequential_1\"\n",
"\n"
],
"text/plain": [
"\u001b[1mModel: \"sequential_1\"\u001b[0m\n"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓\n",
"┃ Layer (type) ┃ Output Shape ┃ Param # ┃\n",
"┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩\n",
"│ dense_3 (Dense) │ (None, 512) │ 6,656 │\n",
"├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
"│ dense_4 (Dense) │ (None, 1024) │ 525,312 │\n",
"├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
"│ dense_5 (Dense) │ (None, 512) │ 524,800 │\n",
"├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
"│ dense_6 (Dense) │ (None, 12) │ 6,156 │\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",
"│ dense_3 (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m512\u001b[0m) │ \u001b[38;5;34m6,656\u001b[0m │\n",
"├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
"│ dense_4 (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m1024\u001b[0m) │ \u001b[38;5;34m525,312\u001b[0m │\n",
"├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
"│ dense_5 (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m512\u001b[0m) │ \u001b[38;5;34m524,800\u001b[0m │\n",
"├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
"│ dense_6 (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m12\u001b[0m) │ \u001b[38;5;34m6,156\u001b[0m │\n",
"└─────────────────────────────────┴────────────────────────┴───────────────┘\n"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"Total params: 1,062,924 (4.05 MB)\n", "\n" ], "text/plain": [ "\u001b[1m Total params: \u001b[0m\u001b[38;5;34m1,062,924\u001b[0m (4.05 MB)\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
Trainable params: 1,062,924 (4.05 MB)\n", "\n" ], "text/plain": [ "\u001b[1m Trainable params: \u001b[0m\u001b[38;5;34m1,062,924\u001b[0m (4.05 MB)\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": [ "model_large = keras.Sequential(\n", " [keras.layers.Input(shape=(12,), dtype=dtype),\n", " keras.layers.Dense(512, activation='relu', dtype=dtype),\n", " keras.layers.Dense(1024, activation='relu', dtype=dtype),\n", " keras.layers.Dense(512, activation='relu', dtype=dtype),\n", " keras.layers.Dense(12, dtype=dtype)\n", " ])\n", "\n", "model_large.compile(optimizer=optimizer_large, loss = loss)\n", "model_large.summary()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Define transformer functions" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "def Safelog(val):\n", " # get range of vector\n", " if val > 0:\n", " return np.log10(val)\n", " elif val < 0:\n", " return -np.log10(-val)\n", " else:\n", " return 0\n", "\n", "def Safeexp(val):\n", " if val > 0:\n", " return -10 ** -val\n", " elif val < 0:\n", " return 10 ** val\n", " else:\n", " return 0" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "# ? Why does the charge is using another logarithm than the other species\n", "\n", "func_dict_in = {\n", " \"H\" : np.log1p,\n", " \"O\" : np.log1p,\n", " \"Charge\" : Safelog,\n", " \"H_0_\" : np.log1p,\n", " \"O_0_\" : np.log1p,\n", " \"Ba\" : np.log1p,\n", " \"Cl\" : np.log1p,\n", " \"S_2_\" : np.log1p,\n", " \"S_6_\" : np.log1p,\n", " \"Sr\" : np.log1p,\n", " \"Barite\" : np.log1p,\n", " \"Celestite\" : np.log1p,\n", "}\n", "\n", "func_dict_out = {\n", " \"H\" : np.expm1,\n", " \"O\" : np.expm1,\n", " \"Charge\" : Safeexp,\n", " \"H_0_\" : np.expm1,\n", " \"O_0_\" : np.expm1,\n", " \"Ba\" : np.expm1,\n", " \"Cl\" : np.expm1,\n", " \"S_2_\" : np.expm1,\n", " \"S_6_\" : np.expm1,\n", " \"Sr\" : np.expm1,\n", " \"Barite\" : np.expm1,\n", " \"Celestite\" : np.expm1,\n", "}\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Read data from `.h5` file and convert it to a `pandas.DataFrame`" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "# os.chdir('/mnt/beegfs/home/signer/projects/model-training')\n", "data_file = h5py.File(\"Barite_50_Data_training.h5\")\n", "\n", "design = data_file[\"design\"]\n", "results = data_file[\"result\"]\n", "\n", "df_design = pd.DataFrame(np.array(design[\"data\"]).transpose(), columns = design[\"names\"].asstr())\n", "df_results = pd.DataFrame(np.array(results[\"data\"]).transpose(), columns = results[\"names\"].asstr())\n", "\n", "data_file.close()" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/signer/bin/miniconda3/envs/training/lib/python3.11/site-packages/sklearn/base.py:1473: ConvergenceWarning: Number of distinct clusters (1) found smaller than n_clusters (2). Possibly due to duplicate points in X.\n", " return fit_method(estimator, *args, **kwargs)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Amount class 0 before: 0.9879169719169719\n", "Amount class 1 before: 0.012083028083028084\n" ] }, { "ename": "KeyError", "evalue": "'Class'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", "File \u001b[0;32m~/bin/miniconda3/envs/training/lib/python3.11/site-packages/pandas/core/indexes/base.py:3805\u001b[0m, in \u001b[0;36mIndex.get_loc\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 3804\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m-> 3805\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_engine\u001b[38;5;241m.\u001b[39mget_loc(casted_key)\n\u001b[1;32m 3806\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m err:\n", "File \u001b[0;32mindex.pyx:167\u001b[0m, in \u001b[0;36mpandas._libs.index.IndexEngine.get_loc\u001b[0;34m()\u001b[0m\n", "File \u001b[0;32mindex.pyx:175\u001b[0m, in \u001b[0;36mpandas._libs.index.IndexEngine.get_loc\u001b[0;34m()\u001b[0m\n", "File \u001b[0;32mpandas/_libs/index_class_helper.pxi:70\u001b[0m, in \u001b[0;36mpandas._libs.index.Int64Engine._check_type\u001b[0;34m()\u001b[0m\n", "\u001b[0;31mKeyError\u001b[0m: 'Class'", "\nThe above exception was the direct cause of the following exception:\n", "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", "Cell \u001b[0;32mIn[9], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m X_train, X_val, X_test, y_train, y_val, y_test \u001b[38;5;241m=\u001b[39m preprocessing(df_design, df_results, func_dict_in, func_dict_out, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124moff\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;241m0.1\u001b[39m)\n", "File \u001b[0;32m~/Documents/model-training/preprocessing.py:164\u001b[0m, in \u001b[0;36mpreprocessing\u001b[0;34m(df_design, df_targets, func_dict_in, func_dict_out, sampling, test_size)\u001b[0m\n\u001b[1;32m 160\u001b[0m df_results_log \u001b[38;5;241m=\u001b[39m FuncTransform(func_dict_in, func_dict_out)\u001b[38;5;241m.\u001b[39mfit_transform(df_targets)\n\u001b[1;32m 162\u001b[0m X_train, X_test, y_train, y_test \u001b[38;5;241m=\u001b[39m sk\u001b[38;5;241m.\u001b[39mtrain_test_split(df_design_log, df_results_log, test_size \u001b[38;5;241m=\u001b[39m test_size, random_state\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m42\u001b[39m)\n\u001b[0;32m--> 164\u001b[0m X_train, y_train \u001b[38;5;241m=\u001b[39m balancer(X_train, y_train, sampling)\n\u001b[1;32m 166\u001b[0m scaler_X \u001b[38;5;241m=\u001b[39m MinMaxScaler()\n\u001b[1;32m 167\u001b[0m scaler_y \u001b[38;5;241m=\u001b[39m MinMaxScaler()\n", "File \u001b[0;32m~/Documents/model-training/preprocessing.py:131\u001b[0m, in \u001b[0;36mbalancer\u001b[0;34m(design, target, strategy, sample_fraction)\u001b[0m\n\u001b[1;32m 128\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 129\u001b[0m classes_resampled \u001b[38;5;241m=\u001b[39m classes\n\u001b[0;32m--> 131\u001b[0m counter \u001b[38;5;241m=\u001b[39m classes_resampled[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mClass\u001b[39m\u001b[38;5;124m\"\u001b[39m]\u001b[38;5;241m.\u001b[39mvalue_counts()\n\u001b[1;32m 132\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mAmount class 0 after:\u001b[39m\u001b[38;5;124m\"\u001b[39m, counter[\u001b[38;5;241m0\u001b[39m] \u001b[38;5;241m/\u001b[39m (counter[\u001b[38;5;241m0\u001b[39m] \u001b[38;5;241m+\u001b[39m counter[\u001b[38;5;241m1\u001b[39m]) )\n\u001b[1;32m 133\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mAmount class 1 after:\u001b[39m\u001b[38;5;124m\"\u001b[39m, counter[\u001b[38;5;241m1\u001b[39m] \u001b[38;5;241m/\u001b[39m (counter[\u001b[38;5;241m0\u001b[39m] \u001b[38;5;241m+\u001b[39m counter[\u001b[38;5;241m1\u001b[39m]) )\n", "File \u001b[0;32m~/bin/miniconda3/envs/training/lib/python3.11/site-packages/pandas/core/series.py:1121\u001b[0m, in \u001b[0;36mSeries.__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 1118\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_values[key]\n\u001b[1;32m 1120\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m key_is_scalar:\n\u001b[0;32m-> 1121\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_get_value(key)\n\u001b[1;32m 1123\u001b[0m \u001b[38;5;66;03m# Convert generator to list before going through hashable part\u001b[39;00m\n\u001b[1;32m 1124\u001b[0m \u001b[38;5;66;03m# (We will iterate through the generator there to check for slices)\u001b[39;00m\n\u001b[1;32m 1125\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m is_iterator(key):\n", "File \u001b[0;32m~/bin/miniconda3/envs/training/lib/python3.11/site-packages/pandas/core/series.py:1237\u001b[0m, in \u001b[0;36mSeries._get_value\u001b[0;34m(self, label, takeable)\u001b[0m\n\u001b[1;32m 1234\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_values[label]\n\u001b[1;32m 1236\u001b[0m \u001b[38;5;66;03m# Similar to Index.get_value, but we do not fall back to positional\u001b[39;00m\n\u001b[0;32m-> 1237\u001b[0m loc \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mindex\u001b[38;5;241m.\u001b[39mget_loc(label)\n\u001b[1;32m 1239\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m is_integer(loc):\n\u001b[1;32m 1240\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_values[loc]\n", "File \u001b[0;32m~/bin/miniconda3/envs/training/lib/python3.11/site-packages/pandas/core/indexes/base.py:3812\u001b[0m, in \u001b[0;36mIndex.get_loc\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 3807\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(casted_key, \u001b[38;5;28mslice\u001b[39m) \u001b[38;5;129;01mor\u001b[39;00m (\n\u001b[1;32m 3808\u001b[0m \u001b[38;5;28misinstance\u001b[39m(casted_key, abc\u001b[38;5;241m.\u001b[39mIterable)\n\u001b[1;32m 3809\u001b[0m \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28many\u001b[39m(\u001b[38;5;28misinstance\u001b[39m(x, \u001b[38;5;28mslice\u001b[39m) \u001b[38;5;28;01mfor\u001b[39;00m x \u001b[38;5;129;01min\u001b[39;00m casted_key)\n\u001b[1;32m 3810\u001b[0m ):\n\u001b[1;32m 3811\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m InvalidIndexError(key)\n\u001b[0;32m-> 3812\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m(key) \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01merr\u001b[39;00m\n\u001b[1;32m 3813\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m:\n\u001b[1;32m 3814\u001b[0m \u001b[38;5;66;03m# If we have a listlike key, _check_indexing_error will raise\u001b[39;00m\n\u001b[1;32m 3815\u001b[0m \u001b[38;5;66;03m# InvalidIndexError. Otherwise we fall through and re-raise\u001b[39;00m\n\u001b[1;32m 3816\u001b[0m \u001b[38;5;66;03m# the TypeError.\u001b[39;00m\n\u001b[1;32m 3817\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_check_indexing_error(key)\n", "\u001b[0;31mKeyError\u001b[0m: 'Class'" ] } ], "source": [ "X_train, X_val, X_test, y_train, y_val, y_test = preprocessing(df_design, df_results, func_dict_in, func_dict_out, \"off\", 0.1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Classify each cell with kmeans" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/signer/bin/miniconda3/envs/training/lib/python3.11/site-packages/sklearn/base.py:1473: ConvergenceWarning: Number of distinct clusters (1) found smaller than n_clusters (2). Possibly due to duplicate points in X.\n", " return fit_method(estimator, *args, **kwargs)\n" ] } ], "source": [ "df_design = clustering(df_design)" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAaEAAAGdCAYAAAC7EMwUAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAIdhJREFUeJzt3X9oXfUd//HX53POufcmbZJvu82k+TaTisFNSoW1rrQ426kt+BWp85+xyuhwA7WtGPyjrvqH3f5IagdFR7YOt+GE4bo/5q8/pjSgphtFSLXFUkEYdF1As2yjJmma++ucz/ePc3OTNOmP/Kifm/p8jEOac2+Sk7MtT97nnHuPcc45AQDggfW9AQCALy8iBADwhggBALwhQgAAb4gQAMAbIgQA8IYIAQC8IUIAAG9C3xtwsSRJ9Omnn6qhoUHGGN+bAwCYJeecRkZG1NraKmsvP+vUXIQ+/fRTtbW1+d4MAMA89ff3a+XKlZd9Ts1FqKGhQZJ0h/6fQkWetwYAMFtllfR3/bX69/xyai5C44fgQkUKDRECgEWn8o6kV3NKhQsTAADeECEAgDdECADgDRECAHhDhAAA3hAhAIA3RAgA4A0RAgB4Q4QAAN4QIQCAN0QIAOANEQIAeEOEAADeECEAgDdECADgDRECAHhDhAAA3hAhAIA3RAgA4A0RAgB4Q4QAAN4QIQCAN0QIAOANEQIAeEOEAADeECEAgDdECADgDRECAHhDhAAA3hAhAIA3RAgA4A0RAgB4Q4QAAN4QIQCAN0QIAOANEQIAeEOEAADeECEAgDdECADgDRECAHhDhAAA3hAhAIA3RAgA4A0RAgB4Q4QAAN4QIQCAN0QIAOANEQIAeEOEAADeECEAgDdECADgDRECAHhDhAAA3hAhAIA3RAgA4A0RAgB4Q4QAAN4QIQCAN0QIAOANEQIAeEOEAADeECEAgDdECADgzbwi1NXVJWOMOjo6quucc9q3b59aW1tVV1enzZs36/Tp0/PdTgDAdWjOEerr69OLL76oNWvWTFl/4MABHTx4UN3d3err61NLS4u2bNmikZGReW8sAOD6MqcInT9/Xg899JB++9vfatmyZdX1zjk9//zzeuaZZ/Tggw9q9erVevnll3XhwgW98sorC7bRAIDrw5witGvXLt1333265557pqw/c+aMBgYGtHXr1uq6bDarTZs26dixYzN+r0KhoOHh4SkLAODLIZztFxw+fFgffvih+vr6pj02MDAgSWpubp6yvrm5WWfPnp3x+3V1delnP/vZbDcDAHAdmNUk1N/fryeeeEJ//OMflcvlLvk8Y8yUz51z09aN27t3r4aGhqpLf3//bDYJALCIzWoS+uCDDzQ4OKi1a9dW18VxrKNHj6q7u1uffPKJpHQiWrFiRfU5g4OD06ajcdlsVtlsdi7bDgBY5GY1Cd199906deqUTp48WV3WrVunhx56SCdPntRNN92klpYW9fT0VL+mWCyqt7dXGzduXPCNBwAsbrOahBoaGrR69eop65YsWaKvfOUr1fUdHR3q7OxUe3u72tvb1dnZqfr6em3fvn3hthoAcF2Y9YUJV7Jnzx6NjY1p586dOnfunNavX68jR46ooaFhoX8UAGCRM84553sjJhseHlZTU5M2a5tCE/neHADALJVdSe/pDQ0NDamxsfGyz+W94wAA3hAhAIA3RAgA4A0RAgB4Q4QAAN4QIQCAN0QIAOANEQIAeEOEAADeECEAgDdECADgDRECAHhDhAAA3hAhAIA3RAgA4A0RAgB4Q4QAAN4QIQCAN0QIAOANEQIAeEOEAADeECEAgDdECADgDRECAHhDhAAA3hAhAIA3RAgA4A0RAgB4Q4QAAN4QIQCAN0QIAOANEQIAeEOEAADeECEAgDdECADgDRECAHhDhAAA3hAhAIA3RAgA4A0RAgB4Q4QAAN4QIQCAN0QIAOANEQIAeEOEAADeECEAgDdECADgDRECAHhDhAAA3hAhAIA3RAgA4A0RAgB4Q4QAAN4QIQCAN0QIAOANEQIAeEOEAADeECEAgDdECADgDRECAHhDhAAA3hAhAIA3RAgA4M2sInTo0CGtWbNGjY2Namxs1IYNG/TWW29VH3fOad++fWptbVVdXZ02b96s06dPL/hGAwCuD7OK0MqVK7V//34dP35cx48f11133aVt27ZVQ3PgwAEdPHhQ3d3d6uvrU0tLi7Zs2aKRkZFrsvEAgMXNOOfcfL7B8uXL9Ytf/EIPP/ywWltb1dHRoaeeekqSVCgU1NzcrOeee06PPPLIVX2/4eFhNTU1abO2KTTRfDYNAOBB2ZX0nt7Q0NCQGhsbL/vcOZ8TiuNYhw8f1ujoqDZs2KAzZ85oYGBAW7durT4nm81q06ZNOnbs2CW/T6FQ0PDw8JQFAPDlMOsInTp1SkuXLlU2m9Wjjz6q1157TbfeeqsGBgYkSc3NzVOe39zcXH1sJl1dXWpqaqoubW1ts90kAMAiNesI3XLLLTp58qTef/99PfbYY9qxY4c+/vjj6uPGmCnPd85NWzfZ3r17NTQ0VF36+/tnu0kAgEUqnO0XZDIZ3XzzzZKkdevWqa+vTy+88EL1PNDAwIBWrFhRff7g4OC06WiybDarbDY7280AAFwH5v06IeecCoWCVq1apZaWFvX09FQfKxaL6u3t1caNG+f7YwAA16FZTUJPP/207r33XrW1tWlkZESHDx/We++9p7ffflvGGHV0dKizs1Pt7e1qb29XZ2en6uvrtX379mu1/QCARWxWEfr3v/+tH/7wh/rss8/U1NSkNWvW6O2339aWLVskSXv27NHY2Jh27typc+fOaf369Tpy5IgaGhquycYDABa3eb9OaKHxOiEAWNy+kNcJAQAwX0QIAOANEQIAeEOEAADeECEAgDdECADgDRECAHhDhAAA3hAhAIA3RAgA4A0RAgB4Q4QAAN4QIQCAN0QIAOANEQIAeEOEAADeECEAgDdECADgDRECAHhDhAAA3hAhAIA3RAgA4A0RAgB4Q4QAAN4QIQCAN0QIAOANEQIAeEOEAADeECEAgDdECADgDRECAHhDhAAA3hAhAIA3RAgA4A0RAgB4Q4QAAN4QIQCAN0QIAOANEQIAeEOEAADeECEAgDdECADgDRECAHhDhAAA3hAhAIA3RAgA4A0RAgB4Q4QAAN4QIQCAN0QIAOANEQIAeEOEAADeECEAgDdECADgDRECAHhDhAAA3hAhAIA3RAgA4A0RAgB4Q4QAAN4QIQCAN0QIAOANEQIAeDOrCHV1den2229XQ0ODbrjhBj3wwAP65JNPpjzHOad9+/aptbVVdXV12rx5s06fPr2gGw0AuD7MKkK9vb3atWuX3n//ffX09KhcLmvr1q0aHR2tPufAgQM6ePCguru71dfXp5aWFm3ZskUjIyMLvvEAgMXNOOfcXL/4P//5j2644Qb19vbqzjvvlHNOra2t6ujo0FNPPSVJKhQKam5u1nPPPadHHnnkit9zeHhYTU1N2qxtCk00100DAHhSdiW9pzc0NDSkxsbGyz53XueEhoaGJEnLly+XJJ05c0YDAwPaunVr9TnZbFabNm3SsWPHZvwehUJBw8PDUxYAwJfDnCPknNOTTz6pO+64Q6tXr5YkDQwMSJKam5unPLe5ubn62MW6urrU1NRUXdra2ua6SQCARWbOEdq9e7c++ugj/elPf5r2mDFmyufOuWnrxu3du1dDQ0PVpb+/f66bBABYZMK5fNHjjz+uN998U0ePHtXKlSur61taWiSlE9GKFSuq6wcHB6dNR+Oy2ayy2excNgMAsMjNahJyzmn37t169dVX9c4772jVqlVTHl+1apVaWlrU09NTXVcsFtXb26uNGzcuzBYDAK4bs5qEdu3apVdeeUVvvPGGGhoaqud5mpqaVFdXJ2OMOjo61NnZqfb2drW3t6uzs1P19fXavn37NfkFAACL16widOjQIUnS5s2bp6x/6aWX9KMf/UiStGfPHo2NjWnnzp06d+6c1q9fryNHjqihoWFBNhgAcP2Y1+uErgVeJwQAi9sX9johAADmgwgBALwhQgAAb4gQAMAbIgQA8IYIAQC8IUIAAG+IEADAGyIEAPCGCAEAvCFCAABviBAAwBsiBADwhggBALwhQgAAb4gQAMAbIgQA8IYIAQC8IUIAAG+IEADAGyIEAPCGCAEAvCFCAABviBAAwBsiBADwhggBALwhQgAAb4gQAMAbIgQA8IYIAQC8IUIAAG+IEADAGyIEAPCGCAEAvCFCAABviBAAwBsiBADwhggBALwhQgAAb4gQAMAbIgQA8IYIAQC8IUIAAG+IEADAGyIEAPCGCAEAvCFCAABviBAAwBsiBADwhggBALwhQgAAb4gQAMAbIgQA8IYIAQC8IUIAAG+IEADAGyIEAPCGCAEAvCFCAABviBAAwBsiBADwhggBALyZdYSOHj2q+++/X62trTLG6PXXX5/yuHNO+/btU2trq+rq6rR582adPn16obYXAHAdmXWERkdHddttt6m7u3vGxw8cOKCDBw+qu7tbfX19amlp0ZYtWzQyMjLvjQUAXF/C2X7Bvffeq3vvvXfGx5xzev755/XMM8/owQcflCS9/PLLam5u1iuvvKJHHnlkflsLALiuLOg5oTNnzmhgYEBbt26trstms9q0aZOOHTu2kD8KAHAdmPUkdDkDAwOSpObm5inrm5ubdfbs2Rm/plAoqFAoVD8fHh5eyE0CANSwa3J1nDFmyufOuWnrxnV1dampqam6tLW1XYtNAgDUoAWNUEtLi6SJiWjc4ODgtOlo3N69ezU0NFRd+vv7F3KTAAA1bEEjtGrVKrW0tKinp6e6rlgsqre3Vxs3bpzxa7LZrBobG6csAIAvh1mfEzp//rz+8Y9/VD8/c+aMTp48qeXLl+vrX/+6Ojo61NnZqfb2drW3t6uzs1P19fXavn37gm44AGDxm3WEjh8/ru9+97vVz5988klJ0o4dO/SHP/xBe/bs0djYmHbu3Klz585p/fr1OnLkiBoaGhZuqwEA1wXjnHO+N2Ky4eFhNTU1abO2KTSR780BAMxS2ZX0nt7Q0NDQFU+x8N5xAABviBAAwBsiBADwhggBALwhQgAAb4gQAMAbIgQA8IYIAQC8IUIAAG+IEADAGyIEAPCGCAEAvCFCAABviBAAwBsiBADwhggBALwhQgAAb4gQAMAbIgQA8Cb0vQGXZEy6TOacn20BAFwTtRuhmRAlALiu1GyETBBI7hJHC11SeVIlSsQIABalmo2QjJWxQfrv8ehIcomTjJ26nhgBwKJUsxEygZExVs45SZUYJU4mUDU+LiFGALCY1WyEFASSCWSkiaiYZCJKiZOxxAgAFrOajZDJZGRMZiIsiZNzTiZJ0riYRM6Zi6ajID1cp8rnxAgAalpNR0gmSj9JnOQSGeeq/3ZxMhGkZFKQZpqOiBEA1KSajZByGRmbrUTGpR/jWHIuDVCYSHFyiSA5GVOJFTECgJpVsxFy9XVyNjMRoUpkzHhw4kSKY7kkkYnjic+dSz+vHK4jRgBQu2o2QvHSjEyYk4nHp6D0fJCLXTU6phxPBKhcrkxEsRTb9N9XEyNCBADe1GyEik1ZJWFGxkkmdjKJkylXJqFykn4sxTLlRCrHaZBK5TRGSSJTLk+JkVMsJVbGJhOvNSJEAOBVzUaosCxQHIaSk2zsZJI0RrbkZMtOtpTIlBLZciVGpVimUJYpldMJqGjlyrGMKU+8D10cT5+KCBEAeFOzEcovswoiK5M42djIxJKJJVt2CkrpR1t0CoqJbDGRLcaymVCmWE5jZG06GQVWrlSSsUbO2hmmIkIEAL7UboSWS2EkmcRUpiDJliVbMuk0VDIKik5BwSgoWgWFQEEhls0HskEgGwZSsSRTtDLGVKciJ8lIhAgAakDNRqi0LFGcTSoTkLkoQka2qDRABaegIIUFp3DMKIisgtBKeSsTGMna6lTkrJGxRipNOjwXa3qIJGIEAF+Amo2QlhXk6qyS2EixkcpWpmxkika2aBQUJVswCgpGYV6K81IcWYV5ozA0cqGRDW06FQVWCtKJSMbIGSNTNhNTUeU8kYsrP5upCAC+EDUboWXLLki5ROXEKo6tSqVAcTlQUrJKCoHiolGQt4oLUpIxCjJGSSQlkVESGiWRURhZBYGRAiNjbToVBVamFMgZm75BarlcPU9kFHDBAgB8gWo2Qv+34XOZ+joV40ClJFC+HCpfClUoRSoWA5ULocrZQKZglWSsgqxRUg2RlERWSViZiiIrGwUKxioTka2cJ5p8eE5KX1M00+E5QgQA10TNRqit/nPZ+rxKzqqYhBotZ3ShnNFYOdJoMaMLxUj5bJTGKBMqydhKhIzijFEcGcWRSyejylTkAqMgtLLWStZMHJ5T5bBcHFc+EiIA+CLUbIRWZs8pyGVVcoHySaSxONL5OKvRclbDUU6jmYxGoqwuZDIqRInKmVAuEyiJxg/NTZqMwspUVDk056yRDdIAmUqMnDEyRU0K0kUhAgAsuJqNUEv0ucIoo5ILlXeRCkmkkTin82FWjVFew6WclkS5apBGMxkVMpHi8akoqkxG49NR5JSERi4wCgOr0BrZyiRkjKlctq1pIZLiyp1cE6YhAFhgNRuhZfa86sJIRReo5EKNJhktDfI6H+d0IcloSVCnJWFB9WFRw1FOdVFW5zMZXchkVMxEUw/RBaYSIMlZmxbGSIEx6T1bKxGaEiKXvmedcWbiHkUclgOABVWzEfo/wZjqbEmxjEou1BIbKe8iLbEFjSZZLQ3yWhrUqzHMazjKaShTp+FMTsOZnEYyWY1lMiplIpUygZLAylWmIGclGZt+lKoxqvyzcsm2U+VShcpUFHN+CACugZqN0FJTUr1NKhEqK+dKKipQvSloic0qn0Sqt0VdSDJqCnNqDPP6PKzX0qigoahOQ5UY5aOMymEkFwRytvIiVSM5Yysf058XSJXJJ13c+OdJkl7OzYUKALDgajZC9TZRvZESSbExKrlYJWeVC0rKuZLyJlK9LehCktWozVcmo4Iaw5wawoLqwiXKBLGGgkSjVoql9NyOsXLj74qgdBwyTpKTgvFDcMnER5ckXDEHANdIzUYoZ4xyJo1HIqdIsRITq+isciZWyRSVd6GW2IIaXKSGJKsGm9dQUK+lYUF1QUm5sKQoiGWN03njVLZRerxNVtKkEI2f8nFOtnouyMklTsYl1amI95oDgIVVsxGKZBQZo0iVaSh9y1FFJlHJJYqNUeQS5VxZeVdSzlQWW1LWlhSZWKGNZeVkTXq77xFJ5WozrIwzkpOMs+mbpCbpXVyDOP1onJNzSXUycoorI5Um7kcEAJizmo1QaKyi6mGzdBqK5ZRIiuRUklOkskrGKOdi5V15IkQmjVBkYgWaGooRSeXqt500ETkruTC9eV7iZJP0NuKmcstwVS5WmLhQgSvmAGC+ajZCVlZR5ZxNokSBMbJySpxTZIyCSpTSIMWKTKLIJWl4TJIumnlSGZFUltKiOCu5yu0iEisTh+mtxMtJegvxOJk4PyTNfKECAGBOajZCgTHVS6cDBYqdk5WUmESxnLIySioTUVBdygrkJJtPP0767WLZKd9/RFI5MZUAmUm3i7CycShbTu/aqiSZen4oCarnh6rH5piGAGBOajZCtvIfaWISkiS5dG2iRDJS5CRr0kusrZyqYRhvzqTfcPJk5JzRiDOKJZXiQCY2smWjoGTSEJVCmTi9RDudiGK5JJhyfih9ISvTEADMVQ1HyEz696QpxlT+4Ls0RZMP06VfMjVEscyMv2XijGJnNJpIcdmoHNuJG+aVrGwxkCklUjmSjdPzQ0oSqXr5dnpYjmkIAOauZiMkTQ1R5UJpWdmJyaj6tgcTU9H0EM18aC6RUSkOFMdWF0qByqX0ZnlphCYOy5nYycRxelguTiYOy8V20tv6MA0BwFzUdIQmGw9SIjcxGV00FV06RJVvEqYBip1VOQmUz0Xp/YqKoUpFq3KhEqLi5MNygWw5komdVI6rh+UUJExDADBPNRuhwFgFJq1HPGnKuDhGU6eiy4collEcWCXOqOBCjcWRxsqRxuoilUvp3VrLpfSwXDB+WK4UyJSdVE7SGFUOy5k4ZhoCgHmq2QhNdqkYzT5EecXOKh9EupCM6XyU1Ug2q/OljArFUIW6QHHBKi5K5eKkw3LlQKYcypRimVJZxlq5YNI0NHljmYYA4KotigiNC4ydV4hiGS2xBeXdmPJBpJEwp6Yor+FMTmO5SKViqKRoq9OQLSmdiorphQq2GMiVQplSmB6aM+XKrSEqrxuKZ95uAMDMFlWEpOlT0dWGKJFTTrFKpqwltqALtqCm8EJ1GrpQyiifizRWSA/LxUWTTkNFKagclrPFUKYQy4SBTFCZhuI4vUY81sRb+TANAcBVWXQRGjd5KrpSiKxzipS+/1zOxMqZkuptQQ02feftpmhM57NZjZXSaahcspUAjcdICrJGQSaQzQZyxfFpqCxZW7k9+KQLFAAAV8Ve+Sm1a3wqkiYuWBi/ci59/ZBVIJMuxigy6Rug5irT0JLKNNQY5tUY5bUkU1Q2V5LJxYrrEpXrnMo5Kc5KccYoyVi5KJDCQAqsFAQyxki2cksIayq3Alc6DQEALmtRR0i6fIiq641RpPRW3pFcJUTpNFRvi1oaFNQQ5dWYzas+W1SYK8llEyXZSoCyJo1QZJSEVi6sBMimH2VMekgOADAriz5C0qVDNHkaSp9nFBgpV3mH7ZwppfcjCvJqDPNaGhbSaShbTqehXKIk66qTUJyxclF6LsiFQToBWVM9JJdeoDBpGgIAXNZ189cyuMQf/vEQWWNkJUWSgvFzQzaN0NIgr6ZwTA1RXg2ZgpZkiwozsVzGKcloYomMkigNkQKbHpazkw7JXTwNcUgOAC7rmkXo17/+tVatWqVcLqe1a9fqb3/727X6UdPMdFhu2rkhVQ7JmfTcUL0tVqehuqikTLYsZWPFGackqgQolJLQyIWVK+OMkQkmHZKTmIIAYBauyV/MP//5z+ro6NAzzzyjEydO6Dvf+Y7uvfde/etf/7oWP67qSoflxqchKykwThklypi4Og0tDQpaEha1JCoqG5Vko0QuckoiNxGgwMgFVgoq08/4+aDxQ3ISh+QA4Cpdk7+UBw8e1I9//GP95Cc/0Te/+U09//zzamtr06FDh67Fj5viUoflpEnTkKZeoJAuRdUHBS0JC6oPi8pFZQVhnEYo1KSlEiJr0xhVQnTJQ3IAgEta8AgVi0V98MEH2rp165T1W7du1bFjxxb6x13W5Hfhnmz8AgUrV7kNeFk5W1K9LareFpULSsoEsaIolgInF0guVPoxkJw16SQ0Hp7LTT2cFwKAS1rwF6v+97//VRzHam5unrK+ublZAwMD055fKBRUKBSqnw8PDy/0JqXnhkyS3hDPONnKXVozJlFJSfVKucjEytqysjZWNigrCJJKhJycNemdwCsfZUx6ccL45GPNReeFeOEqAFzJNTtxYS6aAJxz09ZJUldXl5qamqpLW1vbvH/25Q7JVZ+jyrkhOWUUK2Ni5UxROVtS1pYV2kShTWSCpDL9TCyyRs5oIjqTDsHN9DsCAGa24BH66le/qiAIpk09g4OD06YjSdq7d6+GhoaqS39//4Juz8WH5MbPC0087mRNIqtEgUn/HdpYoUlkjJOxlTu2GsmZND7OaCI8XBUHAHO24IfjMpmM1q5dq56eHn3ve9+rru/p6dG2bdumPT+bzSqbzVY/d5U3/hw+P79780x+t+1ETokSxS79WHaJSnLKO6cLiTTqEo3GsQpJSaVSScVCSfGFvNxYTsmFQC7vVC5ahSUpLiUql2OZuCiXFGWSguRKcq4o54qSi9PfwSRySTz+S83rdwGAxaSskqSJv+eX5a6Bw4cPuyiK3O9//3v38ccfu46ODrdkyRL3z3/+84pf29/f75Tee4GFhYWFZREv/f39V/ybf03eRfv73/++/ve//+nnP/+5PvvsM61evVp//etfdeONN17xa1tbW9Xf36+GhgYZYzQ8PKy2tjb19/ersbHxWmzudYH9dHXYT1eH/XR12E8zc85pZGREra2tV3yuca62jxUNDw+rqalJQ0ND/Jd8Geynq8N+ujrsp6vDfpo/zqYDALwhQgAAb2o+QtlsVs8+++yUK+gwHfvp6rCfrg776eqwn+av5s8JAQCuXzU/CQEArl9ECADgDRECAHhDhAAA3tR8hHzeJrwWHT16VPfff79aW1tljNHrr78+5XHnnPbt26fW1lbV1dVp8+bNOn36tJ+N9aSrq0u33367GhoadMMNN+iBBx7QJ598MuU57Cfp0KFDWrNmjRobG9XY2KgNGzborbfeqj7OPppZV1eXjDHq6OiormNfzV1NR8jXbcJr2ejoqG677TZ1d3fP+PiBAwd08OBBdXd3q6+vTy0tLdqyZYtGRka+4C31p7e3V7t27dL777+vnp4elctlbd26VaOjo9XnsJ+klStXav/+/Tp+/LiOHz+uu+66S9u2bav+8WQfTdfX16cXX3xRa9asmbKefTUP83if0mvu29/+tnv00UenrPvGN77hfvrTn3raotoiyb322mvVz5MkcS0tLW7//v3Vdfl83jU1Nbnf/OY3HrawNgwODjpJrre31znHfrqcZcuWud/97nfsoxmMjIy49vZ219PT4zZt2uSeeOIJ5xz/e5qvmp2Eauk24YvFmTNnNDAwMGWfZbNZbdq06Uu9z4aGhiRJy5cvl8R+mkkcxzp8+LBGR0e1YcMG9tEMdu3apfvuu0/33HPPlPXsq/m5Ju+ivRBme5twqLpfZtpnZ8+e9bFJ3jnn9OSTT+qOO+7Q6tWrJbGfJjt16pQ2bNigfD6vpUuX6rXXXtOtt95a/ePJPkodPnxYH374ofr6+qY9xv+e5qdmIzTuam8Tjgnsswm7d+/WRx99pL///e/THmM/SbfccotOnjypzz//XH/5y1+0Y8cO9fb2Vh9nH0n9/f164okndOTIEeVyuUs+j301NzV7OG62twmH1NLSIknss4rHH39cb775pt59912tXLmyup79NCGTyejmm2/WunXr1NXVpdtuu00vvPAC+2iSDz74QIODg1q7dq3CMFQYhurt7dUvf/lLhWFY3R/sq7mp2QhNvk34ZD09Pdq4caOnraptq1atUktLy5R9ViwW1dvb+6XaZ8457d69W6+++qreeecdrVq1asrj7KdLc86pUCiwjya5++67derUKZ08ebK6rFu3Tg899JBOnjypm266iX01H/6uibiy+dwm/Ho1MjLiTpw44U6cOOEkuYMHD7oTJ064s2fPOuec279/v2tqanKvvvqqO3XqlPvBD37gVqxY4YaHhz1v+Rfnsccec01NTe69995zn332WXW5cOFC9TnsJ+f27t3rjh496s6cOeM++ugj9/TTTztrrTty5Ihzjn10OZOvjnOOfTUfNR0h55z71a9+5W688UaXyWTct771repltl9W77777oz3ct+xY4dzLr1c9Nlnn3UtLS0um826O++80506dcrvRn/BZto/ktxLL71UfQ77ybmHH364+v+tr33ta+7uu++uBsg59tHlXBwh9tXccSsHAIA3NXtOCABw/SNCAABviBAAwBsiBADwhggBALwhQgAAb4gQAMAbIgQA8IYIAQC8IUIAAG+IEADAGyIEAPDm/wObChfBM84L5AAAAABJRU5ErkJggg==", "text/plain": [ "