1099 lines
61 KiB
Plaintext
1099 lines
61 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "ee60f482",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Constants"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"id": "26e2925b",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import os\n",
|
|
"\n",
|
|
"os.environ['TF_FORCE_GPU_ALLOW_GROWTH'] = 'true' # this is required\n",
|
|
"os.environ['CUDA_VISIBLE_DEVICES'] = '0' # set to '0' for GPU0, '1' for GPU1 or '2' for GPU2. Check \"gpustat\" in a terminal."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"id": "9fce01ac",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"glob_path = '/opt/iui-datarelease3-sose2021/*.csv'\n",
|
|
"\n",
|
|
"pickle_file = '../data.pickle'"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "cd018a1c",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Config"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"id": "6884422c",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Possibilities: 'SYY', 'SYN', 'SNY', 'SNN', \n",
|
|
"# 'JYY', 'JYN', 'JNY', 'JNN'\n",
|
|
"cenario = 'SYN'\n",
|
|
"\n",
|
|
"win_sz = 10\n",
|
|
"stride_sz = 5\n",
|
|
"\n",
|
|
"# divisor for neuron count step downs (hard to describe), e.g. dense_step = 3: layer1=900, layer2 = 300, layer3 = 100, layer4 = 33...\n",
|
|
"dense_steps = 3\n",
|
|
"# amount of dense/dropout layers\n",
|
|
"layer_count = 5\n",
|
|
"# how much to drop\n",
|
|
"drop_count = 0.2"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "55ca2da2",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Helper Functions"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 4,
|
|
"id": "c4ec5294",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from matplotlib import pyplot as plt\n",
|
|
"\n",
|
|
"def pplot(dd):\n",
|
|
" x = dd.shape[0]\n",
|
|
" fix = int(x/3)+1\n",
|
|
" fiy = 3\n",
|
|
" fig, axs = plt.subplots(fix, fiy, figsize=(3*fiy, 9*fix))\n",
|
|
" \n",
|
|
" for i in range(x):\n",
|
|
" axs[int(i/3)][i%3].plot(dd[i])"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "b44ffdca",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Loading Data"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"id": "5036fcf3",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"from glob import glob\n",
|
|
"import pandas as pd\n",
|
|
"from tqdm import tqdm\n",
|
|
"\n",
|
|
"def dl_from_blob(filename, user_filter=None):\n",
|
|
" \n",
|
|
" dic_data = []\n",
|
|
" \n",
|
|
" for p in tqdm(glob(glob_path)):\n",
|
|
" path = p\n",
|
|
" filename = path.split('/')[-1].split('.')[0]\n",
|
|
" splitname = filename.split('_')\n",
|
|
" user = int(splitname[0][1:])\n",
|
|
" if (user_filter):\n",
|
|
" if (user != user_filter):\n",
|
|
" continue\n",
|
|
" scenario = splitname[1][len('Scenario'):]\n",
|
|
" heightnorm = splitname[2][len('HeightNormalization'):] == 'True'\n",
|
|
" armnorm = splitname[3][len('ArmNormalization'):] == 'True'\n",
|
|
" rep = int(splitname[4][len('Repetition'):])\n",
|
|
" session = int(splitname[5][len('Session'):])\n",
|
|
" data = pd.read_csv(path)\n",
|
|
" dic_data.append(\n",
|
|
" {\n",
|
|
" 'filename': path,\n",
|
|
" 'user': user,\n",
|
|
" 'scenario': scenario,\n",
|
|
" 'heightnorm': heightnorm,\n",
|
|
" 'armnorm': armnorm,\n",
|
|
" 'rep': rep,\n",
|
|
" 'session': session,\n",
|
|
" 'data': data \n",
|
|
" }\n",
|
|
" )\n",
|
|
" return dic_data"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 6,
|
|
"id": "dbd4313e",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import pickle\n",
|
|
"\n",
|
|
"def save_pickle(f, structure):\n",
|
|
" _p = open(f, 'wb')\n",
|
|
" pickle.dump(structure, _p)\n",
|
|
" _p.close()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 7,
|
|
"id": "d84806e6",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def load_pickles(f) -> list:\n",
|
|
" _p = open(pickle_file, 'rb')\n",
|
|
" _d = pickle.load(_p)\n",
|
|
" _p.close()\n",
|
|
" \n",
|
|
" return _d"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 8,
|
|
"id": "b1700ddf",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Loading data...\n",
|
|
"../data.pickle found...\n",
|
|
"768\n",
|
|
"CPU times: user 604 ms, sys: 2.61 s, total: 3.22 s\n",
|
|
"Wall time: 3.22 s\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"%%time\n",
|
|
"\n",
|
|
"def load_data() -> list:\n",
|
|
" if os.path.isfile(pickle_file):\n",
|
|
" print(f'{pickle_file} found...')\n",
|
|
" return load_pickles(pickle_file)\n",
|
|
" print(f'Didn\\'t find {pickle_file}...')\n",
|
|
" all_data = dl_from_blob(glob_path)\n",
|
|
" print(f'Creating {pickle_file}...')\n",
|
|
" save_pickle(pickle_file, all_data)\n",
|
|
" return all_data\n",
|
|
"\n",
|
|
"print(\"Loading data...\")\n",
|
|
"dic_data = load_data()\n",
|
|
"print(len(dic_data))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 9,
|
|
"id": "ea17c3e6",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"CPU times: user 435 µs, sys: 0 ns, total: 435 µs\n",
|
|
"Wall time: 446 µs\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"%%time\n",
|
|
"\n",
|
|
"# Categorized Data\n",
|
|
"cdata = dict() \n",
|
|
"# Sorting, HeightNorm, ArmNorm\n",
|
|
"cdata['SYY'] = list() \n",
|
|
"cdata['SYN'] = list() \n",
|
|
"cdata['SNY'] = list() \n",
|
|
"cdata['SNN'] = list() \n",
|
|
"\n",
|
|
"# Jenga, HeightNorm, ArmNorm\n",
|
|
"cdata['JYY'] = list() \n",
|
|
"cdata['JYN'] = list() \n",
|
|
"cdata['JNY'] = list() \n",
|
|
"cdata['JNN'] = list() \n",
|
|
"for d in dic_data:\n",
|
|
" if d['scenario'] == 'Sorting':\n",
|
|
" if d['heightnorm']:\n",
|
|
" if d['armnorm']:\n",
|
|
" cdata['SYY'].append(d)\n",
|
|
" else:\n",
|
|
" cdata['SYN'].append(d)\n",
|
|
" else:\n",
|
|
" if d['armnorm']:\n",
|
|
" cdata['SNY'].append(d)\n",
|
|
" else:\n",
|
|
" cdata['SNN'].append(d)\n",
|
|
" elif d['scenario'] == 'Jenga':\n",
|
|
" if d['heightnorm']:\n",
|
|
" if d['armnorm']:\n",
|
|
" cdata['JYY'].append(d)\n",
|
|
" else:\n",
|
|
" cdata['JYN'].append(d)\n",
|
|
" else:\n",
|
|
" if d['armnorm']:\n",
|
|
" cdata['JNY'].append(d)\n",
|
|
" else:\n",
|
|
" cdata['JNN'].append(d)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "5995a0cd",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Preprocessing"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 10,
|
|
"id": "fdb10cc9",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"def drop(entry) -> pd.DataFrame:\n",
|
|
" droptable = ['participantID', 'FrameID', 'Scenario', 'HeightNormalization', 'ArmNormalization', 'Repetition', 'Session', 'Unnamed: 0']\n",
|
|
" centry = pickle.loads(pickle.dumps(entry))\n",
|
|
" return centry['data'].drop(droptable, axis=1)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 11,
|
|
"id": "549dfb1d",
|
|
"metadata": {
|
|
"tags": []
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"def floatize(entry) -> pd.DataFrame:\n",
|
|
" centry = pickle.loads(pickle.dumps(entry))\n",
|
|
" centry['data']['LeftHandTrackingAccuracy'] = (entry['data']['LeftHandTrackingAccuracy'] == 'High') * 1.0\n",
|
|
" centry['data']['RightHandTrackingAccuracy'] = (entry['data']['RightHandTrackingAccuracy'] == 'High') * 1.0\n",
|
|
" return centry['data']"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 12,
|
|
"id": "043de2b6",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import numpy as np\n",
|
|
"right_Hand_ident='right_Hand'\n",
|
|
"left_Hand_ident='left_hand'\n",
|
|
"\n",
|
|
"def rem_low_acc(entry) -> pd.DataFrame:\n",
|
|
" centry = pickle.loads(pickle.dumps(entry))\n",
|
|
" right_Hand_cols = [c for c in centry['data'] if right_Hand_ident in c]\n",
|
|
" left_Hand_cols = [c for c in centry['data'] if left_Hand_ident in c]\n",
|
|
" \n",
|
|
" centry['data'].loc[centry['data']['RightHandTrackingAccuracy'] == 0.0, right_Hand_cols] = np.nan\n",
|
|
" centry['data'].loc[centry['data']['LeftHandTrackingAccuracy'] == 0.0, left_Hand_cols] = np.nan\n",
|
|
" return centry['data']"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 13,
|
|
"id": "6d2b7ff6",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from tensorflow.keras.preprocessing.sequence import pad_sequences\n",
|
|
"\n",
|
|
"stride = 150\n",
|
|
"def pad(entry) -> pd.DataFrame:\n",
|
|
" centry = pickle.loads(pickle.dumps(entry))\n",
|
|
" cols = centry['data'].columns\n",
|
|
" pentry = pad_sequences(centry['data'].T.to_numpy(),\n",
|
|
" maxlen=(int(centry['data'].shape[0]/stride)+1)*stride,\n",
|
|
" dtype='float64',\n",
|
|
" padding='pre', \n",
|
|
" truncating='post',\n",
|
|
" value=np.nan\n",
|
|
" ) \n",
|
|
" pdentry = pd.DataFrame(pentry.T, columns=cols)\n",
|
|
" pdentry.loc[0] = [0 for _ in cols]\n",
|
|
" return pdentry"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 14,
|
|
"id": "e3a1e53a",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def interpol(entry) -> pd.DataFrame:\n",
|
|
" centry = pickle.loads(pickle.dumps(entry))\n",
|
|
" return centry['data'].interpolate(method='linear', axis=0)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 15,
|
|
"id": "4b0d7212",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from tensorflow.keras.preprocessing import timeseries_dataset_from_array\n",
|
|
"\n",
|
|
"def slicing(entry):\n",
|
|
" centry = pickle.loads(pickle.dumps(entry))\n",
|
|
" return timeseries_dataset_from_array(\n",
|
|
" data=centry['data'], \n",
|
|
" targets=[centry['user'] for _ in range(centry['data'].shape[0])], \n",
|
|
" sequence_length=win_sz,\n",
|
|
" sequence_stride=stride_sz, \n",
|
|
" batch_size=8, \n",
|
|
" seed=177013\n",
|
|
" )"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 16,
|
|
"id": "bbbe19e8",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stderr",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"100%|██████████| 96/96 [00:17<00:00, 5.53it/s]"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"CPU times: user 15.8 s, sys: 1.64 s, total: 17.4 s\n",
|
|
"Wall time: 17.4 s\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stderr",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"%%time\n",
|
|
"\n",
|
|
"classes = 16 # dynamic\n",
|
|
"\n",
|
|
"def preproc(data):\n",
|
|
" res_list = list()\n",
|
|
" \n",
|
|
" for e in tqdm(data):\n",
|
|
" res_list.append(preproc_entry(e))\n",
|
|
" \n",
|
|
" return res_list\n",
|
|
" \n",
|
|
"def preproc_entry(entry):\n",
|
|
" entry2 = pickle.loads(pickle.dumps(entry))\n",
|
|
" entry2['data'] = drop(entry2)\n",
|
|
" \n",
|
|
" entry3 = pickle.loads(pickle.dumps(entry2))\n",
|
|
" entry3['data'] = floatize(entry3)\n",
|
|
" \n",
|
|
" entry4 = pickle.loads(pickle.dumps(entry3))\n",
|
|
" entry4['data'] = rem_low_acc(entry4)\n",
|
|
" \n",
|
|
" entry5 = pickle.loads(pickle.dumps(entry4))\n",
|
|
" entry5['data'] = pad(entry5)\n",
|
|
" \n",
|
|
" entry6 = pickle.loads(pickle.dumps(entry5))\n",
|
|
" entry6['data'] = interpol(entry6)\n",
|
|
" \n",
|
|
" entry7 = pickle.loads(pickle.dumps(entry6))\n",
|
|
" entry7['data'] = slicing(entry7)\n",
|
|
" \n",
|
|
" return entry7\n",
|
|
"\n",
|
|
"pdata = preproc(cdata[cenario])"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "4c906571",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Building Model"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 17,
|
|
"id": "16996dda",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import tensorflow as tf\n",
|
|
"from tensorflow.keras.models import Sequential\n",
|
|
"from tensorflow.keras.layers import Dense, Flatten, BatchNormalization, Dropout, LSTM\n",
|
|
"import tensorflow.keras as keras\n",
|
|
"\n",
|
|
"def build_model(shape, classes):\n",
|
|
" \n",
|
|
" model = Sequential()\n",
|
|
" ncount = shape[0]*shape[1]\n",
|
|
" \n",
|
|
" model.add(Flatten(input_shape=shape))\n",
|
|
" \n",
|
|
" model.add(Dropout(drop_count))\n",
|
|
" model.add(BatchNormalization())\n",
|
|
" \n",
|
|
" for i in range(1,layer_count):\n",
|
|
" neurons = int(ncount/pow(dense_steps,i))\n",
|
|
" if neurons <= classes*dense_steps:\n",
|
|
" break\n",
|
|
" model.add(Dropout(drop_count*i))\n",
|
|
" model.add(Dense(neurons, activation='relu'))\n",
|
|
" \n",
|
|
" model.add(Dense(classes, activation='softmax'))\n",
|
|
"\n",
|
|
" model.compile(\n",
|
|
" optimizer=tf.keras.optimizers.Adam(0.001),\n",
|
|
" loss=\"categorical_crossentropy\", \n",
|
|
" metrics=[\"acc\"],\n",
|
|
" )\n",
|
|
"\n",
|
|
" return model\n",
|
|
"\n",
|
|
"def build_mlp(input_shape, nb_classes):\n",
|
|
" input_layer = keras.layers.Input(input_shape)\n",
|
|
"\n",
|
|
" # flatten/reshape because when multivariate all should be on the same axis \n",
|
|
" input_layer_flattened = keras.layers.Flatten()(input_layer)\n",
|
|
" \n",
|
|
" layer_1 = keras.layers.Dropout(0.1)(input_layer_flattened)\n",
|
|
" layer_1 = keras.layers.Dense(500, activation='relu')(layer_1)\n",
|
|
"\n",
|
|
" layer_2 = keras.layers.Dropout(0.2)(layer_1)\n",
|
|
" layer_2 = keras.layers.Dense(500, activation='relu')(layer_2)\n",
|
|
"\n",
|
|
" layer_3 = keras.layers.Dropout(0.2)(layer_2)\n",
|
|
" layer_3 = keras.layers.Dense(500, activation='relu')(layer_3)\n",
|
|
"\n",
|
|
" output_layer = keras.layers.Dropout(0.3)(layer_3)\n",
|
|
" output_layer = keras.layers.Dense(nb_classes, activation='softmax')(output_layer)\n",
|
|
"\n",
|
|
" model = keras.models.Model(inputs=input_layer, outputs=output_layer)\n",
|
|
"\n",
|
|
" model.compile(loss='categorical_crossentropy', optimizer=keras.optimizers.Adadelta(),\n",
|
|
" metrics=['accuracy'])\n",
|
|
"\n",
|
|
" return model"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 18,
|
|
"id": "8e39cb47",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"checkpoint_file = './goat.weights'\n",
|
|
"\n",
|
|
"def train_model(X_train, y_train, X_test, y_test):\n",
|
|
" model = build_model(X_train[0].shape, 16)\n",
|
|
" \n",
|
|
" model.summary()\n",
|
|
"\n",
|
|
" model_checkpoint_callback = tf.keras.callbacks.ModelCheckpoint(\n",
|
|
" filepath = checkpoint_file,\n",
|
|
" save_weights_only=True,\n",
|
|
" monitor='val_acc',\n",
|
|
" mode='max',\n",
|
|
" save_best_only=True\n",
|
|
" )\n",
|
|
" \n",
|
|
" history = model.fit(X_train, \n",
|
|
" y_train,\n",
|
|
" epochs=30,\n",
|
|
" batch_size=128,\n",
|
|
" shuffle=True,\n",
|
|
" verbose=2,\n",
|
|
" validation_data=(X_test, y_test),\n",
|
|
" callbacks=[model_checkpoint_callback]\n",
|
|
" \n",
|
|
" )\n",
|
|
" return model, history\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 19,
|
|
"id": "a32d5263",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"weight_path = 'SYN46 1010/goat.weights.index'\n",
|
|
"def load_model(X_train, y_train, X_test, y_test):\n",
|
|
" model = build_model(X_train[0].shape, 16)\n",
|
|
" \n",
|
|
" model.summary()\n",
|
|
"\n",
|
|
" model.load_weights(weight_path)\n",
|
|
" return model, None"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 20,
|
|
"id": "a4cee4dc",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def train_mlp(X_train, y_train, X_test, y_test):\n",
|
|
" model = build_mlp(X_train[0].shape, 16)\n",
|
|
" model.summary()\n",
|
|
" \n",
|
|
" reduce_lr = keras.callbacks.ReduceLROnPlateau(monitor='loss', factor=0.5, patience=200, min_lr=0.1)\n",
|
|
"\n",
|
|
" model_checkpoint = keras.callbacks.ModelCheckpoint(filepath=checkpoint_file, monitor='loss', \n",
|
|
" save_best_only=True)\n",
|
|
"\n",
|
|
" callbacks = [reduce_lr,model_checkpoint]\n",
|
|
" history = model.fit(X_train, \n",
|
|
" y_train,\n",
|
|
" epochs=5000,\n",
|
|
" batch_size=16,\n",
|
|
" shuffle=True,\n",
|
|
" verbose=2,\n",
|
|
" validation_data=(X_test, y_test),\n",
|
|
" callbacks=callbacks\n",
|
|
" )\n",
|
|
" \n",
|
|
" return model, history"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 21,
|
|
"id": "4184848a",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"CPU times: user 181 µs, sys: 114 µs, total: 295 µs\n",
|
|
"Wall time: 312 µs\n"
|
|
]
|
|
},
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"(48, 48)"
|
|
]
|
|
},
|
|
"execution_count": 21,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"%%time\n",
|
|
"train = np.array([x['data'] for x in pdata if x['session'] == 1])\n",
|
|
"test = np.array([x['data'] for x in pdata if x['session'] == 2])\n",
|
|
"\n",
|
|
"len(train), len(test)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 22,
|
|
"id": "c6d1bcce",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"CPU times: user 26.4 s, sys: 6.68 s, total: 33 s\n",
|
|
"Wall time: 9.52 s\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"%%time\n",
|
|
"\n",
|
|
"X_train = list()\n",
|
|
"y_train = list()\n",
|
|
"\n",
|
|
"X_test = list()\n",
|
|
"y_test = list()\n",
|
|
"\n",
|
|
"train = list()\n",
|
|
"test = list()\n",
|
|
"\n",
|
|
"for x in pdata:\n",
|
|
" if x['session'] == 1:\n",
|
|
" train.append(\n",
|
|
" {\n",
|
|
" 'label': x['user'],\n",
|
|
" 'data': list()\n",
|
|
" })\n",
|
|
" for y in x['data'].unbatch().as_numpy_iterator():\n",
|
|
" X_train.append(y[0])\n",
|
|
" y_train.append(y[1])\n",
|
|
" \n",
|
|
" train[-1]['data'].append(y[0])\n",
|
|
" if x['session'] == 2:\n",
|
|
" test.append(\n",
|
|
" {\n",
|
|
" 'label': x['user'],\n",
|
|
" 'data': list()\n",
|
|
" })\n",
|
|
" for y in x['data'].unbatch().as_numpy_iterator():\n",
|
|
" X_test.append(y[0])\n",
|
|
" y_test.append(y[1])\n",
|
|
" \n",
|
|
" test[-1]['data'].append(y[0])\n",
|
|
"\n",
|
|
"X_train = np.array(X_train)\n",
|
|
"y_train = np.array(y_train)\n",
|
|
"X_test = np.array(X_test)\n",
|
|
"y_test = np.array(y_test)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 23,
|
|
"id": "c975f4b3",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"((30432, 10, 338), (30432,), (20502, 10, 338), (20502,))"
|
|
]
|
|
},
|
|
"execution_count": 23,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"X_train.shape, y_train.shape, X_test.shape, y_test.shape"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 24,
|
|
"id": "e16afa5d",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"CPU times: user 538 ms, sys: 190 ms, total: 728 ms\n",
|
|
"Wall time: 728 ms\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"%%time\n",
|
|
"\n",
|
|
"from sklearn.preprocessing import LabelBinarizer\n",
|
|
"\n",
|
|
"lb = LabelBinarizer()\n",
|
|
"yy_train = lb.fit_transform(y_train)\n",
|
|
"yy_test = lb.fit_transform(y_test)\n",
|
|
"\n",
|
|
"for e in test:\n",
|
|
" e['label'] = lb.transform([e['label']])\n",
|
|
" e['data'] = np.array(e['data'])\n",
|
|
" \n",
|
|
"for e in train:\n",
|
|
" e['label'] = lb.transform([e['label']])\n",
|
|
" e['data'] = np.array(e['data'])\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 25,
|
|
"id": "2ffd01b1",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"(30432, 10, 338)\n",
|
|
"(30432, 16)\n",
|
|
"(20502, 10, 338)\n",
|
|
"(20502, 16)\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"print(X_train.shape)\n",
|
|
"print(yy_train.shape)\n",
|
|
"print(X_test.shape)\n",
|
|
"print(yy_test.shape)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 26,
|
|
"id": "2a3118f0",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Model: \"sequential\"\n",
|
|
"_________________________________________________________________\n",
|
|
"Layer (type) Output Shape Param # \n",
|
|
"=================================================================\n",
|
|
"flatten (Flatten) (None, 3380) 0 \n",
|
|
"_________________________________________________________________\n",
|
|
"dropout (Dropout) (None, 3380) 0 \n",
|
|
"_________________________________________________________________\n",
|
|
"batch_normalization (BatchNo (None, 3380) 13520 \n",
|
|
"_________________________________________________________________\n",
|
|
"dropout_1 (Dropout) (None, 3380) 0 \n",
|
|
"_________________________________________________________________\n",
|
|
"dense (Dense) (None, 1126) 3807006 \n",
|
|
"_________________________________________________________________\n",
|
|
"dropout_2 (Dropout) (None, 1126) 0 \n",
|
|
"_________________________________________________________________\n",
|
|
"dense_1 (Dense) (None, 375) 422625 \n",
|
|
"_________________________________________________________________\n",
|
|
"dropout_3 (Dropout) (None, 375) 0 \n",
|
|
"_________________________________________________________________\n",
|
|
"dense_2 (Dense) (None, 125) 47000 \n",
|
|
"_________________________________________________________________\n",
|
|
"dense_3 (Dense) (None, 16) 2016 \n",
|
|
"=================================================================\n",
|
|
"Total params: 4,292,167\n",
|
|
"Trainable params: 4,285,407\n",
|
|
"Non-trainable params: 6,760\n",
|
|
"_________________________________________________________________\n",
|
|
"Epoch 1/30\n",
|
|
"238/238 - 2s - loss: 1.5191 - acc: 0.5326 - val_loss: 2.9622 - val_acc: 0.2009\n",
|
|
"Epoch 2/30\n",
|
|
"238/238 - 1s - loss: 0.9614 - acc: 0.6869 - val_loss: 3.3264 - val_acc: 0.2228\n",
|
|
"Epoch 3/30\n",
|
|
"238/238 - 1s - loss: 0.7458 - acc: 0.7567 - val_loss: 3.7283 - val_acc: 0.2192\n",
|
|
"Epoch 4/30\n",
|
|
"238/238 - 1s - loss: 0.6155 - acc: 0.8000 - val_loss: 3.8189 - val_acc: 0.2170\n",
|
|
"Epoch 5/30\n",
|
|
"238/238 - 1s - loss: 0.5178 - acc: 0.8315 - val_loss: 4.1898 - val_acc: 0.2235\n",
|
|
"Epoch 6/30\n",
|
|
"238/238 - 1s - loss: 0.4538 - acc: 0.8519 - val_loss: 4.1662 - val_acc: 0.2382\n",
|
|
"Epoch 7/30\n",
|
|
"238/238 - 1s - loss: 0.3923 - acc: 0.8739 - val_loss: 4.2680 - val_acc: 0.2365\n",
|
|
"Epoch 8/30\n",
|
|
"238/238 - 1s - loss: 0.3508 - acc: 0.8874 - val_loss: 4.6401 - val_acc: 0.2174\n",
|
|
"Epoch 9/30\n",
|
|
"238/238 - 1s - loss: 0.3306 - acc: 0.8918 - val_loss: 4.4543 - val_acc: 0.2253\n",
|
|
"Epoch 10/30\n",
|
|
"238/238 - 1s - loss: 0.2976 - acc: 0.9055 - val_loss: 4.3307 - val_acc: 0.2369\n",
|
|
"Epoch 11/30\n",
|
|
"238/238 - 1s - loss: 0.2668 - acc: 0.9140 - val_loss: 4.5158 - val_acc: 0.2258\n",
|
|
"Epoch 12/30\n",
|
|
"238/238 - 1s - loss: 0.2450 - acc: 0.9219 - val_loss: 4.9336 - val_acc: 0.2528\n",
|
|
"Epoch 13/30\n",
|
|
"238/238 - 1s - loss: 0.2316 - acc: 0.9270 - val_loss: 5.1142 - val_acc: 0.2383\n",
|
|
"Epoch 14/30\n",
|
|
"238/238 - 1s - loss: 0.2206 - acc: 0.9305 - val_loss: 4.7743 - val_acc: 0.2416\n",
|
|
"Epoch 15/30\n",
|
|
"238/238 - 1s - loss: 0.2059 - acc: 0.9347 - val_loss: 5.3665 - val_acc: 0.2346\n",
|
|
"Epoch 16/30\n",
|
|
"238/238 - 1s - loss: 0.2003 - acc: 0.9369 - val_loss: 5.0080 - val_acc: 0.2409\n",
|
|
"Epoch 17/30\n",
|
|
"238/238 - 1s - loss: 0.1819 - acc: 0.9418 - val_loss: 5.5792 - val_acc: 0.2261\n",
|
|
"Epoch 18/30\n",
|
|
"238/238 - 1s - loss: 0.1872 - acc: 0.9422 - val_loss: 5.2753 - val_acc: 0.2409\n",
|
|
"Epoch 19/30\n",
|
|
"238/238 - 1s - loss: 0.1726 - acc: 0.9455 - val_loss: 5.4606 - val_acc: 0.2374\n",
|
|
"Epoch 20/30\n",
|
|
"238/238 - 1s - loss: 0.1687 - acc: 0.9485 - val_loss: 5.4344 - val_acc: 0.2400\n",
|
|
"Epoch 21/30\n",
|
|
"238/238 - 1s - loss: 0.1620 - acc: 0.9492 - val_loss: 5.5537 - val_acc: 0.2497\n",
|
|
"Epoch 22/30\n",
|
|
"238/238 - 1s - loss: 0.1550 - acc: 0.9516 - val_loss: 5.4883 - val_acc: 0.2409\n",
|
|
"Epoch 23/30\n",
|
|
"238/238 - 1s - loss: 0.1494 - acc: 0.9554 - val_loss: 5.5419 - val_acc: 0.2491\n",
|
|
"Epoch 24/30\n",
|
|
"238/238 - 1s - loss: 0.1428 - acc: 0.9567 - val_loss: 5.5443 - val_acc: 0.2344\n",
|
|
"Epoch 25/30\n",
|
|
"238/238 - 1s - loss: 0.1400 - acc: 0.9563 - val_loss: 5.7355 - val_acc: 0.2267\n",
|
|
"Epoch 26/30\n",
|
|
"238/238 - 1s - loss: 0.1309 - acc: 0.9600 - val_loss: 5.6823 - val_acc: 0.2572\n",
|
|
"Epoch 27/30\n",
|
|
"238/238 - 1s - loss: 0.1313 - acc: 0.9600 - val_loss: 5.9189 - val_acc: 0.2604\n",
|
|
"Epoch 28/30\n",
|
|
"238/238 - 1s - loss: 0.1309 - acc: 0.9603 - val_loss: 5.7136 - val_acc: 0.2351\n",
|
|
"Epoch 29/30\n",
|
|
"238/238 - 1s - loss: 0.1245 - acc: 0.9623 - val_loss: 5.6745 - val_acc: 0.2411\n",
|
|
"Epoch 30/30\n",
|
|
"238/238 - 1s - loss: 0.1202 - acc: 0.9623 - val_loss: 5.6236 - val_acc: 0.2524\n",
|
|
"CPU times: user 1min 8s, sys: 23.3 s, total: 1min 32s\n",
|
|
"Wall time: 33.1 s\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"%%time\n",
|
|
"\n",
|
|
"model, history = train_model(np.array(X_train), np.array(yy_train), np.array(X_test), np.array(yy_test))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "952ff561",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Eval"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 27,
|
|
"id": "4e59e66b",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def predict(model, entry):\n",
|
|
" p_dict = dict()\n",
|
|
" predictions = np.argmax(model.predict(entry['data']), axis=-1)\n",
|
|
" for p in predictions:\n",
|
|
" if p in p_dict:\n",
|
|
" p_dict[p] += 1\n",
|
|
" else:\n",
|
|
" p_dict[p] = 1\n",
|
|
" prediction = max(p_dict, key=p_dict.get)\n",
|
|
" return prediction+1"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 28,
|
|
"id": "e1fcc40c",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"CPU times: user 2.6 s, sys: 277 ms, total: 2.88 s\n",
|
|
"Wall time: 2.15 s\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"%%time\n",
|
|
"\n",
|
|
"ltest = [lb.inverse_transform(e['label'])[0] for e in test]\n",
|
|
"ptest = [predict(model, e) for e in test]\n",
|
|
"\n",
|
|
"# for e in test:\n",
|
|
"# print(f\"Label: {lb.inverse_transform(e['label'])[0]:2d}\")\n",
|
|
"# print(f\"Prediction: {predict(model, e):2d}\\n_______________\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 29,
|
|
"id": "8828204c",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"CPU times: user 3.02 s, sys: 391 ms, total: 3.41 s\n",
|
|
"Wall time: 2.4 s\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"%%time\n",
|
|
"\n",
|
|
"ltrain = [lb.inverse_transform(e['label'])[0] for e in train]\n",
|
|
"ptrain = [predict(model, e) for e in train]\n",
|
|
"\n",
|
|
"# for e in train:\n",
|
|
"# print(f\"Label: {lb.inverse_transform(e['label'])[0]:2d}\")\n",
|
|
"# print(f\"Prediction: {predict(model, e):2d}\\n_______________\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 30,
|
|
"id": "39c9abae",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAjEAAAGtCAYAAADnIyVRAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/Z1A+gAAAACXBIWXMAAAsTAAALEwEAmpwYAABUU0lEQVR4nO3df3xU133n/9dHo/DDBNuSsYaiEKxivGu+pbYMCWETqCtw7RTc0AKpE+OYbrC23sbpbhe8bhoLQxe2ranbdZu6JQHSuMVep8VZG6W1a5EYGhNiCtYQSGuMilEIjAWIrGu6CGY+3z80TCVASEJz78y9834+HvNg7p07933OHc2dwzn3h7k7IiIiIlFTUewCiIiIiFwJNWJEREQkktSIERERkUhSI0ZEREQiSY0YERERiSQ1YkRERCSS1IgRERGRwJnZBjN7x8y+38frZmZPmtlbZpYys9v6W6caMSIiIhKGrwJ3Xeb1jwOTco9G4Kn+VqhGjIiIiATO3bcBJy+zyCeAr3m37wLXmtlPXG6dlYUsYCE9+OCDoV5K+Kmn+m3wiRTNjh07QsuaMWNGaFkichELNcyskL+1/4nuHpTz1rn7ukG8vxZo7zH9w9y8o329oWQbMSIiIhIduQbLYBotQ6ZGjIiISJkyC7Xjpz9HgPE9pj+Qm9cnHRMjIiJSpsysYI8CeAH4TO4spY8AP3b3PoeSQD0xIiIiEgIzewa4HRhjZj8EVgDvA3D3PwW+Cfw88BZwGviV/tapRoyIiEiZCnM4yd0/1c/rDvzaYNapRoyIiEiZqqiI9lEl0S69iIiIlK1I9cRMnjyZT37yk5gZ3/nOd3j55ZcvWua2225j3rx5uDtHjhxhw4YN3HTTTSxcuDC/zNixY1m/fj2tra1DKs+2bdtYvXo12WyWRYsW0djY2P+bIpClvNLPS6VSbNq0iWw2y6xZs5g3b16v17du3crWrVsxM0aMGMGSJUuora2lra2NjRs35pebP38+U6dOHVJZIPrbs5zz4ly3csgbqhI7O2nQItOIMTPuuecennzySTo7O3nkkUdIpVIcO3Ysv8z111/PXXfdxdq1azl9+jSjR48G4M0332TNmjUAXHXVVaxatYr9+/cPqTyZTIZVq1axceNGkskkCxcupKGhgRtvvHFI6y12lvJKPy+bzfL000+zfPlyqqurWblyJfX19dTW1uaXmTFjBg0NDQDs2bOHZ555hmXLllFbW8tjjz1GIpHg1KlTPProo9x6660kEomSqZ/ytG9RXnii3oiJzHDSDTfcQEdHB8ePHyeTybBr1y5uueWWXst87GMf49VXX+X06dMAvPvuuxet57bbbmPfvn2cPXt2SOVJpVJMmDCB8ePHM2zYMObOnUtLS8uQ1lkKWcor/by2tjaSySQ1NTVUVlYyffp09uzZ02uZkSNH5p+fOXMmv6MaPnx4vsFy9uzZguzAor49yzkvznUrhzwpQiPGzPo9ZepSrr32Wjo7O/PTnZ2dXHvttb2WqampoaamhmXLlvHwww8zefLki9Yzbdo0Xn/99SspQi/pdJqxY8fmp5PJJOl0esjrLXaW8ko/r7Ozk+rq6vx0VVVVr+/Gea+88grLly/nueee4957783PP3jwIF/4whf44he/yP333z+kXhiI/vYs57w4160c8gqhxK4TM2jF6IlZ2dcLZtZoZrvMbNeVDPckEglqamp44oknWL9+Pffee2+v/5FeffXVjBs3bshDSSJRMGfOHB5//HEWLVrEiy++mJ8/ceJE1qxZw4oVK9iyZQtdXV1FLKWIFJMaMZdgZqk+HnuBZF/vc/d17j7N3add2Ity6tQpqqqq8tNVVVWcOnWq1zKdnZ2kUimy2SwnTpzgnXfeoaamJv/61KlTeeONN8hms0OuYzKZ7HU8TjqdJpnss2qRyVJe6edVVVVx8uS/3Qi2s7Oz13fjQtOnT2f37t0XzR83bhwjRozgyJHLXtW7X1HfnuWcF+e6lUOeBNcTkwQ+A9x9iceJK1nh22+/TU1NDddddx2JRIJp06aRSqV6LdPa2spNN90EwKhRo6ipqeH48eP51z/0oQ+xa9euK4m/yJQpUzh06BDt7e10dXXR3NycP5Cy0MLMUl7p59XV1ZFOp+no6ODcuXPs3LmT+vr6Xsv03JG2trbmd6QdHR1kMhkAjh8/ztGjRxkzZswVlwWivz3LOS/OdSuHvEKIek9MUGcnbQHe7+5vXPiCmX37SlaYzWZ59tlneeihh6ioqOC1117j6NGjzJs3j8OHD5NKpdi/fz8333wzTU1NZLNZnn/+ed577z0Aqqurqaqq4sCBA0OpV15lZSVNTU0sXbqUTCbDggULmDRpUkHWXcws5ZV+XiKRYPHixaxdu5ZsNsvMmTOpra1l8+bN1NXVUV9fT0tLC/v27SORSDBq1CgeeOABoPtMvebmZhKJBBUVFdx33335s/hKpX7K075FeeGJ+sXurPsqv6XnwQcfDLVgTz31VJhxIoOyY8eO0LJmzJgRWpaIXCTULo3Ro0cX7Lf23XffDb07JjLXiREREZHCivp1YtSIERERKVNRb8REezBMREREypZ6YkRERMpU1Hti1IgREREpU1FvxGg4SURERCJJPTEiIiJlKuo9MSV7nRigZAsmIiLFEfaPbhF+I0Ot4JgxYwpWwePHj4feItJwkoiIiESShpNERETKVNSHk9SIERERKVNRb8RoOElEREQiST0xIiIiZSrqPTFqxIiIiJQpNWJEREQkkqLeiInVMTHbtm3jzjvv5I477mDdunWxyotz3ZSnPOUVLy/OdQNYv3496XSavXv3Bp4F4dev7Ll7qT4G5dy5cz579mw/fPiwnzlzxu+++24/cODAYFdTknlxrpvylKe84uVFsW50Xwh1wI+ZM2d6fX297927d9Dv7f6JDLd+HvJv7bhx47xQj7DL7u7B9cSY2b83s9lm9v4L5t8VRF4qlWLChAmMHz+eYcOGMXfuXFpaWoKICj0vznVTnvKUV7y8ONftvO3bt3Py5MlAM84rRv2GyswK9iiGQBoxZvZ54P8ADwHfN7NP9Hh5TRCZ6XSasWPH5qeTySTpdDqIqNDz4lw35SlPecXLi3PdiiHu9StFQfXEPABMdff5wO3Ao2b267nX+myumVmjme0ys10aSxQREQlW1Htigjo7qcLd/wXA3Q+Z2e3AX5nZBC7TiHH3dcD51sugbkqVTCY5duxYfjqdTpNMJgdZ7NLMi3PdlKc85RUvL851K4Yo1k9nJ11a2sxuPT+Ra9DMA8YAU4IInDJlCocOHaK9vZ2uri6am5tpaGgIIir0vDjXTXnKU17x8uJct2KIe/1KUVA9MZ8BzvWc4e7ngM+Y2Z8FEVhZWUlTUxNLly4lk8mwYMECJk2aFERU6HlxrpvylKe84uXFuW7nbdq0idtvv50xY8bQ3t7OihUr2LBhQyBZxajfUEW9J8bcBzVqE6aSLZiIiBRH2D+6RfiNDLWCN9xwQ8EqeOjQodBbRLG62J2IiIiUD912QEREpExVVES7L0ONGBERkTIV9WNiot0EExERkbKlnhgREZEyFfWeGDViREREylTUGzEaThIREZFIUk+MxMKOHTuKXYRAzZgxo9hFECkJr732WrGLECtR74lRI0ZERKRMRf0U62iXXkRERMqWemJERETKlIaTREREJJKi3ojRcJKIiIhEknpiREREylTUD+xVI0ZERKRMRX04KVaNmG3btrF69Wqy2SyLFi2isbExNnlxrlsQealUik2bNpHNZpk1axbz5s3r9frWrVvZunUrZsaIESNYsmQJtbW1tLW1sXHjxvxy8+fPZ+rUqSWX15+of37K075loMr9u1fuzN2LXYa+DKpgmUyGO++8k40bN5JMJlm4cCFPPPEEN954YyCFCzMvznUrVF7Pi91ls1n++3//7yxfvpzq6mpWrlzJr/7qr1JbW5tf5l//9V8ZOXIkAHv27KGlpYVly5Zx5swZKisrSSQSnDp1ikcffZQ//MM/JJFI9JkdRt5gLnYXxc9PecXJi2Ldwv6uF+G7F2rXyC233FKwRkBra2vo3TqBDYaZ2YfN7EO555PN7DfM7OeDykulUkyYMIHx48czbNgw5s6dS0tLS1BxoebFuW5B5LW1tZFMJqmpqaGyspLp06ezZ8+eXsuc36kBnDlzJt+lOnz48PxO7OzZswPqag07rz9R//yUp33LQJX7d68QzKxgj2IIZDjJzFYAHwcqzezvgOnAt4BHzKze3VcXOjOdTjN27Nj8dDKZJJVKFTqmKHlxrlsQeZ2dnVRXV+enq6qqaGtru2i5V155hZdeeolMJsPDDz+cn3/w4EHWr1/PiRMnaGxsvGwvTDHy+hP1z0952rcMVLl/9yS4npiFwEeBWcCvAfPd/beBO4Ff7utNZtZoZrvMbNe6desCKppItzlz5vD444+zaNEiXnzxxfz8iRMnsmbNGlasWMGWLVvo6uqKZJ6IdNN3r29R74kJqhFzzt0z7n4aOOju/xfA3f8VyPb1Jndf5+7T3H3aYA+GSiaTHDt2LD+dTqdJJpNXVvoSy4tz3YLIq6qq4uTJk/npzs5Oqqqq+lx++vTp7N69+6L548aNY8SIERw5cqSk8voT9c9Pedq3DFS5f/cKoaKiomCPopQ/oPV2mdlVuef5w73N7Bou04gZiilTpnDo0CHa29vp6uqiubmZhoaGIKJCz4tz3YLIq6urI51O09HRwblz59i5cyf19fW9lum5o2ltbc3vaDo6OshkMgAcP36co0ePMmbMmJLK60/UPz/lad8yUOX+3ZPgTrGe5e5nANy9Z6PlfcD9QQRWVlbS1NTE0qVLyWQyLFiwgEmTJgURFXpenOsWRF4ikWDx4sWsXbuWbDbLzJkzqa2tZfPmzdTV1VFfX09LSwv79u0jkUgwatQoHnjgAQDefPNNmpubSSQSVFRUcN999zF69OiSyutP1D8/5WnfMlDl/t0rhKhfJyY2p1hLeet52mUcDeY0T5E4C/u7XoTvXqitig9/+MMF+6393ve+F59TrEVERESCFKsr9oqIiMjARX04SY0YERGRMhX1G0BGu/QiIiJSttQTIyIiUqY0nCQiIiKRFPVGjIaTREREJJJKtiemDK4FIAWkz0+km/adMhg6sFdEREQiKcwbQJrZXWb2T2b2lpk9conXP2hm3zKzPWaWMrOf72+dasSIiIhIoMwsAXwJ+DgwGfiUmU2+YLEvAs+5ez1wD/An/a23ZIeTREREJFghDid9GHjL3dsAzOxZ4BPA/h7LOHB17vk1wI/6W6kaMSIiImWqkGcnmVkj0Nhj1jp3X5d7Xgu093jth8D0C1bxGPCymT0EjALm9JepRoyIiIgMWa7Bsq7fBfv2KeCr7v77ZjYDeNrMfsrds329QY0YERGRMhXidWKOAON7TH8gN6+nzwJ3Abj7DjMbAYwB3ulrpZFqxKRSKTZt2kQ2m2XWrFnMmzev1+tbt25l69atmBkjRoxgyZIl1NbW0tbWxsaNG/PLzZ8/n6lTpw65PNu2bWP16tVks1kWLVpEY2Nj/2+KQJbylKe8eOWV0r4zznWD8P9WhirEY2JeByaZWR3djZd7gE9fsMxhYDbwVTO7GRgBdFxupZFpxGSzWZ5++mmWL19OdXU1K1eupL6+ntra2vwyM2bMoKGhAYA9e/bwzDPPsGzZMmpra3nsscdIJBKcOnWKRx99lFtvvZVEInHF5clkMqxatYqNGzeSTCZZuHAhDQ0N3HjjjUOuazGzlKc85cUrr5T2nXGuWxD1ixN3P2dmnwNeAhLABnffZ2argF3u/gLw34Avm9l/pfsg3yXu7pdbb2ROsW5rayOZTFJTU0NlZSXTp09nz549vZYZOXJk/vmZM2fy3WTDhw/P/2GePXu2IN1nqVSKCRMmMH78eIYNG8bcuXNpaWkZ8nqLnaU85SkvXnmltO+Mc90g/L+VQgjzOjHu/k13v8ndJ7r76ty8plwDBnff7+4fdfdb3P1Wd3+5v3WG1hNjZl9z989c6fs7Ozuprq7OT1dVVdHW1nbRcq+88govvfQSmUyGhx9+OD//4MGDrF+/nhMnTtDY2Dik1jZAOp1m7Nix+elkMkkqlRrSOkshS3nKU1688kpp3xnnukH4fyuFEPUr9gbSiDGzFy6cBfysmV0L4O6/EEQuwJw5c5gzZw47duzgxRdf5IEHHgBg4sSJrFmzhh/96Ed8+ctfZsqUKQwbNiyoYoiIREqc951xrlu5C6oJ9gHg/wJPAL+fe7zb4/klmVmjme0ys13f+MY3er1WVVXFyZMn89OdnZ1UVVX1WYDp06eze/fui+aPGzeOESNGcOTIhQdFD04ymeTYsWP56XQ6TTKZHNI6SyFLecpTXrzySmnfGee6Qfh/K4UQ5nBSEIJqxEwD/gH4LeDH7v5t4F/d/VV3f7WvN7n7Onef5u7T5s+f3+u1uro60uk0HR0dnDt3jp07d1JfX99rmZ5/PK2trfk/no6ODjKZDADHjx/n6NGjjBkzZkgVnDJlCocOHaK9vZ2uri6am5vzB48VWphZylOe8uKVV0r7zjjXDcL/WymEqDdiAhlOyl2Y5g/M7Ou5f9NDzUokEixevJi1a9eSzWaZOXMmtbW1bN68mbq6Ourr62lpaWHfvn0kEglGjRqV7zJ88803aW5uJpFIUFFRwX333cfo0aOHVMfKykqamppYunQpmUyGBQsWMGnSpCGtsxSylKc85cUrr5T2nXGuWxD1C0PUj4mxfs5eKkyI2Vzgo+7+hYG+Z8eOHcEXrAfdTl5E4mDHjh2h5oW974x7/eg+hjQ0c+fOLdhvbXNzc+jdMaGcneTuzUBzGFkiIiIyMMUaBiqUyFzsTkRERAor6sNJ0S69iIiIlC31xIiIiJQpDSeJiIhIJGk4SURERKQI1BMjIiJSpjScFBBdtyXaHnzwwVDznnrqqVDzREqV9p0yGFFvxGg4SURERCKpZHtiREREJFhR74lRI0ZERKRMRb0Ro+EkERERiST1xIiIiJSpqPfEqBEjIiJSpqLeiNFwkoiIiERSrHpitm3bxurVq8lmsyxatIjGxsbY5EW9bpMnT+aTn/wkZsZ3vvMdXn755YuWue2225g3bx7uzpEjR9iwYQM33XQTCxcuzC8zduxY1q9fT2tr65DKE/XtqTzlRTEriLxUKsWmTZvIZrPMmjWLefPm9Xp969atbN26FTNjxIgRLFmyhNraWtra2ti4cWN+ufnz5zN16tQhlQXC355DFfWemNg0YjKZDKtWrWLjxo0kk0kWLlxIQ0MDN954Y+Tzol43M+Oee+7hySefpLOzk0ceeYRUKsWxY8fyy1x//fXcddddrF27ltOnTzN69GgA3nzzTdasWQPAVVddxapVq9i/f39J1U95yotqXtTrls1mefrpp1m+fDnV1dWsXLmS+vp6amtr88vMmDGDhoYGAPbs2cMzzzzDsmXLqK2t5bHHHiORSHDq1CkeffRRbr31VhKJRMnULwxRb8TEZjgplUoxYcIExo8fz7Bhw5g7dy4tLS2xyIt63W644QY6Ojo4fvw4mUyGXbt2ccstt/Ra5mMf+xivvvoqp0+fBuDdd9+9aD233XYb+/bt4+zZs1dcFoj+9lSe8qKYFUReW1sbyWSSmpoaKisrmT59Onv27Om1zMiRI/PPz5w5k//RHj58eL7Bcvbs2YL8mIe9PSWkRoyZfczMfsPMfi6ojHQ6zdixY/PTyWSSdDodVFyoeVGv27XXXktnZ2d+urOzk2uvvbbXMjU1NdTU1LBs2TIefvhhJk+efNF6pk2bxuuvv37F5Tgv6ttTecqLYlYQeZ2dnVRXV+enq6qqeu1rznvllVdYvnw5zz33HPfee29+/sGDB/nCF77AF7/4Re6///4h9cJA+NuzECoqKgr2KEr5g1ipmX2vx/MHgD8GRgMrzOyRy7yv0cx2mdmudevWBVE0KVGJRIKamhqeeOIJ1q9fz7333tvrf1BXX30148aNG/JQkoiUnzlz5vD444+zaNEiXnzxxfz8iRMnsmbNGlasWMGWLVvo6uoqYimLw8wK9iiGoI6JeV+P543AHe7eYWZrge8Cv3OpN7n7OuB868UHE5hMJnsdY5FOp0kmk4MqdKnmRb1up06doqqqKj9dVVXFqVOnei3T2dnJoUOHyGaznDhxgnfeeYeamhrefvttAKZOncobb7xBNpu94nKcF/XtqTzlRTEriLyqqipOnjyZn+7s7Oy1r7nQ9OnT+drXvnbR/HHjxjFixAiOHDlCXV3dFZcn7O0pwQ0nVZhZlZldB5i7dwC4+3vAuSACp0yZwqFDh2hvb6erq4vm5ub8wVxRz4t63d5++21qamq47rrrSCQSTJs2jVQq1WuZ1tZWbrrpJgBGjRpFTU0Nx48fz7/+oQ99iF27dl1xGXqK+vZUnvKimBVEXl1dHel0mo6ODs6dO8fOnTupr6/vtUzPRkVra2u+UdHR0UEmkwHg+PHjHD16lDFjxlxxWSD87VkI6om5tGuAfwAMcDP7CXc/ambvz80ruMrKSpqamli6dCmZTIYFCxYwadKkIKJCz4t63bLZLM8++ywPPfQQFRUVvPbaaxw9epR58+Zx+PBhUqkU+/fv5+abb6apqYlsNsvzzz/Pe++9B0B1dTVVVVUcOHCgJOunPOVFNS/qdUskEixevJi1a9eSzWaZOXMmtbW1bN68mbq6Ourr62lpaWHfvn0kEglGjRrFAw88AHSf+djc3EwikaCiooL77rsvf1ZkqdQvDFE/O8ncBzVqM7Qws6uApLv/8wAWD69gUnAPPvhgqHlPPfVUqHkiUhw7duwINW/GjBmh5hHQf/T78pnPfKZgv7Vf+9rXQm8RhXqdGHc/DQykASMiIiIBi3pPTGwudiciIiKDE/VGTGwudiciIiLlRT0xIiIiZSrqPTFqxIiIiJSpqDdiNJwkIiIikaSeGBERkTIV9Z6Ykm3E6Doj0abtKSJS+qLeiNFwkoiIiERSyfbEiIiISLCi3hOjRoyIiEiZinojRsNJIiIiEknqiRERESlTUe+JUSNGRESkTEW9EaPhJBEREYmkSPXETJ48mU9+8pOYGd/5znd4+eWXL1rmtttuY968ebg7R44cYcOGDdx0000sXLgwv8zYsWNZv349ra2tQyrPtm3bWL16NdlslkWLFtHY2Dik9ZVKlvKUp7zyyYt63VKpFJs2bSKbzTJr1izmzZvX6/WtW7eydetWzIwRI0awZMkSamtraWtrY+PGjfnl5s+fz9SpU4dUFgh/ew5V1HtiItOIMTPuuecennzySTo7O3nkkUdIpVIcO3Ysv8z111/PXXfdxdq1azl9+jSjR48G4M0332TNmjUAXHXVVaxatYr9+/cPqTyZTIZVq1axceNGkskkCxcupKGhgRtvvHFI6y12lvKUp7zyyYt63bLZLE8//TTLly+nurqalStXUl9fT21tbX6ZGTNm0NDQAMCePXt45plnWLZsGbW1tTz22GMkEglOnTrFo48+yq233koikSiZ+oUh6o2YQIaTzGy6mV2dez7SzFaa2Ytm9rtmds2VrPOGG26go6OD48ePk8lk2LVrF7fcckuvZT72sY/x6quvcvr0aQDefffdi9Zz2223sW/fPs6ePXslxchLpVJMmDCB8ePHM2zYMObOnUtLS8uQ1lkKWcpTnvLKJy/qdWtrayOZTFJTU0NlZSXTp09nz549vZYZOXJk/vmZM2fyP9rDhw/PN1jOnj1bkB/zsLenBHdMzAbgdO75/wKuAX43N29jX2+6nGuvvZbOzs78dGdnJ9dee22vZWpqaqipqWHZsmU8/PDDTJ48+aL1TJs2jddff/1KitBLOp1m7Nix+elkMkk6nR7yeoudpTzlKa988qJet87OTqqrq/PTVVVVvX4nznvllVdYvnw5zz33HPfee29+/sGDB/nCF77AF7/4Re6///4h9cJA+NuzEMysYI9iCKoRU+Hu53LPp7n7f3H3v3f3lcBP9vUmM2s0s11mtutKhnsSiQQ1NTU88cQTrF+/nnvvvbdXK/zqq69m3LhxQx5KEhGR6JgzZw6PP/44ixYt4sUXX8zPnzhxImvWrGHFihVs2bKFrq6uIpayONSIubTvm9mv5J63mtk0ADO7CehzHMfd17n7NHefdmEvyqlTp6iqqspPV1VVcerUqV7LdHZ2kkqlyGaznDhxgnfeeYeampr861OnTuWNN94gm80OsXrdLeyex+Ok02mSyeSQ11vsLOUpT3nlkxf1ulVVVXHy5Mn8dGdnZ6/fiQtNnz6d3bt3XzR/3LhxjBgxgiNHjlxxWSD87SnBNWKWAj9jZgeBycAOM2sDvpx7bdDefvttampquO6660gkEkybNo1UKtVrmdbWVm666SYARo0aRU1NDcePH8+//qEPfYhdu3ZdWY0uMGXKFA4dOkR7eztdXV00NzfnDx4rtDCzlKc85ZVPXtTrVldXRzqdpqOjg3PnzrFz507q6+t7LdOzUdHa2ppvVHR0dJDJZAA4fvw4R48eZcyYMVdcFgh/exZC1HtiAjk7yd1/DCzJHdxbl8v5obtf8eBgNpvl2Wef5aGHHqKiooLXXnuNo0ePMm/ePA4fPkwqlWL//v3cfPPNNDU1kc1mef7553nvvfcAqK6upqqqigMHDhSkjpWVlTQ1NbF06VIymQwLFixg0qRJBVl3MbOUpzzllU9e1OuWSCRYvHgxa9euJZvNMnPmTGpra9m8eTN1dXXU19fT0tLCvn37SCQSjBo1igceeADoPmu1ubmZRCJBRUUF9913X/6M1lKpXxiifnaSuXuxy3BJDz74YKgFe+qpp8KMExGRK7Bjx45Q82bMmBFqHhBqq+Lzn/98wX5rn3zyydBbRJG5ToyIiIgUVtR7YtSIERERKVNRb8To3kkiIiISSeqJERERKVNR74lRI0ZERKRMqREjIiIikRT1RoyOiREREZFIKtmeGF23RUpZ2P97KdXrOYmErQjXbYm1qPfElGwjRkRERIJVURHtAZlol15ERETKlnpiREREypSGk0RERCSSot6I0XCSiIiIRJIaMSIiImXKzAr2GEDWXWb2T2b2lpk90scynzSz/Wa2z8w29bdODSeJiIiUqbCGk8wsAXwJuAP4IfC6mb3g7vt7LDMJ+E3go+7eaWY1/a03Vj0x27Zt48477+SOO+5g3bp1scqLc93inrd+/XrS6TR79+4NNKenOG9P5UU3S3ll7cPAW+7e5u5dwLPAJy5Y5gHgS+7eCeDu7/S30tg0YjKZDKtWreIrX/kKzc3NbNmyhbfeeisWeXGuWznkffWrX+Wuu+4KbP0Xivv2VF40s5RXmgo5nGRmjWa2q8ejsUdULdDeY/qHuXk93QTcZGbfMbPvmlm/O85AGjFm9nkzGx/EuvuSSqWYMGEC48ePZ9iwYcydO5eWlpZY5MW5buWQt337dk6ePBnY+i8U9+2pvGhmKa80FbIR4+7r3H1aj8dgu6IqgUnA7cCngC+b2bWXe0NQPTG/Dew0s+1m9p/N7PqAcvLS6TRjx47NTyeTSdLpdCzy4ly3csgLW9y3p/KimaW8sncE6Nm58YHcvJ5+CLzg7mfd/Z+BN+lu1PQpqEZMG90F/G1gKrDfzP7WzO43s9F9valnV5TGEkVERIIV4tlJrwOTzKzOzIYB9wAvXLDMN+juhcHMxtA9vNR2uZUGdXaSu3sWeBl42czeB3yc7u6htcAle2ZyXU/nWy+DuuNdMpnk2LFj+el0Ok0ymbyCopdeXpzrVg55YYv79lReNLOUV5rCOjvJ3c+Z2eeAl4AEsMHd95nZKmCXu7+Qe+3nzGw/kAGWu/uJy603qJ6YXlsl1zX0grt/CpgQROCUKVM4dOgQ7e3tdHV10dzcTENDQxBRoefFuW7lkBe2uG9P5UUzS3ni7t9095vcfaK7r87Na8o1YPBuv+Huk919irs/2986g+qJ+eW+XnD300EEVlZW0tTUxNKlS8lkMixYsIBJky47lBaZvDjXrRzyNm3axO23386YMWNob29nxYoVbNiwIbC8uG9P5UUzS3mlKeq3HTD3QY3ahKlkCyYS9he/hL+nIlJYoe5cHnvssYLtXB577LHQW0SxuU6MiIiIlBfddkBERKRMVVREuy9DjRgREZEyFfVjYqLdBBMREZGypZ4YERGRMhX1nhg1YkRERMpU1BsxGk4SERGRSOqzJ8bMbrvcG919d+GLIxINYV+3ZceOHaFlzZgxI7SsYghzW0L8t2fY9PkVVtR7Yi43nPT7l3nNAV1LWUREJMJi24hx958NsyAiIiIig9HvMTFmdpWZfdHM1uWmJ5nZvOCLJiIiIkEys4I9imEgB/ZuBLqA/5CbPgL8j8BKJCIiIqEoh0bMRHf/PeAs5O9CHe1BNBEREYm8gVwnpsvMRpK7q7SZTQTOBFoqERERCVxsD+ztYQXwt8B4M/tL4KPAkiALJSIiIsGLfSPG3f/OzHYDH6F7GOnX3f144CW7Atu2bWP16tVks1kWLVpEY2NjbPLiXDflDV4qlWLTpk1ks1lmzZrFvHm9j7XfunUrW7duxcwYMWIES5Ysoba2lra2NjZu3Jhfbv78+UydOnVIZQFtz/OiuD312XWL4mcnA7/twM8AH6N7SOl9wPOBlegKZTIZVq1axcaNG0kmkyxcuJCGhgZuvPHGyOfFuW7KG7xsNsvTTz/N8uXLqa6uZuXKldTX11NbW5tfZsaMGTQ0dF/Kac+ePTzzzDMsW7aM2tpaHnvsMRKJBKdOneLRRx/l1ltvJZFIlEz9ws4r5+2pzy66n12hVFRE+8L9AznF+k+AXwX2At8H/pOZfamf9wwzs8+Y2Zzc9KfN7I/N7NfM7H2FKPiFUqkUEyZMYPz48QwbNoy5c+fS0tISRFToeXGum/IGr62tjWQySU1NDZWVlUyfPp09e/b0WmbkyJH552fOnMl3GQ8fPjy/kz579mxBupK1PaO7PfXZRfezK5Son500kJ6YBuBmz11n3cz+HNjXz3s25tZ9lZndD7wf2AzMBj4M3H/FJe5DOp1m7Nix+elkMkkqlSp0TFHy4lw35Q1eZ2cn1dXV+emqqira2touWu6VV17hpZdeIpPJ8PDDD+fnHzx4kPXr13PixAkaGxuH9D9P0PaM8vbUZxfdz066DaQR8xbwQeDt3PT43LzLmeLuP21mlXRfV2acu2fM7C+A1r7eZGaNQCPAn/3Zn2ksUWQI5syZw5w5c9ixYwcvvvgiDzzwAAATJ05kzZo1/OhHP+LLX/4yU6ZMYdiwYUUubenT9owufXZ9i/qBvX0OJ5nZi2b2AjAa+IGZfdvMvgX8IDfvsus1s2G55a4CrsnNH073MTWX5O7r3H2au08bbAMmmUxy7Nix/HQ6nSaZTA5qHaWaF+e6KW/wqqqqOHnyZH66s7OTqqqqPpefPn06u3dffL/WcePGMWLECI4cOXLFZQFtz/OiuD312XWL4mdXKFEfTrrcMTFr6b4JZBPwcbpPtX6sx/PLWQ/8I/AG8FvA183sy8DrwLNDKnEfpkyZwqFDh2hvb6erq4vm5ub8wVxRz4tz3ZQ3eHV1daTTaTo6Ojh37hw7d+6kvr6+1zI9d6Stra35HWlHRweZTAaA48ePc/ToUcaMGXPFZQFtzyhvT3120f3spNvlbgD56pWu1N3/wMz+d+75j8zsa8Ac4Mvu/r0rXe/lVFZW0tTUxNKlS8lkMixYsIBJkyYFERV6XpzrprzBSyQSLF68mLVr15LNZpk5cya1tbVs3ryZuro66uvraWlpYd++fSQSCUaNGpXvPn/zzTdpbm4mkUhQUVHBfffdx+jR/XWshlu/sPPKeXvqs4vuZ1coUR9Ostzxun0vYPYR4I+Am4FhQAJ4z92vDrhsly+YSBnZsWNHaFkzZswILasYwtyWEP/tGbYy+PxCbVU8+eSTBfut/fznPx96i2ggJ4j/MfAp4AAwElgKXPYUaxEREZGgDegqN+7+FpBw94y7bwTuCrZYIiIiErSoH9g7kFOsT+fONHrDzH4POMoAGz8iIiJSuqJ+TMxAGiP35Zb7HPAe3deJ+aUgCyUiIiLSn4HcAPL8Re7+H7ASIHfm0S8HWC4REREJWNR7YgZ6A8gL6XB7ERGRiIt6I0bHtoiIiEgk9dkTY2a39fUSl7l1gIgUXpjXqgj7f2b9Xauq0HTdlmjT51dYFRXR7su43HDS71/mtX8sdEFEREQkXFEfTrrcbQd+NsyCiIiIiAzGlR7YKyIiIhEX254YERERiTc1YkRERCSSon5gb7+lt26LzawpN/1BM/tw8EUTERER6dtAmmB/QvfF7T6Vm36XEr2L9bZt27jzzju54447WLduXazy4lw35UU7b/369aTTafbu3RtoTk9x3p5h58W5buWQN1RRvwEk7n7ZB7A79++eHvNa+3tfAR6Dcu7cOZ89e7YfPnzYz5w543fffbcfOHBgsKspybw41015pZcHDOoxc+ZMr6+v97179w76vd27oHDrp7ziZClvwIL+be312LBhgxfqEXbZ3X1APTFnzSyR2+FgZtcD2aE1nQovlUoxYcIExo8fz7Bhw5g7dy4tLS2xyItz3ZQX/bzt27dz8uTJwNZ/obhvT+1blCcDN5BGzJPA80CNma0G/h5Y09+bzOwnzWyZmf0vM3vCzH7VzK4eYnn7lE6nGTt2bH46mUySTqeDigs1L851U17088IW9+2pfYvywhT14aR+GzHu/pfAw8D/BI4C893965d7j5l9HvhTYATwIWA4MB74rpndfpn3NZrZLjPbFYWxRBERkSiLeiOm31OszeyDwGngxZ7z3P3wZd72AHCru2fM7Angm+5+u5n9GfB/gPpLvcnd1wHnWy+DuqFKMpnk2LFj+el0Ok0ymRzMKgYlzLw410150c8LW9y3p/YtypOBG8hwUjOwJfdvC9AG/M0A3ne+gTQceD9AruETyM0jp0yZwqFDh2hvb6erq4vm5mYaGhqCiAo9L851U17088IW9+2pfYvywlRRUVGwRzH02xPj7lN6Tufubv2f+3nbV4DXzWwnMBP43dx7rwcCOQKwsrKSpqYmli5dSiaTYcGCBUyaNCmIqNDz4lw35UU/b9OmTdx+++2MGTOG9vZ2VqxYwYYNGwLLi/v21L5FeWGK+hV7zX1QozbdbzLbe2Hj5hLL/H/AzcD33f1K7no9+IKJyJCFvVO7kn2QSIyF+gX8i7/4i4J9ARcvXhx6i2ggx8T8Ro/JCuA24Ef9vc/d9wH7rrxoIiIiEqSo98QM5N5Jo3s8P0f3sTF/HUxxREREJCyxbsTkLnI32t2XhVQeERERkQHpsxFjZpXufs7MPhpmgURERCQcUb+L9eV6Yr5H9/Evb5jZC8DXgffOv+jumwMum4iIiAQo1sNJOSOAE0AD3WcMWe5fNWJERESkaC7XiKnJnZn0ff6t8XKezokUERGJuDj3xCTovtLupWqoRoxITMX9ui07duwINW/GjBmh5ql+hRV2/cIW50bMUXdfFVpJRERERAbhco2YaDfPRERE5LLi3BMzO7RSiIiISOiifop1n6V390Bu1CgiIiJSCAM5xVpERERiKM7DSSIiIhJjUW/ERHswTERERMpWrHpitm3bxurVq8lmsyxatIjGxsbY5MW5bspTXrHzUqkUmzZtIpvNMmvWLObNm9fr9a1bt7J161bMjBEjRrBkyRJqa2tpa2tj48aN+eXmz5/P1KlTh1QWiHf94lw3CP+7MFRR74mJTSMmk8mwatUqNm7cSDKZZOHChTQ0NHDjjTdGPi/OdVOe8oqdl81mefrpp1m+fDnV1dWsXLmS+vp6amtr88vMmDGDhoYGAPbs2cMzzzzDsmXLqK2t5bHHHiORSHDq1CkeffRRbr31VhKJhOpXZnULon5hiO3ZSVGTSqWYMGEC48ePZ9iwYcydO5eWlpZY5MW5bspTXrHz2traSCaT1NTUUFlZyfTp09mzZ0+vZUaOHJl/fubMmfz/XocPH57/0Tt79mxB/lcb5/rFuW4Q/ndBYtQTk06nGTt2bH46mUySSqVikRfnuilPecXO6+zspLq6Oj9dVVVFW1vbRcu98sorvPTSS2QyGR5++OH8/IMHD7J+/XpOnDhBY2PjkP4nD/GuX5zrBuF/Fwoh6sNJJdUTY2aNZrbLzHatW7eu2MUREcmbM2cOjz/+OIsWLeLFF1/Mz584cSJr1qxhxYoVbNmyha6uriKW8srFuX5xrttQmVnBHgPIusvM/snM3jKzRy6z3AIzczOb1t86A2nEmNk1ZvY7ZvaPZnbSzE6Y2Q9y867t633uvs7dp7n7tMEeDJVMJjl27Fh+Op1Ok0wmr7gOpZQX57opT3nFzquqquLkyX+7tmdnZydVVVV9Lj99+nR279590fxx48YxYsQIjhw5csVlgXjXL851g/C/C1FiZgngS8DHgcnAp8xs8iWWGw38OrBzIOsNqifmOaATuN3dq939OuBnc/OeCyJwypQpHDp0iPb2drq6umhubs4fzBX1vDjXTXnKK3ZeXV0d6XSajo4Ozp07x86dO6mvr++1TM8fptbW1vwPU0dHB5lMBoDjx49z9OhRxowZc8VlgXjXL851g/C/C4UQYk/Mh4G33L3N3buAZ4FPXGK53wZ+F/h/Ayl/UMfE3ODuv9tzhrsfA37XzP5jEIGVlZU0NTWxdOlSMpkMCxYsYNKkSUFEhZ4X57opT3nFzkskEixevJi1a9eSzWaZOXMmtbW1bN68mbq6Ourr62lpaWHfvn0kEglGjRrFAw88AMCbb75Jc3MziUSCiooK7rvvPkaPHq36lWHdgqhfGAp5TIyZNQI9h1HWufv5Y0NqgfYer/0QmH7B+28Dxrt7s5ktH1Cmuw+hyH2s1Oxl4BXgz909nZuXBJYAd7j7nAGspvAFE5Gyt2PHjlDzZsyYEWqe6ldYYdcPCPVI27/5m78p2G/txz/+8T7LbmYLgbvcfWlu+j5gurt/LjddAWwFlrj7ITP7NrDM3XddLjOo4aRfBq4DXs0dE3MS+DZQDSwKKFNEREQGoaKiomCPfhwBxveY/kBu3nmjgZ8Cvm1mh4CPAC/0d3BvIMNJ7t4J/Pfcoxcz+xVg40VvEhERkVCFeIr168AkM6uju/FyD/Dp8y+6+4+B/EFJxe6JuZyVRcgUERGRInH3c8DngJeAHwDPufs+M1tlZr9wpesNpCfGzPq6uo8BOt9MRESkBIR5sTt3/ybwzQvmNfWx7O0DWWdQZyclgTvpPqW6JwNeCyhTREREBiHqV+wNqhGzBXi/u79x4Qu5cS4RERGRIQnqwN7PXua1T/f1moiIiIQn6nexjs0NIEUkmsLuzg7i2lilpAjXNQlV3OsXtqgPJ0W7CSYiIiJlS40YERERiSQNJ4mIiJQpDSeJiIiIFIF6YkRERMpU1Hti1IgREREpU1FvxGg4SURERCIpVo2Ybdu2ceedd3LHHXewbt26WOXFuW7KU95grF+/nnQ6zd69ewPN6SnO2zPOdSuHvKEys4I9isLdS/UxKOfOnfPZs2f74cOH/cyZM3733Xf7gQMHBruaksyLc92UpzxgUI+ZM2d6fX297927d9Dv7d7lhVu/Us6Lc90inBfqb+327du9UI+wy+7u8emJSaVSTJgwgfHjxzNs2DDmzp1LS0tLLPLiXDflKW+wtm/fzsmTJwNb/4XivD3jXLdyyJMYDSel02nGjh2bn04mk6TT6VjkxbluylNeqYvz9oxz3cohrxCiPpxUUo0YM2s0s11mtisKY4kiIiJRFvVGTOinWJvZ37j7xy/1mruvA863XgZ1l7ZkMsmxY8fy0+l0mmQyecXlLKW8ONdNecordXHennGuWznkSUA9MWZ2Wx+PqcCtQWROmTKFQ4cO0d7eTldXF83NzTQ0NAQRFXpenOumPOWVujhvzzjXrRzyJLiemNeBV4FL9S9dG0RgZWUlTU1NLF26lEwmw4IFC5g0aVIQUaHnxbluylPeYG3atInbb7+dMWPG0N7ezooVK9iwYUNgeXHennGuWznkFULUL3Zn7oMatRnYSs2+D/yiux+4xGvt7j5+AKspfMFEpOSEvRMNYp8nUkChfiG++93vFuwL8ZGPfCT0FlFQB/Y+dpl1PxRQpoiIiJSRQIaT3P2vLvNyVRCZIiIiMjhRH04qxinWK4uQKSIiIhfQKdaXYGapvl4CdL6ZiIiIDFlQZyclgTuBzgvmG/BaQJkiIiIyCFEfTgqqEbMFeL+7v3HhC2b27YAyRUREZBDUiLkEd//sZV77dBCZIiIiUl5Cv+2AiEhPcb9ui66DI6Us6j0xJXUDSBEREZGBUiNGREREIknDSSIiImUq6sNJasSIiIiUqag3YjScJCIiIpGknhgREZEyFfWeGDViREREylTUGzGxGk7atm0bd955J3fccQfr1q2LVV6c66Y85Snv36xfv550Os3evXsDzTkvztuyHPLKnruX6mNQzp0757Nnz/bDhw/7mTNn/O677/YDBw4MdjUlmRfnuilPeXHPAwb1mDlzptfX1/vevXsH/d7uXXp4dRss5Q1IqL+1ra2tXqhH2GV39/j0xKRSKSZMmMD48eMZNmwYc+fOpaWlJRZ5ca6b8pSnvN62b9/OyZMnA1t/T3HflnHPKwQzK9ijGGLTiEmn04wdOzY/nUwmSafTsciLc92UpzzlFU/ct2Xc8ySgRoyZXW1m/9PMnjazT1/w2p9c5n2NZrbLzHZpLFFERCRYUe+JCerspI3AAeCvgf9oZguAT7v7GeAjfb3J3dcB51svg7qLWTKZ5NixY/npdDpNMpkcbLlLMi/OdVOe8pRXPHHflnHPKwSdnXRpE939EXf/hrv/ArAb2Gpm1wWUx5QpUzh06BDt7e10dXXR3NxMQ0NDUHGh5sW5bspTnvKKJ+7bMu55ElxPzHAzq3D3LIC7rzazI8A24P1BBFZWVtLU1MTSpUvJZDIsWLCASZMmBREVel6c66Y85Smvt02bNnH77bczZswY2tvbWbFiBRs2bAgkK+7bMu55AuY+qFGbga3U7PeAl939lQvm3wX8kbsP5FMtfMFEREIWdnd9EPt0CVWofzD79+8v2B/M5MmTQx+bCqQnxt0f7mP+35rZmiAyRUREpLwU4xTrlUXIFBERkQvo7KRLMLNUXy8BpX2otoiISJmI+tlJQR3YmwTuBDovmG/AawFlioiISBkJqhGzBXi/u79x4Qtm9u2AMkVERGQQ1BNzCe7+2cu89um+XhMREZHwRL0RE5t7J4mIiEh5CWo4SURECP+6LboujQyGemJEREREikCNGBEREYkkDSeJiIiUqagPJ6kRIyIiUqai3ojRcJKIiIhEkhoxIiIiEkmxasRs27aNO++8kzvuuIN169bFKi/OdVOe8pRXvLz169eTTqfZu3dvoDnnxXlbFiNvqKJ+A0jcvVQfg3Lu3DmfPXu2Hz582M+cOeN33323HzhwYLCrKcm8ONdNecpTXmHzgEE9Zs6c6fX19b53795Bv7f7JyS8ug1WRPNC/a09ePCgF+oRdtndPT49MalUigkTJjB+/HiGDRvG3LlzaWlpiUVenOumPOUpr7h527dv5+TJk4Gtv6e4b8uw8woh6j0xsWnEpNNpxo4dm59OJpOk0+lY5MW5bspTnvKKmxemuG/LKH52asSIiIiIFEEgjRgzG2tmT5nZl8zsOjN7zMz2mtlzZvYTl3lfo5ntMrNdgz0gKplMcuzYsfx0Op0mmUxeeSVKKC/OdVOe8pRX3LwwxX1bxvmzK1VB9cR8FdgPtAPfAv4V+HlgO/Cnfb3J3de5+zR3n9bY2DiowClTpnDo0CHa29vp6uqiubmZhoaGK65AKeXFuW7KU57yipsXprhvyyh+dlEfTgrqir1Jd/8jADP7z+7+u7n5f2Rmnw0isLKykqamJpYuXUomk2HBggVMmjQpiKjQ8+JcN+UpT3nFzdu0aRO33347Y8aMob29nRUrVrBhw4ZAsuK+LcPOEzAP4DbqZtbq7rfknv8Pd/9ij9f2uvuUAaxG93cXERmksP9HHMRvSJkL9QM8fPhwwT7AD37wg6F3xwQ1nPR/zOz9ABc0YG4E/imgTBERESlRZnaXmf2Tmb1lZo9c4vXfMLP9ZpYysxYzm9DfOgNpxLh7k7v/yyXmvwU0B5EpIiIipcnMEsCXgI8Dk4FPmdnkCxbbA0xz958G/gr4vf7WW4xTrFcWIVNEREQuEOKBvR8G3nL3NnfvAp4FPtFzAXf/lrufzk1+F/hAfysN5MBeM0v19RKg881ERERKQCGPoTKzRqDnqcXr3P389VJq6T5j+bwfAtMvs7rPAn/TX2ZgZycBdwKdF8w34LWAMkVERGQQCtmIyTVYhnzXSzNbDEwDfqa/ZYNqxGwB3u/ub1z4gpl9O6BMERERKU1HgPE9pj+Qm9eLmc0Bfgv4GXc/099KAznFukBKtmAiIqVKp1hHXqgf4JEjRwr2AdbW1vZZdjOrBN4EZtPdeHkd+LS77+uxTD3dB/Te5e4HBpIZVE+MiIigRoWUtrD+Pt39nJl9DngJSAAb3H2fma0Cdrn7C8DjwPuBr+fKddjdf+Fy61VPjIhIgNSIkUEK9Q/mRz/6UcH+YMaNGxf6xe7UEyMiIlKminXPo0JRI0ZERKRMRb0RU4yL3YmIiIgMmRoxIiIiEkkaThIRESlTGk4SERERKYJYNWK2bdvGnXfeyR133MG6dUO+8nFJ5cW5bspTnvL+zfr160mn0+zduzfQnPPivC3LIW+oQrwBZDDcvVQfg3Lu3DmfPXu2Hz582M+cOeN33323HzhwYLCrKcm8ONdNecqLex7d17wa8GPmzJleX1/ve/fuHfR7u3fp4dVtsJQ3IKH+1qbTaS/UI+yyu3t8emJSqRQTJkxg/PjxDBs2jLlz59LS0hKLvDjXTXnKU15v27dv5+TJk4Gtv6e4b8u450mMhpPS6TRjx47NTyeTSdLpdCzy4lw35SlPecUT920Z97xCiPpwUmiNGDOrGcAyjWa2y8x2RWEsUUREJMqi3ogJ5BRrM6u+cBbwvdwdKs3dL9lX6u7rgPOtl0HdzyGZTHLs2LH8dDqdJplMDmYVgxJmXpzrpjzlKa944r4t454nwfXEHAf+ocdjF1AL7M49L7gpU6Zw6NAh2tvb6erqorm5mYaGhiCiQs+Lc92UpzzlFU/ct2Xc8wpBPTGXthy4A1ju7nsBzOyf3b0uoDwqKytpampi6dKlZDIZFixYwKRJk4KKCzUvznVTnvKU19umTZu4/fbbGTNmDO3t7axYsYINGzYEkhX3bRn3POke2glmxWYfAP4AaAdWAK3u/pODWIXuJy8ikRf2/1CD2qdLaEL9gzlx4kTB/mCuu+660LtjArvtgLv/EFhkZr8A/B1wVVBZIiIiMni67UA/3P0F4GeBOQBm9itBZ4qIiEj/on5MTGDDSX0Gmh129w8OYFH1iYpI5Gk4SQYp1D+Yzs7Ogv3BVFVVxWM4ycxSfb0E6HwzERERGbKgjolJAncCnRfMN+C1gDJFRERkEKJ+TExQjZgtwPvd/Y0LXzCzbweUKSIiImUk9GNiBqFkCyYiMlA6JkYGKdQ/mB//+McF+4O55ppr4nFMjIhIqdqxY0eoeWpUFNaDDz4Yat5TTz0Vap4MTmzuYi0iIiLlRT0xIiIiZSrqB/aqJ0ZEREQiSY0YERERiSQNJ4mIiJQpDSeJiIiIFIEaMSIiIhJJsRpO2rZtG6tXryabzbJo0SIaGxtjkxfnuilPecXOS6VSbNq0iWw2y6xZs5g3b16v17du3crWrVsxM0aMGMGSJUuora2lra2NjRs35pebP38+U6dOHVJZQPuWwZg8eTKf/OQnMTO+853v8PLLL1+0zG233ca8efNwd44cOcKGDRu46aabWLhwYX6ZsWPHsn79elpbW4dUnrC351BFfTgpNo2YTCbDqlWr2LhxI8lkkoULF9LQ0MCNN94Y+bw41015yit2Xjab5emnn2b58uVUV1ezcuVK6uvrqa2tzS8zY8YMGhoaANizZw/PPPMMy5Yto7a2lscee4xEIsGpU6d49NFHufXWW0kkEiVTv1LJCiLPzLjnnnt48skn6ezs5JFHHiGVSnHs2LH8Mtdffz133XUXa9eu5fTp04wePRqAN998kzVr1gBw1VVXsWrVKvbv319S9ZP+xWY4KZVKMWHCBMaPH8+wYcOYO3cuLS0tsciLc92Up7xi57W1tZFMJqmpqaGyspLp06ezZ8+eXsuMHDky//zMmTP5/70OHz4832A5e/ZsQf5Xq33LwN1www10dHRw/PhxMpkMu3bt4pZbbum1zMc+9jFeffVVTp8+DcC777570Xpuu+029u3bx9mzZ6+4LBD+9pQYNWLS6TRjx47NTyeTSdLpdCzy4lw35Smv2HmdnZ1UV1fnp6uqqujs7LxouVdeeYXly5fz3HPPce+99+bnHzx4kC984Qt88Ytf5P777x9SLwxo3zIY1157ba/PqrOzk2uvvbbXMjU1NdTU1LBs2TIefvhhJk+efNF6pk2bxuuvv37F5Tgv7O1ZCGZWsEcxlFQjxswazWyXme1at25dsYsjIpI3Z84cHn/8cRYtWsSLL76Ynz9x4kTWrFnDihUr2LJlC11dXUUspVwokUhQU1PDE088wfr167n33nt79axdffXVjBs3bshDSVIcgTRizOyuHs+vMbP1ZpYys01mluzrfe6+zt2nufu0wR4MlUwme42DptNpksk+o4YszLw41015yit2XlVVFSdPnsxPd3Z2UlVV1efy06dPZ/fu3RfNHzduHCNGjODIkSNXXBbQvmUwTp061euzqqqq4tSpU72W6ezsJJVKkc1mOXHiBO+88w41NTX516dOncobb7xBNpu94nKcF/b2lOB6Ytb0eP77wFHgbuB14M+CCJwyZQqHDh2ivb2drq4umpub8wfiRT0vznVTnvKKnVdXV0c6naajo4Nz586xc+dO6uvrey3T84eptbU1/8PU0dFBJpMB4Pjx4xw9epQxY8ZccVlA+5bBePvtt6mpqeG6664jkUgwbdo0UqlUr2VaW1u56aabABg1ahQ1NTUcP348//qHPvQhdu3adcVl6Cns7VkIUR9OCuPspGnufmvu+R+Y2f1BhFRWVtLU1MTSpUvJZDIsWLCASZMmBREVel6c66Y85RU7L5FIsHjxYtauXUs2m2XmzJnU1tayefNm6urqqK+vp6WlhX379pFIJBg1ahQPPPAA0H2GS3NzM4lEgoqKCu6777782S+lUr9SyQoiL5vN8uyzz/LQQw9RUVHBa6+9xtGjR5k3bx6HDx8mlUqxf/9+br75Zpqamshmszz//PO89957AFRXV1NVVcWBAwdKsn7SP3P3wq/U7IfAE4ABvwZM9FyQmaXc/acHsJrCF0xEyt6OHTtCzZsxY0aoeXH34IMPhpr31FNPhZpH9+9maE6fPl2w39qrrroq9O6YoHpivgyc/+/InwNjgA4zGwu8EVCmiIiIDIIudncJ7r6yj/nHzOxbQWSKiIhIeSnGKdaXbOCIiIiIDEYgPTFmlurrJUDnm4mIiJQADSddWhK4E7jwspcGvBZQpoiIiJSRoBoxW4D3u/sbF75gZt8OKFNERETKSFAH9n72Mq99OohMERERKS+BXCemQEq2YCJhC/vaJmHSdVSklBXhukKhHqRy5syZgv3WDh8+PPQDbErqBpAiIiIiA6VGjIiIiERSGPdOEhERkRIU9VOs1RMjIiIikaRGjIiIiESShpNERETKlIaTRERERIogVj0x27ZtY/Xq1WSzWRYtWkRjY2Ns8uJcN+UNXiqVYtOmTWSzWWbNmsW8efN6vb5161a2bt2KmTFixAiWLFlCbW0tbW1tbNy4Mb/c/PnzmTp1asnl9Sfqn18p5cW5bkHkldp3oey5e6k+BuXcuXM+e/ZsP3z4sJ85c8bvvvtuP3DgwGBXU5J5ca6b8gbmtddeyz/+/u//3j/60Y/6N77xDd+2bZvPnj3bv/71r/dapqWlJf/8S1/6kv/SL/2Sv/baa/6tb33Lt2/f7q+99pp/85vf9KlTp+an+3oEnVeM7am88LOimhf2d89D/q09e/asF+oRdtndPT7DSalUigkTJjB+/HiGDRvG3LlzaWlpiUVenOumvMFra2sjmUxSU1NDZWUl06dPZ8+ePb2WGTlyZP75mTNn8uPew4cPJ5FIAHD27NkBjYeHndefqH9+pZQX57oFkVdq3wWJ0XBSOp1m7Nix+elkMkkqlYpFXpzrprzB6+zspLq6Oj9dVVVFW1vbRcu98sorvPTSS2QyGR5++OH8/IMHD7J+/XpOnDhBY2NjfsdaKnn9ifrnV0p5ca5bEHml9l2QEA/sNbPrBrBMo5ntMrNd69atC6NYIrE1Z84cHn/8cRYtWsSLL76Ynz9x4kTWrFnDihUr2LJlC11dXZHMEylVUfoumFnBHsUQSCPGzH7HzMbknk8zszZgp5m9bWY/09f73H2du09z92mDPfgqmUxy7Nix/HQ6nSaZTF5hDUorL851U97gVVVVcfLkyfx0Z2cnVVVVfS4/ffp0du/efdH8cePGMWLECI4cOVJSef2J+udXSnlxrlsQeaX2XZDgemLmuvvx3PPHgV929xuBO4DfDyJwypQpHDp0iPb2drq6umhubqahoSGIqNDz4lw35Q1eXV0d6XSajo4Ozp07x86dO6mvr++1TM8dd2tra37H3dHRQSaTAeD48eMcPXqUMWPGlFRef6L++ZVSXpzrFkReqX0XJLhjYirNrNLdzwEj3f11AHd/08yGBxJYWUlTUxNLly4lk8mwYMECJk2aFERU6HlxrpvyBi+RSLB48WLWrl1LNptl5syZ1NbWsnnzZurq6qivr6elpYV9+/aRSCQYNWoUDzzwAABvvvkmzc3NJBIJKioquO+++xg9enRJ5fUn6p9fKeXFuW5B5JXad6EQon6Asbl74Vdq9hBwN/A7wCygCtgMNAA/6e73DWA1hS+YSETt2LGj2EUIzIwZM4pdBJE+hf3dmzFjRqitimw2W7Df2oqKitBbRIH0xLj7H5nZXuBB4KZcziTgG8BvB5EpIiIi5SWwU6zd/dvAty+cb2a/Amy8cL6IiIiEK+rDScW42N3KImSKiIhIzATSE2NmfV1NyIDgzqcTERGRshHUcFISuBPovGC+Aa8FlCkiIiKDEOZwkpndBfwvIAF8xd1/54LXhwNfA6YCJ+i+PMuhy60zqEbMFuD97v7GhS+Y2bcDyhQREZESZGYJ4Et0Xy/uh8DrZvaCu+/vsdhngU53v9HM7gF+F/jly603kGNi3P2z7v73fbz26SAyRUREpGR9GHjL3dvcvQt4FvjEBct8Avjz3PO/AmZbf11Fxbh1dpAPoFF5ylNevOumPOUpr/QeQCOwq8ejscdrC+keQjo/fR/wxxe8//vAB3pMHwTGXC6zGGcnBW1wN11SnvLimxfnuilPecorMd7j/oe5R+B3co5jI0ZERERKyxFgfI/pD+TmXXIZM6sErqH7AN8+qREjIiIiQXsdmGRmdWY2DLgHeOGCZV4A7s89Xwhs9dy4Ul8Cu2JvEQXefaU85UUkL851U57ylBch7n7OzD4HvET3KdYb3H2fma0Cdrn7C8B64Gkzews4SXdD57ICuQGkiIiISNA0nCQiIiKRpEaMiIiIRFJsGjFmtsHM3jGz74eUN97MvmVm+81sn5n9esB5I8zse2bWmssL/EaaZpYwsz1mtiWErENmttfM3jCzXSHkXWtmf2Vm/2hmPzCzGQFm/btcvc4//q+Z/Zeg8nKZ/zX3d/J9M3vGzEYEnPfruax9QdTtUt9vM6s2s78zswO5f6sCzluUq1/WzKYVKusyeY/n/j5TZva8mV0bcN5v57LeMLOXzWxckHk9XvtvZuZmNibIPDN7zMyO9Pge/nxQWbn5D+U+v31m9nuFyJKLxaYRA3wVuCvEvHPAf3P3ycBHgF8zs8kB5p0BGtz9FuBW4C4z+0iAeQC/Dvwg4Iyeftbdb3X3gv5A9OF/AX/r7v8euIUA6+nu/5Sr16103xPkNPB8UHlmVgt8Hpjm7j9F90F0/R4gN4S8nwIeoPuKnLcA88zsxgLHfJWLv9+PAC3uPgloyU0Hmfd94JeAbQXMuVze3wE/5e4/DbwJ/GbAeY+7+0/n/k63AE0B52Fm44GfAw4XMKvPPOAPzn8X3f2bQWWZ2c/SffXZW9z9/wPWFihLLhCbRoy7b6P7aOaw8o66++7c83fp/hGsDTDP3f1fcpPvyz0COyrbzD4AzAW+ElRGsZjZNcAsuo+Ex9273P1USPGzgYPu/nbAOZXAyNy1Fq4CfhRg1s3ATnc/7e7ngFfp/rEvmD6+3z0vUf7nwPwg89z9B+7+T4XKGEDey7ntCfBduq+rEWTe/+0xOYoC7l8us3/+A+DhQmb1k1dwfWQ9CPyOu5/JLfNOGGUpR7FpxBSTmd0A1AM7A85JmNkbwDvA37l7kHl/SPfOJRtgRk8OvGxm/2BmQV/psg7oADbmhsu+YmajAs487x7gmSAD3P0I3f/zOwwcBX7s7i8HGPl9YKaZXWdmVwE/T++LWgUl6e5Hc8+PAckQMovlPwJ/E3SIma02s3bgXgrbE3OprE8AR9y9NcicC3wuN2S2oZDDj5dwE93fiZ1m9qqZfSjArLKmRswQmdn7gb8G/ssF/5MpOHfP5Lp6PwB8ONeNX3BmNg94x93/IYj19+Fj7n4b8HG6h+ZmBZhVCdwGPOXu9cB7FHYo4pKs+wJPvwB8PeCcKrp7KeqAccAoM1scVJ67/4Duu82+DPwt8AaQCSqvjzI4AfZMFpOZ/Rbdw9d/GXSWu/+Wu4/PZX0uqJxcY/cLBNxQusBTwES6h+OPAr8fYFYlUE33oQbLgefM+rmRoVwRNWKGwMzeR3cD5i/dfXNYubmhj28R3DFAHwV+wcwO0X2n0QYz+4uAsoB878H5btfn6T6+Iig/BH7Yoyfrr+hu1ATt48Bud08HnDMH+Gd373D3s8Bm4D8EGeju6919qrvPAjrpPoYjaGkz+wmA3L+x67I3syXAPODe/q5cWmB/CSwIcP0T6W5kt+b2Mx8AdpvZ2KAC3T2d+49gFvgywe9jNucOA/ge3T3aBTtwWf6NGjFXKNeqXg/8wN2fCCHv+vNnJ5jZSOAO4B+DyHL333T3D7j7DXQPf2x198D+J29mo8xs9PnndB/oF9hZZu5+DGg3s3+XmzUb2B9UXg+fIuChpJzDwEfM7Krc3+lsAj5A28xqcv9+kO7jYTYFmZfT8xLl9wP/J4TM0JjZXXQP6f6Cu58OIW9Sj8lPEND+BcDd97p7jbvfkNvP/BC4LffdDMT5Bm/OLxLgPgb4BvCzudybgGHA8QDzytflbnEdpQfdPw5HgbN0fyE+G3Dex+juvk7R3X3+BvDzAeb9NLAnl/d9oCmk7Xo7sCXgjJ8EWnOPfcBvhVCvW+m+VXyK7h1OVcB5o+i+kdk1IX1uK+n+Efo+8DQwPOC87XQ3BFuB2QGs/6LvN3Ad3WclHQBeAaoDzvvF3PMzQBp4KeC8t4D2HvuXPw04769zfy8p4EWgNsi8C14/BIwJuH5PA3tz9XsB+IkAs4YBf5HbnrvpPrO0oN8JPbofuu2AiIiIRJKGk0RERCSS1IgRERGRSFIjRkRERCJJjRgRERGJJDViREREJJLUiBEpIjPL5O6o+30z+3ruSqZXuq6vmtnC3POvXO6GpGZ2u5kN+gJ41n238Ysu2tXX/D7WscTM/rgQuSJS3tSIESmuf/XuO+r+FNAF/GrPF3M3cBw0d1/q7pe7gN/tBHwVXxGRoKkRI1I6tgM35npJtpvZC8D+3I0/Hzez13M3r/tP0H3VaDP7YzP7JzN7Bag5vyIz+7aZTcs9v8vMdptZq5m15G5Y+qvAf831As3MXRH6r3MZr5vZR3Pvvc7MXjazfWb2FWDA938xsw+b2Y7cTTZf63GFZIDxuTIeMLMVPd6z2My+lyvXn5lZ4so3p4jE3RX9L09ECivX4/Jxum+gCN33cvopd//n3F29f+zuHzKz4cB3zOxluu+c/u+AyXTfwXk/sOGC9V5P931iZuXWVe3uJ83sT4F/cfe1ueU2AX/g7n+fu3XAS8DNwArg7919lZnNpftqpAP1j8BMdz9nZnOANfzb/Xg+DPwUcBp43cya6b4R5y8DH3X3s2b2J3TfTflrg8gUkTKiRoxIcY00szdyz7fTfT+u/wB8z93/OTf/54CfPn+8C3ANMAmYBTzj7hngR2a29RLr/wiw7fy63P1kH+WYA0zucaPdq3N3aJ9F972QcPdmM+scRN2uAf48d08eB97X47W/c/cTAGa2me7beJwDptLdqAEYSQxv6igihaNGjEhx/au739pzRu4H/L2es4CH3P2lC5b7+QKWowL4iLv/v0uU5Ur9NvAtd//F3BDWt3u8duH9Tpzuev65u//mUEJFpHzomBiR0vcS8KCZvQ+674qbu9v3NuCXc8fM/AS5u+Ze4LvALDOry723Ojf/XWB0j+VeBh46P2Fmt+aebgM+nZv3caBqEOW+BjiSe77kgtfuMLPq3B3Z5wPfoftmjgt73BG72swmDCJPRMqMGjEipe8rdB/vstvMvg/8Gd29qM/TfQfn/XQfN7Ljwje6ewfQCGw2s1bgf+deehH4xfMH9gKfB6blDhzez7+dJbWS7kbQPrqHlQ5fppwpM/th7vEE8HvA/zSzPVzc6/s9uu+anAL+2t135c6m+iLwspmlgL8DfmKA20hEypDuYi0iIiKRpJ4YERERiSQ1YkRERCSS1IgRERGRSFIjRkRERCJJjRgRERGJJDViREREJJLUiBEREZFI+v8BitzNqvo726YAAAAASUVORK5CYII=\n",
|
|
"text/plain": [
|
|
"<Figure size 720x504 with 2 Axes>"
|
|
]
|
|
},
|
|
"metadata": {
|
|
"needs_background": "light"
|
|
},
|
|
"output_type": "display_data"
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
" precision recall f1-score support\n",
|
|
"\n",
|
|
" 1 0.40 0.67 0.50 3\n",
|
|
" 2 0.00 0.00 0.00 3\n",
|
|
" 3 0.00 0.00 0.00 3\n",
|
|
" 4 0.00 0.00 0.00 3\n",
|
|
" 5 1.00 0.67 0.80 3\n",
|
|
" 6 0.00 0.00 0.00 3\n",
|
|
" 7 0.75 1.00 0.86 3\n",
|
|
" 8 0.33 0.33 0.33 3\n",
|
|
" 9 0.75 1.00 0.86 3\n",
|
|
" 10 0.00 0.00 0.00 3\n",
|
|
" 11 0.00 0.00 0.00 3\n",
|
|
" 12 0.50 1.00 0.67 3\n",
|
|
" 13 0.60 1.00 0.75 3\n",
|
|
" 14 0.00 0.00 0.00 3\n",
|
|
" 15 0.18 0.67 0.29 3\n",
|
|
" 16 1.00 0.33 0.50 3\n",
|
|
"\n",
|
|
" accuracy 0.42 48\n",
|
|
" macro avg 0.34 0.42 0.35 48\n",
|
|
"weighted avg 0.34 0.42 0.35 48\n",
|
|
"\n",
|
|
"CPU times: user 652 ms, sys: 221 ms, total: 873 ms\n",
|
|
"Wall time: 643 ms\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"%%time\n",
|
|
"\n",
|
|
"from sklearn.metrics import confusion_matrix\n",
|
|
"import seaborn as sn\n",
|
|
"\n",
|
|
"from sklearn.metrics import classification_report\n",
|
|
"\n",
|
|
"set_digits = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }\n",
|
|
"\n",
|
|
"train_cm = confusion_matrix(ltrain, ptrain, normalize='true')\n",
|
|
"test_cm = confusion_matrix(ltest, ptest, normalize='true')\n",
|
|
"\n",
|
|
"df_cm = pd.DataFrame(test_cm, index=set_digits, columns=set_digits)\n",
|
|
"plt.figure(figsize = (10,7))\n",
|
|
"sn_plot = sn.heatmap(df_cm, annot=True, cmap=\"Greys\")\n",
|
|
"plt.ylabel(\"True Label\")\n",
|
|
"plt.xlabel(\"Predicted Label\")\n",
|
|
"plt.show()\n",
|
|
"\n",
|
|
"print(classification_report(ltest, ptest, zero_division=0))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 31,
|
|
"id": "dfa99cac",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"cenario: SYN\n",
|
|
"win_sz: 10\n",
|
|
"stride_sz: 5\n",
|
|
"dense_steps: 3\n",
|
|
"layer_count: 5\n",
|
|
"drop_count: 0.2\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"print(f'cenario: {cenario}')\n",
|
|
"print(f'win_sz: {win_sz}')\n",
|
|
"print(f'stride_sz: {stride_sz}')\n",
|
|
"print(f'dense_steps: {dense_steps}')\n",
|
|
"print(f'layer_count: {layer_count}')\n",
|
|
"print(f'drop_count: {drop_count}')\n",
|
|
"\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "2a0c0db8",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": []
|
|
}
|
|
],
|
|
"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.8.10"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 5
|
|
}
|