{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Lecture 8: Fine-tuning for Classification\n",
    "\n",
    "Lecture 8 | CMU ANLP Fall 2025 | Instructor: Sean Welleck\n",
    "\n",
    "\n",
    "This notebook shows fine-tuning a language model with a classification head. \n",
    "\n",
    "Task: Given a name, predict how many vowels (a, e, i, o, u) it contains."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Total names: 32033\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "['paityn',\n",
       " 'evalyn',\n",
       " 'luz',\n",
       " 'nathalia',\n",
       " 'winnie',\n",
       " 'chandler',\n",
       " 'ciara',\n",
       " 'danica',\n",
       " 'nailah',\n",
       " 'rilynn']"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data = open('names.txt').read().splitlines()\n",
    "print(f\"Total names: {len(data)}\")\n",
    "data[1000:1010]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Count vowels in names"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "emma: 2 vowels\n",
      "olivia: 4 vowels\n",
      "charlotte: 3 vowels\n",
      "xyz: 0 vowels\n",
      "aaa: 3 vowels\n"
     ]
    }
   ],
   "source": [
    "def count_vowels(name):\n",
    "    \"\"\"Count the number of vowels in a name.\"\"\"\n",
    "    vowels = 'aeiou'\n",
    "    return sum(1 for char in name.lower() if char in vowels)\n",
    "\n",
    "# Test the function\n",
    "test_names = ['emma', 'olivia', 'charlotte', 'xyz', 'aaa']\n",
    "for name in test_names:\n",
    "    print(f\"{name}: {count_vowels(name)} vowels\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Load the base model and tokenizer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/opt/miniconda3/envs/anlp/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n",
      "The new embeddings will be initialized from a multivariate normal distribution that has old embeddings' mean and covariance. As described in this article: https://nlp.stanford.edu/~johnhew/vocab-expansion.html. To disable this, use `mean_resizing=False`\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model parameters: 134,516,160\n"
     ]
    }
   ],
   "source": [
    "import transformers\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "from transformers import AutoTokenizer, AutoModelForCausalLM\n",
    "\n",
    "model_name = \"HuggingFaceTB/SmolLM2-135M\"\n",
    "\n",
    "tokenizer = AutoTokenizer.from_pretrained(model_name)\n",
    "base_model = AutoModelForCausalLM.from_pretrained(model_name)\n",
    "\n",
    "# Add special tokens\n",
    "tokenizer.add_special_tokens({\n",
    "    \"pad_token\": \"<|pad|>\",\n",
    "    \"bos_token\": \"<|startoftext|>\",\n",
    "})\n",
    "\n",
    "# Resize embeddings\n",
    "base_model.resize_token_embeddings(len(tokenizer))\n",
    "\n",
    "print(f\"Model parameters: {sum(p.numel() for p in base_model.parameters()):,}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create dataset and dataloaders"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train: 25626, Dev: 3203, Test: 3204\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input_ids': tensor([[49153, 44450,  1299,   337,     0, 49152],\n",
       "         [49153,   257,   101,  1241,     0, 49152],\n",
       "         [49153, 16427,   382,  5488,     0, 49152],\n",
       "         [49153,  1140,    81,     0, 49152, 49152],\n",
       "         [49153, 25616,   272,  1744,    94,     0],\n",
       "         [49153,  2432,  1437,     0, 49152, 49152],\n",
       "         [49153,  2292,    92,  1287,     0, 49152],\n",
       "         [49153,  7483,   903,     0, 49152, 49152],\n",
       "         [49153,    91,   576,   105,     0, 49152],\n",
       "         [49153,    83,  3231,     0, 49152, 49152],\n",
       "         [49153,    84,  1437, 18133,     0, 49152],\n",
       "         [49153,   105,   336,     0, 49152, 49152],\n",
       "         [49153,   322,  2889,   317,     0, 49152],\n",
       "         [49153,   106,  1951,  1905,    81,     0],\n",
       "         [49153,   344,  2554, 37602,     0, 49152],\n",
       "         [49153, 22258,  1437,     0, 49152, 49152],\n",
       "         [49153,    86,  9809,     0, 49152, 49152],\n",
       "         [49153,    99,  1287,   272,   519,     0],\n",
       "         [49153,   432,   672,  2292,     0, 49152],\n",
       "         [49153,    94,  1787,     0, 49152, 49152],\n",
       "         [49153,  1018,  1145,   257,     0, 49152],\n",
       "         [49153,    90,    88,  8026,  5488,     0],\n",
       "         [49153,   672,   369, 10945,     0, 49152],\n",
       "         [49153, 11074,   542,     0, 49152, 49152],\n",
       "         [49153, 12744,   541,     0, 49152, 49152],\n",
       "         [49153,  3804,   936,  1744,     0, 49152],\n",
       "         [49153,    99,  1145,  2554,     0, 49152],\n",
       "         [49153,    91,  2108,  1287,     0, 49152],\n",
       "         [49153,    90,  2231,   611,   292,     0],\n",
       "         [49153,   299,  1299,   306,     0, 49152],\n",
       "         [49153,    90,  1813,   258,     0, 49152],\n",
       "         [49153,   105,   721,   306,     0, 49152]]),\n",
       " 'attention_mask': tensor([[1, 1, 1, 1, 1, 0],\n",
       "         [1, 1, 1, 1, 1, 0],\n",
       "         [1, 1, 1, 1, 1, 0],\n",
       "         [1, 1, 1, 1, 0, 0],\n",
       "         [1, 1, 1, 1, 1, 1],\n",
       "         [1, 1, 1, 1, 0, 0],\n",
       "         [1, 1, 1, 1, 1, 0],\n",
       "         [1, 1, 1, 1, 0, 0],\n",
       "         [1, 1, 1, 1, 1, 0],\n",
       "         [1, 1, 1, 1, 0, 0],\n",
       "         [1, 1, 1, 1, 1, 0],\n",
       "         [1, 1, 1, 1, 0, 0],\n",
       "         [1, 1, 1, 1, 1, 0],\n",
       "         [1, 1, 1, 1, 1, 1],\n",
       "         [1, 1, 1, 1, 1, 0],\n",
       "         [1, 1, 1, 1, 0, 0],\n",
       "         [1, 1, 1, 1, 0, 0],\n",
       "         [1, 1, 1, 1, 1, 1],\n",
       "         [1, 1, 1, 1, 1, 0],\n",
       "         [1, 1, 1, 1, 0, 0],\n",
       "         [1, 1, 1, 1, 1, 0],\n",
       "         [1, 1, 1, 1, 1, 1],\n",
       "         [1, 1, 1, 1, 1, 0],\n",
       "         [1, 1, 1, 1, 0, 0],\n",
       "         [1, 1, 1, 1, 0, 0],\n",
       "         [1, 1, 1, 1, 1, 0],\n",
       "         [1, 1, 1, 1, 1, 0],\n",
       "         [1, 1, 1, 1, 1, 0],\n",
       "         [1, 1, 1, 1, 1, 1],\n",
       "         [1, 1, 1, 1, 1, 0],\n",
       "         [1, 1, 1, 1, 1, 0],\n",
       "         [1, 1, 1, 1, 1, 0]]),\n",
       " 'labels': tensor([3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 1, 1, 3, 3, 4, 2, 2, 4, 2, 2, 3, 3, 3, 3,\n",
       "         1, 1, 3, 2, 3, 3, 3, 2])}"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from torch.utils.data import Dataset, DataLoader\n",
    "import random\n",
    "\n",
    "class VowelCountDataset(Dataset):\n",
    "    def __init__(self, names, tokenizer, max_length=32):\n",
    "        self.names = names\n",
    "        self.tokenizer = tokenizer\n",
    "        self.max_length = max_length\n",
    "        self.labels = [min(count_vowels(name), 10) for name in names]  # Cap at 10\n",
    "    \n",
    "    def __len__(self):\n",
    "        return len(self.names)\n",
    "    \n",
    "    def __getitem__(self, idx):\n",
    "        name = self.names[idx]\n",
    "        label = self.labels[idx]\n",
    "        \n",
    "        # Tokenize the name\n",
    "        text = self.tokenizer.bos_token + name + self.tokenizer.eos_token\n",
    "        \n",
    "        return {\n",
    "            'text': text,\n",
    "            'label': label,\n",
    "            'name': name  # For debugging\n",
    "        }\n",
    "    \n",
    "    def collate_fn(self, batch):\n",
    "        texts = [item['text'] for item in batch]\n",
    "        labels = torch.tensor([item['label'] for item in batch])\n",
    "        \n",
    "        # Tokenize and pad\n",
    "        inputs = self.tokenizer(\n",
    "            texts,\n",
    "            padding=True,\n",
    "            truncation=True,\n",
    "            max_length=self.max_length,\n",
    "            return_tensors=\"pt\"\n",
    "        )\n",
    "        \n",
    "        return {\n",
    "            'input_ids': inputs['input_ids'],\n",
    "            'attention_mask': inputs['attention_mask'],\n",
    "            'labels': labels\n",
    "        }\n",
    "\n",
    "# Split data\n",
    "random.seed(123)\n",
    "random.shuffle(data)\n",
    "\n",
    "n1 = int(0.8 * len(data))\n",
    "n2 = int(0.9 * len(data))\n",
    "\n",
    "train_data = data[:n1]\n",
    "dev_data = data[n1:n2]\n",
    "test_data = data[n2:]\n",
    "\n",
    "print(f\"Train: {len(train_data)}, Dev: {len(dev_data)}, Test: {len(test_data)}\")\n",
    "\n",
    "# Create datasets\n",
    "train_dataset = VowelCountDataset(train_data, tokenizer)\n",
    "dev_dataset = VowelCountDataset(dev_data, tokenizer)\n",
    "test_dataset = VowelCountDataset(test_data, tokenizer)\n",
    "\n",
    "# Create dataloaders\n",
    "train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True, collate_fn=train_dataset.collate_fn)\n",
    "dev_loader = DataLoader(dev_dataset, batch_size=32, shuffle=False, collate_fn=dev_dataset.collate_fn)\n",
    "test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False, collate_fn=test_dataset.collate_fn)\n",
    "\n",
    "next(train_loader.__iter__())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Examine the label distribution"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA2QAAAIjCAYAAABswtioAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAR1ZJREFUeJzt3QecVNXdP/4D0myAJVIsYIuCDUVj7wRU9NFHk9glscQYjC3RSGJvWCL2SOwmsSexobGBJSo27A01wUgkgokiNvr8X9/z/Gb/s8tSdl256877/XqNw71z986dO3fG+5lzzve2KpVKpQQAAMBC13rhPyUAAABBIAMAACiIQAYAAFAQgQwAAKAgAhkAAEBBBDIAAICCCGQAAAAFEcgAAAAKIpABAAAURCADvhFOOeWU1KpVq4XyXNtss02+lT3yyCP5uf/0pz8tlOf/4Q9/mHr27Jmas88++ywdfPDBqWvXrnnfHHXUUamlqPv+0zL20Vf5XC3M7x+g+ghkwEJ33XXX5ZOb8q1Dhw6pe/fuacCAAeniiy9On376aZM8z4QJE/KJ1Isvvpiam+a8bQvirLPOyu/jYYcdlv7whz+k/ffff45lnn/++fz+nnDCCXNdz9tvv52XOeaYY9I30axZs9K1116bw8nSSy+d2rdvn0/6f/SjH6XnnnsuNQevv/56Ptbefffd1BxVfhfM6xY/jFSjCJKV+2GJJZZIq6yySvre976X/vznP6fZs2c3et033nhjuvDCC5t0e4GGa1UqlUqN+DuARosT+ThhPe2009LKK6+cZsyYkT744IN8wvXggw+mlVZaKd11111p3XXXrfmbmTNn5luEtwUVJ8QbbbRRPmGOk5oFNX369Hzfrl27fB/bte2226bbbrstnwQ1hXltW+yPOMmKk/vmapNNNklt2rRJjz/++DyX69WrV96ff//73+t9/NRTT81hYcyYMWmDDTZIzUG55Wd+AeDLL79Mu+++e7rvvvvSVlttlXbZZZccyiL43Hrrremtt95K7733XlphhRVSkaJl9/vf/356+OGHm6xVq+5n5Kv44x//WGv697//ff4eiKBf6bvf/W7q0qVLo5/nq3yuGvP901Ti++Hmm29OV111Vc1x989//jPdfffd6eWXX87v6Z133pk6duzY4HXvvPPO6dVXX222YR2qRZuiNwCoXjvuuGPacMMNa6aHDBmSRo0alU8S/ud//ie98cYbadFFF82Pxcl/3L5OX3zxRVpsscWa5CTzq2jbtm1q7iZNmpR69+493+X23XffdOKJJ6annnoqh7i6brrpprTmmms2mzDWEMcee2wOYxdccMEcXTZPPvnkPL+lasrPyH777VdrOo6VCGR158/t87owPlcL4/tnfs9fd3+cccYZ6eyzz87fm4cccki65ZZbCts+4KvRZRFoVrbbbrt8Ah+/AFf+cl7fGI44adtiiy1S586dczeeNdZYI/3qV7+qad2IFqgQrXHl7j7ROhfiV+W11147t8xE60ac2JX/dm7jY6J7WiwT46YWX3zxHBrHjx9fa5norlZfa1zlOue3bfWNdfn888/Tz3/+87TiiivmX/jjtf7mN79JdTs5xHoOP/zwdMcdd+TXF8uutdZaOTgsaNA66KCDcktEtAast9566frrr59jPN24cePSPffcU7Ptc/uFPQJZuWtUXbHvx44dW7NM+O1vf5u3N7Y7urEOHjw4TZ48uebx6NK6yCKL1Jp3/vnnz9HtMd6rJZdcMv3yl7+smRetI9E9K9Yfry1e46GHHpo+/vjj1FD/+te/0u9+97vcalPf+LnYxl/84he1WsdeeOGF/CNEtGTE8br99tvn8LEgY5XK3Xwr93McI/HjRbRSfuc738mvKbqyRQtT5d9F61iIVt663f+ipTa6Ci+77LL5x49osT7wwAMbPc4yWgbPPPPM/Lpje+I1vvPOOwuwR+f/fHP7vEbr0MCBA/PxEsfNqquumk4//fR8DFSq+7mKfRnbHJ+jK664Iv9d/H18Np999tn5vi8N+azF/okfn2KfxPPEsdMU49KOP/741L9//9x6Hy2yZQuyT2Kfxmc4vmvLx0V5/0QL6EknnZT69u2bOnXqlL/vttxyy9zKCjQ9LWRAsxPjkeJk64EHHsi//Nbntddeyyej0a0xuj7GSUec+D3xxBM1XeVifpxU/PjHP84nE2GzzTarWcd///vffIK811575V+f59cdKk4046QlTvIjuMTJfb9+/fI4sHJL3oJYkG2rFKErwl+cDEVY6tOnT7r//vtzC837778/R0tMnKD/5S9/ST/96U9zKIkQs8cee+Tuc8sss8xctyu6QsVJWuzHONGMk/M40YsT2QhARx55ZN726Ep29NFH55PuCInhW9/6Vr3rjHXE64oT9djOCCpl5ZC2zz775Ps4QY0ujLFPY2xahLXLL788nxzH+xotHLGvIljFa4z3P/ztb39LrVu3zveV4ScKj8TJe1mEr3J32SOOOCKHyksvvTQvW17/gvrrX/+au7DVN3ZubsdrbHuEseOOOy4/V5yUx/5+9NFH08Ybb5waI96r6EYbx8WgQYPSNddck9+vOJGOcBCvP15rHAPxmYr3L8R9HMNxMh/vXZzYxw8bEVLi2GmsaLGJ9yLC6CeffJLOPffcHLiffvrp9FXN7fMa72kE3AjkcR+t7PHZmjJlSjrvvPPmu944DmPcahwf8fmObY6uqP/4xz/me0wsyGctjq8ddtghdevWLR/fEYri8z+3z0xDxTEY35XxA9W3v/3tBd4nv/71r/N7FD8ulL9DYtkQy0UXyb333jt/B8f+ufrqq3N4f+aZZ/J3ENCEYgwZwMJ07bXXRrNO6dlnn53rMp06dSqtv/76NdMnn3xy/puyCy64IE9/+OGHc11HrD+Wieera+utt86PDR8+vN7H4lb28MMP52WXX3750pQpU2rm33rrrXn+RRddVDOvR48epUGDBs13nfPatvj7WE/ZHXfckZc944wzai33ve99r9SqVavSO++8UzMvlmvXrl2teS+99FKef8kll5Tm5cILL8zL/fGPf6yZN3369NKmm25aWmKJJWq99ti+gQMHlhbEZZddltd7//3318ybNWtW3p+x7jBp0qS83f3798+PlV166aX5b6+55pqav+vYsWPpuOOOy9OzZ88uLbPMMqXvf//7pUUWWaT06aef5vnDhg0rtW7duvTxxx/n6b/97W95PTfccEOtbbvvvvvmmF/3varP0Ucfnf/uhRdeWKB9sNtuu+XX9/e//71m3oQJE0pLLrlkaauttprrcV73MzNu3Lha70HMe+yxx2rmxX5s37596ec//3nNvNtuuy0vF8dxpdtvv32+n8O5mdtnpFevXqVp06bVzI/PRsx/5ZVXFnjdgwcPnmMfzOvz+sUXX8wx79BDDy0ttthipalTp871cxX7MtYZx89HH31UM//OO+/M8+++++55vi8L+lnbZZdd8ra8//77NfPefvvtUps2bep9r+uK7V588cXn+ngcg7GeOCYbuk/iM1y5T8pmzpxZ630M8Vnq0qVL6cADD5zvNgMNo8si0CzFL7XzqrYYv+aX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AACqMZA99thjaZdddkndu3dPrVq1SnfccUetx0ulUjrppJNSt27d0qKLLpr69euX3n777VrLfPTRR2nfffdNHTt2TJ07d04HHXRQ+uyzz2ot8/LLL6ctt9wydejQIa244orp3HPPnWNbbrvttrTmmmvmZdZZZ5107733fk2vGgAAoBkEss8//zytt9566bLLLqv38QhOF198cRo+fHh6+umn0+KLL54GDBiQpk6dWrNMhLHXXnstPfjgg2nEiBE55P34xz+ueXzKlCmpf//+qUePHmnMmDHpvPPOS6ecckq64oorapZ58skn0957753D3AsvvJB22223fHv11Ve/5j0AAABUs1alaIZqBqKF7Pbbb89BKMRmRcvZz3/+8/SLX/wiz/vkk09Sly5d0nXXXZf22muv9MYbb6TevXunZ599Nm244YZ5mfvuuy/ttNNO6V//+lf++8svvzz9+te/Th988EFq165dXub444/PrXFvvvlmnt5zzz1zOIxAV7bJJpukPn365DC4ICL4derUKW9jtNYBAADVaUoDskGzHUM2bty4HKKim2JZvKiNN944jR49Ok/HfXRTLIexEMu3bt06t6iVl9lqq61qwliIVraxY8emjz/+uGaZyucpL1N+nvpMmzYt7+jKGwAAQEM020AWYSxEi1ilmC4/FvfLLbdcrcfbtGmTll566VrL1LeOyueY2zLlx+szdOjQHBDLtxibBgAA0CICWXM3ZMiQ3ARZvo0fP77oTQIAAL5hmm0g69q1a76fOHFirfkxXX4s7idNmlTr8ZkzZ+bKi5XL1LeOyueY2zLlx+vTvn373B+08gYAANAQbVIztfLKK+dANHLkyFxcI8Q4rRgbdthhh+XpTTfdNE2ePDlXT+zbt2+eN2rUqDR79uw81qy8TBT1mDFjRmrbtm2eFxUZ11hjjbTUUkvVLBPPc9RRR9U8fywT8+Gbrufx96SW5N2zBxa9CQAALaOFLK4X9uKLL+ZbuZBH/Pu9997LVRcjIJ1xxhnprrvuSq+88ko64IADcuXEciXGXr16pR122CEdcsgh6ZlnnklPPPFEOvzww3MFxlgu7LPPPrmgR5S0j/L4t9xyS7rooovSMcccU7MdRx55ZK7OeP755+fKi1EW/7nnnsvrAgAAaJEtZBF6tt1225rpckgaNGhQLm1/3HHH5XL0cV2xaAnbYostcnCKizeX3XDDDTk4bb/99rm64h577JGvXVYWBTceeOCBNHjw4NyKtuyyy+aLTVdeq2yzzTZLN954YzrhhBPSr371q7T66qvnsvhrr732QtsXAABA9Wk21yH7pnMdMporXRYBABauFnEdMgAAgJZOIAMAACiIQAYAAFAQgQwAAKAgAhkAAEBBBDIAAICCCGQAAAAFEcgAAAAKIpABAAAURCADAAAoiEAGAABQEIEMAACgIAIZAABAQQQyAACAgghkAAAABRHIAAAACiKQAQAAFEQgAwAAKIhABgAAUBCBDAAAoCACGQAAQEEEMgAAgIIIZAAAAAURyAAAAAoikAEAABREIAMAACiIQAYAAFCQNkU9McDC0vP4e1JL8u7ZA4veBACgiWghAwAAKIhABgAAUBCBDAAAoCACGQAAQEEEMgAAgIIIZAAAAAURyAAAAAoikAEAABREIAMAACiIQAYAAFAQgQwAAKAgAhkAAEBBBDIAAICCCGQAAAAFEcgAAAAKIpABAAAURCADAAAoiEAGAABQEIEMAACgIAIZAABAQQQyAACAgghkAAAABRHIAAAACiKQAQAAFEQgAwAAKIhABgAAUBCBDAAAoCACGQAAQEEEMgAAgIIIZAAAAAURyAAAAAoikAEAABREIAMAACiIQAYAAFAQgQwAAKAgAhkAAEBBBDIAAICCCGQAAAAFEcgAAAAKIpABAAAURCADAAAoiEAGAABQEIEMAACgIAIZAABAQQQyAACAgghkAAAABRHIAAAACiKQAQAAFEQgAwAAKIhABgAAUJBmHchmzZqVTjzxxLTyyiunRRddNK266qrp9NNPT6VSqWaZ+PdJJ52UunXrlpfp169fevvtt2ut56OPPkr77rtv6tixY+rcuXM66KCD0meffVZrmZdffjltueWWqUOHDmnFFVdM55577kJ7nQAAQHVq1oHsnHPOSZdffnm69NJL0xtvvJGnIyhdcsklNcvE9MUXX5yGDx+enn766bT44ounAQMGpKlTp9YsE2HstddeSw8++GAaMWJEeuyxx9KPf/zjmsenTJmS+vfvn3r06JHGjBmTzjvvvHTKKaekK664YqG/ZgAAoHq0Sc3Yk08+mXbdddc0cODAPN2zZ8900003pWeeeaamdezCCy9MJ5xwQl4u/P73v09dunRJd9xxR9prr71ykLvvvvvSs88+mzbccMO8TAS6nXbaKf3mN79J3bt3TzfccEOaPn16uuaaa1K7du3SWmutlV588cU0bNiwWsENAACgalrINttsszRy5Mj01ltv5emXXnopPf7442nHHXfM0+PGjUsffPBB7qZY1qlTp7Txxhun0aNH5+m4j26K5TAWYvnWrVvnFrXyMltttVUOY2XRyjZ27Nj08ccf17tt06ZNyy1rlTcAAIAW00J2/PHH56Cz5pprpkUWWSSPKTvzzDNzF8QQYSxEi1ilmC4/FvfLLbdcrcfbtGmTll566VrLxDi1uusoP7bUUkvNsW1Dhw5Np556apO+XgAAoLo06xayW2+9NXcnvPHGG9Pzzz+frr/++tzNMO6LNmTIkPTJJ5/U3MaPH1/0JgEAAN8wzbqF7Nhjj82tZDEWLKyzzjrpn//8Z26dGjRoUOratWueP3HixFxlsSym+/Tpk/8dy0yaNKnWemfOnJkrL5b/Pu7jbyqVp8vL1NW+fft8AwAAaJEtZF988UUe61Upui7Onj07/zu6GUZginFmZdHFMcaGbbrppnk67idPnpyrJ5aNGjUqryPGmpWXicqLM2bMqFkmKjKuscYa9XZXBAAAaPGBbJdddsljxu6555707rvvpttvvz1XPvzf//3f/HirVq3SUUcdlc4444x01113pVdeeSUdcMABuXLibrvtlpfp1atX2mGHHdIhhxySqzM+8cQT6fDDD8+tbrFc2GeffXJBj7g+WZTHv+WWW9JFF12UjjnmmEJfPwAA0LI16y6LUZ4+Lgz905/+NHc7jAB16KGH5gtBlx133HHp888/z+XpoyVsiy22yGXu4wLPZTEOLULY9ttvn1vc9thjj3ztssrKjA888EAaPHhw6tu3b1p22WXzcyh5DwAAfJ1aleJiXnxl0VUygl0U+OjYsWPRmwM1eh5/T2pJ3j37/65L2BD2AQDQXLNBs+6yCAAA0JIJZAAAAAURyAAAAAoikAEAABREIAMAACiIQAYAAFAQgQwAAKAgAhkAAEBBBDIAAICCCGQAAAAFEcgAAAAKIpABAAAURCADAAAoiEAGAABQEIEMAACgIAIZAABAQQQyAACAgghkAAAABRHIAAAACiKQAQAAFEQgAwAAKIhABgAAUBCBDAAAoCACGQAAQEEEMgAAgIIIZAAAAAURyAAAAAoikAEAABREIAMAACiIQAYAAFAQgQwAAKAgAhkAAEBBBDIAAICCCGQAAAAFEcgAAAAKIpABAAAURCADAAAoiEAGAABQEIEMAACgIAIZAABAQQQyAACAgghkAAAABRHIAAAACiKQAQAAFEQgAwAAKIhABgAAUBCBDAAAoCACGQAAQEEEMgAAgIIIZAAAAAURyAAAAAoikAEAABREIAMAACiIQAYAAFAQgQwAAKAgAhkAAEBBBDIAAICCCGQAAAAFEcgAAAAKIpABAAAURCADAAAoiEAGAABQEIEMAACgIAIZAABAQQQyAACAgghkAAAABRHIAAAACiKQAQAAFEQgAwAAKIhABgAAUBCBDAAAoCACGQAAQEEEMgAAgIIIZAAAAAURyAAAAAoikAEAABREIAMAAChIsw9k77//ftpvv/3SMssskxZddNG0zjrrpOeee67m8VKplE466aTUrVu3/Hi/fv3S22+/XWsdH330Udp3331Tx44dU+fOndNBBx2UPvvss1rLvPzyy2nLLbdMHTp0SCuuuGI699xzF9prBAAAqlOzDmQff/xx2nzzzVPbtm3TX//61/T666+n888/Py211FI1y0Rwuvjii9Pw4cPT008/nRZffPE0YMCANHXq1JplIoy99tpr6cEHH0wjRoxIjz32WPrxj39c8/iUKVNS//79U48ePdKYMWPSeeedl0455ZR0xRVXLPTXDAAAVI82qRk755xzcmvVtddeWzNv5ZVXrtU6duGFF6YTTjgh7brrrnne73//+9SlS5d0xx13pL322iu98cYb6b777kvPPvts2nDDDfMyl1xySdppp53Sb37zm9S9e/d0ww03pOnTp6drrrkmtWvXLq211lrpxRdfTMOGDasV3AAAAKqmheyuu+7KIer73/9+Wm655dL666+frrzyyprHx40blz744IPcTbGsU6dOaeONN06jR4/O03Ef3RTLYSzE8q1bt84tauVlttpqqxzGyqKVbezYsbmVrj7Tpk3LLWuVNwAAgBYTyP7xj3+kyy+/PK2++urp/vvvT4cddlg64ogj0vXXX58fjzAWokWsUkyXH4v7CHOV2rRpk5Zeeulay9S3jsrnqGvo0KE5/JVv0ZIHAADQYgLZ7Nmz0wYbbJDOOuus3DoW3QcPOeSQPF6saEOGDEmffPJJzW38+PFFbxIAAPAN06wDWVRO7N27d615vXr1Su+9917+d9euXfP9xIkTay0T0+XH4n7SpEm1Hp85c2auvFi5TH3rqHyOutq3b5+rNlbeAAAAWkwgiwqLMY6r0ltvvZWrIZYLfERgGjlyZM3jMZYrxoZtuummeTruJ0+enKsnlo0aNSq3vsVYs/IyUXlxxowZNctERcY11lijVkVHAACAqglkRx99dHrqqadyl8V33nkn3XjjjbkU/eDBg/PjrVq1SkcddVQ644wzcgGQV155JR1wwAG5cuJuu+1W06K2ww475K6OzzzzTHriiSfS4YcfniswxnJhn332yQU94vpkUR7/lltuSRdddFE65phjCn39AABAy9asy95vtNFG6fbbb8/jtU477bTcIhZl7uO6YmXHHXdc+vzzz/P4smgJ22KLLXKZ+7jAc1mUtY8Qtv322+fqinvssUe+dllZFOV44IEHctDr27dvWnbZZfPFppW8BwAAvk6tSnExL76y6CoZwS4KfBhPRnPS8/h7Ukvy7tkDG/w39gEA0FyzQbPusggAANCSCWQAAAAFEcgAAAAKIpABAAAURCADAAAoiEAGAABQEIEMAACgIAIZAABAQQQyAACAgghkAAAA36RAtsoqq6T//ve/c8yfPHlyfgwAAICvKZC9++67adasWXPMnzZtWnr//fcbs0oAAICq06YhC9911101/77//vtTp06daqYjoI0cOTL17NmzabcQAACghWpQINttt93yfatWrdKgQYNqPda2bdscxs4///ym3UIAAIAWqkGBbPbs2fl+5ZVXTs8++2xadtllv67tAgAAaPEaFMjKxo0b1/RbAgAAUGUaFchCjBeL26RJk2pazsquueaaptg2AACAFq1RgezUU09Np512Wtpwww1Tt27d8pgyAAAAFkIgGz58eLruuuvS/vvv35g/BwAAoLHXIZs+fXrabLPNmn5rAAAAqkijAtnBBx+cbrzxxqbfGgAAgCrSqC6LU6dOTVdccUV66KGH0rrrrpuvQVZp2LBhTbV9AAAALVajAtnLL7+c+vTpk//96quv1npMgQ8AAICvMZA9/PDDjfkzAAAAvuoYMgAAAApqIdt2223n2TVx1KhRX2WbAAAAqkKjAll5/FjZjBkz0osvvpjHkw0aNKiptg0AAKBFa1Qgu+CCC+qdf8opp6TPPvvsq24TAABAVWjSMWT77bdfuuaaa5pylQAAAC1Wkway0aNHpw4dOjTlKgEAAFqsRnVZ3H333WtNl0ql9O9//zs999xz6cQTT2yqbQMAAGjRGhXIOnXqVGu6devWaY011kinnXZa6t+/f1NtGwAAQIvWqEB27bXXNv2WAAAAVJlGBbKyMWPGpDfeeCP/e6211krrr79+U20XAABAi9eoQDZp0qS01157pUceeSR17tw5z5s8eXK+YPTNN9+cvvWtbzX1dgIAALQ4jaqy+LOf/Sx9+umn6bXXXksfffRRvsVFoadMmZKOOOKIpt9KAACAFqhRLWT33Xdfeuihh1KvXr1q5vXu3TtddtllinoAAAB8nS1ks2fPTm3btp1jfsyLxwAAAPiaAtl2222XjjzyyDRhwoSaee+//346+uij0/bbb9+YVQIAAFSdRgWySy+9NI8X69mzZ1p11VXzbeWVV87zLrnkkqbfSgAAgBaoUWPIVlxxxfT888/ncWRvvvlmnhfjyfr169fU2wcAANBiNaiFbNSoUbl4R7SEtWrVKn33u9/NFRfjttFGG+Vrkf3tb3/7+rYWAACgWgPZhRdemA455JDUsWPHOR7r1KlTOvTQQ9OwYcOacvsAAABarAYFspdeeintsMMOc308St6PGTOmKbYLAACgxWtQIJs4cWK95e7L2rRpkz788MOm2C4AAIAWr0GBbPnll0+vvvrqXB9/+eWXU7du3ZpiuwAAAFq8BgWynXbaKZ144olp6tSpczz25ZdfppNPPjntvPPOTbl9AAAALVaDyt6fcMIJ6S9/+Uv69re/nQ4//PC0xhpr5PlR+v6yyy5Ls2bNSr/+9a+/rm0FAABoURoUyLp06ZKefPLJdNhhh6UhQ4akUqmU50cJ/AEDBuRQFssAAADwNVwYukePHunee+9NH3/8cXrnnXdyKFt99dXTUkst1dBVAQAAVLUGB7KyCGBxMWgAAAAWQlEPAAAAmo5ABgAAUBCBDAAAoCACGQAAQEEEMgAAgIIIZAAAAAURyAAAAAoikAEAABREIAMAACiIQAYAAFAQgQwAAKAgAhkAAEBBBDIAAICCCGQAAAAFEcgAAAAKIpABAAAURCADAAAoiEAGAABQEIEMAACgIAIZAABAQQQyAACAgghkAAAABRHIAAAACiKQAQAAFEQgAwAAKIhABgAAUBCBDAAAoCDfqEB29tlnp1atWqWjjjqqZt7UqVPT4MGD0zLLLJOWWGKJtMcee6SJEyfW+rv33nsvDRw4MC222GJpueWWS8cee2yaOXNmrWUeeeSRtMEGG6T27dun1VZbLV133XUL7XUBAADV6RsTyJ599tn0u9/9Lq277rq15h999NHp7rvvTrfddlt69NFH04QJE9Luu+9e8/isWbNyGJs+fXp68skn0/XXX5/D1kknnVSzzLhx4/Iy2267bXrxxRdz4Dv44IPT/fffv1BfIwAAUF2+EYHss88+S/vuu2+68sor01JLLVUz/5NPPklXX311GjZsWNpuu+1S375907XXXpuD11NPPZWXeeCBB9Lrr7+e/vjHP6Y+ffqkHXfcMZ1++unpsssuyyEtDB8+PK288srp/PPPT7169UqHH354+t73vpcuuOCCwl4zAADQ8n0jAll0SYwWrH79+tWaP2bMmDRjxoxa89dcc8200korpdGjR+fpuF9nnXVSly5dapYZMGBAmjJlSnrttddqlqm77limvI76TJs2La+j8gYAANAQbVIzd/PNN6fnn38+d1ms64MPPkjt2rVLnTt3rjU/wlc8Vl6mMoyVHy8/Nq9lImR9+eWXadFFF53juYcOHZpOPfXUJniFAABAtWrWLWTjx49PRx55ZLrhhhtShw4dUnMyZMiQ3GWyfIttBQAAaDGBLLokTpo0KVc/bNOmTb5F4Y6LL744/ztasWIc2OTJk2v9XVRZ7Nq1a/533Netulient8yHTt2rLd1LEQ1xni88gYAANBiAtn222+fXnnllVz5sHzbcMMNc4GP8r/btm2bRo4cWfM3Y8eOzWXuN9100zwd97GOCHZlDz74YA5QvXv3rlmmch3lZcrrAAAAqLoxZEsuuWRae+21a81bfPHF8zXHyvMPOuigdMwxx6Sll146h6yf/exnOUhtsskm+fH+/fvn4LX//vunc889N48XO+GEE3KhkGjlCj/5yU/SpZdemo477rh04IEHplGjRqVbb7013XPPPQW8agAAoFo060C2IKI0fevWrfMFoaPyYVRH/O1vf1vz+CKLLJJGjBiRDjvssBzUItANGjQonXbaaTXLRMn7CF9xTbOLLroorbDCCumqq67K6wIAAPi6tCqVSqWvbe1VJCoydurUKRf4MJ6M5qTn8S2rpffdswc2+G/sAwCguWaDZj2GDAAAoCUTyAAAAAoikAEAABREIAMAACiIQAYAAFAQgQwAAKAgAhkAAEBBBDIAAICCCGQAAAAFEcgAAAAKIpABAAAURCADAAAoiEAGAABQEIEMAACgIAIZAABAQQQyAACAgghkAAAABRHIAAAACiKQAQAAFEQgAwAAKIhABgAAUJA2RT0xAAtPz+PvSS3Nu2cPLHoTAOAr00IGAABQEIEMAACgIAIZAABAQQQyAACAgghkAAAABRHIAAAACiKQAQAAFEQgAwAAKIhABgAAUBCBDAAAoCACGQAAQEEEMgAAgIIIZAAAAAURyAAAAAoikAEAABREIAMAACiIQAYAAFAQgQwAAKAgAhkAAEBBBDIAAICCCGQAAAAFEcgAAAAKIpABAAAURCADAAAoiEAGAABQEIEMAACgIAIZAABAQQQyAACAgghkAAAABRHIAAAACiKQAQAAFEQgAwAAKIhABgAAUBCBDAAAoCACGQAAQEEEMgAAgIIIZAAAAAURyAAAAAoikAEAABREIAMAAChIm6KeGBaGnsffk1qSd88eWPQmAADQhLSQAQAAFEQgAwAAKIhABgAAUBCBDAAAoCACGQAAQEEEMgAAgIIIZAAAAAURyAAAAAoikAEAABREIAMAACiIQAYAAFAQgQwAAKAgAhkAAEBBBDIAAICCNOtANnTo0LTRRhulJZdcMi233HJpt912S2PHjq21zNSpU9PgwYPTMsssk5ZYYom0xx57pIkTJ9Za5r333ksDBw5Miy22WF7Psccem2bOnFlrmUceeSRtsMEGqX379mm11VZL11133UJ5jQAAQPVq1oHs0UcfzWHrqaeeSg8++GCaMWNG6t+/f/r8889rljn66KPT3XffnW677ba8/IQJE9Luu+9e8/isWbNyGJs+fXp68skn0/XXX5/D1kknnVSzzLhx4/Iy2267bXrxxRfTUUcdlQ4++OB0//33L/TXDAAAVI82qRm77777ak1HkIoWrjFjxqStttoqffLJJ+nqq69ON954Y9puu+3yMtdee23q1atXDnGbbLJJeuCBB9Lrr7+eHnroodSlS5fUp0+fdPrpp6df/vKX6ZRTTknt2rVLw4cPTyuvvHI6//zz8zri7x9//PF0wQUXpAEDBhTy2gEAgJavWbeQ1RUBLCy99NL5PoJZtJr169evZpk111wzrbTSSmn06NF5Ou7XWWedHMbKImRNmTIlvfbaazXLVK6jvEx5HfWZNm1aXkflDQAAoEUGstmzZ+euhJtvvnlae+2187wPPvggt3B17ty51rIRvuKx8jKVYaz8ePmxeS0TIevLL7+c6/i2Tp061dxWXHHFJny1AABANfjGBLIYS/bqq6+mm2++OTUHQ4YMyS125dv48eOL3iQAAOAbplmPISs7/PDD04gRI9Jjjz2WVlhhhZr5Xbt2zcU6Jk+eXKuVLKosxmPlZZ555pla6ytXYaxcpm5lxpju2LFjWnTRRevdpqjGGDcAAIAW2UJWKpVyGLv99tvTqFGjcuGNSn379k1t27ZNI0eOrJkXZfGjzP2mm26ap+P+lVdeSZMmTapZJio2Rtjq3bt3zTKV6ygvU14HAABA1bWQRTfFqKB455135muRlcd8xZitaLmK+4MOOigdc8wxudBHhKyf/exnOUhFhcUQZfIjeO2///7p3HPPzes44YQT8rrLLVw/+clP0qWXXpqOO+64dOCBB+bwd+utt6Z77rmn0NcPAAC0bM26hezyyy/P47O22Wab1K1bt5rbLbfcUrNMlKbfeeed8wWhoxR+dD/8y1/+UvP4Iosskrs7xn0Etf322y8dcMAB6bTTTqtZJlreInxFq9h6662Xy99fddVVSt4DAADV20IWXRbnp0OHDumyyy7Lt7np0aNHuvfee+e5ngh9L7zwQqO2EwAAoMW1kAEAALRkAhkAAEBBBDIAAICCCGQAAAAFEcgAAAAKIpABAAAURCADAAAoiEAGAABQEIEMAACgIAIZAABAQQQyAACAgghkAAAABRHIAAAACiKQAQAAFEQgAwAAKIhABgAAUBCBDAAAoCACGQAAQEEEMgAAgIIIZAAAAAURyAAAAAoikAEAABREIAMAACiIQAYAAFAQgQwAAKAgAhkAAEBBBDIAAICCCGQAAAAFEcgAAAAKIpABAAAURCADAAAoiEAGAABQEIEMAACgIAIZAABAQQQyAACAgghkAAAABRHIAAAACiKQAQAAFEQgAwAAKIhABgAAUBCBDAAAoCACGQAAQEEEMgAAgIIIZAAAAAURyAAAAAoikAEAABREIAMAACiIQAYAAFAQgQwAAKAgAhkAAEBBBDIAAICCCGQAAAAFEcgAAAAKIpABAAAURCADAAAoiEAGAABQEIEMAACgIAIZAABAQQQyAACAgghkAAAABRHIAAAACtKmqCcGgIWt5/H3pJbk3bMHFr0JAHxFWsgAAAAKIpABAAAURCADAAAoiEAGAABQEIEMAACgIAIZAABAQQQyAACAgghkAAAABRHIAAAACiKQAQAAFEQgAwAAKIhABgAAUBCBDAAAoCACGQAAQEEEMgAAgIK0KeqJ+Xr1PP6e1NK8e/bAojcBAACalBayOi677LLUs2fP1KFDh7TxxhunZ555puhNAgAAWigtZBVuueWWdMwxx6Thw4fnMHbhhRemAQMGpLFjx6bllluu6M0DgK+spfWg0HsC+KbTQlZh2LBh6ZBDDkk/+tGPUu/evXMwW2yxxdI111xT9KYBAAAtkBay/2f69OlpzJgxaciQITXzWrdunfr165dGjx49x/LTpk3Lt7JPPvkk30+ZMiU1B7OnfZFamsbs25a2H+wD+yDYB//HfrAPGrsP1j75/tSSvHrqgKI3AZjLd1OpVErz06q0IEtVgQkTJqTll18+Pfnkk2nTTTetmX/cccelRx99ND399NO1lj/llFPSqaeeWsCWAgAA3wTjx49PK6ywwjyX0ULWSNGSFuPNymbPnp0++uijtMwyy6RWrVqlakn+K664Yj7QOnbsmKqV/WAfBPvAPiizH+yDYB/YB2X2Q3Xug1KplD799NPUvXv3+S4rkP0/yy67bFpkkUXSxIkTa82P6a5du86xfPv27fOtUufOnVM1ig9WtXy45sV+sA+CfWAflNkP9kGwD+yDMvuh+vZBp06dFmg5RT3+n3bt2qW+ffumkSNH1mr1iunKLowAAABNRQtZheiCOGjQoLThhhum73znO7ns/eeff56rLgIAADQ1gazCnnvumT788MN00kknpQ8++CD16dMn3XfffalLly5Fb1qzFF02Tz755Dm6blYb+8E+CPaBfVBmP9gHwT6wD8rsB/tgflRZBAAAKIgxZAAAAAURyAAAAAoikAEAABREIAMAACiIQEajXXbZZalnz56pQ4cOaeONN07PPPNMqiaPPfZY2mWXXfIV2Fu1apXuuOOOVG2GDh2aNtpoo7Tkkkum5ZZbLu22225p7NixqZpcfvnlad1116252GVct/Cvf/1rqmZnn312/kwcddRRqVqccsop+TVX3tZcc81Ubd5///203377pWWWWSYtuuiiaZ111knPPfdcqibx/8W6x0LcBg8enKrFrFmz0oknnphWXnnlfBysuuqq6fTTT0/VVkfu008/zd+DPXr0yPths802S88++2yqhnOgeK+janm3bt3ya+/Xr196++23C9ve5k4go1FuueWWfN22KGH6/PPPp/XWWy8NGDAgTZo0KVWLuEZdvO4IptXq0UcfzScZTz31VHrwwQfTjBkzUv/+/fO+qRYrrLBCDiBjxozJJ57bbbdd2nXXXdNrr72WqlGcbPzud7/LIbXarLXWWunf//53ze3xxx9P1eTjjz9Om2++eWrbtm3+UeL1119P559/flpqqaVStX0GKo+D+G4M3//+91O1OOecc/KPVZdeeml644038vS5556bLrnkklRNDj744Pz+/+EPf0ivvPJK/v9jBJP44aKlnwPF+33xxRen4cOHp6effjotvvji+Txx6tSp9S7/7rvv5mBXtaLsPTTUd77zndLgwYNrpmfNmlXq3r17aejQoaVqFB+l22+/vVTtJk2alPfFo48+WqpmSy21VOmqq64qVZtPP/20tPrqq5cefPDB0tZbb1068sgjS9Xi5JNPLq233nqlavbLX/6ytMUWWxS9Gc1OfA5WXXXV0uzZs0vVYuDAgaUDDzyw1rzdd9+9tO+++5aqxRdffFFaZJFFSiNGjKg1f4MNNij9+te/LrXkc6A41rt27Vo677zzauZNnjy51L59+9JNN91U7zrGjRuX11OttJDRYNOnT8+tAfErT1nr1q3z9OjRowvdNor1ySef5Pull146VaPopnPzzTfnXw6j62K1idbSgQMH1vpuqCbRHSe676yyyipp3333Te+9916qJnfddVfacMMNc0tQdGFef/3105VXXpmq/f+Xf/zjH9OBBx5YVb/+R9e8kSNHprfeeitPv/TSS7nFeMcdd0zVYubMmfn/CTGso1J032vprefjxo1LH3zwQa3/F3Tq1CkPb3GeWL82c5kPc/Wf//wnf8l06dKl1vyYfvPNNwvbLoo1e/bs3Fc+uiytvfbaqZpEV5QIYNEVY4kllki333576t27d9GbtVBFEI3uyy1lfERDxYnGddddl9ZYY43cTe3UU09NW265ZXr11VfzGMtq8I9//CN3U4vu7L/61a/ysXDEEUekdu3apUGDBqVqFONqJk+enH74wx+manL88cenKVOm5HGUiyyySD5nOPPMM/MPFdUiPvfx/4UYO9erV698jnTTTTflQLLaaqullizCWKjvPLH8GLUJZECTtY7EyWdL/+WvPnES/uKLL+YWwj/96U/55DPG11VLKBs/fnw68sgj81iJur8GV4vKX/5j/FwEtBjIf+utt6aDDjooVcuPMtFCdtZZZ+XpaCGL74QYQ1Ktgezqq6/Ox0a0nFaTOO5vuOGGdOONN+axlfH9GD/YxX6opmMhxo5F6+jyyy+fg+kGG2yQ9t5779zLiP8bd/vPf/4z/7tc8CV+1CyLH7WqpUiWQEaDLbvssvmLZeLEibXmx3TXrl0L2y6Kc/jhh6cRI0bkqktR5KLaRAtA+RfPvn375paBiy66KBe3qAZxchEFfeJkoyx+EY/jIQb1T5s2LX9nVJPOnTunb3/72+mdd95J1SKqqdX9ESJaBv785z+nahQnmg899FD6y1/+kqrNsccem1vJ9tprrzwd1TZjf0Rl3moKZFFdMn6ci27s0WIYn5E999wzd2tuycrngnFeGK+5LKb79OlTM33vvffmYmAhCp1ss802ObxXdu+sFsaQ0aiTzzjpjP7hlb+MxnQ1jpupZvGLVoSx6KI3atSoXOKY//s8RAipFttvv33uthn/Iy3foqUkuifFv6stjIXPPvss/f3vf691MtLSRXflupe9iDFE0VJYja699to8li7GVVabL774Io8trxTfA/HdWI2iwmB8F0Ql0vvvvz9X4m3J4lwgQlnleWIE0qi2WHmeGN8N8WPmaqutVvM9UZ6OW7QsVgstZDRKjBGIX7nipOs73/lOuvDCC/MvQD/60Y9SNZ1wVf76HYNY4+QzClqstNJKqVq6KUaXlDvvvDP3ly/3DY/Bu9Xyy9aQIUNyl6R4z+OaM7E/Hnnkkfw/3WoR733dcYNxAhLXoqqW8YS/+MUv8jV54qRiwoQJ+ZIgcQIa3ZOqxdFHH52LOUSXxR/84Af52pRXXHFFvlWbCB4RyOL/k23aVN+pVnwWYsxYfC9Gt7QXXnghDRs2LHffqybx/4H44TK6tcf5QrQcxri6lnCuNL9zoOiiesYZZ6TVV189B7S4Ll10WY3rlVKPoss88s11ySWXlFZaaaVSu3btchn8p556qlRNHn744Vyite5t0KBBpWpR3+uP27XXXluqFlHauUePHvlz8K1vfau0/fbblx544IFStau2svd77rlnqVu3bvk4WH755fP0O++8U6o2d999d2nttdfO5a3XXHPN0hVXXFGqRvfff3/+Lhw7dmypGk2ZMiV//uMcoUOHDqVVVlkll3qfNm1aqZrccsst+bXH90KUgY/LBUX592o4B4rS9yeeeGKpS5cu+fsg/t84r8/DuCove98q/lNfUAMAAODrZQwZAABAQQQyAACAgghkAAAABRHIAAAACiKQAQAAFEQgAwAAKIhABgAAUBCBDAAAoCACGQAt1rvvvptatWqVXnzxxdRcvPnmm2mTTTZJHTp0SH369EnfFLEf77jjjqI3A6DFEcgA+Nr88Ic/zCfyZ599dq35cWIf86vRySefnBZffPE0duzYNHLkyDke32WXXdIOO+xQ79/+7W9/y/vt5ZdfXghbCsDCIJAB8LWKlqBzzjknffzxx6mlmD59eqP/9u9//3vaYostUo8ePdIyyywzx+MHHXRQevDBB9O//vWvOR679tpr04YbbpjWXXfdRj8/AM2LQAbA16pfv36pa9euaejQoXNd5pRTTpmj+96FF16YevbsWau1bbfddktnnXVW6tKlS+rcuXM67bTT0syZM9Oxxx6bll566bTCCivk0FJfN8HNNtssh8O11147Pfroo7Uef/XVV9OOO+6Yllhiibzu/fffP/3nP/+peXybbbZJhx9+eDrqqKPSsssumwYMGFDv65g9e3beptiO9u3b59d033331TwerVtjxozJy8S/43XXtfPOO6dvfetb6brrrqs1/7PPPku33XZbDmzhz3/+c1prrbXy88R+Ov/882uWvfTSS/PrrNsiOXz48FrvywknnFAzfeedd6YNNtgg76NVVlklnXrqqXnfzi2Qxv7o1q1bXj7C5bzeXwDmTiAD4Gu1yCKL5BB1ySWX1Nvq0xCjRo1KEyZMSI899lgaNmxY7v4XAWappZZKTz/9dPrJT36SDj300DmeJwLbz3/+8/TCCy+kTTfdNHcL/O9//5sfmzx5ctpuu+3S+uuvn5577rkcoCZOnJh+8IMf1FrH9ddfn9q1a5eeeOKJWsGm0kUXXZSD0W9+85vcrTCC2//8z/+kt99+Oz/+73//O4eo2Jb49y9+8Ys51tGmTZt0wAEH5EBWKpVq5kcYmzVrVtp7771zqIvt22uvvdIrr7ySg92JJ55YE+K23nrr9Prrr6cPP/wwT0cAjSD5yCOP5OkZM2ak0aNH56BZ7goZz3nkkUfmv/vd736X13XmmWfW+zovvvjidNddd6Vbb701d7284YYbaoVnABqgBABfk0GDBpV23XXX/O9NNtmkdOCBB+Z/33777ZE0apY7+eSTS+utt16tv73gggtKPXr0qLWumJ41a1bNvDXWWKO05ZZb1kzPnDmztPjii5duuummPD1u3Lj8PGeffXbNMjNmzCitsMIKpXPOOSdPn3766aX+/fvXeu7x48fnvxs7dmye3nrrrUvrr7/+fF9v9+7dS2eeeWateRtttFHppz/9ac10vM54vfPyxhtv5Od/+OGHa+bF69xvv/3yv/fZZ5/Sd7/73Vp/c+yxx5Z69+6d/z179uzSMsssU7rtttvydJ8+fUpDhw4tde3aNU8//vjjpbZt25Y+//zzPL399tuXzjrrrFrr+8Mf/lDq1q1bzXRsT7xv4Wc/+1lpu+22y88DwFejhQyAhSLGkUUr0xtvvNHodUTrUuvW////uqJ74TrrrFOrNS7GZU2aNKnW30WrWGULVIzDKm/HSy+9lB5++OHcXbF8W3PNNWvGe5X17dt3nts2ZcqU3Hq3+eab15of0w19zfH80cXymmuuydPvvPNObsUqd1eM9dX3PNESF61o0T1xq622yi1i0QIYrV4//elP07Rp03L3zWgx22ijjdJiiy1Wsw+iG2XlPjjkkENyK94XX3wxx/ZF99GoXLnGGmukI444Ij3wwAMNen0A/P8EMgAWiggI0YVvyJAhczwWIauye165W11dbdu2rTUdwaO+eTGWa0HF2KzowhgBo/IW4Sa2uSwqIy5MEb5inNinn36ax8WtuuqquSvigoruiBHIIshFd8yOHTvWhLQIZJXrin0QY8YqX390hYx9EGPE6oqxZuPGjUunn356+vLLL3P3ye9973tN9toBqolABsBCE+Xv77777jx+qVIUsfjggw9qhbKmvHbYU089VfPvKFQRY7B69epVEy5ee+21PAZqtdVWq3VrSAiLwNO9e/c8xqxSTPfu3bvB2xwhJ4LqjTfemH7/+9+nAw88sOZSAbHt9T3Pt7/97dxKWDmOLMaelceKxf1DDz2Uly3PK++DGAtW9/XHrbJFsu7r3XPPPdOVV16ZbrnllhweP/roowa/ToBq16boDQCgekT3wn333TcXhagU4SAKUJx77rm5pSUKa/z1r3/NJ/1N4bLLLkurr756DjIXXHBBLsEfAScMHjw4h4oolnHcccflao3RRfDmm29OV111VU3AWRBRPCQKjURrVlRYjJatCJZR9KKhottgBJ5oUYzukNFNsCyKgkSXw2ihimUi4EZlxd/+9rc1y0Rp/Ch2EoFuxIgRNfs5ColEsKvs8njSSSfl4igrrbRS3v8RwqIbY1SfPOOMM+bYtiioEhUWo+Utlo3QF5U0o/IlAA2jhQyAhSrGKtXtUhhBKcJEBKf11lsvPfPMM/VWIPwqLXNxi3U//vjjuUJgVB0M5VatGHvVv3//HBqjvH2Ei7m1Ds1NjKc65phjcmCK9USwjOeKMNjYbosRHqOrZ2xnZYtWVDiM0Bjl7SNQxX6tDG0Rurbccst8H9c9K4e0CLkxhq6y9S/WH6EtxoJF0Ntkk01ycI1y9vVZcsklc3iO9cTy7777brr33nsbvL8ASKlVVPYoeiMAAACqkZ+yAAAACiKQAQAAFEQgAwAAKIhABgAAUBCBDAAAoCACGQAAQEEEMgAAgIIIZAAAAAURyAAAAAoikAEAABREIAMAAEjF+P8Ad6PWmi8DJAsAAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 1000x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Vowels 0: 96 names (0.4%)\n",
      "Vowels 1: 2435 names (9.5%)\n",
      "Vowels 2: 9942 names (38.8%)\n",
      "Vowels 3: 9238 names (36.0%)\n",
      "Vowels 4: 3360 names (13.1%)\n",
      "Vowels 5: 474 names (1.8%)\n",
      "Vowels 6: 61 names (0.2%)\n",
      "Vowels 7: 19 names (0.1%)\n",
      "Vowels 8: 1 names (0.0%)\n",
      "Vowels 9: 0 names (0.0%)\n",
      "Vowels 10+: 0 names (0.0%)\n"
     ]
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "from collections import Counter\n",
    "\n",
    "# Count label distribution in training data\n",
    "train_labels = [min(count_vowels(name), 10) for name in train_data]\n",
    "label_counts = Counter(train_labels)\n",
    "\n",
    "# Plot distribution\n",
    "plt.figure(figsize=(10, 6))\n",
    "labels = list(range(11))\n",
    "counts = [label_counts.get(i, 0) for i in labels]\n",
    "plt.bar(labels, counts)\n",
    "plt.xlabel('Number of Vowels')\n",
    "plt.ylabel('Count')\n",
    "plt.title('Distribution of Vowel Counts in Training Data')\n",
    "plt.xticks(labels, [str(i) if i < 10 else '10+' for i in labels])\n",
    "plt.show()\n",
    "\n",
    "for i in range(11):\n",
    "    count = label_counts.get(i, 0)\n",
    "    print(f\"Vowels {i if i < 10 else '10+'}: {count} names ({count/len(train_data)*100:.1f}%)\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### LM with a classifier head"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class LMClassifier(nn.Module):\n",
    "    def __init__(self, base_model, num_classes=11):\n",
    "        super().__init__()\n",
    "        self.base_model = base_model\n",
    "        self.num_classes = num_classes\n",
    "        self.hidden_size = base_model.config.hidden_size\n",
    "        self.classifier = nn.Linear(self.hidden_size,  num_classes)\n",
    "\n",
    "    def _last_token_hidden(self, hidden_states, attention_mask):\n",
    "        # Find the position of the last non-padding token\n",
    "        if attention_mask is not None:\n",
    "            seq_lengths = attention_mask.sum(dim=1) - 1\n",
    "            batch_size = hidden_states.size(0)\n",
    "            last_hidden = hidden_states[torch.arange(batch_size), seq_lengths]\n",
    "        else:\n",
    "            last_hidden = hidden_states[:, -1, :]  # Last token\n",
    "        return last_hidden\n",
    "\n",
    "    def forward(self, input_ids, attention_mask=None):\n",
    "        outputs = self.base_model(\n",
    "            input_ids,\n",
    "            attention_mask=attention_mask,\n",
    "            output_hidden_states=True\n",
    "        )\n",
    "        hs = outputs.hidden_states[-1]  # (batch_size, seq_len, hidden_size)\n",
    "        h_last = self._last_token_hidden(hs, attention_mask)\n",
    "        logits = self.classifier(h_last)\n",
    "        return logits\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Training and eval code"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch.optim as optim\n",
    "from tqdm import tqdm\n",
    "\n",
    "def train_epoch(model, loss_fn, dataloader, optimizer, device):\n",
    "    model.train()\n",
    "    total_loss = 0\n",
    "    correct = 0\n",
    "    total = 0\n",
    "    \n",
    "    progress_bar = tqdm(dataloader, desc=\"Training\")\n",
    "    for batch_idx, batch in enumerate(progress_bar):\n",
    "        input_ids = batch['input_ids'].to(device)\n",
    "        attention_mask = batch['attention_mask'].to(device)\n",
    "        labels = batch['labels'].to(device)\n",
    "        \n",
    "        optimizer.zero_grad()\n",
    "        \n",
    "        logits = model(input_ids, attention_mask=attention_mask, labels=labels)\n",
    "        loss = loss_fn(logits, labels)\n",
    "        \n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "        \n",
    "        total_loss += loss.item()\n",
    "        \n",
    "        # Calculate accuracy\n",
    "        predictions = torch.argmax(logits, dim=-1)\n",
    "        correct += (predictions == labels).sum().item()\n",
    "        total += labels.size(0)\n",
    "        \n",
    "        # Update progress bar with running metrics\n",
    "        avg_loss = total_loss / (batch_idx + 1)\n",
    "        accuracy = correct / total\n",
    "        progress_bar.set_postfix({\n",
    "            'batch_loss': f'{loss.item():.4f}',\n",
    "            'avg_loss': f'{avg_loss:.4f}', \n",
    "            'batch_acc': f'{(predictions == labels).sum().item()/labels.size(0)}',\n",
    "            'avg_acc': f'{accuracy:.4f}'\n",
    "        })\n",
    "    \n",
    "    avg_loss = total_loss / len(dataloader)\n",
    "    accuracy = correct / total\n",
    "    return avg_loss, accuracy\n",
    "\n",
    "def evaluate(model, loss_fn, dataloader, device):\n",
    "    model.eval()\n",
    "    total_loss = 0\n",
    "    correct = 0\n",
    "    total = 0\n",
    "    \n",
    "    with torch.no_grad():\n",
    "        for batch in tqdm(dataloader, desc=\"Evaluating\"):\n",
    "            input_ids = batch['input_ids'].to(device)\n",
    "            attention_mask = batch['attention_mask'].to(device)\n",
    "            labels = batch['labels'].to(device)\n",
    "            \n",
    "            logits = model(input_ids, attention_mask=attention_mask, labels=labels)\n",
    "            loss = loss_fn(logits, labels)\n",
    "            \n",
    "            total_loss += loss.item()\n",
    "            \n",
    "            predictions = torch.argmax(logits, dim=-1)\n",
    "            correct += (predictions == labels).sum().item()\n",
    "            total += labels.size(0)\n",
    "    \n",
    "    avg_loss = total_loss / len(dataloader)\n",
    "    accuracy = correct / total\n",
    "    return avg_loss, accuracy\n",
    "\n",
    "# Training hyperparameters\n",
    "device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Run the fine-tuning"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Trainable parameters: 134,522,507\n",
      "\n",
      "Fine-tuning entire model\n",
      "==================================================\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "huggingface/tokenizers: The current process just got forked, after parallelism has already been used. Disabling parallelism to avoid deadlocks...\n",
      "To disable this warning, you can either:\n",
      "\t- Avoid using `tokenizers` before the fork if possible\n",
      "\t- Explicitly set the environment variable TOKENIZERS_PARALLELISM=(true | false)\n",
      "Training: 100%|██████████| 801/801 [05:30<00:00,  2.43it/s, batch_loss=0.2800, avg_loss=0.6852, batch_acc=0.9615384615384616, avg_acc=0.7433]\n",
      "Evaluating: 100%|██████████| 101/101 [00:08<00:00, 11.51it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/2\n",
      "  Train Loss: 0.6852, Train Acc: 0.7433\n",
      "  Val Loss: 0.1599, Val Acc: 0.9603\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Training: 100%|██████████| 801/801 [05:37<00:00,  2.37it/s, batch_loss=0.0826, avg_loss=0.1021, batch_acc=0.9615384615384616, avg_acc=0.9726]\n",
      "Evaluating: 100%|██████████| 101/101 [00:09<00:00, 10.81it/s]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 2/2\n",
      "  Train Loss: 0.1021, Train Acc: 0.9726\n",
      "  Val Loss: 0.1014, Val Acc: 0.9716\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "MAX_VOWELS=11\n",
    "\n",
    "model =LMClassifier(base_model, num_classes=MAX_VOWELS)\n",
    "model = model.to(device)\n",
    "print(f\"Trainable parameters: {sum(p.numel() for p in model.parameters() if p.requires_grad):,}\")\n",
    "\n",
    "learning_rate = 1e-5\n",
    "num_epochs = 2\n",
    "\n",
    "optimizer = optim.Adam(model.parameters(), lr=learning_rate)\n",
    "scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=num_epochs)\n",
    "loss_fn = nn.CrossEntropyLoss()\n",
    "\n",
    "print(\"\\nFine-tuning entire model\")\n",
    "print(\"=\"*50)\n",
    "\n",
    "for epoch in range(num_epochs):\n",
    "    train_loss, train_acc = train_epoch(model, loss_fn, train_loader, optimizer, device)\n",
    "    val_loss, val_acc = evaluate(model, loss_fn, dev_loader, device)\n",
    "    \n",
    "    print(f\"Epoch {epoch+1}/{num_epochs}\")\n",
    "    print(f\"  Train Loss: {train_loss:.4f}, Train Acc: {train_acc:.4f}\")\n",
    "    print(f\"  Val Loss: {val_loss:.4f}, Val Acc: {val_acc:.4f}\")\n",
    "    \n",
    "    scheduler.step()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Evaluate on test set"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|██████████| 101/101 [00:08<00:00, 11.24it/s]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Test Loss: 0.1049\n",
      "Test Accuracy: 0.9685\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "test_loss, test_acc = evaluate(model, loss_fn, test_loader, device)\n",
    "print(f\"Test Loss: {test_loss:.4f}\")\n",
    "print(f\"Test Accuracy: {test_acc:.4f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Confusion Matrix"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA6kAAANXCAYAAADXRxnaAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAjRNJREFUeJzs3Qd4FOXWwPGTUBJ679KkhF4EpAkqHbxIU0QRERGRpoAiRAVEkFAsNBEFKdJRgYtcQBGw00VAQERFAamh95b9nvP67W4mZGkmu7M7/9995oadmWxmz86ue/ac950wl8vlEgAAAAAAbCA80AcAAAAAAIAbSSoAAAAAwDZIUgEAAAAAtkGSCgAAAACwDZJUAAAAAIBtkKQCAAAAAGyDJBUAAAAAYBskqQAAAAAA2yBJBQAAAADYBkkqgETt2rVLGjRoIJkyZZKwsDBZuHBhkt7/n3/+ae536tSpSXq/wey+++4zS1I5c+aMPP3005I7d24T6549eybZfYeCpI53qNNz6LXXXrPV83Xo0CF56KGHJFu2bOb4Ro0aJV999ZX5t/70t0KFCsmTTz7p978LAKGGJBWwsd9//106d+4sd955p0RGRkrGjBmlZs2aMnr0aDl//nyy/u327dvL1q1b5Y033pDp06dL5cqVJVToh0j9EKvxTCyOmqDrdl3efPPNW77//fv3mw/zP/30kwTS0KFDzZcAXbp0Mc9hu3btkuXv/PjjjyZWr776qs993DHt3bu3BKOrV6/KlClTTJKUNWtWiYiIMAlJhw4dZMOGDWIH27dvN+edfgF0K/Q8ffzxxyV//vzmcenjq1evnnm8+rjtrFevXvL5559LdHS0OccbNWqU7H/zhx9+MHE+ceJEsv8tAHCqlIE+AACJ+9///icPP/yw+dD4xBNPSJkyZeTSpUvy3XffSZ8+fWTbtm3ywQcfJMvf1sRt9erV8sorr0j37t2T5W8ULFjQ/J1UqVJJIKRMmVLOnTsnn332mbRu3dqybebMmeZLgQsXLtzWfWuSOmjQIJPEVKhQ4aZ/74svvpCktHLlSqlWrZoMHDhQktNdd90lJUqUkNmzZ8uQIUMS3WfWrFnmpyZDwUbP05YtW8qyZcukdu3a8vLLL5tETpPBefPmybRp02TPnj1yxx13BDxJ1fNOE2k9927GpEmT5Nlnn5VcuXKZLzGKFSsmp0+flhUrVkjHjh3lwIED5vHaQWKvDz3HmzVrJi+++KJnXfHixc1zljp16mRLUjXO+mVX5syZLdt27twp4eF8/w8A/xZJKmBDu3fvljZt2phETj+E5cmTx7OtW7du8ttvv5kkNrkcOXLE/Ez4ASwpaVVNE8FA0eRfq9KaWCVMUjWheuCBB+TTTz/1y7Fospw2bdok/1B9+PBhKVWqVJLd35UrVyQuLi7R42zbtq30799f1qxZYxLjhDTOmshqQhts9EshTVDfeeeda1qm9QsAXR+M9LnSBLV69eqyZMkSyZAhg2ebPk6tEP/8889iF4mdd3qOJ3yf0iQxUO8t+r4CAEgCLgC28+yzz7r05fn999/f1P6XL192vf76664777zTlTp1alfBggVd0dHRrgsXLlj20/UPPPCA69tvv3VVqVLFFRER4SpcuLBr2rRpnn0GDhxo/nb8RX9PtW/f3vPv+Ny/E98XX3zhqlmzpitTpkyudOnSuYoXL26OyW337t3md6ZMmWL5vRUrVrjuueceV9q0ac3vPvjgg67t27cn+vd27dpljkn3y5gxo+vJJ590nT179obx0t/RY5o6daqJwfHjxz3b1q1bZ+77008/NT9Hjhzp2Xb06FHXCy+84CpTpoz5/QwZMrgaNWrk+umnnzz7rFq16pr4xX+c9957r6t06dKuDRs2uGrVquVKkyaN6/nnn/ds08XtiSeeMMeX8PE3aNDAlTlzZtfff/+d6OPzdQwac3Xo0CHXU0895cqZM6e5/3LlyplYxOd+fvTxv/POO+bcCg8Pd23atCnRv/nHH3+Y/Xv06HHNNn2sum3w4MGede+++66rVKlS5nzNkyePq2vXrpbnYfTo0ebvxV/35ptvmvvp1auXZ92VK1dc6dOnd7300kuedVevXjXHrPevj08f5zPPPOM6duyY5bgSxjsxe/fudaVMmdJVv35918368ccfzXmh54eeJ3Xq1HGtXr36hq8ZpedJ/OfqZl+37t9LuOi54Iseoz62v/7666Yel96fHrfbn3/+6erSpYt5bUdGRrqyZs3qeuihhyzHri5duuR67bXXXEWLFjXHrvvpe4O+R7gdOHDAvH7z5ctnzoncuXOb1378+4r/fPl6vPHP/4SPfc2aNa7GjRub146+v5QtW9Y1atQoz/bNmzeb9waNrR5nrly5XB06dHDFxsZe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",
      "text/plain": [
       "<Figure size 1200x1000 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Class 0: 0.000 (10 samples)\n",
      "Class 1: 0.984 (249 samples)\n",
      "Class 2: 0.984 (1251 samples)\n",
      "Class 3: 0.986 (1173 samples)\n",
      "Class 4: 0.929 (452 samples)\n",
      "Class 5: 0.864 (59 samples)\n",
      "Class 6: 0.000 (8 samples)\n",
      "Class 7: 0.000 (2 samples)\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "from sklearn.metrics import confusion_matrix\n",
    "import seaborn as sns\n",
    "\n",
    "# Collect predictions and labels\n",
    "model.eval()\n",
    "all_predictions = []\n",
    "all_labels = []\n",
    "\n",
    "with torch.no_grad():\n",
    "    for batch in test_loader:\n",
    "        input_ids = batch['input_ids'].to(device)\n",
    "        attention_mask = batch['attention_mask'].to(device)\n",
    "        labels = batch['labels'].to(device)\n",
    "        \n",
    "        logits = model(input_ids, attention_mask=attention_mask)\n",
    "        predictions = torch.argmax(logits, dim=-1)\n",
    "        \n",
    "        all_predictions.extend(predictions.cpu().numpy())\n",
    "        all_labels.extend(labels.cpu().numpy())\n",
    "\n",
    "# Create confusion matrix with all possible labels (0-10)\n",
    "cm = confusion_matrix(all_labels, all_predictions, labels=list(range(11)))\n",
    "\n",
    "# Plot confusion matrix\n",
    "plt.figure(figsize=(12, 10))\n",
    "sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', \n",
    "            xticklabels=[str(i) if i < 10 else '10+' for i in range(11)],\n",
    "            yticklabels=[str(i) if i < 10 else '10+' for i in range(11)])\n",
    "plt.xlabel('Predicted')\n",
    "plt.ylabel('Actual')\n",
    "plt.title('Confusion Matrix for Vowel Count Classification')\n",
    "plt.show()\n",
    "\n",
    "# Per-class accuracy\n",
    "for i in range(11):\n",
    "    if cm[i].sum() > 0:\n",
    "        class_acc = cm[i, i] / cm[i].sum()\n",
    "        print(f\"Class {i if i < 10 else '10+'}: {class_acc:.3f} ({cm[i].sum()} samples)\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Example predictions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Name\t\tActual\tPredicted\tCorrect\n",
      "==================================================\n",
      "emma        \t2\t2\t\t✓\n",
      "olivia      \t4\t4\t\t✓\n",
      "noah        \t2\t2\t\t✓\n",
      "william     \t3\t3\t\t✓\n",
      "isabella    \t4\t4\t\t✓\n",
      "james       \t2\t2\t\t✓\n",
      "charlotte   \t3\t3\t\t✓\n",
      "benjamin    \t3\t3\t\t✓\n",
      "mia         \t2\t2\t\t✓\n",
      "theodore    \t4\t4\t\t✓\n",
      "xyz         \t0\t1\t\t✗\n",
      "bcd         \t0\t1\t\t✗\n",
      "aeiou       \t5\t4\t\t✗\n",
      "rhythm      \t0\t1\t\t✗\n",
      "sky         \t0\t1\t\t✗\n"
     ]
    }
   ],
   "source": [
    "def predict_vowel_count(model, name, tokenizer, device):\n",
    "    \"\"\"Predict the number of vowels in a name.\"\"\"\n",
    "    model.eval()\n",
    "    \n",
    "    text = tokenizer.bos_token + name + tokenizer.eos_token\n",
    "    inputs = tokenizer(text, return_tensors=\"pt\", padding=True, truncation=True, max_length=32)\n",
    "    \n",
    "    input_ids = inputs['input_ids'].to(device)\n",
    "    attention_mask = inputs['attention_mask'].to(device)\n",
    "    \n",
    "    with torch.no_grad():\n",
    "        logits = model(input_ids, attention_mask=attention_mask)\n",
    "        prediction = torch.argmax(logits, dim=-1).item()\n",
    "    \n",
    "    return prediction\n",
    "\n",
    "# Test on some examples\n",
    "test_names = [\n",
    "    'emma', 'olivia', 'noah', 'william', 'isabella',\n",
    "    'james', 'charlotte', 'benjamin', 'mia', 'theodore',\n",
    "    'xyz', 'bcd', 'aeiou', 'rhythm', 'sky'\n",
    "]\n",
    "\n",
    "print(\"Name\\t\\tActual\\tPredicted\\tCorrect\")\n",
    "print(\"=\"*50)\n",
    "\n",
    "for name in test_names:\n",
    "    actual = min(count_vowels(name), 10)\n",
    "    predicted = predict_vowel_count(model, name, tokenizer, device)\n",
    "    correct = \"✓\" if actual == predicted else \"✗\"\n",
    "    print(f\"{name:12}\\t{actual}\\t{predicted}\\t\\t{correct}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Errors: 9 / 100\n",
      "\n",
      "Some misclassified examples:\n",
      "Name\t\tActual\tPredicted\n",
      "===================================\n",
      "anthony     \t2\t3\n",
      "million     \t3\t2\n",
      "sherly      \t1\t2\n",
      "sylvester   \t2\t3\n",
      "esequiel    \t5\t4\n",
      "aaradhya    \t4\t3\n",
      "brynn       \t0\t1\n",
      "taliesin    \t4\t3\n",
      "aceion      \t4\t3\n"
     ]
    }
   ],
   "source": [
    "# Find examples where the model made mistakes\n",
    "model.eval()\n",
    "errors = []\n",
    "correct_examples = []\n",
    "\n",
    "with torch.no_grad():\n",
    "    for i in range(min(250, len(test_data))):\n",
    "        name = test_data[i]\n",
    "        actual = min(count_vowels(name), 10)\n",
    "        predicted = predict_vowel_count(model, name, tokenizer, device)\n",
    "        \n",
    "        if actual != predicted:\n",
    "            errors.append((name, actual, predicted))\n",
    "        else:\n",
    "            correct_examples.append((name, actual))\n",
    "\n",
    "print(f\"Errors: {len(errors)} / 100\")\n",
    "print(\"\\nSome misclassified examples:\")\n",
    "print(\"Name\\t\\tActual\\tPredicted\")\n",
    "print(\"=\"*35)\n",
    "\n",
    "for name, actual, predicted in errors[:15]:\n",
    "    print(f\"{name:12}\\t{actual}\\t{predicted}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "anlp",
   "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.10.18"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
