diff --git a/.gitignore b/.gitignore
index 988c81eb83b80be97164153692a1b51b5c4c55a1..6c0f0271a15f556971f40084067cc32c2b4fc673 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@
 .data/
 *.pyc
 colab_copy.ipynb
+.model/Viennese Poets.mp3
diff --git a/.model/attention/keras_metadata.pb b/.model/attention/keras_metadata.pb
index 1306f144c426de97e9581c893da239d2dba815ef..72379680cfa923ee7fb556bd8bf5b2530579f7cc 100755
--- a/.model/attention/keras_metadata.pb
+++ b/.model/attention/keras_metadata.pb
@@ -1,15 +1,15 @@
 
-ÿroot"_tf_keras_sequential*Ú{"name": "sequential", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "must_restore_from_config": false, "class_name": "Sequential", "config": {"name": "sequential", "layers": [{"class_name": "InputLayer", "config": {"batch_input_shape": {"class_name": "__tuple__", "items": [null, 10, 7]}, "dtype": "float32", "sparse": false, "ragged": false, "name": "input_1"}}, {"class_name": "CustomAttention", "config": {"name": "custom_attention", "trainable": true, "dtype": "float32", "units": 32}}, {"class_name": "Dense", "config": {"name": "dense", "trainable": true, "dtype": "float32", "units": 4, "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}]}, "shared_object_id": 5, "input_spec": [{"class_name": "InputSpec", "config": {"dtype": null, "shape": {"class_name": "__tuple__", "items": [null, 10, 7]}, "ndim": 3, "max_ndim": null, "min_ndim": null, "axes": {}}}], "build_input_shape": {"class_name": "TensorShape", "items": [null, 10, 7]}, "is_graph_network": true, "full_save_spec": {"class_name": "__tuple__", "items": [[{"class_name": "TypeSpec", "type_spec": "tf.TensorSpec", "serialized": [{"class_name": "TensorShape", "items": [null, 10, 7]}, "float32", "input_1"]}], {}]}, "save_spec": {"class_name": "TypeSpec", "type_spec": "tf.TensorSpec", "serialized": [{"class_name": "TensorShape", "items": [null, 10, 7]}, "float32", "input_1"]}, "keras_version": "2.8.0", "backend": "tensorflow", "model_config": {"class_name": "Sequential", "config": {"name": "sequential", "layers": [{"class_name": "InputLayer", "config": {"batch_input_shape": {"class_name": "__tuple__", "items": [null, 10, 7]}, "dtype": "float32", "sparse": false, "ragged": false, "name": "input_1"}, "shared_object_id": 0}, {"class_name": "CustomAttention", "config": {"name": "custom_attention", "trainable": true, "dtype": "float32", "units": 32}, "shared_object_id": 1}, {"class_name": "Dense", "config": {"name": "dense", "trainable": true, "dtype": "float32", "units": 4, "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 2}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 3}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 4}]}}, "training_config": {"loss": {"class_name": "CategoricalCrossentropy", "config": {"reduction": "auto", "name": "categorical_crossentropy", "from_logits": true, "label_smoothing": 0.0, "axis": -1}, "shared_object_id": 7}, "metrics": [[{"class_name": "CategoricalCrossentropy", "config": {"name": "categorical_crossentropy", "dtype": "float32", "from_logits": true, "label_smoothing": 0}, "shared_object_id": 8}, {"class_name": "CategoricalAccuracy", "config": {"name": "categorical_accuracy", "dtype": "float32"}, "shared_object_id": 9}, {"class_name": "CategoricalHinge", "config": {"name": "categorical_hinge", "dtype": "float32"}, "shared_object_id": 10}]], "weighted_metrics": null, "loss_weights": null, "optimizer_config": {"class_name": "Adam", "config": {"name": "Adam", "learning_rate": 0.001, "decay": 0.0, "beta_1": 0.9, "beta_2": 0.999, "epsilon": 1e-07, "amsgrad": false}}}}2
-Éroot.layer_with_weights-0"_tf_keras_layer*’{"name": "custom_attention", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "CustomAttention", "config": {"name": "custom_attention", "trainable": true, "dtype": "float32", "units": 32}, "shared_object_id": 1, "build_input_shape": {"class_name": "TensorShape", "items": [null, 10, 7]}}2
-Âroot.layer_with_weights-1"_tf_keras_layer*‹{"name": "dense", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "Dense", "config": {"name": "dense", "trainable": true, "dtype": "float32", "units": 4, "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 2}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 3}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 4, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 2, "axes": {"-1": 32}}, "shared_object_id": 11}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 32]}}2
-ø-root.layer_with_weights-0.attention_score_vec"_tf_keras_layer*­{"name": "attention_score_vec", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "Dense", "config": {"name": "attention_score_vec", "trainable": true, "dtype": "float32", "units": 7, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 12}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 13}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 14, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 2, "axes": {"-1": 7}}, "shared_object_id": 15}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 10, 7]}}2
+³root"_tf_keras_sequential*Ž{"name": "sequential_1", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "must_restore_from_config": false, "class_name": "Sequential", "config": {"name": "sequential_1", "layers": [{"class_name": "InputLayer", "config": {"batch_input_shape": {"class_name": "__tuple__", "items": [null, 5, 7]}, "dtype": "float32", "sparse": false, "ragged": false, "name": "input_2"}}, {"class_name": "CustomAttention", "config": {"name": "custom_attention_1", "trainable": true, "dtype": "float32", "units": 32}}, {"class_name": "Dense", "config": {"name": "dense_1", "trainable": true, "dtype": "float32", "units": 4, "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}]}, "shared_object_id": 5, "input_spec": [{"class_name": "InputSpec", "config": {"dtype": null, "shape": {"class_name": "__tuple__", "items": [null, 5, 7]}, "ndim": 3, "max_ndim": null, "min_ndim": null, "axes": {}}}], "build_input_shape": {"class_name": "TensorShape", "items": [null, 5, 7]}, "is_graph_network": true, "full_save_spec": {"class_name": "__tuple__", "items": [[{"class_name": "TypeSpec", "type_spec": "tf.TensorSpec", "serialized": [{"class_name": "TensorShape", "items": [null, 5, 7]}, "float32", "input_2"]}], {}]}, "save_spec": {"class_name": "TypeSpec", "type_spec": "tf.TensorSpec", "serialized": [{"class_name": "TensorShape", "items": [null, 5, 7]}, "float32", "input_2"]}, "keras_version": "2.8.0", "backend": "tensorflow", "model_config": {"class_name": "Sequential", "config": {"name": "sequential_1", "layers": [{"class_name": "InputLayer", "config": {"batch_input_shape": {"class_name": "__tuple__", "items": [null, 5, 7]}, "dtype": "float32", "sparse": false, "ragged": false, "name": "input_2"}, "shared_object_id": 0}, {"class_name": "CustomAttention", "config": {"name": "custom_attention_1", "trainable": true, "dtype": "float32", "units": 32}, "shared_object_id": 1}, {"class_name": "Dense", "config": {"name": "dense_1", "trainable": true, "dtype": "float32", "units": 4, "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 2}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 3}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 4}]}}, "training_config": {"loss": {"class_name": "CategoricalCrossentropy", "config": {"reduction": "auto", "name": "categorical_crossentropy", "from_logits": true, "label_smoothing": 0.0, "axis": -1}, "shared_object_id": 7}, "metrics": [[{"class_name": "CategoricalCrossentropy", "config": {"name": "categorical_crossentropy", "dtype": "float32", "from_logits": true, "label_smoothing": 0}, "shared_object_id": 8}, {"class_name": "CategoricalAccuracy", "config": {"name": "categorical_accuracy", "dtype": "float32"}, "shared_object_id": 9}, {"class_name": "CategoricalHinge", "config": {"name": "categorical_hinge", "dtype": "float32"}, "shared_object_id": 10}]], "weighted_metrics": null, "loss_weights": null, "optimizer_config": {"class_name": "Adam", "config": {"name": "Adam", "learning_rate": 0.0010000000474974513, "decay": 0.0, "beta_1": 0.8999999761581421, "beta_2": 0.9990000128746033, "epsilon": 1e-07, "amsgrad": false}}}}2
+Ìroot.layer_with_weights-0"_tf_keras_layer*•{"name": "custom_attention_1", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "CustomAttention", "config": {"name": "custom_attention_1", "trainable": true, "dtype": "float32", "units": 32}, "shared_object_id": 1, "build_input_shape": {"class_name": "TensorShape", "items": [null, 5, 7]}}2
+Æroot.layer_with_weights-1"_tf_keras_layer*{"name": "dense_1", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "Dense", "config": {"name": "dense_1", "trainable": true, "dtype": "float32", "units": 4, "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 2}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 3}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 4, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 2, "axes": {"-1": 32}}, "shared_object_id": 11}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 32]}}2
+÷-root.layer_with_weights-0.attention_score_vec"_tf_keras_layer*¬{"name": "attention_score_vec", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "Dense", "config": {"name": "attention_score_vec", "trainable": true, "dtype": "float32", "units": 7, "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 12}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 13}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 14, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 2, "axes": {"-1": 7}}, "shared_object_id": 15}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 5, 7]}}2
 Ý
root.layer_with_weights-0.h_t"_tf_keras_layer*¢{"name": "last_hidden_state", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "Lambda", "config": {"name": "last_hidden_state", "trainable": true, "dtype": "float32", "function": {"class_name": "__tuple__", "items": ["4wEAAAAAAAAAAAAAAAEAAAAFAAAAUwAAAHMWAAAAfABkAGQAhQJkAWQAZACFAmYDGQBTACkCTun/\n////qQApAdoBeHICAAAAcgIAAAD6TS9Vc2Vycy9ub3dhZG1pbi9Eb2N1bWVudHMvU2Nob29sIEZv\nbGRlci9DUyA0MzcvTGFiL0ZpbmFsIFByb2plY3QvYXR0ZW50aW9uLnB52gg8bGFtYmRhPhoAAABz\nAgAAABYA\n", null, null]}, "function_type": "lambda", "module": "attention", "output_shape": {"class_name": "__tuple__", "items": [7]}, "output_shape_type": "raw", "output_shape_module": null, "arguments": {}}, "shared_object_id": 16}2
-™)root.layer_with_weights-0.attention_score"_tf_keras_layer*Ò{"name": "attention_score", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "Dot", "config": {"name": "attention_score", "trainable": true, "dtype": "float32", "axes": [1, 2], "normalize": false}, "shared_object_id": 17, "build_input_shape": [{"class_name": "TensorShape", "items": [null, 7]}, {"class_name": "TensorShape", "items": [null, 10, 7]}]}2
+˜)root.layer_with_weights-0.attention_score"_tf_keras_layer*Ñ{"name": "attention_score", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "Dot", "config": {"name": "attention_score", "trainable": true, "dtype": "float32", "axes": [1, 2], "normalize": false}, "shared_object_id": 17, "build_input_shape": [{"class_name": "TensorShape", "items": [null, 7]}, {"class_name": "TensorShape", "items": [null, 5, 7]}]}2
 —*root.layer_with_weights-0.attention_weight"_tf_keras_layer*Ï{"name": "attention_weight", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "Activation", "config": {"name": "attention_weight", "trainable": true, "dtype": "float32", "activation": "softmax"}, "shared_object_id": 18}2
-—(root.layer_with_weights-0.context_vector"_tf_keras_layer*Ñ{"name": "context_vector", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "Dot", "config": {"name": "context_vector", "trainable": true, "dtype": "float32", "axes": [1, 1], "normalize": false}, "shared_object_id": 19, "build_input_shape": [{"class_name": "TensorShape", "items": [null, 10, 7]}, {"class_name": "TensorShape", "items": [null, 10]}]}2
+•(root.layer_with_weights-0.context_vector"_tf_keras_layer*Ï{"name": "context_vector", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "Dot", "config": {"name": "context_vector", "trainable": true, "dtype": "float32", "axes": [1, 1], "normalize": false}, "shared_object_id": 19, "build_input_shape": [{"class_name": "TensorShape", "items": [null, 5, 7]}, {"class_name": "TensorShape", "items": [null, 5]}]}2
 ˆ*root.layer_with_weights-0.attention_output"_tf_keras_layer*À{"name": "attention_output", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "Concatenate", "config": {"name": "attention_output", "trainable": true, "dtype": "float32", "axis": -1}, "shared_object_id": 20, "build_input_shape": [{"class_name": "TensorShape", "items": [null, 7]}, {"class_name": "TensorShape", "items": [null, 7]}]}2
 ì*root.layer_with_weights-0.attention_vector"_tf_keras_layer*¤{"name": "attention_vector", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "Dense", "config": {"name": "attention_vector", "trainable": true, "dtype": "float32", "units": 32, "activation": "tanh", "use_bias": false, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 21}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 22}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 23, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 2, "axes": {"-1": 14}}, "shared_object_id": 24}, "build_input_shape": {"class_name": "TensorShape", "items": [null, 14]}}2
-¹_root.keras_api.metrics.0"_tf_keras_metric*‚{"class_name": "Mean", "name": "loss", "dtype": "float32", "config": {"name": "loss", "dtype": "float32"}, "shared_object_id": 25}2
-ž`root.keras_api.metrics.1"_tf_keras_metric*ç{"class_name": "CategoricalCrossentropy", "name": "categorical_crossentropy", "dtype": "float32", "config": {"name": "categorical_crossentropy", "dtype": "float32", "from_logits": true, "label_smoothing": 0}, "shared_object_id": 8}2
-çaroot.keras_api.metrics.2"_tf_keras_metric*°{"class_name": "CategoricalAccuracy", "name": "categorical_accuracy", "dtype": "float32", "config": {"name": "categorical_accuracy", "dtype": "float32"}, "shared_object_id": 9}2
-ßbroot.keras_api.metrics.3"_tf_keras_metric*¨{"class_name": "CategoricalHinge", "name": "categorical_hinge", "dtype": "float32", "config": {"name": "categorical_hinge", "dtype": "float32"}, "shared_object_id": 10}2
\ No newline at end of file
+¹droot.keras_api.metrics.0"_tf_keras_metric*‚{"class_name": "Mean", "name": "loss", "dtype": "float32", "config": {"name": "loss", "dtype": "float32"}, "shared_object_id": 25}2
+žeroot.keras_api.metrics.1"_tf_keras_metric*ç{"class_name": "CategoricalCrossentropy", "name": "categorical_crossentropy", "dtype": "float32", "config": {"name": "categorical_crossentropy", "dtype": "float32", "from_logits": true, "label_smoothing": 0}, "shared_object_id": 8}2
+çfroot.keras_api.metrics.2"_tf_keras_metric*°{"class_name": "CategoricalAccuracy", "name": "categorical_accuracy", "dtype": "float32", "config": {"name": "categorical_accuracy", "dtype": "float32"}, "shared_object_id": 9}2
+ßgroot.keras_api.metrics.3"_tf_keras_metric*¨{"class_name": "CategoricalHinge", "name": "categorical_hinge", "dtype": "float32", "config": {"name": "categorical_hinge", "dtype": "float32"}, "shared_object_id": 10}2
\ No newline at end of file
diff --git a/.model/attention/saved_model.pb b/.model/attention/saved_model.pb
index f6723fe642fc128973775f2a31a00316c259dabe..15845c1e0175ab2d73e2a3128b84d08f98fab6cd 100644
Binary files a/.model/attention/saved_model.pb and b/.model/attention/saved_model.pb differ
diff --git a/.model/attention/variables/variables.data-00000-of-00001 b/.model/attention/variables/variables.data-00000-of-00001
index 34dad5eb6ebaa13a8fbc124005614d9298a1ccf3..6143d14fc3e2a8e2b048ee2f5eae0406d7d8b1ad 100644
Binary files a/.model/attention/variables/variables.data-00000-of-00001 and b/.model/attention/variables/variables.data-00000-of-00001 differ
diff --git a/.model/attention/variables/variables.index b/.model/attention/variables/variables.index
index 656368049b507b76cdb628c7e9ca686b324b69f9..eb48bac38ec4829ec957a0bec7f83a8b4e3a52e8 100755
Binary files a/.model/attention/variables/variables.index and b/.model/attention/variables/variables.index differ
diff --git a/.model/attention_model.tflite b/.model/attention_model.tflite
new file mode 100644
index 0000000000000000000000000000000000000000..760ce93b62c639c2122cad3e6a0dc41046b07c18
Binary files /dev/null and b/.model/attention_model.tflite differ
diff --git a/alarm.py b/alarm.py
index deb6a2220dd62504adf0440fe8ddae1816d8c092..7e90526727114a621f79c0af3090d3293f838c4e 100644
--- a/alarm.py
+++ b/alarm.py
@@ -1,5 +1,6 @@
 from enum import Enum, auto
 from datetime import time, datetime
+from typing import Optional
 
 
 class AlarmState(Enum):
@@ -12,10 +13,10 @@ class AlarmState(Enum):
 
 class BaseAlarmClock:
     _current_state: AlarmState
-    _wake_time: time
+    _wake_time: datetime
 
-    def __init__(self, wake_time: time = datetime.now().time()):
-        self._wake_time = wake_time
+    def __init__(self, wake_time: Optional[datetime] = None):
+        self._wake_time = wake_time if wake_time else datetime.utcnow()
         self._current_state = AlarmState.DEACTIVATED
 
     @property
@@ -26,7 +27,7 @@ class BaseAlarmClock:
     def wake_time(self):
         return self._wake_time
 
-    def set_alarm(self, wake_time: time) -> time:
+    def set_alarm(self, wake_time: datetime) -> datetime:
         # print(f"SETTING ALARM to: {wake_time}")
         self._current_state = AlarmState.SET
         self._wake_time = wake_time
@@ -50,5 +51,5 @@ class BaseAlarmClock:
         #     print("STOPPED PLAYING ALARM")
         self._current_state = AlarmState.DEACTIVATED if deactivate else AlarmState.SET
 
-    def alarm_check_reached(self, current_time: time) -> bool:
+    def alarm_check_reached(self, current_time: datetime) -> bool:
         return current_time >= self._wake_time if self._current_state is AlarmState.RUNNING else False
diff --git a/simple_alarm_clock.py b/simple_alarm_clock.py
index 0f9f9978ee33e558a86cca664bb32f71375f625f..413f51fb6cab9768aab5e479530258fd1f81c61b 100644
--- a/simple_alarm_clock.py
+++ b/simple_alarm_clock.py
@@ -1,7 +1,12 @@
+import subprocess
+from typing import Optional
+
 from alarm import BaseAlarmClock, AlarmState
-from datetime import time, datetime
+from datetime import time, datetime, timezone, timedelta
 from time import sleep
-from pynput import keyboard
+# from pynput import keyboard
+import keyboard as kb
+# SONG = ".model/Viennese_Poets.mp3"
 
 # Simple Alarm Clock has 5 functionalities:
 # 1. Set Alarm Time - Set the time for the alarm to sound, does NOT make the alarm active
@@ -10,14 +15,22 @@ from pynput import keyboard
 # 4. Snooze Alarm   - Stop the alarm sound and wait for some time to play the alarm again
 # 5. Stop Alarm     - If the alarm is active or is playing, stop the alarm (deactivate state)
 
-ALARM_TIME = time(15, 5, 0)
-SNOOZE_KEY = keyboard.Key.up
-ALARM_OFF_KEY = keyboard.Key.esc
+# ALARM_TIME = datetime.combine(
+#     datetime.today(),
+#     # (datetime.today() + timedelta(days=1)).date(),
+#     time(16, 40, 0)
+# ).astimezone(tz=timezone.utc)
+# SNOOZE_KEY = keyboard.Key.up
+# ALARM_OFF_KEY = keyboard.Key.esc
 SNOOZE_SEC = 5
 
 
 class SimpleAlarmClock(BaseAlarmClock):
-    def set_alarm(self, wake_time: time) -> time:
+    def __init__(self, wake_time: Optional[datetime] = None):
+        super().__init__(wake_time)
+        # self._alarm = None
+
+    def set_alarm(self, wake_time: datetime) -> datetime:
         print(f"SETTING ALARM to: {wake_time}")
         return super(SimpleAlarmClock, self).set_alarm(wake_time)
 
@@ -27,37 +40,66 @@ class SimpleAlarmClock(BaseAlarmClock):
 
     def sound_alarm(self) -> None:
         print("SOUNDING ALARM")
+        # if not self._alarm:
+        #     self._alarm = subprocess.Popen(["omxplayer", "--no-keys", SONG, "&"])
         super(SimpleAlarmClock, self).sound_alarm()
 
     def snooze_alarm(self) -> None:
-        print("SNOOZING ALARM")
-        super(SimpleAlarmClock, self).snooze_alarm()
+        if self.current_state is AlarmState.PLAYING:
+            print("SNOOZING ALARM")
+            # print(f"{self._alarm.pid}")
+            # if self._alarm is not None:
+            #     subprocess.Popen(["sudo", "kill", f"{self._alarm.pid}"])
+            #     self._alarm = None
+            super(SimpleAlarmClock, self).snooze_alarm()
+            sleep(SNOOZE_SEC)
+            print("UN-SNOOZING ALARM")
+            self.sound_alarm()
 
     def stop_alarm(self, deactivate: bool = True) -> None:
         print("STOPPING ALARM")
-        if self._current_state is AlarmState.PLAYING:
+        # if self._alarm is not None:
+        #     subprocess.Popen(["sudo", "kill", f"{self._alarm.pid}"])
+        #     self._alarm = None
+        if self.current_state is AlarmState.PLAYING:
             print("STOPPED PLAYING ALARM")
         super(SimpleAlarmClock, self).stop_alarm()
 
-    def simple_alarm_mode(self):
+    def alarm_check_reached(self, current_time: datetime) -> bool:
+        # print(f"{self.wake_time}")
+        # print(f"{current_time}")
+        return super(SimpleAlarmClock, self).alarm_check_reached(current_time)
+
+    def run_simple_alarm_mode(self):
         self.start_alarm()
-        while not self.alarm_check_reached(datetime.now().time()):
-            print(f"ALARM SLEEPING @: {datetime.now().time()}")
+        kb.add_hotkey("esc", lambda: self.stop_alarm())
+        kb.add_hotkey("space", lambda: self.snooze_alarm())
+        while not self.alarm_check_reached(datetime.now(tz=timezone.utc)):
+            print(f"ALARM SLEEPING @: {datetime.now(tz=timezone.utc)}")
             sleep(1)
         self.sound_alarm()
         while self.current_state is not AlarmState.DEACTIVATED:
-            with keyboard.Events() as events:
-                for event in events:
-                    if event.key == ALARM_OFF_KEY:
-                        self.stop_alarm()
-                        break
-                    elif event.key == SNOOZE_KEY:
-                        self.snooze_alarm()
-                        sleep(SNOOZE_SEC)
-                        self.sound_alarm()
-                        break
+            pass
+            # with keyboard.Events() as events:
+            #     for event in events:
+            #         if event.key == ALARM_OFF_KEY:
+            #             self.stop_alarm()
+            #             break
+            #         elif event.key == SNOOZE_KEY:
+            #             self.snooze_alarm()
+            #             break
 
 
 if __name__ == '__main__':
-    alarm_clock = SimpleAlarmClock(ALARM_TIME)
-    alarm_clock.simple_alarm_mode()
+    alarm_hour = int(input("What hour do you want the alarm to go off at? "))
+    alarm_minute = int(input("What minute do you want the alarm to go off at? "))
+    run_alarm_today = map(
+        lambda response: response.lower() in ["y", "yes", "t", "true"],
+        input("Will the alarm run today? [y/N]")
+    )
+    alarm_time = datetime.combine(
+        datetime.today() if run_alarm_today else (datetime.today() + timedelta(days=1)).date(),
+        time(alarm_hour, alarm_minute, 0)
+    ).astimezone(tz=timezone.utc)
+    alarm_clock = SimpleAlarmClock(alarm_time)
+    alarm_clock.run_simple_alarm_mode()
diff --git a/smart_alarm_clock mac_copy.py b/smart_alarm_clock mac_copy.py
new file mode 100644
index 0000000000000000000000000000000000000000..0a8d06b1231a400fa222bfbb8681aa5a6f60afb3
--- /dev/null
+++ b/smart_alarm_clock mac_copy.py	
@@ -0,0 +1,178 @@
+from typing import Optional, Deque
+
+import sys
+from alarm import BaseAlarmClock, AlarmState
+from datetime import time, datetime, timedelta, timezone
+from time import sleep
+from pynput import keyboard
+from collections import deque
+import numpy as np
+import tensorflow as tf
+import vlc
+
+# Lower threshold means it only needs to be somewhat probable to be early wake up
+# >= 1 means never allow early wake up in that time
+WAKE_THRESHOLD = np.array([0.5, 0.75, 0.9, 1.0])
+
+SNOOZE_KEY = keyboard.Key.up
+ALARM_OFF_KEY = keyboard.Key.esc
+MODEL_MEMORY: int = 5
+
+SECONDS_IN_MINUTE: float = 5
+SNOOZE_SEC: float = 60
+WAIT_SEC: float = 1
+
+MODEL_PATH: str = ".model/attention"
+VLC_INSTANCE = vlc.Instance("--input-repeat=999")
+VLC_PLAYER = VLC_INSTANCE.media_player_new()
+SONG = VLC_INSTANCE.media_new(".model/Viennese Poets.mp3" )
+
+# Input Array: [minutes_since_start, current_hour_utc, current_minute_utc, awake_prob, rem_prob, light_prob, deep_prob]
+# Output Array: [awake_prob, rem_prob, light_prob, deep_prob]
+
+
+def softmax(arr):
+    val = np.exp(arr)
+    return val / sum(val)
+
+
+def model_prediction(model, current_times_prob: Deque[np.array]) -> np.array:
+    # print(f"{np.array([current_times_prob]).shape = }")
+    if model:
+        prediction = model.predict(np.array([current_times_prob]))[0]
+        # print(f"{prediction = }")
+        # print(f"{softmax(prediction) = }")
+        return softmax(prediction)
+    else:
+        return np.zeros(4)
+
+
+class SmartAlarmClock(BaseAlarmClock):
+
+    def __init__(self,
+                 wake_time: Optional[datetime] = None,
+                 earliest_wake: Optional[timedelta] = None,
+                 wake_threshold: np.array = WAKE_THRESHOLD,
+                 vlc_player=VLC_PLAYER,
+                 default_song=SONG):
+        super().__init__(wake_time)
+        self.earliest_wake = self.wake_time
+        if wake_time:
+            self.set_alarm(wake_time, earliest_wake)
+        self.wake_threshold = wake_threshold
+        self._vlc_player = vlc_player
+        self.song = default_song
+        if self._vlc_player and self.song:
+            self._vlc_player.set_media(self.song)
+
+    def set_alarm(self, wake_time: datetime, earliest_wake: Optional[timedelta] = None) -> datetime:
+        print(f"SETTING ALARM to: {wake_time}")
+        returned_value = super(SmartAlarmClock, self).set_alarm(wake_time)
+        self.earliest_wake = wake_time - earliest_wake if earliest_wake else self.wake_time
+        return returned_value
+
+    def start_alarm(self) -> None:
+        print("STARTING ALARM")
+        super(SmartAlarmClock, self).start_alarm()
+
+    def sound_alarm(self, override_song=None) -> None:
+        print("SOUNDING ALARM")
+        if override_song:
+            self.song = override_song
+        if self._vlc_player:
+            self._vlc_player.set_media(self.song)
+            self._vlc_player.play()
+        super(SmartAlarmClock, self).sound_alarm()
+
+    def snooze_alarm(self) -> None:
+        print("SNOOZING ALARM")
+        super(SmartAlarmClock, self).snooze_alarm()
+        if self._vlc_player:
+            self._vlc_player.pause()
+        sleep(SNOOZE_SEC)
+        if self._vlc_player:
+            self._vlc_player.play()
+        print("UN-SNOOZING ALARM")
+        self.sound_alarm()
+
+    def stop_alarm(self, deactivate: bool = True) -> None:
+        print("STOPPING ALARM")
+        if self._vlc_player:
+            self._vlc_player.stop()
+            print("STOPPED PLAYING ALARM")
+        super(SmartAlarmClock, self).stop_alarm()
+
+    def check_early_alarm_reached(self, current_time: datetime, prior_prediction: np.array) -> bool:
+        if current_time < self.earliest_wake:
+            return False
+        return np.max(prior_prediction - self.wake_threshold) > 0
+
+    def smart_alarm_mode(self, model, model_memory=MODEL_MEMORY):
+        self.start_alarm()
+        time_queue = deque(maxlen=model_memory)
+        # While not filled time_queue keep appending awake data (warm-up machine)
+        minutes_since_start = 0
+        while len(time_queue) < time_queue.maxlen:
+            current_utc = datetime.utcnow()
+            time_queue.append(np.array(
+                [minutes_since_start, current_utc.hour, current_utc.minute, 1.0, 0.0, 0.0, 0.0]
+            ))
+            # Wait a minute for next timestep
+            while (datetime.utcnow() - current_utc).seconds < SECONDS_IN_MINUTE:
+                sleep(WAIT_SEC)
+            minutes_since_start += 1
+            print(f"{time_queue = }")
+
+        # While the alarm clock is running
+        last_checked = datetime.utcnow()
+        last_prediction = model_prediction(model, time_queue)
+        print(f"{last_checked}: {last_prediction = }")
+        print(f"{self.check_early_alarm_reached(datetime.now(tz=timezone.utc), last_prediction) = }")
+        while self.current_state is AlarmState.RUNNING \
+                and not self.check_early_alarm_reached(datetime.now(tz=timezone.utc), last_prediction) \
+                and not self.alarm_check_reached(datetime.now(tz=timezone.utc)):
+            print(f"ALARM SLEEPING @: {datetime.now()}")
+            if (datetime.utcnow() - last_checked).seconds < SECONDS_IN_MINUTE:
+                last_checked = datetime.utcnow()
+                time_queue.append(
+                    np.concatenate(
+                        (
+                            [minutes_since_start, last_checked.hour, last_checked.minute],
+                            last_prediction
+                        )
+                    )
+                )
+                print(f"{time_queue = }")
+            last_prediction = model_prediction(model, time_queue)
+            print(f"{datetime.utcnow()}: {last_prediction = }")
+            sleep(WAIT_SEC)
+
+        # While the alarm clock is sounding off
+        self.sound_alarm()
+        while self.current_state is not AlarmState.DEACTIVATED:
+            with keyboard.Events() as events:
+                for event in events:
+                    if event.key == ALARM_OFF_KEY:
+                        self.stop_alarm()
+                        break
+                    elif event.key == SNOOZE_KEY:
+                        self.snooze_alarm()
+                        break
+
+
+if __name__ == '__main__':
+    alarm_hour = int(input("What hour do you want the alarm to go off at? "))
+    alarm_minute = int(input("What minute do you want the alarm to go off at? "))
+    early_minutes = int(input("How many minute early would you have the alarm go off at? "))
+    run_alarm_today = map(
+        lambda response: response.lower() in ["y", "yes", "t", "true"],
+        input("Will the alarm run today? [y/N]")
+    )
+    alarm_time = datetime.combine(
+        datetime.today() if run_alarm_today else (datetime.today() + timedelta(days=1)).date(),
+        time(alarm_hour, alarm_minute, 0)
+    ).astimezone(tz=timezone.utc)
+    early_time = alarm_time - timedelta(minutes=early_minutes)
+    alarm_clock_class = SmartAlarmClock(alarm_time, earliest_wake=timedelta(minutes=early_minutes))
+    ai_model = tf.keras.models.load_model(MODEL_PATH)
+    alarm_clock_class.smart_alarm_mode(ai_model)
diff --git a/smart_alarm_clock.py b/smart_alarm_clock.py
index 12c4eb584fafec5456447f6c955d76716da3dbdc..93885cc233388cecefbfb1630dba16dfd4a2d1bc 100644
--- a/smart_alarm_clock.py
+++ b/smart_alarm_clock.py
@@ -1,16 +1,38 @@
-from typing import Optional
+from typing import Optional, Deque
 
+import sys
 from alarm import BaseAlarmClock, AlarmState
-from datetime import time, datetime
+from datetime import time, datetime, timedelta, timezone
 from time import sleep
-from pynput import keyboard
+# from pynput import keyboard
+import keyboard as kb
+from collections import deque
 import numpy as np
+# import tensorflow as tf
+import tflite_runtime.interpreter as tflite
 import vlc
 
+# Lower threshold means it only needs to be somewhat probable to be early wake up
+# >= 1 means never allow early wake up in that time
+WAKE_THRESHOLD = np.array([0.5, 0.75, 0.9, 1.0])
 
-SNOOZE_KEY = keyboard.Key.up
-ALARM_OFF_KEY = keyboard.Key.esc
-SNOOZE_SEC = 5
+# SNOOZE_KEY = keyboard.Key.up
+SNOOZE_KEY = "space"
+# ALARM_OFF_KEY = keyboard.Key.esc
+ALARM_OFF_KEY = "esc"
+MODEL_MEMORY: int = 5
+
+SECONDS_IN_MINUTE: float = 60
+SNOOZE_SEC: float = 60
+WAIT_SEC: float = 1
+
+MODEL_PATH: str = ".model/attention_model.tflite"
+VLC_INSTANCE = vlc.Instance("--input-repeat=999")
+VLC_PLAYER = VLC_INSTANCE.media_player_new()
+SONG = VLC_INSTANCE.media_new(".model/Viennese Poets.mp3" )
+
+# Input Array: [minutes_since_start, current_hour_utc, current_minute_utc, awake_prob, rem_prob, light_prob, deep_prob]
+# Output Array: [awake_prob, rem_prob, light_prob, deep_prob]
 
 
 def softmax(arr):
@@ -18,59 +40,159 @@ def softmax(arr):
     return val / sum(val)
 
 
+def model_prediction(model_func, current_times_prob: Deque[np.array]) -> np.array:
+    # print(f"Prob shape: {np.array([current_times_prob]).shape}")
+    if model_func:
+        # prediction = model.predict(np.array([current_times_prob]))[0]
+        prediction = model_func(x=np.array([current_times_prob]))
+        print(prediction)
+        # print(f"prediction: {prediction}")
+        # print(f"softmax: {softmax(prediction)}")
+        return softmax(prediction)
+    else:
+        return np.zeros(4)
+
+
 class SmartAlarmClock(BaseAlarmClock):
 
-    def __init__(self, wake_time: Optional[time] = None, vlc_player=None):
+    def __init__(self,
+                 wake_time: Optional[datetime] = None,
+                 earliest_wake: Optional[timedelta] = None,
+                 wake_threshold: np.array = WAKE_THRESHOLD,
+                 vlc_player=VLC_PLAYER,
+                 default_song=SONG):
+        super().__init__(wake_time)
+        self.earliest_wake = self.wake_time
         if wake_time:
-            self.set_alarm(wake_time)
-        super().__init__()
-
-    def set_alarm(self, wake_time: time) -> time:
+            self.set_alarm(wake_time, earliest_wake)
+        self.wake_threshold = wake_threshold
+        self._vlc_player = vlc_player
+        self.song = default_song
+        if self._vlc_player and self.song:
+            self._vlc_player.set_media(self.song)
+
+    def set_alarm(self, wake_time: datetime, earliest_wake: Optional[timedelta] = None) -> datetime:
         print(f"SETTING ALARM to: {wake_time}")
-        return super(SmartAlarmClock, self).set_alarm(wake_time)
+        returned_value = super(SmartAlarmClock, self).set_alarm(wake_time)
+        self.earliest_wake = wake_time - earliest_wake if earliest_wake else self.wake_time
+        print(returned_value)
+        return returned_value
 
     def start_alarm(self) -> None:
         print("STARTING ALARM")
         super(SmartAlarmClock, self).start_alarm()
 
-    def sound_alarm(self) -> None:
+    def sound_alarm(self, override_song=None) -> None:
         print("SOUNDING ALARM")
+        if override_song:
+            self.song = override_song
+        if self._vlc_player:
+            self._vlc_player.set_media(self.song)
+            self._vlc_player.play()
         super(SmartAlarmClock, self).sound_alarm()
 
     def snooze_alarm(self) -> None:
         print("SNOOZING ALARM")
         super(SmartAlarmClock, self).snooze_alarm()
+        if self._vlc_player:
+            self._vlc_player.pause()
+        sleep(SNOOZE_SEC)
+        if self._vlc_player:
+            self._vlc_player.play()
+        print("UN-SNOOZING ALARM")
+        self.sound_alarm()
 
     def stop_alarm(self, deactivate: bool = True) -> None:
         print("STOPPING ALARM")
-        if self._current_state is AlarmState.PLAYING:
+        if self._vlc_player:
+            self._vlc_player.stop()
             print("STOPPED PLAYING ALARM")
         super(SmartAlarmClock, self).stop_alarm()
 
-    def smart_alarm_mode(self, model):
+    def check_early_alarm_reached(self, current_time: datetime, prior_prediction: np.array) -> bool:
+        if current_time < self.earliest_wake:
+            return False
+        return np.max(prior_prediction - self.wake_threshold) > 0
+
+    def smart_alarm_mode(self, fn, model_memory=MODEL_MEMORY):
+        print("STARTING IN SMART MODE")
+        kb.on_press_key("c", lambda x: sys.exit())
+        kb.on_press_key(SNOOZE_KEY, lambda x: self.snooze_alarm())
+        kb.on_press_key(ALARM_OFF_KEY, lambda x: self.stop_alarm())
         self.start_alarm()
-        while not self.alarm_check_reached(datetime.now().time()):
-            print(f"ALARM SLEEPING @: {datetime.now().time()}")
-            sleep(1)
+        time_queue = deque(maxlen=model_memory)
+        # While not filled time_queue keep appending awake data (warm-up machine)
+        minutes_since_start = 0
+        while len(time_queue) < time_queue.maxlen:
+            current_utc = datetime.utcnow()
+            time_queue.append(np.array(
+                [minutes_since_start, current_utc.hour, current_utc.minute, 1.0, 0.0, 0.0, 0.0]
+            ))
+            print(f"time_queue: {time_queue}")
+            # Wait a minute for next timestep
+            while (datetime.utcnow() - current_utc).seconds < SECONDS_IN_MINUTE:
+                sleep(WAIT_SEC)
+            minutes_since_start += 1
+
+        # While the alarm clock is running
+        last_checked = datetime.utcnow()
+        last_prediction = model_prediction(fn, time_queue)
+        print(f"{last_checked}: last_prediction = {last_prediction}")
+        print(f"check early: {self.check_early_alarm_reached(datetime.now(tz=timezone.utc), last_prediction)}")
+        while self.current_state is AlarmState.RUNNING \
+                and not self.check_early_alarm_reached(datetime.now(tz=timezone.utc), last_prediction) \
+                and not self.alarm_check_reached(datetime.now(tz=timezone.utc)):
+            print(f"ALARM SLEEPING @: {datetime.now()}")
+            if (datetime.utcnow() - last_checked).seconds < SECONDS_IN_MINUTE:
+                last_checked = datetime.utcnow()
+                time_queue.append(
+                    np.concatenate(
+                        (
+                            [minutes_since_start, last_checked.hour, last_checked.minute],
+                            last_prediction
+                        )
+                    )
+                )
+                print(f"time_queue = {time_queue}")
+            last_prediction = model_prediction(fn, time_queue)
+            print(f"{datetime.utcnow()}: last_prediction = {last_prediction}")
+            sleep(WAIT_SEC)
+
+        # While the alarm clock is sounding off
         self.sound_alarm()
         while self.current_state is not AlarmState.DEACTIVATED:
-            with keyboard.Events() as events:
-                for event in events:
-                    if event.key == ALARM_OFF_KEY:
-                        self.stop_alarm()
-                        break
-                    elif event.key == SNOOZE_KEY:
-                        self.snooze_alarm()
-                        sleep(SNOOZE_SEC)
-                        self.sound_alarm()
-                        break
+            pass
+            # event = kb.read_event()
+            # if event.event_type == kb.KEY_DOWN:
+            # # with keyboard.Events() as events:
+            # #     for event in events:
+            # #         if event.key == ALARM_OFF_KEY:
+            #         if event.name == ALARM_OFF_KEY:
+            #             self.stop_alarm()
+            #             break
+            #         elif event.name == SNOOZE_KEY:
+            #         # elif event.key == SNOOZE_KEY:
+            #             self.snooze_alarm()
+            #             break
 
 
 if __name__ == '__main__':
-    alarm_hour = int(input("What hour do you want the alarm to go off at?"))
-    alarm_minute = int(input("What minute do you want the alarm to go off at?"))
-    alarm_second = int(input("What second do you want the alarm to go off at?"))
-    alarm_time = time(alarm_hour, alarm_minute, alarm_second)
-    alarm_clock_class = SmartAlarmClock(alarm_time)
-    ai_model = None
-    alarm_clock_class.smart_alarm_mode(ai_model)
+    alarm_hour = int(input("What hour do you want the alarm to go off at? "))
+    alarm_minute = int(input("What minute do you want the alarm to go off at? "))
+    early_minutes = int(input("How many minute early would you have the alarm go off at? "))
+    run_alarm_today = map(
+        lambda response: response.lower() in ["y", "yes", "t", "true"],
+        input("Will the alarm run today? [y/N]")
+    )
+    alarm_time = datetime.combine(
+        datetime.today() if run_alarm_today else (datetime.today() + timedelta(days=1)).date(),
+        time(alarm_hour, alarm_minute, 0)
+    ).astimezone(tz=timezone.utc)
+    early_time = alarm_time - timedelta(minutes=early_minutes)
+    alarm_clock_class = SmartAlarmClock(alarm_time, earliest_wake=timedelta(minutes=early_minutes))
+    print(f"Loading model from: {MODEL_PATH}")
+    ai_model = tflite.Interpreter(model_path=MODEL_PATH)
+    ai_model.allocate_tensors()  # tf.keras.models.load_model(MODEL_PATH)
+    model_fn = ai_model.get_signature_runner()
+    print(f"Initialized Model")
+    alarm_clock_class.smart_alarm_mode(model_fn)
diff --git a/tf_model.ipynb b/tf_model.ipynb
index dbce03e928aa643cc0cca0910a02d1340ccd4aa9..a8b45ad83c92c5cdac615751acbad3ab339ed6f3 100644
--- a/tf_model.ipynb
+++ b/tf_model.ipynb
@@ -1045,7 +1045,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 54,
+   "execution_count": 49,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -1061,7 +1061,7 @@
    "outputs": [],
    "source": [
     "def noisy_randomizer(confusion_matrix_index: int, sample_count: int = SAMPLE_COUNT, confusion_matrix = CONFUSION_MATRIX):\n",
-    "    return np.random.multinomial(sample_count, confusion_matrix[confusion_matrix_index], size=1)[0]/sample_count"
+    "    return softmax(np.random.multinomial(sample_count, confusion_matrix[confusion_matrix_index], size=1)[0])"
    ]
   },
   {
@@ -1194,7 +1194,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 18,
+   "execution_count": 28,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -1688,7 +1688,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 32,
+   "execution_count": 88,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -1698,20 +1698,20 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 33,
+   "execution_count": 89,
    "metadata": {},
    "outputs": [
     {
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "Model: \"sequential_1\"\n",
+      "Model: \"sequential_2\"\n",
       "_________________________________________________________________\n",
       " Layer (type)                Output Shape              Param #   \n",
       "=================================================================\n",
       " gru (GRU)                   (None, 16)                1200      \n",
       "                                                                 \n",
-      " dense_1 (Dense)             (None, 4)                 68        \n",
+      " dense_2 (Dense)             (None, 4)                 68        \n",
       "                                                                 \n",
       "=================================================================\n",
       "Total params: 1,268\n",
@@ -1733,19 +1733,31 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 46,
+   "execution_count": 91,
    "metadata": {},
    "outputs": [
     {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "9647/9647 [==============================] - 97s 10ms/step - loss: 0.4700 - categorical_crossentropy: 0.4700 - categorical_accuracy: 0.8416 - categorical_hinge: 0.4278 - val_loss: 0.2088 - val_categorical_crossentropy: 0.2087 - val_categorical_accuracy: 0.9595 - val_categorical_hinge: 0.1954\n"
+     "ename": "KeyboardInterrupt",
+     "evalue": "",
+     "output_type": "error",
+     "traceback": [
+      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+      "\u001b[0;31mKeyboardInterrupt\u001b[0m                         Traceback (most recent call last)",
+      "\u001b[1;32m/Users/nowadmin/Documents/School Folder/CS 437/Lab/Final Project/tf_model.ipynb Cell 65'\u001b[0m in \u001b[0;36m<cell line: 1>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> <a href='vscode-notebook-cell:/Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/tf_model.ipynb#ch0000054?line=0'>1</a>\u001b[0m gru_history \u001b[39m=\u001b[39m compile_and_fit(model\u001b[39m=\u001b[39;49mgru_model, window\u001b[39m=\u001b[39;49mwg)\n",
+      "\u001b[1;32m/Users/nowadmin/Documents/School Folder/CS 437/Lab/Final Project/tf_model.ipynb Cell 42'\u001b[0m in \u001b[0;36mcompile_and_fit\u001b[0;34m(model, window, loss, optimizer, metrics, early_stop, patience, baseline, epochs)\u001b[0m\n\u001b[1;32m     <a href='vscode-notebook-cell:/Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/tf_model.ipynb#ch0000033?line=13'>14</a>\u001b[0m     callbacks\u001b[39m.\u001b[39mappend(early_stopping)\n\u001b[1;32m     <a href='vscode-notebook-cell:/Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/tf_model.ipynb#ch0000033?line=15'>16</a>\u001b[0m model\u001b[39m.\u001b[39mcompile(\n\u001b[1;32m     <a href='vscode-notebook-cell:/Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/tf_model.ipynb#ch0000033?line=16'>17</a>\u001b[0m     loss\u001b[39m=\u001b[39mloss,\n\u001b[1;32m     <a href='vscode-notebook-cell:/Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/tf_model.ipynb#ch0000033?line=17'>18</a>\u001b[0m     optimizer\u001b[39m=\u001b[39moptimizer,\n\u001b[1;32m     <a href='vscode-notebook-cell:/Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/tf_model.ipynb#ch0000033?line=18'>19</a>\u001b[0m     metrics\u001b[39m=\u001b[39mmetrics,\n\u001b[1;32m     <a href='vscode-notebook-cell:/Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/tf_model.ipynb#ch0000033?line=19'>20</a>\u001b[0m )\n\u001b[0;32m---> <a href='vscode-notebook-cell:/Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/tf_model.ipynb#ch0000033?line=21'>22</a>\u001b[0m \u001b[39mreturn\u001b[39;00m model\u001b[39m.\u001b[39;49mfit(window\u001b[39m.\u001b[39;49mtraining_ds, epochs\u001b[39m=\u001b[39;49mepochs, validation_data\u001b[39m=\u001b[39;49mwindow\u001b[39m.\u001b[39;49mvalidation_ds, callbacks\u001b[39m=\u001b[39;49mcallbacks)\n",
+      "File \u001b[0;32m~/Documents/School Folder/CS 437/Lab/Final Project/venv/lib/python3.10/site-packages/keras/utils/traceback_utils.py:64\u001b[0m, in \u001b[0;36mfilter_traceback.<locals>.error_handler\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m     <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/utils/traceback_utils.py?line=61'>62</a>\u001b[0m filtered_tb \u001b[39m=\u001b[39m \u001b[39mNone\u001b[39;00m\n\u001b[1;32m     <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/utils/traceback_utils.py?line=62'>63</a>\u001b[0m \u001b[39mtry\u001b[39;00m:\n\u001b[0;32m---> <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/utils/traceback_utils.py?line=63'>64</a>\u001b[0m   \u001b[39mreturn\u001b[39;00m fn(\u001b[39m*\u001b[39;49margs, \u001b[39m*\u001b[39;49m\u001b[39m*\u001b[39;49mkwargs)\n\u001b[1;32m     <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/utils/traceback_utils.py?line=64'>65</a>\u001b[0m \u001b[39mexcept\u001b[39;00m \u001b[39mException\u001b[39;00m \u001b[39mas\u001b[39;00m e:  \u001b[39m# pylint: disable=broad-except\u001b[39;00m\n\u001b[1;32m     <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/utils/traceback_utils.py?line=65'>66</a>\u001b[0m   filtered_tb \u001b[39m=\u001b[39m _process_traceback_frames(e\u001b[39m.\u001b[39m__traceback__)\n",
+      "File \u001b[0;32m~/Documents/School Folder/CS 437/Lab/Final Project/venv/lib/python3.10/site-packages/keras/engine/training.py:1372\u001b[0m, in \u001b[0;36mModel.fit\u001b[0;34m(self, x, y, batch_size, epochs, verbose, callbacks, validation_split, validation_data, shuffle, class_weight, sample_weight, initial_epoch, steps_per_epoch, validation_steps, validation_batch_size, validation_freq, max_queue_size, workers, use_multiprocessing)\u001b[0m\n\u001b[1;32m   <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/engine/training.py?line=1368'>1369</a>\u001b[0m data_handler\u001b[39m.\u001b[39m_initial_epoch \u001b[39m=\u001b[39m (  \u001b[39m# pylint: disable=protected-access\u001b[39;00m\n\u001b[1;32m   <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/engine/training.py?line=1369'>1370</a>\u001b[0m     \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_maybe_load_initial_epoch_from_ckpt(initial_epoch))\n\u001b[1;32m   <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/engine/training.py?line=1370'>1371</a>\u001b[0m logs \u001b[39m=\u001b[39m \u001b[39mNone\u001b[39;00m\n\u001b[0;32m-> <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/engine/training.py?line=1371'>1372</a>\u001b[0m \u001b[39mfor\u001b[39;00m epoch, iterator \u001b[39min\u001b[39;00m data_handler\u001b[39m.\u001b[39menumerate_epochs():\n\u001b[1;32m   <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/engine/training.py?line=1372'>1373</a>\u001b[0m   \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mreset_metrics()\n\u001b[1;32m   <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/engine/training.py?line=1373'>1374</a>\u001b[0m   callbacks\u001b[39m.\u001b[39mon_epoch_begin(epoch)\n",
+      "File \u001b[0;32m~/Documents/School Folder/CS 437/Lab/Final Project/venv/lib/python3.10/site-packages/keras/engine/data_adapter.py:1191\u001b[0m, in \u001b[0;36mDataHandler.enumerate_epochs\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m   <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/engine/data_adapter.py?line=1188'>1189</a>\u001b[0m \u001b[39m\"\"\"Yields `(epoch, tf.data.Iterator)`.\"\"\"\u001b[39;00m\n\u001b[1;32m   <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/engine/data_adapter.py?line=1189'>1190</a>\u001b[0m \u001b[39mwith\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_truncate_execution_to_epoch():\n\u001b[0;32m-> <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/engine/data_adapter.py?line=1190'>1191</a>\u001b[0m   data_iterator \u001b[39m=\u001b[39m \u001b[39miter\u001b[39;49m(\u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_dataset)\n\u001b[1;32m   <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/engine/data_adapter.py?line=1191'>1192</a>\u001b[0m   \u001b[39mfor\u001b[39;00m epoch \u001b[39min\u001b[39;00m \u001b[39mrange\u001b[39m(\u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_initial_epoch, \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_epochs):\n\u001b[1;32m   <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/keras/engine/data_adapter.py?line=1192'>1193</a>\u001b[0m     \u001b[39mif\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_insufficient_data:  \u001b[39m# Set by `catch_stop_iteration`.\u001b[39;00m\n",
+      "File \u001b[0;32m~/Documents/School Folder/CS 437/Lab/Final Project/venv/lib/python3.10/site-packages/tensorflow/python/data/ops/dataset_ops.py:486\u001b[0m, in \u001b[0;36mDatasetV2.__iter__\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/tensorflow/python/data/ops/dataset_ops.py?line=483'>484</a>\u001b[0m \u001b[39mif\u001b[39;00m context\u001b[39m.\u001b[39mexecuting_eagerly() \u001b[39mor\u001b[39;00m ops\u001b[39m.\u001b[39minside_function():\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/tensorflow/python/data/ops/dataset_ops.py?line=484'>485</a>\u001b[0m   \u001b[39mwith\u001b[39;00m ops\u001b[39m.\u001b[39mcolocate_with(\u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_variant_tensor):\n\u001b[0;32m--> <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/tensorflow/python/data/ops/dataset_ops.py?line=485'>486</a>\u001b[0m     \u001b[39mreturn\u001b[39;00m iterator_ops\u001b[39m.\u001b[39;49mOwnedIterator(\u001b[39mself\u001b[39;49m)\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/tensorflow/python/data/ops/dataset_ops.py?line=486'>487</a>\u001b[0m \u001b[39melse\u001b[39;00m:\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/tensorflow/python/data/ops/dataset_ops.py?line=487'>488</a>\u001b[0m   \u001b[39mraise\u001b[39;00m \u001b[39mRuntimeError\u001b[39;00m(\u001b[39m\"\u001b[39m\u001b[39m`tf.data.Dataset` only supports Python-style \u001b[39m\u001b[39m\"\u001b[39m\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/tensorflow/python/data/ops/dataset_ops.py?line=488'>489</a>\u001b[0m                      \u001b[39m\"\u001b[39m\u001b[39miteration in eager mode or within tf.function.\u001b[39m\u001b[39m\"\u001b[39m)\n",
+      "File \u001b[0;32m~/Documents/School Folder/CS 437/Lab/Final Project/venv/lib/python3.10/site-packages/tensorflow/python/data/ops/iterator_ops.py:755\u001b[0m, in \u001b[0;36mOwnedIterator.__init__\u001b[0;34m(self, dataset, components, element_spec)\u001b[0m\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/tensorflow/python/data/ops/iterator_ops.py?line=750'>751</a>\u001b[0m   \u001b[39mif\u001b[39;00m (components \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m \u001b[39mor\u001b[39;00m element_spec \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m):\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/tensorflow/python/data/ops/iterator_ops.py?line=751'>752</a>\u001b[0m     \u001b[39mraise\u001b[39;00m \u001b[39mValueError\u001b[39;00m(\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/tensorflow/python/data/ops/iterator_ops.py?line=752'>753</a>\u001b[0m         \u001b[39m\"\u001b[39m\u001b[39mWhen `dataset` is provided, `element_spec` and `components` must \u001b[39m\u001b[39m\"\u001b[39m\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/tensorflow/python/data/ops/iterator_ops.py?line=753'>754</a>\u001b[0m         \u001b[39m\"\u001b[39m\u001b[39mnot be specified.\u001b[39m\u001b[39m\"\u001b[39m)\n\u001b[0;32m--> <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/tensorflow/python/data/ops/iterator_ops.py?line=754'>755</a>\u001b[0m   \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_create_iterator(dataset)\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/tensorflow/python/data/ops/iterator_ops.py?line=756'>757</a>\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_get_next_call_count \u001b[39m=\u001b[39m \u001b[39m0\u001b[39m\n",
+      "File \u001b[0;32m~/Documents/School Folder/CS 437/Lab/Final Project/venv/lib/python3.10/site-packages/tensorflow/python/data/ops/iterator_ops.py:787\u001b[0m, in \u001b[0;36mOwnedIterator._create_iterator\u001b[0;34m(self, dataset)\u001b[0m\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/tensorflow/python/data/ops/iterator_ops.py?line=781'>782</a>\u001b[0m \u001b[39melse\u001b[39;00m:\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/tensorflow/python/data/ops/iterator_ops.py?line=782'>783</a>\u001b[0m   \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_iterator_resource, \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_deleter \u001b[39m=\u001b[39m (\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/tensorflow/python/data/ops/iterator_ops.py?line=783'>784</a>\u001b[0m       gen_dataset_ops\u001b[39m.\u001b[39manonymous_iterator_v2(\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/tensorflow/python/data/ops/iterator_ops.py?line=784'>785</a>\u001b[0m           output_types\u001b[39m=\u001b[39m\u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_flat_output_types,\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/tensorflow/python/data/ops/iterator_ops.py?line=785'>786</a>\u001b[0m           output_shapes\u001b[39m=\u001b[39m\u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_flat_output_shapes))\n\u001b[0;32m--> <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/tensorflow/python/data/ops/iterator_ops.py?line=786'>787</a>\u001b[0m   gen_dataset_ops\u001b[39m.\u001b[39;49mmake_iterator(ds_variant, \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_iterator_resource)\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/tensorflow/python/data/ops/iterator_ops.py?line=787'>788</a>\u001b[0m   \u001b[39m# Delete the resource when this object is deleted\u001b[39;00m\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/tensorflow/python/data/ops/iterator_ops.py?line=788'>789</a>\u001b[0m   \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_resource_deleter \u001b[39m=\u001b[39m IteratorResourceDeleter(\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/tensorflow/python/data/ops/iterator_ops.py?line=789'>790</a>\u001b[0m       handle\u001b[39m=\u001b[39m\u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_iterator_resource,\n\u001b[1;32m    <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/tensorflow/python/data/ops/iterator_ops.py?line=790'>791</a>\u001b[0m       deleter\u001b[39m=\u001b[39m\u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_deleter)\n",
+      "File \u001b[0;32m~/Documents/School Folder/CS 437/Lab/Final Project/venv/lib/python3.10/site-packages/tensorflow/python/ops/gen_dataset_ops.py:3315\u001b[0m, in \u001b[0;36mmake_iterator\u001b[0;34m(dataset, iterator, name)\u001b[0m\n\u001b[1;32m   <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/tensorflow/python/ops/gen_dataset_ops.py?line=3312'>3313</a>\u001b[0m \u001b[39mif\u001b[39;00m tld\u001b[39m.\u001b[39mis_eager:\n\u001b[1;32m   <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/tensorflow/python/ops/gen_dataset_ops.py?line=3313'>3314</a>\u001b[0m   \u001b[39mtry\u001b[39;00m:\n\u001b[0;32m-> <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/tensorflow/python/ops/gen_dataset_ops.py?line=3314'>3315</a>\u001b[0m     _result \u001b[39m=\u001b[39m pywrap_tfe\u001b[39m.\u001b[39;49mTFE_Py_FastPathExecute(\n\u001b[1;32m   <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/tensorflow/python/ops/gen_dataset_ops.py?line=3315'>3316</a>\u001b[0m       _ctx, \u001b[39m\"\u001b[39;49m\u001b[39mMakeIterator\u001b[39;49m\u001b[39m\"\u001b[39;49m, name, dataset, iterator)\n\u001b[1;32m   <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/tensorflow/python/ops/gen_dataset_ops.py?line=3316'>3317</a>\u001b[0m     \u001b[39mreturn\u001b[39;00m _result\n\u001b[1;32m   <a href='file:///Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/venv/lib/python3.10/site-packages/tensorflow/python/ops/gen_dataset_ops.py?line=3317'>3318</a>\u001b[0m   \u001b[39mexcept\u001b[39;00m _core\u001b[39m.\u001b[39m_NotOkStatusException \u001b[39mas\u001b[39;00m e:\n",
+      "\u001b[0;31mKeyboardInterrupt\u001b[0m: "
      ]
     }
    ],
    "source": [
-    "gru_history = compile_and_fit(model=gru_model, window=wg, epochs=1)"
+    "gru_history = compile_and_fit(model=gru_model, window=wg)"
    ]
   },
   {
@@ -1757,7 +1769,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 13,
+   "execution_count": 29,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -1766,21 +1778,21 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 14,
+   "execution_count": 32,
    "metadata": {},
    "outputs": [
     {
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "Model: \"sequential\"\n",
+      "Model: \"sequential_1\"\n",
       "_________________________________________________________________\n",
       " Layer (type)                Output Shape              Param #   \n",
       "=================================================================\n",
-      " custom_attention (CustomAtt  (None, 32)               497       \n",
-      " ention)                                                         \n",
+      " custom_attention_1 (CustomA  (None, 32)               497       \n",
+      " ttention)                                                       \n",
       "                                                                 \n",
-      " dense (Dense)               (None, 4)                 132       \n",
+      " dense_1 (Dense)             (None, 4)                 132       \n",
       "                                                                 \n",
       "=================================================================\n",
       "Total params: 629\n",
@@ -1802,16 +1814,16 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 15,
+   "execution_count": 31,
    "metadata": {},
    "outputs": [
     {
      "data": {
       "text/plain": [
-       "<tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x12aaebe50>"
+       "<tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x111063e80>"
       ]
      },
-     "execution_count": 15,
+     "execution_count": 31,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1822,56 +1834,70 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 17,
+   "execution_count": 38,
    "metadata": {},
-   "outputs": [],
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Epoch 1/20\n",
+      "4682/4682 [==============================] - 67s 14ms/step - loss: 0.9233 - categorical_crossentropy: 0.9233 - categorical_accuracy: 0.5880 - categorical_hinge: 0.9024 - val_loss: 0.4996 - val_categorical_crossentropy: 0.4996 - val_categorical_accuracy: 0.9040 - val_categorical_hinge: 0.3196\n",
+      "Epoch 2/20\n",
+      "4682/4682 [==============================] - 56s 12ms/step - loss: 0.3929 - categorical_crossentropy: 0.3929 - categorical_accuracy: 0.8920 - categorical_hinge: 0.3280 - val_loss: 0.4142 - val_categorical_crossentropy: 0.4142 - val_categorical_accuracy: 0.8625 - val_categorical_hinge: 0.3925\n",
+      "Epoch 3/20\n",
+      "4682/4682 [==============================] - 76s 16ms/step - loss: 0.3686 - categorical_crossentropy: 0.3686 - categorical_accuracy: 0.8872 - categorical_hinge: 0.3478 - val_loss: 0.2700 - val_categorical_crossentropy: 0.2700 - val_categorical_accuracy: 0.9252 - val_categorical_hinge: 0.2614\n",
+      "Epoch 4/20\n",
+      "4682/4682 [==============================] - 72s 15ms/step - loss: 0.3373 - categorical_crossentropy: 0.3373 - categorical_accuracy: 0.9026 - categorical_hinge: 0.3137 - val_loss: 0.2795 - val_categorical_crossentropy: 0.2796 - val_categorical_accuracy: 0.9248 - val_categorical_hinge: 0.2698\n",
+      "Epoch 5/20\n",
+      "4682/4682 [==============================] - 68s 15ms/step - loss: 0.3296 - categorical_crossentropy: 0.3296 - categorical_accuracy: 0.9050 - categorical_hinge: 0.3312 - val_loss: 0.2372 - val_categorical_crossentropy: 0.2372 - val_categorical_accuracy: 0.9532 - val_categorical_hinge: 0.2263\n",
+      "Epoch 6/20\n",
+      "4682/4682 [==============================] - 69s 15ms/step - loss: 0.3081 - categorical_crossentropy: 0.3081 - categorical_accuracy: 0.9170 - categorical_hinge: 0.2994 - val_loss: 0.2517 - val_categorical_crossentropy: 0.2517 - val_categorical_accuracy: 0.9449 - val_categorical_hinge: 0.2452\n",
+      "Epoch 7/20\n",
+      "4682/4682 [==============================] - 67s 14ms/step - loss: 0.2663 - categorical_crossentropy: 0.2663 - categorical_accuracy: 0.9360 - categorical_hinge: 0.2524 - val_loss: 0.2238 - val_categorical_crossentropy: 0.2238 - val_categorical_accuracy: 0.9565 - val_categorical_hinge: 0.2128\n",
+      "Epoch 8/20\n",
+      "4682/4682 [==============================] - 68s 15ms/step - loss: 0.2747 - categorical_crossentropy: 0.2747 - categorical_accuracy: 0.9330 - categorical_hinge: 0.2739 - val_loss: 0.2166 - val_categorical_crossentropy: 0.2166 - val_categorical_accuracy: 0.9571 - val_categorical_hinge: 0.2089\n",
+      "Epoch 9/20\n",
+      "4682/4682 [==============================] - 72s 15ms/step - loss: 0.2510 - categorical_crossentropy: 0.2510 - categorical_accuracy: 0.9430 - categorical_hinge: 0.2354 - val_loss: 0.2189 - val_categorical_crossentropy: 0.2189 - val_categorical_accuracy: 0.9560 - val_categorical_hinge: 0.1976\n",
+      "Epoch 10/20\n",
+      "4682/4682 [==============================] - 71s 15ms/step - loss: 0.2810 - categorical_crossentropy: 0.2810 - categorical_accuracy: 0.9311 - categorical_hinge: 0.2874 - val_loss: 0.2320 - val_categorical_crossentropy: 0.2321 - val_categorical_accuracy: 0.9555 - val_categorical_hinge: 0.2230\n"
+     ]
+    }
+   ],
    "source": [
-    "am_history = compile_and_fit(model=am_model, window=wg, epochs=0)"
+    "am_history = compile_and_fit(model=am_model, window=wg)"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 18,
+   "execution_count": 42,
    "metadata": {},
    "outputs": [
     {
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "1442/1442 [==============================] - 14s 4ms/step - loss: 0.1957 - categorical_crossentropy: 0.1957 - categorical_accuracy: 0.9598 - categorical_hinge: 0.1911\n"
+      "1450/1450 [==============================] - 13s 4ms/step - loss: 0.2333 - categorical_crossentropy: 0.2333 - categorical_accuracy: 0.9552 - categorical_hinge: 0.2243\n"
      ]
-    },
-    {
-     "data": {
-      "text/plain": [
-       "[0.19572481513023376,\n",
-       " 0.19574123620986938,\n",
-       " 0.9597873687744141,\n",
-       " 0.1910863220691681]"
-      ]
-     },
-     "execution_count": 18,
-     "metadata": {},
-     "output_type": "execute_result"
     }
    ],
    "source": [
-    "am_model.evaluate(wg.testing_ds)"
+    "results = am_model.evaluate(wg.testing_ds)"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 47,
+   "execution_count": 46,
    "metadata": {},
    "outputs": [
     {
      "data": {
       "text/plain": [
-       "array([[ 0.59796184, -0.20803624, -0.2916372 , -0.7170148 ]],\n",
+       "array([[-0.4693893 , -0.7194527 ,  0.18727879, -0.47643274]],\n",
        "      dtype=float32)"
       ]
      },
-     "execution_count": 47,
+     "execution_count": 46,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1883,12 +1909,7 @@
     "        [ 151.,  10., 21.,  0.5,  0.2,  0.,  0.3],\n",
     "        [ 152.,  10., 22.,  0.5,  0.3,  0.,  0.2],\n",
     "        [ 153.,  10., 23.,  0.5,  0.4,  0.,  0.1],\n",
-    "        [ 154.,  10., 24.,  0.2,  0.1,  0.,  0.],\n",
-    "        [ 155.,  10., 25.,  0.2,  0.2,  0.,  0.],\n",
-    "        [ 156.,  10., 26.,  0.2,  0.2,  0.1,  0.],\n",
-    "        [ 157.,  10., 27.,  0.2,  0.25,  0.15,  0.],\n",
-    "        [ 158.,  10., 28.,  0.2,  0.3,  0.2,  0.],\n",
-    "        [ 159.,  10., 29.,  0.2,  0.3,  0.3,  0.]\n",
+    "        [ 154.,  10., 24.,  0.2,  0.1,  0.7,  0.],\n",
     "    ]]\n",
     "))\n",
     "norm = np.linalg.norm(answer)\n",
@@ -1898,11 +1919,44 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 43,
+   "execution_count": 52,
    "metadata": {},
-   "outputs": [],
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(array([[-2.8096135, -4.3064127,  1.1209906, -2.8517733]], dtype=float32),\n",
+       " array([0.0188252 , 0.00421393, 0.95891285, 0.01804803], dtype=float32))"
+      ]
+     },
+     "execution_count": 52,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
    "source": [
-    "pd.DataFrame.from_dict(am_history.history).to_csv(f\"{HISTORY_DIR}/am_220512.csv\", index=False)"
+    "answer, softmax(answer[0])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 41,
+   "metadata": {},
+   "outputs": [
+    {
+     "ename": "NameError",
+     "evalue": "name 'HISTORY_DIR' is not defined",
+     "output_type": "error",
+     "traceback": [
+      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+      "\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)",
+      "\u001b[1;32m/Users/nowadmin/Documents/School Folder/CS 437/Lab/Final Project/tf_model.ipynb Cell 73'\u001b[0m in \u001b[0;36m<cell line: 1>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> <a href='vscode-notebook-cell:/Users/nowadmin/Documents/School%20Folder/CS%20437/Lab/Final%20Project/tf_model.ipynb#ch0000069?line=0'>1</a>\u001b[0m pd\u001b[39m.\u001b[39mDataFrame\u001b[39m.\u001b[39mfrom_dict(am_history\u001b[39m.\u001b[39mhistory)\u001b[39m.\u001b[39mto_csv(\u001b[39mf\u001b[39m\u001b[39m\"\u001b[39m\u001b[39m{\u001b[39;00mHISTORY_DIR\u001b[39m}\u001b[39;00m\u001b[39m/am_noisy_run_hist.csv\u001b[39m\u001b[39m\"\u001b[39m, index\u001b[39m=\u001b[39m\u001b[39mFalse\u001b[39;00m)\n",
+      "\u001b[0;31mNameError\u001b[0m: name 'HISTORY_DIR' is not defined"
+     ]
+    }
+   ],
+   "source": [
+    "pd.DataFrame.from_dict(am_history.history).to_csv(f\"{HISTORY_DIR}/am_noisy_run_hist.csv\", index=False)"
    ]
   },
   {
@@ -1985,14 +2039,14 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 35,
+   "execution_count": 136,
    "metadata": {},
    "outputs": [
     {
      "name": "stderr",
      "output_type": "stream",
      "text": [
-      "2022-05-12 12:12:02.267722: W tensorflow/python/util/util.cc:368] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.\n",
+      "2022-05-12 18:50:44.183309: W tensorflow/python/util/util.cc:368] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.\n",
       "WARNING:absl:Found untraced functions such as attention_score_vec_layer_call_fn, attention_score_vec_layer_call_and_return_conditional_losses, last_hidden_state_layer_call_fn, last_hidden_state_layer_call_and_return_conditional_losses, attention_score_layer_call_fn while saving (showing 5 of 14). These functions will not be directly callable after loading.\n"
      ]
     },
@@ -2024,7 +2078,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 34,
+   "execution_count": 43,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -2160,12 +2214,277 @@
     "    pass"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Model Conversion"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 139,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import tensorflow as tf"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 140,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "model = tf.keras.models.load_model(f\"{MODEL_DIR}/attention\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 141,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Model: \"sequential_1\"\n",
+      "_________________________________________________________________\n",
+      " Layer (type)                Output Shape              Param #   \n",
+      "=================================================================\n",
+      " custom_attention_1 (CustomA  (None, 32)               497       \n",
+      " ttention)                                                       \n",
+      "                                                                 \n",
+      " dense_1 (Dense)             (None, 4)                 132       \n",
+      "                                                                 \n",
+      "=================================================================\n",
+      "Total params: 629\n",
+      "Trainable params: 629\n",
+      "Non-trainable params: 0\n",
+      "_________________________________________________________________\n"
+     ]
+    }
+   ],
+   "source": [
+    "model.summary()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 142,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "WARNING:absl:Found untraced functions such as attention_score_vec_layer_call_fn, attention_score_vec_layer_call_and_return_conditional_losses, last_hidden_state_layer_call_fn, last_hidden_state_layer_call_and_return_conditional_losses, attention_score_layer_call_fn while saving (showing 5 of 14). These functions will not be directly callable after loading.\n"
+     ]
+    },
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "INFO:tensorflow:Assets written to: /var/folders/bm/yzlckp6x6vl9_bm329kms7680000gp/T/tmpvx4lpz4_/assets\n"
+     ]
+    },
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "INFO:tensorflow:Assets written to: /var/folders/bm/yzlckp6x6vl9_bm329kms7680000gp/T/tmpvx4lpz4_/assets\n",
+      "2022-05-12 20:51:22.624411: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:357] Ignored output_format.\n",
+      "2022-05-12 20:51:22.624442: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:360] Ignored drop_control_dependency.\n",
+      "WARNING:absl:Buffer deduplication procedure will be skipped when flatbuffer library is not properly loaded\n",
+      "2022-05-12 20:51:22.635394: I tensorflow/cc/saved_model/reader.cc:43] Reading SavedModel from: /var/folders/bm/yzlckp6x6vl9_bm329kms7680000gp/T/tmpvx4lpz4_\n",
+      "2022-05-12 20:51:22.637424: I tensorflow/cc/saved_model/reader.cc:78] Reading meta graph with tags { serve }\n",
+      "2022-05-12 20:51:22.637987: I tensorflow/cc/saved_model/reader.cc:119] Reading SavedModel debug info (if present) from: /var/folders/bm/yzlckp6x6vl9_bm329kms7680000gp/T/tmpvx4lpz4_\n",
+      "2022-05-12 20:51:22.648488: I tensorflow/cc/saved_model/loader.cc:228] Restoring SavedModel bundle.\n",
+      "2022-05-12 20:51:22.709625: I tensorflow/cc/saved_model/loader.cc:212] Running initialization op on SavedModel bundle at path: /var/folders/bm/yzlckp6x6vl9_bm329kms7680000gp/T/tmpvx4lpz4_\n",
+      "2022-05-12 20:51:22.730280: I tensorflow/cc/saved_model/loader.cc:301] SavedModel load for tags { serve }; Status: success: OK. Took 94895 microseconds.\n",
+      "2022-05-12 20:51:22.759351: I tensorflow/compiler/mlir/tensorflow/utils/dump_mlir_util.cc:237] disabling MLIR crash reproducer, set env var `MLIR_CRASH_REPRODUCER_DIRECTORY` to enable.\n"
+     ]
+    }
+   ],
+   "source": [
+    "# Convert the model.\n",
+    "converter = tf.lite.TFLiteConverter.from_keras_model(model)\n",
+    "tflite_model = converter.convert()\n",
+    "\n",
+    "# Save the model.\n",
+    "with open(f'{MODEL_DIR}/attention_model.tflite', 'wb') as f:\n",
+    "  f.write(tflite_model)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
     "# Scratch"
    ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 100,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from collections import deque\n",
+    "import datetime"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 96,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "d = deque(maxlen=10)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 97,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "while len(d) < d.maxlen:\n",
+    "    d.append(np.array([1,2,3, 4.0]))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 87,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "d.append(5)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 98,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "array([[1., 2., 3., 4.],\n",
+       "       [1., 2., 3., 4.],\n",
+       "       [1., 2., 3., 4.],\n",
+       "       [1., 2., 3., 4.],\n",
+       "       [1., 2., 3., 4.],\n",
+       "       [1., 2., 3., 4.],\n",
+       "       [1., 2., 3., 4.],\n",
+       "       [1., 2., 3., 4.],\n",
+       "       [1., 2., 3., 4.],\n",
+       "       [1., 2., 3., 4.]])"
+      ]
+     },
+     "execution_count": 98,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "np.array(d)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 115,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "curr_time = datetime.datetime.utcnow()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 118,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "23"
+      ]
+     },
+     "execution_count": 118,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "curr_time.hour"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 135,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "False"
+      ]
+     },
+     "execution_count": 135,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "datetime.datetime.now() < datetime.datetime.now() - datetime.timedelta(minutes=30)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 121,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "array([120.  ,  15.  ,   2.  ,   0.25,   0.25,   0.25,   0.25])"
+      ]
+     },
+     "execution_count": 121,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "np.concatenate([[120, 15, 2], np.array([0.25,0.25,0.25,0.25])]) -"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 138,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "array([0.40961474, 0.20340866, 0.1934883 , 0.1934883 ])"
+      ]
+     },
+     "execution_count": 138,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "softmax(np.array([0.25,0.25,0.25,0.25]) - np.array([0.25,0.95,1.0,1.0]))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
   }
  ],
  "metadata": {