{ "cells": [ { "cell_type": "markdown", "id": "2bc4ab88", "metadata": {}, "source": [ "## Constants" ] }, { "cell_type": "code", "execution_count": 1, "id": "c767cb34", "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": "f783fc7f", "metadata": {}, "outputs": [], "source": [ "glob_path = '/opt/iui-datarelease3-sose2021/*.csv'\n", "\n", "pickle_file = '../data.pickle'\n", "\n", "checkpoint_path = \"training_1/cp.ckpt\"\n", "checkpoint_dir = os.path.dirname(checkpoint_path)" ] }, { "cell_type": "markdown", "id": "bb1c9c9b", "metadata": {}, "source": [ "# Config" ] }, { "cell_type": "code", "execution_count": 3, "id": "3d812543", "metadata": {}, "outputs": [], "source": [ "# Possibilities: 'SYY', 'SYN', 'SNY', 'SNN', \n", "# 'JYY', 'JYN', 'JNY', 'JNN'\n", "cenario = 'SYN'\n", "\n", "win_sz = 30\n", "stride_sz = 2\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": "8cef4021", "metadata": {}, "source": [ "# Helper Functions" ] }, { "cell_type": "code", "execution_count": 4, "id": "cde65835", "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": "476851ec", "metadata": {}, "source": [ "# Loading Data" ] }, { "cell_type": "code", "execution_count": 5, "id": "199e4435", "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": "9e2817c1", "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": "12c5098e", "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": "00ee7490", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Loading data...\n", "../data.pickle found...\n", "768\n", "CPU times: user 548 ms, sys: 2.56 s, total: 3.11 s\n", "Wall time: 3.11 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": "d1db1537", "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CPU times: user 95 µs, sys: 297 µs, total: 392 µs\n", "Wall time: 396 µ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", "\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": "46382aad", "metadata": {}, "source": [ "# Preprocessing" ] }, { "cell_type": "code", "execution_count": 10, "id": "f7842338", "metadata": { "tags": [] }, "outputs": [], "source": [ "def drop(entry, data=True) -> pd.DataFrame:\n", " droptable = ['participantID', 'FrameID', 'Scenario', 'HeightNormalization', 'ArmNormalization', 'Repetition', 'Session', 'Unnamed: 0']\n", " if data:\n", " centry = pickle.loads(pickle.dumps(entry['data']))\n", " else:\n", " centry = pickle.loads(pickle.dumps(entry))\n", "\n", " return centry.drop(droptable, axis=1)\n", " \n" ] }, { "cell_type": "code", "execution_count": 11, "id": "b73d9485", "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, data=True) -> pd.DataFrame:\n", " if data:\n", " centry = pickle.loads(pickle.dumps(entry['data']))\n", " else:\n", " centry = pickle.loads(pickle.dumps(entry))\n", " \n", " centry['LeftHandTrackingAccuracy'] = (centry['LeftHandTrackingAccuracy'] == 'High') * 1.0\n", " centry['RightHandTrackingAccuracy'] = (centry['RightHandTrackingAccuracy'] == 'High') * 1.0\n", " \n", " right_Hand_cols = [c for c in centry if right_Hand_ident in c]\n", " left_Hand_cols = [c for c in centry if left_Hand_ident in c]\n", " \n", " centry.loc[centry['RightHandTrackingAccuracy'] == 0.0, right_Hand_cols] = np.nan\n", " centry.loc[centry['LeftHandTrackingAccuracy'] == 0.0, left_Hand_cols] = np.nan\n", " \n", " return centry\n", "\n" ] }, { "cell_type": "code", "execution_count": 12, "id": "1a298d6d", "metadata": {}, "outputs": [], "source": [ "from tensorflow.keras.preprocessing.sequence import pad_sequences\n", "\n", "def pad(entry, data=True) -> pd.DataFrame:\n", " if data:\n", " centry = pickle.loads(pickle.dumps(entry['data']))\n", " else:\n", " centry = pickle.loads(pickle.dumps(entry))\n", " \n", " cols = centry.columns\n", " pentry = pad_sequences(centry.T.to_numpy(),\n", " maxlen=(int(centry.shape[0]/stride_sz)+1)*stride_sz,\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": 13, "id": "3be1bd3f", "metadata": {}, "outputs": [], "source": [ "def interpol(entry, data=True) -> pd.DataFrame:\n", " if data:\n", " centry = pickle.loads(pickle.dumps(entry['data']))\n", " else:\n", " centry = pickle.loads(pickle.dumps(entry))\n", " \n", " return centry.interpolate(method='linear', axis=0)" ] }, { "cell_type": "code", "execution_count": 14, "id": "2a7f4e26", "metadata": {}, "outputs": [], "source": [ "from tensorflow.keras.preprocessing import timeseries_dataset_from_array\n", "\n", "def slicing(entry, label, data=True):\n", " if data:\n", " centry = pickle.loads(pickle.dumps(entry['data']))\n", " else:\n", " centry = pickle.loads(pickle.dumps(entry))\n", " \n", " return timeseries_dataset_from_array(\n", " data=centry, \n", " targets=[label for _ in range(centry.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": 15, "id": "b012b0f7", "metadata": { "tags": [] }, "outputs": [], "source": [ "# %%time \n", "\n", "# acc_data = pd.DataFrame()\n", "\n", "# for d in tqdm(cdata['SYY']):\n", "# acc_data = acc_data.append(d['data'])\n", "\n", "\n", "# dacc_data = drop(acc_data, False)\n", "# ddacc_data = rem_low_acc(dacc_data, False)\n", "\n", "# for c in ddacc_data:\n", "# print(f\"{c}: {dacc_data[c].min()}, {dacc_data[c].max()}\")" ] }, { "cell_type": "code", "execution_count": 16, "id": "a2440d77", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "100%|██████████| 96/96 [00:09<00:00, 10.55it/s]" ] }, { "name": "stdout", "output_type": "stream", "text": [ "CPU times: user 8.07 s, sys: 1.19 s, total: 9.27 s\n", "Wall time: 9.1 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, data = True):\n", " entry2 = pickle.loads(pickle.dumps(entry))\n", " entry2['data'] = drop(entry2, data)\n", " \n", " entry4 = pickle.loads(pickle.dumps(entry2))\n", " entry4['data'] = rem_low_acc(entry4, data)\n", " \n", " entry5 = pickle.loads(pickle.dumps(entry4))\n", " entry5['data'] = pad(entry5, data)\n", " \n", "# entry6 = pickle.loads(pickle.dumps(entry5))\n", "# entry6['data'] = interpol(entry6, data)\n", " \n", " entry7 = pickle.loads(pickle.dumps(entry5))\n", " entry7['data'] = slicing(entry7, entry7['user'], data)\n", " \n", " return entry7\n", "\n", "pdata = preproc(cdata[cenario])" ] }, { "cell_type": "code", "execution_count": 17, "id": "11e96fef", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CPU times: user 96 µs, sys: 107 µs, total: 203 µs\n", "Wall time: 214 µs\n" ] }, { "data": { "text/plain": [ "(48, 48)" ] }, "execution_count": 17, "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": 18, "id": "1807a2f7", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CPU times: user 1min, sys: 13.2 s, total: 1min 13s\n", "Wall time: 21.1 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": 19, "id": "44330b34", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(37902, 30, 338) (73692, 30, 338) (37902,) (73692,)\n", "(32745, 30, 338) (48773, 30, 338) (32745,) (48773,)\n" ] } ], "source": [ "XX_train = list()\n", "yy_train = list()\n", "XX_test = list()\n", "yy_test = list()\n", "\n", "for X,y in zip(X_train, y_train):\n", " if not np.isnan(X).any():\n", " XX_train.append(X)\n", " yy_train.append(y)\n", "\n", "for X,y in zip(X_test, y_test):\n", " if not np.isnan(X).any():\n", " XX_test.append(X)\n", " yy_test.append(y)\n", " \n", "XX_train = np.array(XX_train)\n", "yy_train = np.array(yy_train)\n", "XX_test = np.array(XX_test)\n", "yy_test = np.array(yy_test)\n", "\n", "print(np.array(XX_train).shape, X_train.shape, np.array(yy_train).shape, np.array(y_train).shape)\n", "print(np.array(XX_test).shape, X_test.shape, np.array(yy_test).shape, np.array(y_test).shape)" ] }, { "cell_type": "code", "execution_count": 20, "id": "bd805e81", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
0
011
111
211
311
411
......
378979
378989
378999
379009
379019
\n", "

37902 rows × 1 columns

\n", "
" ], "text/plain": [ " 0\n", "0 11\n", "1 11\n", "2 11\n", "3 11\n", "4 11\n", "... ..\n", "37897 9\n", "37898 9\n", "37899 9\n", "37900 9\n", "37901 9\n", "\n", "[37902 rows x 1 columns]" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = pd.DataFrame(yy_train)\n", "a" ] }, { "cell_type": "code", "execution_count": 21, "id": "f6416fc1", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "16 0.113055\n", "11 0.110680\n", "10 0.103662\n", "3 0.102739\n", "5 0.095404\n", "13 0.069469\n", "15 0.061738\n", "7 0.048625\n", "9 0.044826\n", "6 0.043955\n", "14 0.043375\n", "1 0.038890\n", "4 0.036489\n", "8 0.035433\n", "12 0.032030\n", "2 0.019630\n", "dtype: float64" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "b = a.value_counts(normalize=True)\n", "b" ] }, { "cell_type": "code", "execution_count": 22, "id": "1885329b", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
0
0
160.113055
110.110680
100.103662
30.102739
50.095404
130.069469
150.061738
70.048625
90.044826
60.043955
140.043375
10.038890
40.036489
80.035433
120.032030
20.019630
\n", "
" ], "text/plain": [ " 0\n", "0 \n", "16 0.113055\n", "11 0.110680\n", "10 0.103662\n", "3 0.102739\n", "5 0.095404\n", "13 0.069469\n", "15 0.061738\n", "7 0.048625\n", "9 0.044826\n", "6 0.043955\n", "14 0.043375\n", "1 0.038890\n", "4 0.036489\n", "8 0.035433\n", "12 0.032030\n", "2 0.019630" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "c = pd.DataFrame(b)\n", "c" ] }, { "cell_type": "code", "execution_count": 23, "id": "7dfe2339", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "c.plot.pie(y=0, legend=False)" ] }, { "cell_type": "code", "execution_count": 24, "id": "fd3f3f1e", "metadata": {}, "outputs": [ { "ename": "SyntaxError", "evalue": "unmatched ']' (, line 1)", "output_type": "error", "traceback": [ "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m ]]]\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m unmatched ']'\n" ] } ], "source": [ "]" ] }, { "cell_type": "code", "execution_count": null, "id": "615cccc9", "metadata": {}, "outputs": [], "source": [ "%%time\n", "\n", "from sklearn.preprocessing import LabelBinarizer\n", "\n", "\n", "lb = LabelBinarizer()\n", "yyy_train = lb.fit_transform(yy_train)\n", "yyy_test = lb.fit_transform(yy_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'])" ] }, { "cell_type": "code", "execution_count": null, "id": "f56e5055", "metadata": {}, "outputs": [], "source": [ "print(XX_train.shape)\n", "print(yyy_train.shape)\n", "print(XX_test.shape)\n", "print(yyy_test.shape)" ] }, { "cell_type": "markdown", "id": "f046e211", "metadata": {}, "source": [ "# Building Model" ] }, { "cell_type": "code", "execution_count": null, "id": "53832797", "metadata": {}, "outputs": [], "source": [ "import tensorflow as tf\n", "from tensorflow.keras.regularizers import l2\n", "from tensorflow.keras.models import Sequential\n", "from tensorflow.keras.layers import Dense, Flatten, BatchNormalization, Dropout\n", "from tensorflow.keras.callbacks import ModelCheckpoint, ReduceLROnPlateau\n", "from tensorflow.keras.optimizers import Adam\n", "\n", "def build_model(shape, classes):\n", " model = Sequential()\n", " \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", " kernel_regularizer=l2(0.001))\n", " )\n", " \n", " model.add(Dense(classes, activation='softmax'))\n", " \n", " model.compile(\n", " optimizer=Adam(),\n", " loss=\"categorical_crossentropy\", \n", " metrics=[\"acc\"],\n", " )\n", " \n", " return model\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "id": "64f2aaa7", "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", " # Create a callback that saves the model's weights\n", " model_checkpoint = ModelCheckpoint(filepath=checkpoint_path, monitor='loss', \n", "\t\t\tsave_best_only=True)\n", " \n", " reduce_lr = ReduceLROnPlateau(monitor='loss', factor=0.5, patience=5, min_lr=0.0001)\n", "\n", " callbacks = [model_checkpoint, reduce_lr]\n", " \n", " history = model.fit(X_train, \n", " y_train,\n", " epochs=50,\n", " batch_size=32,\n", " verbose=2,\n", " validation_data=(X_test, y_test),\n", " callbacks=callbacks\n", " )\n", " \n", " model.load_weights(checkpoint_path)\n", " return model, history\n" ] }, { "cell_type": "code", "execution_count": null, "id": "858670a8", "metadata": {}, "outputs": [], "source": [ "%%time\n", "\n", "model, history = train_model(np.array(XX_train), np.array(yyy_train), np.array(XX_test), np.array(yyy_test))" ] }, { "cell_type": "markdown", "id": "6e905067", "metadata": {}, "source": [ "# Eval" ] }, { "cell_type": "code", "execution_count": null, "id": "196dae1d", "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": null, "id": "49aebfaa", "metadata": {}, "outputs": [], "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", "# 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": null, "id": "6577cf5f", "metadata": {}, "outputs": [], "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_______________\")\n" ] }, { "cell_type": "code", "execution_count": null, "id": "d62e2063", "metadata": {}, "outputs": [], "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 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }\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": null, "id": "5041115f", "metadata": {}, "outputs": [], "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}')" ] }, { "cell_type": "code", "execution_count": null, "id": "d25d662c", "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 }