{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 78,
   "id": "dfe8a223-5519-4fb0-ae27-633d055f8c4a",
   "metadata": {},
   "outputs": [],
   "source": [
    "import tensorflow as tf\n",
    "\n",
    "\n",
    "import numpy as np\n",
    "import os\n",
    "import time\n",
    "from IPython.display import Image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "id": "4e01c8c5-e847-42a9-a894-70b9b5757416",
   "metadata": {},
   "outputs": [],
   "source": [
    "path_to_file = \"C:/Users/Mark Huntly/Desktop/uwtsd/Lecturing/APLLIEDML/ML_Sem2_24-25/Datasets/AliceInWonerland.txt\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "id": "bc57c992-494b-478c-a9e1-4b9acd271bac",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Length of text: 153547 characters\n"
     ]
    }
   ],
   "source": [
    "# Read, then decode for py2 compat.\n",
    "text = open(path_to_file, 'rb').read().decode(encoding = \"ISO-8859-1\")\n",
    "# length of text is the number of characters in it\n",
    "print ('Length of text: {} characters'.format(len(text)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "id": "bab88d16-668c-4a91-9d68-21b921f22e05",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CHAPTER I.\n",
      "Down the Rabbit-Hole\n",
      "Alice was beginning to get very tired of sitting by her sister on the\n",
      "bank, and of having nothing to do: once or twice she had peeped into\n",
      "the book her sister was reading, but it had no pictures or\n",
      "conversations i\n"
     ]
    }
   ],
   "source": [
    "# Take a look at the first 250 characters in text\n",
    "print(text[:250])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "id": "b3bc997d-1c75-4310-9e7a-3f12952adc42",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "77 unique characters\n"
     ]
    }
   ],
   "source": [
    "# The unique characters in the file\n",
    "vocab = sorted(set(text))\n",
    "print ('{} unique characters'.format(len(vocab)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "id": "307d369a-82e3-4ea3-a7ea-3e9ec0f835d7",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Creating a mapping from unique characters to indices\n",
    "char2idx = {u:i for i, u in enumerate(vocab)}\n",
    "idx2char = np.array(vocab)\n",
    "\n",
    "text_as_int = np.array([char2idx[c] for c in text])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "id": "b8960c09-6493-41b8-8514-9b5c074d3e39",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\n",
      "  '\\n':   0,\n",
      "  '\\r':   1,\n",
      "  ' ' :   2,\n",
      "  '!' :   3,\n",
      "  '(' :   4,\n",
      "  ')' :   5,\n",
      "  '*' :   6,\n",
      "  ',' :   7,\n",
      "  '-' :   8,\n",
      "  '.' :   9,\n",
      "  ':' :  10,\n",
      "  ';' :  11,\n",
      "  '?' :  12,\n",
      "  'A' :  13,\n",
      "  'B' :  14,\n",
      "  'C' :  15,\n",
      "  'D' :  16,\n",
      "  'E' :  17,\n",
      "  'F' :  18,\n",
      "  'G' :  19,\n",
      "  ...\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "print('{')\n",
    "for char,_ in zip(char2idx, range(20)):\n",
    "    print('  {:4s}: {:3d},'.format(repr(char), char2idx[char]))\n",
    "print('  ...\\n}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 85,
   "id": "cf617629-85a3-4c5f-abe7-0ff696e6938f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "'CHAPTER I.\\r\\nD' ---- characters mapped to int ---- > [15 20 13 28 32 17 30  2 21  9  1  0 16]\n"
     ]
    }
   ],
   "source": [
    "# Show how the first 13 characters from the text are mapped to integers\n",
    "print ('{} ---- characters mapped to int ---- > {}'.format(repr(text[:13]), text_as_int[:13]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 86,
   "id": "14aaa180-04e8-4c89-a87b-128c9d7253c9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "C\n",
      "H\n",
      "A\n",
      "P\n",
      "T\n",
      "E\n",
      "R\n",
      " \n",
      "I\n",
      ".\n",
      "\n",
      "\n",
      "\n",
      "D\n",
      "o\n",
      "w\n",
      "n\n",
      " \n",
      "t\n",
      "h\n",
      "e\n",
      " \n",
      "R\n",
      "a\n",
      "b\n",
      "b\n",
      "i\n",
      "t\n",
      "-\n",
      "H\n",
      "o\n",
      "l\n",
      "e\n",
      "\n",
      "\n",
      "\n",
      "A\n",
      "l\n",
      "i\n",
      "c\n",
      "e\n",
      " \n",
      "w\n",
      "a\n",
      "s\n",
      " \n",
      "b\n",
      "e\n",
      "g\n",
      "i\n",
      "n\n",
      "n\n"
     ]
    }
   ],
   "source": [
    "# The maximum length sentence we want for a single input in characters\n",
    "seq_length = 100\n",
    "examples_per_epoch = len(text)//seq_length\n",
    "\n",
    "# Create training examples / targets\n",
    "char_dataset = tf.data.Dataset.from_tensor_slices(text_as_int)\n",
    "\n",
    "for i in char_dataset.take(50):\n",
    "  print(idx2char[i.numpy()])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "id": "8d13f6d9-2e2c-4adb-81bc-4c9f161a2c2f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "'CHAPTER I.\\r\\nDown the Rabbit-Hole\\r\\nAlice was beginning to get very tired of sitting by her sister on t'\n",
      "'he\\r\\nbank, and of having nothing to do: once or twice she had peeped into\\r\\nthe book her sister was rea'\n",
      "'ding, but it had no pictures or\\r\\nconversations in it, â\\x80\\x9cand what is the use of a book,â\\x80\\x9d thought Al'\n",
      "'ice\\r\\nâ\\x80\\x9cwithout pictures or conversations?â\\x80\\x9d\\r\\n\\r\\nSo she was considering in her own mind (as well as s'\n",
      "'he could, for the\\r\\nhot day made her feel very sleepy and stupid), whether the pleasure of\\r\\nmaking a d'\n"
     ]
    }
   ],
   "source": [
    "sequences = char_dataset.batch(seq_length+1, drop_remainder=True)\n",
    "\n",
    "for item in sequences.take(5):\n",
    "  print(repr(''.join(idx2char[item.numpy()])))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "id": "64165885-ba99-4a3f-8bc1-a23f6d47bfc2",
   "metadata": {},
   "outputs": [],
   "source": [
    "def split_input_target(chunk):\n",
    "    input_text = chunk[:-1]\n",
    "    target_text = chunk[1:]\n",
    "    return input_text, target_text\n",
    "\n",
    "dataset = sequences.map(split_input_target)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 89,
   "id": "19e68a67-f15f-4526-9897-9d42ec739c5f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Input data:  'CHAPTER I.\\r\\nDown the Rabbit-Hole\\r\\nAlice was beginning to get very tired of sitting by her sister on '\n",
      "Target data: 'HAPTER I.\\r\\nDown the Rabbit-Hole\\r\\nAlice was beginning to get very tired of sitting by her sister on t'\n"
     ]
    }
   ],
   "source": [
    "for input_example, target_example in  dataset.take(1):\n",
    "  print ('Input data: ', repr(''.join(idx2char[input_example.numpy()])))\n",
    "  print ('Target data:', repr(''.join(idx2char[target_example.numpy()])))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 90,
   "id": "99dd9014-25cc-45bb-93b2-41c8c4d24a56",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Step    0\n",
      "  input: 15 ('C')\n",
      "  expected output: 20 ('H')\n",
      "Step    1\n",
      "  input: 20 ('H')\n",
      "  expected output: 13 ('A')\n",
      "Step    2\n",
      "  input: 13 ('A')\n",
      "  expected output: 28 ('P')\n",
      "Step    3\n",
      "  input: 28 ('P')\n",
      "  expected output: 32 ('T')\n",
      "Step    4\n",
      "  input: 32 ('T')\n",
      "  expected output: 17 ('E')\n"
     ]
    }
   ],
   "source": [
    "for i, (input_idx, target_idx) in enumerate(zip(input_example[:5], target_example[:5])):\n",
    "    print(\"Step {:4d}\".format(i))\n",
    "    print(\"  input: {} ({:s})\".format(input_idx, repr(idx2char[input_idx])))\n",
    "    print(\"  expected output: {} ({:s})\".format(target_idx, repr(idx2char[target_idx])))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "id": "15a70b7d-a4a6-4486-9556-5ab2cb06b5d4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<_BatchDataset element_spec=(TensorSpec(shape=(64, 100), dtype=tf.int32, name=None), TensorSpec(shape=(64, 100), dtype=tf.int32, name=None))>"
      ]
     },
     "execution_count": 91,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Batch size (number of 100 word sequences to train on)\n",
    "BATCH_SIZE = 64\n",
    "\n",
    "# How many individual training steps per samples (steps)\n",
    "steps_per_epoch = examples_per_epoch//BATCH_SIZE\n",
    "\n",
    "# Buffer size to shuffle the dataset\n",
    "# (TF data is designed to work with possibly infinite sequences, \n",
    "# so it doesn't attempt to shuffle the entire sequence in memory. Instead, \n",
    "# it maintains a buffer in which it shuffles elements).\n",
    "BUFFER_SIZE = 10000\n",
    "\n",
    "dataset = dataset.shuffle(BUFFER_SIZE).batch(BATCH_SIZE, drop_remainder=True)\n",
    "\n",
    "dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 92,
   "id": "a78b2941-cd7c-4669-b6c0-7e647fcd7e5b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Length of the vocabulary in chars\n",
    "vocab_size = len(vocab)\n",
    "\n",
    "# The embedding dimension \n",
    "embedding_dim = 256\n",
    "\n",
    "# Number of RNN units\n",
    "rnn_units = 1024"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 93,
   "id": "b324df2d-1779-482d-8bc0-94405a4044cb",
   "metadata": {},
   "outputs": [],
   "source": [
    "if tf.test.is_gpu_available():\n",
    "  rnn = tf.keras.layers.CuDNNGRU\n",
    "  rnn2 = tf.keras.layers.CuDNNGRU\n",
    "else:\n",
    "  import functools\n",
    "  rnn = functools.partial(\n",
    "    tf.keras.layers.GRU, recurrent_activation='sigmoid')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 94,
   "id": "73992e3d-0ff2-4b68-b003-bcbd899f2181",
   "metadata": {},
   "outputs": [],
   "source": [
    "def build_model(vocab_size, embedding_dim, rnn_units, batch_size):\n",
    "    model = tf.keras.Sequential([\n",
    "        # ✅ Use input_shape instead of batch_input_shape\n",
    "        tf.keras.layers.Embedding(vocab_size, embedding_dim, input_length=None),\n",
    "\n",
    "        # ✅ Ensure 'rnn' is properly defined (LSTM, GRU, etc.)\n",
    "        tf.keras.layers.GRU(\n",
    "            rnn_units,\n",
    "            return_sequences=True,\n",
    "            recurrent_initializer='glorot_uniform',\n",
    "            stateful=True\n",
    "        ),\n",
    "\n",
    "        tf.keras.layers.Dense(vocab_size)\n",
    "    ])\n",
    "    \n",
    "    return model\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 95,
   "id": "4e09996b-94c4-46aa-aee5-83e13a175250",
   "metadata": {},
   "outputs": [],
   "source": [
    "model = build_model(\n",
    "  vocab_size = vocab_size, \n",
    "  embedding_dim=embedding_dim, \n",
    "  rnn_units=rnn_units, \n",
    "  batch_size=BATCH_SIZE)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 96,
   "id": "de17ebca-12cc-4bfc-82d8-1b3302858079",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(64, 100, 77) # (batch_size, sequence_length, vocab_size)\n"
     ]
    }
   ],
   "source": [
    "for input_example_batch, target_example_batch in dataset.take(1): \n",
    "  example_batch_predictions = model(input_example_batch)\n",
    "  print(example_batch_predictions.shape, \"# (batch_size, sequence_length, vocab_size)\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 97,
   "id": "5a8769fd-bd11-490d-8c03-87b26bf42a29",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\">Model: \"sequential_4\"</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1mModel: \"sequential_4\"\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓\n",
       "┃<span style=\"font-weight: bold\"> Layer (type)                    </span>┃<span style=\"font-weight: bold\"> Output Shape           </span>┃<span style=\"font-weight: bold\">       Param # </span>┃\n",
       "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩\n",
       "│ embedding_4 (<span style=\"color: #0087ff; text-decoration-color: #0087ff\">Embedding</span>)         │ (<span style=\"color: #00af00; text-decoration-color: #00af00\">64</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">100</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">256</span>)         │        <span style=\"color: #00af00; text-decoration-color: #00af00\">19,712</span> │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ gru_4 (<span style=\"color: #0087ff; text-decoration-color: #0087ff\">GRU</span>)                     │ (<span style=\"color: #00af00; text-decoration-color: #00af00\">64</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">100</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">1024</span>)        │     <span style=\"color: #00af00; text-decoration-color: #00af00\">3,938,304</span> │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ dense_4 (<span style=\"color: #0087ff; text-decoration-color: #0087ff\">Dense</span>)                 │ (<span style=\"color: #00af00; text-decoration-color: #00af00\">64</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">100</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">77</span>)          │        <span style=\"color: #00af00; text-decoration-color: #00af00\">78,925</span> │\n",
       "└─────────────────────────────────┴────────────────────────┴───────────────┘\n",
       "</pre>\n"
      ],
      "text/plain": [
       "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓\n",
       "┃\u001b[1m \u001b[0m\u001b[1mLayer (type)                   \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mOutput Shape          \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m      Param #\u001b[0m\u001b[1m \u001b[0m┃\n",
       "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩\n",
       "│ embedding_4 (\u001b[38;5;33mEmbedding\u001b[0m)         │ (\u001b[38;5;34m64\u001b[0m, \u001b[38;5;34m100\u001b[0m, \u001b[38;5;34m256\u001b[0m)         │        \u001b[38;5;34m19,712\u001b[0m │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ gru_4 (\u001b[38;5;33mGRU\u001b[0m)                     │ (\u001b[38;5;34m64\u001b[0m, \u001b[38;5;34m100\u001b[0m, \u001b[38;5;34m1024\u001b[0m)        │     \u001b[38;5;34m3,938,304\u001b[0m │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ dense_4 (\u001b[38;5;33mDense\u001b[0m)                 │ (\u001b[38;5;34m64\u001b[0m, \u001b[38;5;34m100\u001b[0m, \u001b[38;5;34m77\u001b[0m)          │        \u001b[38;5;34m78,925\u001b[0m │\n",
       "└─────────────────────────────────┴────────────────────────┴───────────────┘\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\"> Total params: </span><span style=\"color: #00af00; text-decoration-color: #00af00\">4,036,941</span> (15.40 MB)\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1m Total params: \u001b[0m\u001b[38;5;34m4,036,941\u001b[0m (15.40 MB)\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\"> Trainable params: </span><span style=\"color: #00af00; text-decoration-color: #00af00\">4,036,941</span> (15.40 MB)\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1m Trainable params: \u001b[0m\u001b[38;5;34m4,036,941\u001b[0m (15.40 MB)\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\"> Non-trainable params: </span><span style=\"color: #00af00; text-decoration-color: #00af00\">0</span> (0.00 B)\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1m Non-trainable params: \u001b[0m\u001b[38;5;34m0\u001b[0m (0.00 B)\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "model.summary()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 98,
   "id": "cde6debe-e6f2-4b7c-9636-46d1757f2d35",
   "metadata": {},
   "outputs": [],
   "source": [
    "# sampled_indices = tf.random.multinomial(example_batch_predictions[0], num_samples=1) # TF 1.12\n",
    "sampled_indices = tf.random.categorical(example_batch_predictions[0], num_samples=1)\n",
    "sampled_indices = tf.squeeze(sampled_indices,axis=-1).numpy()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 99,
   "id": "829fdac8-15b1-4207-a44e-6590e035e533",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([53, 10, 53, 57, 41, 49, 35, 11, 44, 54, 24,  9,  8, 46,  1, 65, 47,\n",
       "       22,  1, 13, 17, 11, 53, 33, 68, 36, 67, 16,  7, 75, 58, 70, 51, 71,\n",
       "        8, 41,  6, 47, 65,  6, 56,  8, 41,  7, 54, 43, 19, 43, 41, 31, 75,\n",
       "        3, 22, 41, 48, 71, 37, 26, 37, 43, 19, 55,  7, 76, 70,  5,  3, 33,\n",
       "       20, 74,  7, 53, 27, 56, 52, 55, 53,  5, 47, 31, 69, 38, 75, 20, 23,\n",
       "        3, 42, 75,  0, 58,  9, 39, 43, 47,  5, 75, 11, 74, 52, 35],\n",
       "      dtype=int64)"
      ]
     },
     "execution_count": 99,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sampled_indices"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 100,
   "id": "fa00e931-f2a8-4726-8bdf-428f9975bc1d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Input: \n",
      " 's,\\r\\n    And took them quite away!â\\x80\\x9d\\r\\n\\r\\n\\r\\nâ\\x80\\x9cConsider your verdict,â\\x80\\x9d the King said to the jury.\\r\\n\\r'\n",
      "\n",
      "Next Char Predictions: \n",
      " 'l:lp_hW;cmL.-e\\rxfJ\\rAE;lU\\x80XzD,Ãq\\x98j\\x99-_*fx*o-_,mbGb_SÃ!J_g\\x99YNYbGn,â\\x98)!UH¹,lOoknl)fS\\x94ZÃHK!aÃ\\nq.[bf)Ã;¹kW'\n"
     ]
    }
   ],
   "source": [
    "print(\"Input: \\n\", repr(\"\".join(idx2char[input_example_batch[0]])))\n",
    "print()\n",
    "print(\"Next Char Predictions: \\n\", repr(\"\".join(idx2char[sampled_indices ])))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 101,
   "id": "5256641d-04c1-40a1-b38d-8542db377ecf",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Prediction shape:  (64, 100, 77)  # (batch_size, sequence_length, vocab_size)\n",
      "scalar_loss:       4.3452363\n"
     ]
    }
   ],
   "source": [
    "def loss(labels, logits):\n",
    "  return tf.keras.losses.sparse_categorical_crossentropy(labels, logits, from_logits=True)\n",
    "\n",
    "example_batch_loss  = loss(target_example_batch, example_batch_predictions)\n",
    "print(\"Prediction shape: \", example_batch_predictions.shape, \" # (batch_size, sequence_length, vocab_size)\") \n",
    "print(\"scalar_loss:      \", example_batch_loss.numpy().mean())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 102,
   "id": "8e95f04f-b9fd-4e0f-bc3e-b063ccf0766a",
   "metadata": {},
   "outputs": [],
   "source": [
    "model.compile(\n",
    "    optimizer = tf.keras.optimizers.Adam(learning_rate=0.001),\n",
    "    loss = loss)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 103,
   "id": "ffd06e9c-3176-4f15-8b10-8e5a5aafca95",
   "metadata": {},
   "outputs": [],
   "source": [
    "checkpoint_dir = \"./training\"\n",
    "os.makedirs(checkpoint_dir, exist_ok=True)  # Ensure the directory exists\n",
    "\n",
    "# ✅ Change the file extension to `.weights.h5`\n",
    "checkpoint_prefix = os.path.join(checkpoint_dir, \"ckpt_{epoch}.weights.h5\")\n",
    "\n",
    "checkpoint_callback = tf.keras.callbacks.ModelCheckpoint(\n",
    "    filepath=checkpoint_prefix,\n",
    "    save_weights_only=True  # Since we're only saving weights, use .weights.h5\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 104,
   "id": "4c755f13-74cf-4af8-871e-325eb61599c8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m26s\u001b[0m 1s/step - loss: 4.2594\n",
      "Epoch 2/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 2.6668\n",
      "Epoch 3/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 2.2668\n",
      "Epoch 4/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 2.1199\n",
      "Epoch 5/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 1.9972\n",
      "Epoch 6/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 1.8941\n",
      "Epoch 7/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 1.8074\n",
      "Epoch 8/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 1.7213\n",
      "Epoch 9/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 1.6340\n",
      "Epoch 10/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 1.5708\n",
      "Epoch 11/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m23s\u001b[0m 1s/step - loss: 1.5008\n",
      "Epoch 12/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 1.4422\n",
      "Epoch 13/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 1.3741\n",
      "Epoch 14/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 1.3241\n",
      "Epoch 15/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 1.2786\n",
      "Epoch 16/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 1.2324\n",
      "Epoch 17/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 1.1914\n",
      "Epoch 18/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 1.1482\n",
      "Epoch 19/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 1.1092\n",
      "Epoch 20/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 1.0600\n",
      "Epoch 21/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 1.0288\n",
      "Epoch 22/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 0.9992\n",
      "Epoch 23/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 0.9603\n",
      "Epoch 24/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.9233\n",
      "Epoch 25/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 0.8856\n",
      "Epoch 26/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.8523\n",
      "Epoch 27/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.8098\n",
      "Epoch 28/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.7755\n",
      "Epoch 29/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 0.7429\n",
      "Epoch 30/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.7015\n",
      "Epoch 31/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.6636\n",
      "Epoch 32/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.6327\n",
      "Epoch 33/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 0.5944\n",
      "Epoch 34/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.5596\n",
      "Epoch 35/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 0.5287\n",
      "Epoch 36/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 0.4971\n",
      "Epoch 37/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.4716\n",
      "Epoch 38/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.4385\n",
      "Epoch 39/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.4170\n",
      "Epoch 40/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.3905\n",
      "Epoch 41/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 0.3685\n",
      "Epoch 42/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.3475\n",
      "Epoch 43/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.3301\n",
      "Epoch 44/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.3147\n",
      "Epoch 45/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.2992\n",
      "Epoch 46/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.2849\n",
      "Epoch 47/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.2700\n",
      "Epoch 48/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.2616\n",
      "Epoch 49/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.2517\n",
      "Epoch 50/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.2480\n",
      "Epoch 51/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m23s\u001b[0m 1s/step - loss: 0.2389\n",
      "Epoch 52/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m23s\u001b[0m 999ms/step - loss: 0.2352\n",
      "Epoch 53/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.2243\n",
      "Epoch 54/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.2184\n",
      "Epoch 55/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.2135\n",
      "Epoch 56/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 0.2058\n",
      "Epoch 57/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.2047\n",
      "Epoch 58/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m23s\u001b[0m 1000ms/step - loss: 0.2026\n",
      "Epoch 59/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1972\n",
      "Epoch 60/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 0.1978\n",
      "Epoch 61/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1903\n",
      "Epoch 62/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 0.1881\n",
      "Epoch 63/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 0.1862\n",
      "Epoch 64/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 0.1837\n",
      "Epoch 65/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1821\n",
      "Epoch 66/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 0.1762\n",
      "Epoch 67/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1759\n",
      "Epoch 68/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1706\n",
      "Epoch 69/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1696\n",
      "Epoch 70/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1692\n",
      "Epoch 71/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1652\n",
      "Epoch 72/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1661\n",
      "Epoch 73/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1660\n",
      "Epoch 74/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1626\n",
      "Epoch 75/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1599\n",
      "Epoch 76/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 0.1549\n",
      "Epoch 77/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 0.1551\n",
      "Epoch 78/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1536\n",
      "Epoch 79/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1511\n",
      "Epoch 80/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 0.1482\n",
      "Epoch 81/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1460\n",
      "Epoch 82/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1491\n",
      "Epoch 83/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1490\n",
      "Epoch 84/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1472\n",
      "Epoch 85/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1478\n",
      "Epoch 86/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1435\n",
      "Epoch 87/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1482\n",
      "Epoch 88/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1445\n",
      "Epoch 89/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1422\n",
      "Epoch 90/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1415\n",
      "Epoch 91/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1418\n",
      "Epoch 92/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1403\n",
      "Epoch 93/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1409\n",
      "Epoch 94/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 0.1406\n",
      "Epoch 95/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1401\n",
      "Epoch 96/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 0.1372\n",
      "Epoch 97/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 0.1377\n",
      "Epoch 98/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1355\n",
      "Epoch 99/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m24s\u001b[0m 1s/step - loss: 0.1368\n",
      "Epoch 100/100\n",
      "\u001b[1m23/23\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m25s\u001b[0m 1s/step - loss: 0.1346\n"
     ]
    }
   ],
   "source": [
    "history = model.fit(dataset.repeat(), epochs=100, steps_per_epoch=steps_per_epoch, callbacks=[checkpoint_callback])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 105,
   "id": "6ef5018e-c738-4cb1-903d-bc68cce4cc3f",
   "metadata": {},
   "outputs": [],
   "source": [
    "checkpoint_path = tf.train.latest_checkpoint(checkpoint_dir)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 106,
   "id": "23f2a57a-28c8-4938-8e8b-16e1b291485e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# if checkpoint_path:\n",
    "model = build_model(vocab_size, embedding_dim, rnn_units, batch_size=1)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 107,
   "id": "e076a754-0eea-46a6-b609-0f91e56f4377",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "model.build(tf.TensorShape([1, None]))  # ✅ Ensure the model is built\n",
    "model.load_weights(\"./training/ckpt_20.weights.h5\")  # ✅ Load weights safely\n",
    "# print(\"Checkpoint loaded:\", checkpoint_path)\n",
    "# else:\n",
    "   # print(\"No checkpoint found. Skipping weight loading.\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 108,
   "id": "d6a45650-c0db-40cd-81ae-82b4d3bdb770",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\">Model: \"sequential_5\"</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1mModel: \"sequential_5\"\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓\n",
       "┃<span style=\"font-weight: bold\"> Layer (type)                    </span>┃<span style=\"font-weight: bold\"> Output Shape           </span>┃<span style=\"font-weight: bold\">       Param # </span>┃\n",
       "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩\n",
       "│ embedding_5 (<span style=\"color: #0087ff; text-decoration-color: #0087ff\">Embedding</span>)         │ (<span style=\"color: #00af00; text-decoration-color: #00af00\">1</span>, <span style=\"color: #00d7ff; text-decoration-color: #00d7ff\">None</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">256</span>)         │        <span style=\"color: #00af00; text-decoration-color: #00af00\">19,712</span> │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ gru_5 (<span style=\"color: #0087ff; text-decoration-color: #0087ff\">GRU</span>)                     │ (<span style=\"color: #00af00; text-decoration-color: #00af00\">1</span>, <span style=\"color: #00d7ff; text-decoration-color: #00d7ff\">None</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">1024</span>)        │     <span style=\"color: #00af00; text-decoration-color: #00af00\">3,938,304</span> │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ dense_5 (<span style=\"color: #0087ff; text-decoration-color: #0087ff\">Dense</span>)                 │ (<span style=\"color: #00af00; text-decoration-color: #00af00\">1</span>, <span style=\"color: #00d7ff; text-decoration-color: #00d7ff\">None</span>, <span style=\"color: #00af00; text-decoration-color: #00af00\">77</span>)          │        <span style=\"color: #00af00; text-decoration-color: #00af00\">78,925</span> │\n",
       "└─────────────────────────────────┴────────────────────────┴───────────────┘\n",
       "</pre>\n"
      ],
      "text/plain": [
       "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓\n",
       "┃\u001b[1m \u001b[0m\u001b[1mLayer (type)                   \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mOutput Shape          \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m      Param #\u001b[0m\u001b[1m \u001b[0m┃\n",
       "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩\n",
       "│ embedding_5 (\u001b[38;5;33mEmbedding\u001b[0m)         │ (\u001b[38;5;34m1\u001b[0m, \u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m256\u001b[0m)         │        \u001b[38;5;34m19,712\u001b[0m │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ gru_5 (\u001b[38;5;33mGRU\u001b[0m)                     │ (\u001b[38;5;34m1\u001b[0m, \u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m1024\u001b[0m)        │     \u001b[38;5;34m3,938,304\u001b[0m │\n",
       "├─────────────────────────────────┼────────────────────────┼───────────────┤\n",
       "│ dense_5 (\u001b[38;5;33mDense\u001b[0m)                 │ (\u001b[38;5;34m1\u001b[0m, \u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m77\u001b[0m)          │        \u001b[38;5;34m78,925\u001b[0m │\n",
       "└─────────────────────────────────┴────────────────────────┴───────────────┘\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\"> Total params: </span><span style=\"color: #00af00; text-decoration-color: #00af00\">4,036,941</span> (15.40 MB)\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1m Total params: \u001b[0m\u001b[38;5;34m4,036,941\u001b[0m (15.40 MB)\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\"> Trainable params: </span><span style=\"color: #00af00; text-decoration-color: #00af00\">4,036,941</span> (15.40 MB)\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1m Trainable params: \u001b[0m\u001b[38;5;34m4,036,941\u001b[0m (15.40 MB)\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-weight: bold\"> Non-trainable params: </span><span style=\"color: #00af00; text-decoration-color: #00af00\">0</span> (0.00 B)\n",
       "</pre>\n"
      ],
      "text/plain": [
       "\u001b[1m Non-trainable params: \u001b[0m\u001b[38;5;34m0\u001b[0m (0.00 B)\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "model.summary()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 109,
   "id": "6a44726b-9836-49ab-9aea-788736b833f3",
   "metadata": {},
   "outputs": [],
   "source": [
    "def generate_text(model, start_string):\n",
    "    num_generate = 1000  # Number of characters to generate\n",
    "    input_eval = [char2idx[s] for s in start_string]  # Convert start string to numbers\n",
    "    input_eval = tf.expand_dims(input_eval, 0)  # Add batch dimension\n",
    "\n",
    "    text_generated = []\n",
    "    temperature = 1.0  # Adjust for randomness\n",
    "\n",
    "    # ✅ Reset states correctly for stateful RNNs\n",
    "    for layer in model.layers:\n",
    "        if hasattr(layer, \"reset_states\"):\n",
    "            layer.reset_states()\n",
    "\n",
    "    for i in range(num_generate):\n",
    "        predictions = model(input_eval)\n",
    "        predictions = tf.squeeze(predictions, 0)  # Remove batch dimension\n",
    "        predicted_id = tf.random.categorical(predictions / temperature, num_samples=1)[-1, 0].numpy()\n",
    "        \n",
    "        input_eval = tf.expand_dims([predicted_id], 0)\n",
    "        text_generated.append(idx2char[predicted_id])\n",
    "\n",
    "    return start_string + ''.join(text_generated)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 110,
   "id": "55976bbb-f873-4025-acc8-eb5cf819c1b6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Alice; âand a sen it\n",
      "far of elioting so say did it! Oh, feelingol door, now,â the Hatter was just becknsember:\n",
      "âput on do,â Alice said, âand Iâd near ot washing so crance in?â\n",
      "\n",
      "âIâm seems to grevels to hers!â\n",
      "\n",
      "âWhat didâbut Iâmlen a tracK among to to, you know at the bottle, so\n",
      "soose it was going on trember, glease dishing ot a boot again.\n",
      "\n",
      "Alice knew that if I wonder? Can have, but at they, we headââ resh. So\n",
      "everything realing purdses, the roommolened herself to listen quistles rugh, and was\n",
      "getting on their fig; It do wo do sin_ a coming eathe King.\n",
      "\n",
      "âWell in be Mychosice\n",
      "trimes and see there) time she fall naver of it op go on. âDo you donât know, Iâll be\n",
      "gived oncy wouble on blea-thing,â said Alice,\n",
      "       not breathe!fle tells on her Nonger\n",
      "question. Alice head impatiently. âWhat does that?â\n",
      "\n",
      "âI couldnât mutt pressed.\n",
      "\n",
      "âWhat donât talk how cr, they _is_ a Can, you shouted it,\n",
      "for she can of thein crisn not rose\n"
     ]
    }
   ],
   "source": [
    "print(generate_text(model, start_string=u\"Alice\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e7f24cef-31ee-429f-8516-98d6e9b49829",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.12.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
