{ "cells": [ { "cell_type": "code", "execution_count": 1, "id": "dab4afe8", "metadata": {}, "outputs": [], "source": [ "import os\n", "\n", "glob_path = '/opt/iui-datarelease2-sose2021/*/split_letters_csv/*'\n", "\n", "pickle_file = 'data.pickle'\n", "\n", "checkpoint_path = \"training_1/cp.ckpt\"\n", "checkpoint_dir = os.path.dirname(checkpoint_path)\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 = 2\n", "# amount of dense/dropout layers\n", "layer_count = 3\n", "# how much to drop\n", "drop_count = 0.1" ] }, { "cell_type": "code", "execution_count": 2, "id": "fb114dda", "metadata": {}, "outputs": [], "source": [ "from glob import glob\n", "import pandas as pd\n", "from tqdm import tqdm\n", "\n", "def dl_from_blob(filename) -> list:\n", " all_data = []\n", " \n", " for path in tqdm(glob(filename)):\n", " path = path\n", " df = pd.read_csv(path, ';')\n", " u = path.split('/')[3]\n", " l = ''.join(filter(lambda x: x.isalpha(), path.split('/')[5]))[0] \n", " d = {\n", " 'file': path,\n", " 'data': df,\n", " 'user': u,\n", " 'label': l\n", " }\n", " all_data.append(d)\n", " return all_data" ] }, { "cell_type": "code", "execution_count": 3, "id": "6b23cd45", "metadata": {}, "outputs": [], "source": [ "def save_pickle(f, structure):\n", " _p = open(f, 'wb')\n", " pickle.dump(structure, _p)\n", " _p.close()" ] }, { "cell_type": "code", "execution_count": 4, "id": "e3cdaf0c", "metadata": {}, "outputs": [], "source": [ "import pickle\n", "\n", "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": 5, "id": "c1b8c553", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Loading data...\n", "data.pickle found...\n" ] } ], "source": [ "import os\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", "data = load_data()\n", "# plot_pd(data[0]['data'], False)" ] }, { "cell_type": "code", "execution_count": 6, "id": "7d8249b6", "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "\n", "def plot_pd(data, force=True):\n", " fig, axs = plt.subplots(5, 3, figsize=(3*3, 3*5))\n", " axs[0][0].plot(data['Acc1 X'])\n", " axs[0][1].plot(data['Acc1 Y'])\n", " axs[0][2].plot(data['Acc1 Z'])\n", " axs[1][0].plot(data['Acc2 X'])\n", " axs[1][1].plot(data['Acc2 Y'])\n", " axs[1][2].plot(data['Acc2 Z'])\n", " axs[2][0].plot(data['Gyro X'])\n", " axs[2][1].plot(data['Gyro Y'])\n", " axs[2][2].plot(data['Gyro Z'])\n", " axs[3][0].plot(data['Mag X'])\n", " axs[3][1].plot(data['Mag Y'])\n", " axs[3][2].plot(data['Mag Z'])\n", " axs[4][0].plot(data['Time'])\n", "\n", " if force:\n", " for a in axs:\n", " for b in a:\n", " b.plot(data['Force'])\n", " else:\n", " axs[4][1].plot(data['Force'])\n", "\n", "def plot_np(data, force=True):\n", " fig, axs = plt.subplots(5, 3, figsize=(3*3, 3*5))\n", " axs[0][0].plot(data[0])\n", " axs[0][1].plot(data[1])\n", " axs[0][2].plot(data[2])\n", " axs[1][0].plot(data[3])\n", " axs[1][1].plot(data[4])\n", " axs[1][2].plot(data[5])\n", " axs[2][0].plot(data[6])\n", " axs[2][1].plot(data[7])\n", " axs[2][2].plot(data[8])\n", " axs[3][0].plot(data[9])\n", " axs[3][1].plot(data[10])\n", " axs[3][2].plot(data[11])\n", " axs[4][0].plot(data[13])\n", "\n", " if force:\n", " for a in axs:\n", " for b in a:\n", " b.plot(data[12])\n", " else:\n", " axs[4][1].plot(data[12])\n" ] }, { "cell_type": "code", "execution_count": 7, "id": "6e7b9412", "metadata": {}, "outputs": [], "source": [ "def mill_drop(entry):\n", " #drop millis on single\n", " data_wo_mill = entry['data'].drop(labels='Millis', axis=1, inplace=False)\n", " drop_entry = entry\n", " drop_entry['data'] = data_wo_mill.reset_index(drop=True)\n", " \n", " return drop_entry" ] }, { "cell_type": "code", "execution_count": 8, "id": "52de5447", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "\n", "def cut_force(drop_entry):\n", " # force trans\n", " shorten_entry = drop_entry\n", " shorten_data = shorten_entry['data']\n", " sf_entry = shorten_data['Force']\n", " leeway = 10\n", " \n", " try:\n", " thresh = 70\n", " temps_over_T = np.where(sf_entry > thresh)[0]\n", " shorten_data = shorten_data[max(temps_over_T.min()-leeway,0):min(len(sf_entry)-1,temps_over_T.max()+leeway)]\n", " except:\n", " thresold = 0.05\n", " thresh = sf_entry.max()*thresold\n", " temps_over_T = np.where(sf_entry > thresh)[0]\n", " shorten_data = shorten_data[max(temps_over_T.min()-leeway,0):min(len(sf_entry)-1,temps_over_T.max()+leeway)]\n", " \n", " shorten_entry['data'] = shorten_data.reset_index(drop=True)\n", " return shorten_entry" ] }, { "cell_type": "code", "execution_count": 9, "id": "e962f5cf", "metadata": {}, "outputs": [], "source": [ "def norm_force(shorten_entry, flist):\n", " fnorm_entry = shorten_entry\n", " u = fnorm_entry['user']\n", " d = fnorm_entry['data']\n", " \n", " \n", " d['Force'] = ((d['Force'] - flist[u].mean())/flist[u].std())\n", " \n", " fnorm_entry['data'] = fnorm_entry['data'].reset_index(drop=True)\n", " return fnorm_entry" ] }, { "cell_type": "code", "execution_count": 10, "id": "56056f60", "metadata": {}, "outputs": [], "source": [ "def time_trans(fnorm_entry):\n", " #timetrans\n", " time_entry = fnorm_entry\n", " \n", " time_entry['data']['Time'] = fnorm_entry['data']['Time']-fnorm_entry['data']['Time'][0]\n", " \n", " time_entry['data'] = time_entry['data'].reset_index(drop=True)\n", "\n", " return time_entry" ] }, { "cell_type": "code", "execution_count": 11, "id": "2a353441", "metadata": {}, "outputs": [], "source": [ "def norm(time_entry):\n", " # normalize\n", " norm_entry = time_entry\n", " \n", " norm_entry['data']['Acc1 X'] = norm_entry['data']['Acc1 X'] / 32768\n", " norm_entry['data']['Acc1 Y'] = norm_entry['data']['Acc1 Y'] / 32768\n", " norm_entry['data']['Acc1 Z'] = norm_entry['data']['Acc1 Z'] / 32768\n", " norm_entry['data']['Acc2 X'] = norm_entry['data']['Acc2 X'] / 8192\n", " norm_entry['data']['Acc2 Y'] = norm_entry['data']['Acc2 Y'] / 8192\n", " norm_entry['data']['Acc2 Z'] = norm_entry['data']['Acc2 Z'] / 8192\n", " norm_entry['data']['Gyro X'] = norm_entry['data']['Gyro X'] / 32768\n", " norm_entry['data']['Gyro Y'] = norm_entry['data']['Gyro Y'] / 32768\n", " norm_entry['data']['Gyro Z'] = norm_entry['data']['Gyro Z'] / 32768\n", " norm_entry['data']['Mag X'] = norm_entry['data']['Mag X'] / 8192\n", " norm_entry['data']['Mag Y'] = norm_entry['data']['Mag Y'] / 8192\n", " norm_entry['data']['Mag Z'] = norm_entry['data']['Mag Z'] / 8192\n", " \n", " norm_entry['data'] = norm_entry['data'].reset_index(drop=True)\n", " \n", " return norm_entry" ] }, { "cell_type": "code", "execution_count": 12, "id": "ee11dd88", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Preprocessing...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "100%|██████████| 26179/26179 [01:30<00:00, 290.54it/s]\n" ] } ], "source": [ "def preproc(d):\n", " flist = {} \n", " d_res = []\n", " for e in data:\n", " if e['user'] not in flist:\n", " flist[e['user']] = e['data']['Force']\n", " else:\n", " flist[e['user']] = flist[e['user']].append(e['data']['Force'])\n", " \n", " for e in tqdm(data):\n", " d_res.append(preproc_entry(e, flist))\n", " return d_res\n", " \n", "def preproc_entry(entry, flist):\n", " drop_entry = mill_drop(entry)\n", "# plot_pd(drop_entry['data'])\n", "# \n", " shorten_entry = cut_force(drop_entry)\n", "# plot_pd(shorten_entry['data'])\n", "# \n", " fnorm_entry = norm_force(shorten_entry, flist)\n", "# plot_pd(fnorm_entry['data'])\n", "# \n", " time_entry = time_trans(shorten_entry)\n", "# plot_pd(time_entry['data'])\n", "# \n", " norm_entry = norm(time_entry)\n", "# plot_pd(norm_entry['data'], False)\n", " return norm_entry\n", "\n", "print(\"Preprocessing...\")\n", "pdata = preproc(data)\n", "# plot_pd(pdata[0]['data'], False)" ] }, { "cell_type": "code", "execution_count": 13, "id": "d32af185", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Truncating...\n" ] } ], "source": [ "def throw(pdata):\n", " llist = pd.Series([len(x['data']) for x in pdata])\n", " threshold = int(llist.quantile(threshold_p))\n", " longdex = np.where(llist <= threshold)[0]\n", " return np.array(pdata)[longdex]\n", "\n", "llist = pd.Series([len(x['data']) for x in pdata])\n", "threshold_p = 0.75\n", "threshold = int(llist.quantile(threshold_p))\n", "\n", "print(\"Truncating...\")\n", "tpdata = throw(pdata)\n", "# plot_pd(tpdata[0]['data'], False)" ] }, { "cell_type": "code", "execution_count": 14, "id": "317f32d3", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ " 9%|▉ | 1822/19640 [00:00<00:00, 18211.98it/s]" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Padding...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "100%|██████████| 19640/19640 [00:01<00:00, 18646.81it/s]\n" ] } ], "source": [ "from tensorflow.keras.preprocessing.sequence import pad_sequences\n", "# ltpdata = []\n", "def elong(tpdata):\n", " for x in tqdm(tpdata):\n", " y = x['data'].to_numpy().T\n", " x['data'] = pad_sequences(y, dtype=float, padding='post', maxlen=threshold)\n", " return tpdata\n", "\n", "print(\"Padding...\")\n", "ltpdata = elong(tpdata)\n", "# plot_np(ltpdata[0]['data'], False)" ] }, { "cell_type": "code", "execution_count": 15, "id": "39e3d5e1", "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, name='flatten'))\n", " \n", " model.add(Dropout(drop_count, name=f'dropout_{drop_count*100}'))\n", " model.add(BatchNormalization(name='batchNorm'))\n", " \n", " for i in range(1,layer_count+1):\n", " neurons = int(ncount/pow(dense_steps,i))\n", " if neurons <= classes:\n", " break\n", " model.add(Dropout(drop_count*i, name=f'HiddenDropout_{drop_count*i*100:.0f}'))\n", " model.add(Dense(neurons, activation='relu', \n", " kernel_regularizer=l2(0.001), name=f'Hidden_{i}')\n", " )\n", " \n", " model.add(Dense(classes, activation='softmax', name='Output'))\n", " \n", " model.compile(\n", " optimizer=Adam(),\n", " loss=\"categorical_crossentropy\", \n", " metrics=[\"acc\"],\n", " )\n", " \n", " return model" ] }, { "cell_type": "code", "execution_count": 16, "id": "13ba96af", "metadata": {}, "outputs": [], "source": [ "checkpoint_file = './goat.weights'\n", "\n", "def train(X_train, y_train, X_test, y_test):\n", " model = build_model(X_train[0].shape, 52)\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", " history = model.fit(X_train, y_train, \n", " epochs=30,\n", " batch_size=256,\n", " shuffle=True,\n", " validation_data=(X_test, y_test),\n", " verbose=2,\n", " callbacks=[model_checkpoint]\n", " )\n", " \n", " \n", " model.load_weights(checkpoint_path)\n", " print(\"Evaluate on test data\")\n", " return model, history" ] }, { "cell_type": "code", "execution_count": 17, "id": "519579cf", "metadata": { "tags": [] }, "outputs": [], "source": [ "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": 18, "id": "3bf061f4", "metadata": {}, "outputs": [], "source": [ "from sklearn.model_selection import train_test_split\n", "from sklearn.preprocessing import LabelEncoder, LabelBinarizer\n", "\n", "X = np.array([x['data'] for x in ltpdata])\n", "y = np.array([x['label'] for x in ltpdata])\n", "\n", "lb = LabelBinarizer()\n", "y_tran = lb.fit_transform(y)\n", "\n", "X_train, X_test, y_train, y_test = train_test_split(X, y_tran, test_size=0.2, random_state=177013)\n", "\n", "X_train=X_train.reshape(X_train.shape[0],X_train.shape[1],X_train.shape[2])\n", "X_test=X_test.reshape(X_test.shape[0],X_test.shape[1],X_test.shape[2])\n", "\n", "train_shape = X_train[0].shape\n", "classes = y_train[0].shape[0]" ] }, { "cell_type": "code", "execution_count": 19, "id": "a8dffb69", "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Model: \"sequential\"\n", "_________________________________________________________________\n", "Layer (type) Output Shape Param # \n", "=================================================================\n", "flatten (Flatten) (None, 1050) 0 \n", "_________________________________________________________________\n", "dropout_10.0 (Dropout) (None, 1050) 0 \n", "_________________________________________________________________\n", "batchNorm (BatchNormalizatio (None, 1050) 4200 \n", "_________________________________________________________________\n", "HiddenDropout_10 (Dropout) (None, 1050) 0 \n", "_________________________________________________________________\n", "Hidden_1 (Dense) (None, 525) 551775 \n", "_________________________________________________________________\n", "HiddenDropout_20 (Dropout) (None, 525) 0 \n", "_________________________________________________________________\n", "Hidden_2 (Dense) (None, 262) 137812 \n", "_________________________________________________________________\n", "HiddenDropout_30 (Dropout) (None, 262) 0 \n", "_________________________________________________________________\n", "Hidden_3 (Dense) (None, 131) 34453 \n", "_________________________________________________________________\n", "Output (Dense) (None, 52) 6864 \n", "=================================================================\n", "Total params: 735,104\n", "Trainable params: 733,004\n", "Non-trainable params: 2,100\n", "_________________________________________________________________\n", "Epoch 1/30\n", "62/62 - 2s - loss: 4.8415 - acc: 0.0671 - val_loss: 4.7052 - val_acc: 0.0723\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Epoch 2/30\n", "62/62 - 0s - loss: 4.0441 - acc: 0.1667 - val_loss: 4.2215 - val_acc: 0.1181\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Epoch 3/30\n", "62/62 - 0s - loss: 3.4989 - acc: 0.2526 - val_loss: 3.8433 - val_acc: 0.1752\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Epoch 4/30\n", "62/62 - 0s - loss: 3.1289 - acc: 0.3177 - val_loss: 3.5399 - val_acc: 0.2210\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Epoch 5/30\n", "62/62 - 0s - loss: 2.8521 - acc: 0.3696 - val_loss: 3.3127 - val_acc: 0.2620\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Epoch 6/30\n", "62/62 - 0s - loss: 2.6560 - acc: 0.4068 - val_loss: 3.0547 - val_acc: 0.3236\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Epoch 7/30\n", "62/62 - 0s - loss: 2.5031 - acc: 0.4395 - val_loss: 2.7836 - val_acc: 0.3933\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Epoch 8/30\n", "62/62 - 0s - loss: 2.3725 - acc: 0.4638 - val_loss: 2.5959 - val_acc: 0.4104\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Epoch 9/30\n", "62/62 - 0s - loss: 2.2528 - acc: 0.4946 - val_loss: 2.3562 - val_acc: 0.4705\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Epoch 10/30\n", "62/62 - 0s - loss: 2.1670 - acc: 0.5070 - val_loss: 2.1431 - val_acc: 0.5323\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Epoch 11/30\n", "62/62 - 0s - loss: 2.0921 - acc: 0.5232 - val_loss: 2.0376 - val_acc: 0.5624\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Epoch 12/30\n", "62/62 - 0s - loss: 2.0189 - acc: 0.5411 - val_loss: 1.9824 - val_acc: 0.5644\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Epoch 13/30\n", "62/62 - 0s - loss: 1.9840 - acc: 0.5495 - val_loss: 1.8983 - val_acc: 0.5802\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Epoch 14/30\n", "62/62 - 0s - loss: 1.9388 - acc: 0.5557 - val_loss: 1.8335 - val_acc: 0.6003\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Epoch 15/30\n", "62/62 - 0s - loss: 1.8664 - acc: 0.5737 - val_loss: 1.8275 - val_acc: 0.5909\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Epoch 16/30\n", "62/62 - 0s - loss: 1.8345 - acc: 0.5861 - val_loss: 1.7849 - val_acc: 0.5942\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Epoch 17/30\n", "62/62 - 0s - loss: 1.8048 - acc: 0.5876 - val_loss: 1.7853 - val_acc: 0.5950\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Epoch 18/30\n", "62/62 - 0s - loss: 1.7855 - acc: 0.5971 - val_loss: 1.7390 - val_acc: 0.6227\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Epoch 19/30\n", "62/62 - 0s - loss: 1.7337 - acc: 0.6060 - val_loss: 1.7086 - val_acc: 0.6225\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Epoch 20/30\n", "62/62 - 0s - loss: 1.7177 - acc: 0.6057 - val_loss: 1.6970 - val_acc: 0.6303\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Epoch 21/30\n", "62/62 - 0s - loss: 1.6984 - acc: 0.6166 - val_loss: 1.7008 - val_acc: 0.6151\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Epoch 22/30\n", "62/62 - 0s - loss: 1.6698 - acc: 0.6223 - val_loss: 1.6824 - val_acc: 0.6171\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Epoch 23/30\n", "62/62 - 0s - loss: 1.6533 - acc: 0.6265 - val_loss: 1.6574 - val_acc: 0.6329\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Epoch 24/30\n", "62/62 - 0s - loss: 1.6341 - acc: 0.6373 - val_loss: 1.6485 - val_acc: 0.6283\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Epoch 25/30\n", "62/62 - 0s - loss: 1.6232 - acc: 0.6366 - val_loss: 1.6606 - val_acc: 0.6334\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Epoch 26/30\n", "62/62 - 0s - loss: 1.6098 - acc: 0.6337 - val_loss: 1.6851 - val_acc: 0.6225\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Epoch 27/30\n", "62/62 - 0s - loss: 1.5843 - acc: 0.6459 - val_loss: 1.6324 - val_acc: 0.6377\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Epoch 28/30\n", "62/62 - 0s - loss: 1.5674 - acc: 0.6538 - val_loss: 1.6377 - val_acc: 0.6286\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Epoch 29/30\n", "62/62 - 0s - loss: 1.5684 - acc: 0.6464 - val_loss: 1.6092 - val_acc: 0.6459\n", "Epoch 30/30\n", "62/62 - 0s - loss: 1.5520 - acc: 0.6559 - val_loss: 1.6159 - val_acc: 0.6433\n", "INFO:tensorflow:Assets written to: training_1/cp.ckpt/assets\n", "Evaluate on test data\n", "CPU times: user 40.5 s, sys: 4.35 s, total: 44.9 s\n", "Wall time: 37.2 s\n" ] } ], "source": [ "%%time\n", "if 'model' not in locals():\n", " tf.keras.backend.clear_session()\n", " model, history = train(np.array(X_train), np.array(y_train), np.array(X_test), np.array(y_test))\n", "else:\n", " print(\"Loaded weights...\")\n", " model.load_weights(checkpoint_path)" ] }, { "cell_type": "code", "execution_count": 20, "id": "9b57245b", "metadata": {}, "outputs": [ { "data": { "image/png": "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0yZWr17NBx98wA8//EBQUBBZWdZktvPnz+fee+9l1qxZlJSUUFVV1ejPoTk0qSul2o3Zs2fj7e0NQG5uLtdccw379+9HRCgvL691n4svvhh/f3/8/f3p1q0bR48eJSYm5pQy48ePr143evRoUlJSCAkJoX///tX9wOfOncuLL/78QUzl5eXcfvvtbNu2DW9vb3788UcA1q5dy7XXXktQUBAAERER5Ofnk5aWxqxZswBrAFFr06SuVAfXlBp1SwkODq5efuCBBzj33HN5//33SUlJYfLkybXu4+/vX73s7e1NRUVFk8rU5emnn6Z79+5s376dqqqqNknUjaFt6kqpdik3N5fo6GgAXnnlFZcff/DgwSQnJ5OSkgLAO++8U2ccPXr0wMvLi9dff53KykoALrjgAl5++WWKiooAyMrKIjQ0lJiYGJYvXw5AaWlp9fbWokldKdUu/f73v+ePf/wjY8aMaVTN2lmBgYE899xzTJ06lbFjxxIaGkp4ePjPyt166628+uqrjBo1in379lV/m5g6dSozZswgPj6e0aNHs3jxYgBef/11/v73vzNy5EgmTpzIkSNHXB57fRp8RmlL0YdkKNV29u7dy9ChQ9s6jDZXUFBASEgIxhhuu+02Bg4cyN13393WYVWr7TqJyGZjTJ19OrWmrpTqsP71r38xevRohg8fTm5uLjfddFNbh9RseqNUKdVh3X333e2qZu4KWlNXSikPokldKaU8iCZ1pZTyIJrUlVLKg2hSV0q1unPPPZc1a9acsu6ZZ57hlltuqXOfyZMnc6Ib9LRp08jJyflZmUWLFlX3F6/L8uXL2bNnT/XrBx98kLVr1zYi+vZNk7pSqtXNnTuXJUuWnLJuyZIldU6qVdOqVavo1KlTk967ZlJ/+OGHmTJlSpOO1R5pUldKtbrLL7+clStXVj8QIyUlhcOHDzNp0iRuueUW4uPjGT58OA899FCt+/ft25fjx48D8OijjzJo0CDOOuus6ul5weqDPm7cOEaNGsVll11GUVER3377LStWrOCee+5h9OjRJCUlsWDBApYtWwbAZ599xpgxY4iNjeW6666jtLS0+v0eeugh4uLiiI2NZd++fT+Lqb1M0av91JXq6FbfC0d2uvaYUbFw0WN1bo6IiGD8+PGsXr2amTNnsmTJEubMmYOI8OijjxIREUFlZSXnn38+O3bsYOTIkbUeZ/PmzSxZsoRt27ZRUVFBXFwcY8eOBeDSSy/lhhtuAOD+++/npZde4o477mDGjBlMnz6dyy+//JRjlZSUsGDBAj777DMGDRrE1VdfzfPPP89dd90FQJcuXdiyZQvPPfccixcv5t///vcp+7eXKXq1pq6UahOOTTCOTS9Lly4lLi6OMWPGsHv37lOaSmr66quvmDVrFkFBQYSFhTFjxozqbbt27WLSpEnExsby5ptvsnv37nrjSUhIoF+/fgwaNAiAa665hvXr11dvv/TSSwEYO3Zs9SRgjsrLy7nhhhuIjY1l9uzZ1XE7O0Xvie3NpTV1pTq6emrULWnmzJncfffdbNmyhaKiIsaOHcuBAwdYvHgxGzdupHPnzixYsICSkpImHX/BggUsX76cUaNG8corr7Bu3bpmxXti+t66pu5tL1P0ak1dKdUmQkJCOPfcc7nuuuuqa+l5eXkEBwcTHh7O0aNHWb16db3HOPvss1m+fDnFxcXk5+fz4YcfVm/Lz8+nR48elJeX8+abb1avDw0NJT8//2fHGjx4MCkpKSQmJgLWbIvnnHOO0+fTXqbo1aSulGozc+fOZfv27dVJfdSoUYwZM4YhQ4Ywb948zjzzzHr3j4uL44orrmDUqFFcdNFFjBs3rnrbI488woQJEzjzzDMZMmRI9forr7ySJ598kjFjxpxyczIgIICXX36Z2bNnExsbi5eXFzfffLPT59JepujVqXeV6oB06l33oFPvKqVUB6dJXSmlPIgmdaU6qLZqelXOaer10aSuVAcUEBBAZmamJvZ2yhhDZmZmk7pFaj91pTqgmJgYUlNTOXbsWFuHouoQEBBATExMo/fTpK5UB+Tr60u/fv3aOgzVArT5RSmlPIhTSV1EpopIgogkisi9tWzvLSJfiMhWEdkhItNcH6pSSqmGNJjURcQbeBa4CBgGzBWRYTWK3Q8sNcaMAa4EnnN1oEoppRrmTE19PJBojEk2xpQBS4CZNcoYIMxeDgcOuy5EpZRSznImqUcDhxxep9rrHC0CrhKRVGAVcEdtBxKRG0Vkk4hs0rvuSinleq66UToXeMUYEwNMA14XkZ8d2xjzojEm3hgT37VrVxe9tVJKqROcSeppQC+H1zH2OkfXA0sBjDHfAQFAF1cEqJRSynnOJPWNwEAR6Sciflg3QlfUKPMTcD6AiAzFSuravqKUUq2swaRujKkAbgfWAHuxernsFpGHReTEs6N+B9wgItuBt4EFRscfK6VUq3NqRKkxZhXWDVDHdQ86LO8B6p/NXimlVIvTEaVKKeVBNKkrpZQH0aSulFIeRJO6Ukp5EE3qSinlQTSpK6WUB9GkrpRSHkSTulJKeRBN6kop5UE0qSullAfRpK6UUh5Ek7pSSnkQTepKKeVBNKkrpZQH0aSulFIeRJO6Ukp5EE3qSinlQTSpK6WUB9GkrpRSHkSTulJKeRBN6kop5UE0qSullAfRpK6UUh5Ek7pSSnkQn7YOQCml2iNjDLnF5YT4++Dj3bz6b2WV4cDxQnYfzmVXWi7TYnswpndnF0V6Kk3qSqkOzRjDkbwS9h8tIDGjgP0ZBSRm5LM/o4CconJ8vYXeEUH07xpC/67BDOhi/e7XJZiIYD9E5JTjlVdWkZhRwK60XHYfzmNXWi570vMoKqsEwM/Hi9O6hWhSV0qp5iopr2TboRy2H8pxSOAFFJRWVJfpHOTLwG6hTIvtQb/IYLKKykg+VkDysUK+TDhGWWVVddnwQF/6dwni7JDDxJSnkJhrSMiBnIoACgigyjeYXlHduWJsT4bHRDAiOowBXUPwbWbNvz6a1JVSHqusoortqTl8l5TJd0mZbPkpm9IKKyl3DfVnYLcQLouL5rTuoQzsFsLAbiFEhvjXebzKKkNadjFJxwvI+OlHIpLeZ9jxNUQfO3SykLf9c0KG/bMrGPxDwD8UJv8RYi9viVPWpK6Uaj05RWVs/SmH7ak5lFVU4eMleHt54eMteHuJ/VpOWe/v40VogA9hAb6EBvgSFuhDaIAvwX7etTZ97EjN5ftkK4lvOphFSbmVxIf2COO20b5c6L2RPj7ZBETHQo+R0HUg+NSdyB15l2TTO/l9eu9YCoe+t1b2OQtGLoS+k6C8GErzoawASvOgtMDhdf7Jn6AIl36ujjSpK+VJjIEaia5VFGXBvpWQuR+Cu0JIFFXBXTlYGsrmbD++T6tky6Ecko8VAlaIPl5CeaWxXlNFBPlESTbdJevkb7IpAb43A9hcNYj9Jhpjd9rzEk4meX9fAny9SDiST6Hddj24eyhXxvdiSuRxxhZ9Q2DSati104rXJwA2l1jLXr7QdYiV4KNiIcr+HRBmbS8vhh8/hh1LYf+nUFVulT//Iau23al3q33MzhBjTJu8cXx8vNm0aVObvLdSbq+iFDIT4dg+OJZw8icrCboNhSG/hKHTreTTUkm+4Bjs+xD2rMAcWI+YSqrEBy9T8bOiZfiQ7x1BRVBXfDv1JCwyCp+yfMhPx+QdhvwjSFX5KfsYhKrgblBZjndJFgDlvqEcDx9JWmgsyQEj2O87iOPl/uSXlFNQWsHAbqGc3q8zZwUkEZ6yBvZ9BNkpgECvCdZnMuRi6NQHsg7Ake1wZCek74AjO6Dw2MkAOveFyNPg0Aar1h3aw0risXOspN8W/3kCIrLZGBNf53ZN6kq1AWMg+QsozLSSgwggIF61L5fmw/ETyXuflZBMpX0wgYh+0GWwlYjSNkHqRmtTxAA7kf0SoseCl/M36MorqziaV8LhnBKO5ZdyvKCUkqxUehxey6CszxlYshNvqkgxPVhZOY5VlRPYbfoSJsWM71LGhG4VxIaXMjCokIiqbKQwAwqOQv5RK3kGhFmJMqxn7b9DuoO3j/VZZSVbyfXQD9bvjD2AsT6jbsOh13irpn14K+xbBYUZVg28/zkwZDoMngah3Ru+JgVHTyb4Izvg2I8QHQcj51jNK17e9R+jFWhSV6q9SdsMq++F1A2N28/LByL6Q9fBVg286xBrOfI08A08tWxeOiSshL0fQcpXUFVhJcrB06wk33cSuaWQllPM4ZxiDucW28slHM4pJj27kLz8PPxNKZ2kgHO8dnCR9w+Mlf14ieGgVy82B59NQuT5lEcMoUuYP12C/YnuHMjImHBCA3xd93nVpiQXUjedTPSpm6AsH/xC4LQpMPSXMPACCAhv2TjagCZ1pdqLvHT47GHY/hYEd4PzH4DeZ1g1RAyYqp8vmyrrtW8QdO4HPn6Nf9/ibCr2fUzB9uUE/7QO36oS8ghmW2V//KSCQEoJopRAKSVEygiUUvxN6c8OU9F1OF7DL8Fr+EzrP5P2pKrS+vYSHgO+AW0dTYtqKKk7daNURKYC/4vVUeffxpjHaikzB1gEGGC7MWZekyJWytOUl8B3/4CvnrJusp15F0z63ckbcS0gq7CMLQez2fxTNpsPZrMjtRMl5Vfjz5VcErqPSwO3MsQcwts/GN+A7vgFhuAfGIL4BYNfkPWfiG8Q+AVbP70m4BM5oMXibTYvb+hyWltH0S40mNRFxBt4FrgASAU2isgKY8wehzIDgT8CZxpjskWkW0sFrJTbMAb2fACfPgA5P1ltu794xGpCaUBucTn70vPYm57HnvQ80nNLEBG8BLzs36e+FkSst9ybnkfycauXiY+XMLxnGHPH9ya+TwRxfTrRI3xWS5+5akPO1NTHA4nGmGQAEVkCzAT2OJS5AXjWGJMNYIzJcHWgSjmlKAv2rrDaVnuMthJoI24Oukz6Dvj4j3Dwa+tG3tUrrJt2NVRVGVKzi9ljJ++99k9qdnF1mYhgP3pFBCFYQ9qrDFTZv63XJ9dhoH/XYC6Pj2Fs786MjOlEoF/b39xTrceZpB4NOAyXIhWYUKPMIAAR+QariWaRMebjmgcSkRuBGwF6925ffTuVmzu8FTb8G3Ytg4qSk+v9QqzuZz1GnfzpMtjqVeGMqkrrplxxNlSWWTccqyqs9dXLNdb9uAa2vAaBneHipyDuGvD2oaKyiuTjhexMzWWXPbHT3vT86iHqXgL9ugQzulcn5k3ozdAeYQzrEUa3UP+fDbJRqi6uGnzkAwwEJgMxwHoRiTXG5DgWMsa8CLwI1o1SF7236qjKS2D3+7DxX1aPEt8gGHUlxF9ndXVL337yZ8trUF5k7ecTAN2HWwm+U28oybOSdkmO9bs4G4pzrJ/S3MbH5eVD5YSb2T/kVnZkCrs+2lc9qdOJ0Y2Bvt4M6xnGpXHRDO0RxtAeYQzuHqq1atVsziT1NKCXw+sYe52jVOAHY0w5cEBEfsRK8htdEqVSjrJTYNPLsPV1KMqEyIEw9XEYPffULmxRsTDmKmu5qtIarOOY6He+ayVtLx8I6GTVrAM7Wf2juw6pXpdLMEn5PuRVeFNWJZQZb0qrvCitEkorhTLjRUmFUFLlRUklHCgJ4dtvfCn70hq9GOznzfCe4cwb34cR0WHERofTv2sI3l5a+1au50xS3wgMFJF+WMn8SqBmz5blwFzgZRHpgtUck+zCOFVHV1UFSZ/Bxn9bzRsiVp/r8TdAv3MaHt3n5W337x5sDSQB665iWaHVu8Nh/+MFpXyfnMm3SZl8tyeTA/ZNR0ciEODjjb+vF37eXvj7euHv442/jxfhgb5cc0YYI6LDGREdTr/IYLw0gatW0mBSN8ZUiMjtwBqs9vL/GGN2i8jDwCZjzAp72y9EZA9QCdxjjMlsycBVB1BZAT99a/Ug2fuhNdovuBucvRDGLrD6JDeHCPiHkFtczg92Ev8+OZN9R/IBCPH3YUK/COZP6M3p/SPp2SkQPx8v/H288PESbedW7ZIOPlLtS2U5HPgS9qyw5u0oygSfQGt04PBZVrfApgzAqeFwTjHvb01jze4j7ErLpcpAgK8X4/pGcMaASCYO6MKInmHNfuKNUq7mksFHSrWoilJI+sLqirhvpXXD0i8EBl0Iw2Zaw779gpv9NsVllXy8O513N6fxTdJxjIGxfTpzx3kDmTggktG9O+HvozcqlXvTpK5cr7zY6mJYWgDlhVBWZPU8KS+ylx3WleRCytfWLHj+4TD4IiuRDzjPJcO9jTFsTMlm2eZDrNp5hILSCnpFBPKb8wdyWVwMvSKCXHDCSrUfmtSVa+1fCyt/CzkH6y7jE3hyKLpfMAybAUNnWoNznHxYQUMOZRXx3pY03t2Syk9ZRQT7eTMttgeXj41hXN8IvXGpPJYmdeUa+Ufg43utfuNdBsGc1yAs2k7cDnOJ+Aa5bIRnZZXhaF4Jh7KKOJRdbP8uIvlYIdsO5SACEwdEcteUgUwdEUWQn/65K8+nf+WqeaoqYdN/rNkHK0rh3PvhzDtdVuM+MYx+35E8ko4Vcii7yEreWUWk5RRXPzkHrM4sUWEB9OocxMJfDGJWXAzRnQLrObpSnkeTumq69B3w0V3WaM7+k60h8U2cyc8Yw/GCMhKO5JNwNJ+EI3kkHC1g/9F8isoqq8t1DvKlV0QQw3uGM3VED3pFBNKrcxC9IoLo2SlAb3SqDk+Tumq80gJY9z/w/fPWA3Qv/RfEzm7U472MMew+nMeH2w+zIzWXhKP5ZBWWVW+PDPZjcFQoc+J7MSQqlEFR1tPeW/zhC0q5OU3qqnESVsPKhZCXag0AmrLIGl7vpIy8EpZvS+PdzWkkHM3H11sY1jOcC4Z2Z1BUKEOiQhkcFUqXENc03yjV0WhSV86pLIf3boTd70G3YXD5Guh9ulO7lpRXsmb3Ed7bksZX+49RZWB0r048cskIfjmyB52Cmj+YSCll0aSunLN2kZXQJ98Hk34L3vU3g5zoH/7u5lRW7Uwnv7SCnuEB3DJ5AJfGxTCga0jrxK1UB6NJXTVs13vW49jG3wST/1BnMWMM+47ks3JHOh9sT+NQVjFBft5cNKIHl42N5vR+kdo/XKkWpkld1S9jH3xwO8SMh1/8pdYiPx7N56Md6azccZikY4V4CUwc0IW7pwzS/uFKtTL916bqVpIH71xlDR6a8+opE2klZpxI5OnszyjAS+D0/pFcd1Y/LhwepTc6lWojmtRV7YyBD26FrGS4+gNMaA+SMvJZtfMIK3ekk3A0HxEY3zeCR2YOZ+qIHnQN1USuVFvTpK5q9+3/wd4P2TZ0Ia/8EMJ3b3/G0bxSRGBcnwj+PGM4F42IoltY8yfdUkq5jiZ1Ve1oXgnfJWVydPun/DrlIT6uHM9tW8fQJeQ4Zwzowhn9IzlvSDeiwjWRK9VeaVLv4BKO5PPG9wf5Nuk4SccKiSKTVQEPkOEbQ855z/DJ4N4M7BaiT/lRyk1oUu+gSsor+b/P9/PCl8n4+Xgxvl8Ec8dGMW/PYgJzqpAbljG/6+C2DlMp1Uia1Dug75Iyue/9nRw4XshlcTHcf/FQOgf7wap7IGMLzH7FekCzUsrtaFLvQHKLyvnrqr28s+kQvSOCeP368Uwa2NXauP0d2PAinHG79SxQpZRb0qTeARhjWLkznUUr9pBdVMZNZ/fnrimDCPSzp6k9sgs+/A30OdOaoEsp5bY0qXu4wznFPPjBLtbuzWBEdBivXDuOEdHhJwsU51gDjALC4fKXG5zTRSnVvmlS91CVVYY3vj/IEx/vo8rAn6YN5doz++Lj7fAouaoqa+bF3EOwYCWEdm+7gJVSLqFJ3cMYY/gmMZMnP0lg+6EcJg3swl9nxdIrIujnhdf9FfavgWmLnZ5GVynVvmlS9xDGGL5NyuSZtT+yMSWbHuEBPH3FKC4ZHV17H/M9K2D9kzDmKhj369YPWCnVIjSpe4Bvk47zzNr9bDiQRVRYAI/MHM6ccb3qfl7n0T3w/s0QHQ/T/taox9Appdo3Tepu7PvkTJ7+9Ed+OJBF9zB//jxjOFeM60WAbz0PXy7OhiXzwD8ErngdfHXIv1KeRJO6G9pwIIunP/2R75Iz6Rbqz0O/HMbc8b3rT+YAVZWw7HrITbVujIb1bJ2AlVKtRpO6G9lzOI9HV+3hm8RMuob68+D0Ycyb4EQyP+HzRyDpM5j+DPSe0KKxKqXahiZ1N1BRWcXz65L438/2Ex7oy/0XD2X+hD4nBw85Y9d78PXTMPZaiL+25YJVSrUpTertXGJGPr9bup3tqbnMGNWTP88Ybs3T0hhHdsIHt0Gv0+GiJ1omUKVUu6BJvZ2qrDL85+sDPPlJAsF+3jw7L46LR/Zo/IGKsmDJfGvE6JzXTnkknVLK82hSb4cOZhay8L/b2ZiSzZSh3fmfS2Ob9qi4ygpYdi3kp8O1q3XEqFIdgCb1dsQYwxs//MRfV+7Fx1v42+xRXBpXx+AhZ6x9CJLXwcxnISbepbEqpdonTertxOGcYv7w7g6+2n+cSQO78PhlI+nZKbDpB9yxFL77B4y/0Ro1qpTqEJxK6iIyFfhfwBv4tzHmsTrKXQYsA8YZYza5LEoPZozhvS1pLFqxm0pjeHTWCOaN79202nn+Udj9npXQD2+xptK98K+uD1op1W41mNRFxBt4FrgASAU2isgKY8yeGuVCgd8AP7REoJ6ouKySBz7YxbLNqYzvG8Hi2aPoHVnLxFv1KcmFvR/Bzv/CgS/BVEHUSLjgERh7jU6lq1QH40xNfTyQaIxJBhCRJcBMYE+Nco8AjwP3uDRCD5VyvJCb39jMviP53HneafxmyiC8vZysnVeUwv5PrESe8DFUlkLnvjDpdxA7Wx9Fp1QH5kxSjwYOObxOBU4ZjigicUAvY8xKEakzqYvIjcCNAL179258tB5ize4jLFy6HW9v4eVrx3Hu4G7O7ZibBuv+x5phsTQXgrpYtfHYOdaNUJ2YS6kOr9k3SkXEC3gKWNBQWWPMi8CLAPHx8aa57+1uKiqreHJNAi+sT2ZkTDjPzY8jprOTzS2V5dYTijL2wrCZVo28/2Tw1nvdSqmTnMkIaUAvh9cx9roTQoERwDr75l4UsEJEZujN0pMy8kq4/e2tbDiQxVWn9+aB6cPqnhq3NusXWzc/Z78Kwy9psTiVUu7NmaS+ERgoIv2wkvmVwLwTG40xuUCXE69FZB2wUBP6Sd8nZ3LH21spKKng6StGMWtMTOMOkLrZeqDFyCs0oSul6tVgUjfGVIjI7cAarC6N/zHG7BaRh4FNxpgVLR2kuzLG8OL6ZJ5Yk0CfiCDeuH4Cg6NCG3eQsiJ4/0YIjdJ5W5RSDXKqQdYYswpYVWPdg3WUndz8sNxfXkk5C5du55M9R5kWG8Xjl40kNKAJ3QvXPgSZiXD1BxDYyeVxKqU8i95lawFFZRVc+/JGth/K4YHpw7juzL5NG0yU+BlseBEm3GLdFFVKqQZoUnexsooqbn5jC1t/yua5+XFMHdGEmRXBml3xg9ugy2CY8pBrg1RKeSxN6i5UWWW4+51trP/xGE9cNrLpCR1g1UIoPAZzl4BvM+aAUUp1KF5tHYCnMMZw//KdrNyZzv0XD2XOuF4N71SXnctg17twzr3Qc7TLYlRKeT5N6i7y2Mf7eHvDIW4/9zR+Pal/0w+UdxhW/hZixsFZd7suQKVUh6BJ3QWeX5fEC18m86vT+/C7Xwxq+oGMsdrRK8th1gs6WlQp1WiaNZrprR9+4vGP9zFztPX80CY/0AJg478h6XO4+G8QOcB1QSqlOgytqTfDh9sP86flOzlvSDcWzx6Fl7OzLNbm+H745AE4bQrEX++6IJVSHYom9SZal5DBb5duY1yfCJ6dF4evdzM+yspyeO9G8A2AGf/Q2RaVUk2mzS9NsCkli5vf2MzAbqH8e0E8gX6NmJirNl/9zZqs6/KXIawZ3SCVUh2e1tQbac/hPK59ZSM9wwN57frxhDVl6L+jA1/Bl09YU+mOuNQ1QSqlOixN6o2QkV/CNS9vIMTfh9d/PYEuIf7NO2BuGvx3gXVT9OKnXBKjUqpj0+YXJ50YLZpfUs4Ht51FdKdmjvKsKIX/XgMVJXDFGxAQ5ppAlVIdmiZ1Jz2/LpFvEjN5/LLYxk+fW5s190HqRpj9ij5TVCnlMtr84oQNB7J46tMfmTm6J3PimzH8/4Rtb1t90ifeAcNnNf94Sill06TegKzCMu58eyu9I4J4dFZs8wYXAaTvgI/ugr6T4PxFrghRKaWqafNLPYwxLPzvdrIKy3jv1omE+Dfz4yrKsh4eHRhhdV/UaQCUUi6mWaUeL319gM/3ZfDnGcMZER3evINVVVkDjPIOw7WrIaSra4JUSikHmtTrsO1QDo+t3seFw7tz9Rl9mn/ALx+HxE+teV16jWv+8ZRSqhbapl6L3OJybn9rC93DAnjislHNb0f/cQ18+RiMmqfzuiilWpTW1GswxnDvuzs4klvC0pvPIDyomSNGs5LhvRsgKhamP6XzuiilWpTW1Gt444efWL3rCPdcOJi43p2bd7CyInjnakCsAUb6WDqlVAvTmrqD3YdzeeSjPUwe3JUbmvP0IrAeePHRXXB0F8xfBp37uiJEpZSql9bUbQWlFdzx1lY6B/nyt+bOjW4MfPFX2PEOnHsfDJziukCVUqoeWlPHfmj0+ztJySzkrRtOJ7I5E3WVl8CKO2DnUuvG6KSFrgtUKaUaoEkdWLH9MMu3HebuKYM4vX9k0w9UeByWzIdD38N591sJXW+MKqVaUYdP6kVlFfx11V5GxYRz+3mnNf1AxxLgzdlQcNQaLapzoyul2kCHT+ovrk/maF4pz86Lw7up7ehJX8DSa8DHDxashJh41waplFJO6tA3So/mlfDCl8lMi40ivm9E0w6y6WV44zIIj4YbPteErpRqUx26pr54TQKVVYY/TB3S+J2rKuHTB+G7f8BpU6wmF33QhVKqjXXYpL4rLZdlW1K5YVJ/+kQGN27n0gJrlGjCKhh/I1z4PzrjolKqXeiQmcgYw6Mr99Ip0Jfbzm3kzdHcNHj7Cji6Gy56Aibc1DJBKqVUE3TIpP7Z3gy+S87k4ZnDCQ9sxNwuxxLgtUugNA/mvgODftFiMSqlVFN0uKReXlnFX1ftZUDXYOaO7+38junb4fVZIN5w3cfWBF1KKdXOONX7RUSmikiCiCSKyL21bP+tiOwRkR0i8pmIuGAC8pbx5vcHST5eyH3ThuLr7WTnn0Mb4JVfgk+gJnSlVLvWYFYTEW/gWeAiYBgwV0SG1Si2FYg3xowElgFPuDpQV8gtKueZz/Zz5mmRnDekm3M7JX9pNbkER1oJPXJAi8aolFLN4UxVdTyQaIxJNsaUAUuAmY4FjDFfGGOK7JffAzGuDdM1/u/z/eQWl/OnacOce/BFwsfWKNHOfaxH0HXq1fJBKqVUMziT1KOBQw6vU+11dbkeWF3bBhG5UUQ2icimY8eOOR+lCxzMLOTV71KYM7YXw3o60Z9817vwznzoPswaJRoa1fJBKqVUM7l0RKmIXAXEA0/Wtt0Y86IxJt4YE9+1a+s+ePmx1fvw9fbid78Y1HDhLa/DsushZjxcvQKCmjjaVCmlWpkzST0NcGx3iLHXnUJEpgB/AmYYY0pdE55rbDiQxepdR7j5nAF0Cwuov/D3/4QVt8OA8+Cqd3WUqFLKrTiT1DcCA0Wkn4j4AVcCKxwLiMgY4AWshJ7h+jCbrqrK8JeVe4gKC6j/aUbGwPon4eM/wJDpMPdt8AtqvUCVUsoFGkzqxpgK4HZgDbAXWGqM2S0iD4vIDLvYk0AI8F8R2SYiK+o4XKtbsf0wO1Jz+f3UwQT6eddeyBhYuwg+/wuMvBJmvwo+zXhQhlJKtRGnBh8ZY1YBq2qse9BhuV0+r624rJLHP95HbHQ4l4yu597u53+Bb56B+Otg2t/Aq0NPXqmUcmMenb1e+jqZ9NwS7r94aN3PHN32Fny1GOKuhouf0oSulHJrHpvBsgvLeH5dEhcO786Euh5Rl/INrLgT+p1jJXR99JxSys15bFJ/+dsUCssq+d0vBtdeIDPJ6ofeuS/MeRW8GzGxl1JKtVMemdTzSsp55ZsDTB0exaDuoT8vUJwNb10BCMxfCoGdWz1GpZRqCR45S+Pr3x0kr6Si9gdJV5bD0qshOwWu/gAi6unmqJRSbsbjknpRWQUvfX2Acwd3ZUR0+KkbjYFVC+HAerjkn9D3zLYJUimlWojHNb+89cNPZBWWcft5A3++8btnYfMrMOl3MHpuq8emlFItzaOSekl5JS+sT2bigEjG9qnRTr5vFXxyPwybCefe3zYBKqVUC/OopP7fTYc4ll/687b09B3w7q+h5xir2UX7oiulPJTHZLeyiir++WUyY/t05gzHful56VZPl8DOOp+LUsrjeUxSX741jbScYu4477STD8AoK4Ilc6EkF+Yt0TnRlVIezyN6v1RUVvHcukRio8M5Z5A9T3tlObx3AxzeZtXQ9bmiSqkOwCNq6it3ppOSWcTtJ2rpFaWw9BrY9xFMfQwGX9TWISqlVKtw+5p6VZXhH58nMrh7KBcM7Q5lhbBkPiR/ARc9ARNuausQlVKq1bh9TX3N7iPszyjgtvNOw6ssD964DA58CTOf1YSulOpw3Lqmbozh/z5PpF+XYC4e4AevzoCju+Cyl2DEpW0dnlJKtTq3TupfJGSwJz2P/5veA+9Xp0NWMlz5Fgy6sK1DU0qpNuG2Sd0Yw98/S2RseD7TN/8JCjLgqmXQ7+y2Dk0ppdqM2yb1b5MyyU3dy0fhTyLFxdaMi73GtXVYSinVptw2qX+wZg3L/B8hyMsHfvUR9BjZ1iEppVSbc8ukvmfj59yXcQ++AcHItSuh66C2DkkppdoF9+vSmPI1/VbNo0CC8b5+jSZ0pZRy4HZJ/WBKEj9VRvL5Ga8R0E2fWqSUUo7crvllrc/ZvODTlc/OiW/rUJRSqt1xu6R+/Vn9mBMfQ2iAb1uHopRS7Y7bNb8AmtCVUqoObpnUlVJK1U6TulJKeRBN6kop5UE0qSullAfRpK6UUh5Ek7pSSnkQTepKKeVBNKkrpZQH0aSulFIexKmkLiJTRSRBRBJF5N5atvuLyDv29h9EpK/LI1VKKdWgBpO6iHgDzwIXAcOAuSIyrEax64FsY8xpwNPA464OVCmlVMOcqamPBxKNMcnGmDJgCTCzRpmZwKv28jLgfBER14WplFLKGc7M0hgNHHJ4nQpMqKuMMaZCRHKBSOC4YyERuRG40X5ZICIJTQka6FLz2B7A087J084HPO+cPO18wPPOqbbz6VPfDq069a4x5kXgxeYeR0Q2GWM8akJ1TzsnTzsf8Lxz8rTzAc87p6acjzPNL2lAL4fXMfa6WsuIiA8QDmQ2JhCllFLN50xS3wgMFJF+IuIHXAmsqFFmBXCNvXw58LkxxrguTKWUUs5osPnFbiO/HVgDeAP/McbsFpGHgU3GmBXAS8DrIpIIZGEl/pbU7CacdsjTzsnTzgc875w87XzA886p0ecjWqFWSinPoSNKlVLKg2hSV0opD+J2Sb2hKQvcjYikiMhOEdkmIpvaOp6mEJH/iEiGiOxyWBchIp+KyH77d+e2jLEx6jifRSKSZl+nbSIyrS1jbCwR6SUiX4jIHhHZLSK/sde75XWq53zc9jqJSICIbBCR7fY5/dle38+efiXRno7Fr97juFObuj1lwY/ABViDoDYCc40xe9o0sGYQkRQg3hjjtgMmRORsoAB4zRgzwl73BJBljHnM/s+3szHmD20Zp7PqOJ9FQIExZnFbxtZUItID6GGM2SIiocBm4BJgAW54neo5nzm46XWyR+EHG2MKRMQX+Br4DfBb4D1jzBIR+Sew3RjzfF3HcbeaujNTFqhWZoxZj9XryZHj1BGvYv2Dcwt1nI9bM8akG2O22Mv5wF6skeBueZ3qOR+3ZSwF9ktf+8cA52FNvwJOXCN3S+q1TVng1hcS66J9IiKb7WkUPEV3Y0y6vXwE6N6WwbjI7SKyw26ecYtmitrYs6iOAX7AA65TjfMBN75OIuItItuADOBTIAnIMcZU2EUazHnultQ90VnGmDisWTBvs7/6exR7IJr7tPPV7nlgADAaSAf+1qbRNJGIhADvAncZY/Ict7njdarlfNz6OhljKo0xo7FG7o8HhjT2GO6W1J2ZssCtGGPS7N8ZwPtYF9ITHLXbPU+0f2a0cTzNYow5av+DqwL+hRteJ7ud9l3gTWPMe/Zqt71OtZ2PJ1wnAGNMDvAFcAbQyZ5+BZzIee6W1J2ZssBtiEiwfZMHEQkGfgHsqn8vt+E4dcQ1wAdtGEuznUh8tlm42XWyb8K9BOw1xjzlsMktr1Nd5+PO10lEuopIJ3s5EKtDyF6s5H65XazBa+RWvV8A7C5Kz3ByyoJH2zaiphOR/li1c7CmbHjLHc9HRN4GJmNNE3oUeAhYDiwFegMHgTnGGLe4+VjH+UzG+kpvgBTgJoe26HZPRM4CvgJ2AlX26vuw2qHd7jrVcz5zcdPrJCIjsW6EemNVuJcaYx6288QSIALYClxljCmt8zjultSVUkrVzd2aX5RSStVDk7pSSnkQTepKKeVBNKkrpZQH0aSulFIeRJO6Ukp5EE3qSinlQf4fG4aqTJJ2HIcAAAAASUVORK5CYII=\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "def plot_keras_history(history, name='', acc='acc'):\n", " \"\"\"Plots keras history.\"\"\"\n", " import matplotlib.pyplot as plt\n", "\n", " training_acc = history.history[acc]\n", " validation_acc = history.history['val_' + acc]\n", " loss = history.history['loss']\n", " val_loss = history.history['val_loss']\n", "\n", " epochs = range(len(training_acc))\n", "\n", " plt.ylim(0, 1)\n", " plt.plot(epochs, training_acc, 'tab:blue', label='Training acc')\n", " plt.plot(epochs, validation_acc, 'tab:orange', label='Validation acc')\n", " plt.title('Training and validation accuracy ' + name)\n", " plt.legend()\n", "\n", " plt.figure()\n", "\n", " plt.plot(epochs, loss, 'tab:green', label='Training loss')\n", " plt.plot(epochs, val_loss, 'tab:red', label='Validation loss')\n", " plt.title('Training and validation loss ' + name)\n", " plt.legend()\n", " plt.show()\n", " plt.close()\n", "plot_keras_history(history)" ] }, { "cell_type": "code", "execution_count": null, "id": "4960be86", "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 }