{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Homework part I: Prohibited Comment Classification (3 points)\n",
    "\n",
    "![img](https://github.com/yandexdataschool/nlp_course/raw/master/resources/banhammer.jpg)\n",
    "\n",
    "__In this notebook__ you will build an algorithm that classifies social media comments into normal or toxic.\n",
    "Like in many real-world cases, you only have a small (10^3) dataset of hand-labeled examples to work with. We'll tackle this problem using both classical nlp methods and embedding-based approach."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>should_ban</th>\n",
       "      <th>comment_text</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>50</th>\n",
       "      <td>0</td>\n",
       "      <td>\"Those who're in advantageous positions are th...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>250</th>\n",
       "      <td>1</td>\n",
       "      <td>Fartsalot56 says f**k you motherclucker!!</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>450</th>\n",
       "      <td>1</td>\n",
       "      <td>Are you a fool? \\n\\nI am sorry, but you seem t...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>650</th>\n",
       "      <td>1</td>\n",
       "      <td>I AM NOT A VANDAL!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>850</th>\n",
       "      <td>0</td>\n",
       "      <td>Citing sources\\n\\nCheck out the Wikipedia:Citi...</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     should_ban                                       comment_text\n",
       "50            0  \"Those who're in advantageous positions are th...\n",
       "250           1          Fartsalot56 says f**k you motherclucker!!\n",
       "450           1  Are you a fool? \\n\\nI am sorry, but you seem t...\n",
       "650           1    I AM NOT A VANDAL!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n",
       "850           0  Citing sources\\n\\nCheck out the Wikipedia:Citi..."
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import pandas as pd\n",
    "data = pd.read_csv(\"comments.tsv\", sep='\\t')\n",
    "\n",
    "texts = data['comment_text'].values\n",
    "target = data['should_ban'].values\n",
    "data[50::200]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.model_selection import train_test_split\n",
    "texts_train, texts_test, y_train, y_test = train_test_split(texts, target, test_size=0.5, random_state=42)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__Note:__ it is generally a good idea to split data into train/test before anything is done to them.\n",
    "\n",
    "It guards you against possible data leakage in the preprocessing stage. For example, should you decide to select words present in obscene tweets as features, you should only count those words over the training set. Otherwise your algoritm can cheat evaluation."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Preprocessing and tokenization\n",
    "\n",
    "Comments contain raw text with punctuation, upper/lowercase letters and even newline symbols.\n",
    "\n",
    "To simplify all further steps, we'll split text into space-separated tokens using one of nltk tokenizers."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "before: How to be a grown-up at work: replace \"fuck you\" with \"Ok, great!\".\n",
      "after: how to be a grown-up at work : replace \" fuck you \" with \" ok , great ! \" .\n"
     ]
    }
   ],
   "source": [
    "from nltk.tokenize import TweetTokenizer\n",
    "tokenizer = TweetTokenizer()\n",
    "preprocess = lambda text: ' '.join(tokenizer.tokenize(text.lower()))\n",
    "\n",
    "text = 'How to be a grown-up at work: replace \"fuck you\" with \"Ok, great!\".'\n",
    "print(\"before:\", text)\n",
    "print(\"after:\", preprocess(text))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [],
   "source": [
    "# task: preprocess each comment in train and test\n",
    "\n",
    "texts_train = [preprocess(text) for text in texts_train]\n",
    "texts_test = [preprocess(text) for text in texts_test]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [],
   "source": [
    "assert texts_train[5] ==  'who cares anymore . they attack with impunity .'\n",
    "assert texts_test[89] == 'hey todds ! quick q ? why are you so gay'\n",
    "assert len(texts_test) == len(y_test)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "tags": []
   },
   "source": [
    "### Solving it: bag of words\n",
    "\n",
    "![img](http://www.novuslight.com/uploads/n/BagofWords.jpg)\n",
    "\n",
    "One traditional approach to such problem is to use bag of words features:\n",
    "1. build a vocabulary of frequent words (use train data only)\n",
    "2. for each training sample, count the number of times a word occurs in it (for each word in vocabulary).\n",
    "3. consider this count a feature for some classifier\n",
    "\n",
    "__Note:__ in practice, you can compute such features using sklearn. Please don't do that in the current assignment, though.\n",
    "* `from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "example features: ['!', '12:20', '300', '_', 'adorned', 'alternative', 'archive', 'average', 'benkner', 'bout', 'came', 'chest', 'combined', 'consumers', 'cricket', 'decisions', 'dickheads', 'domestic', 'eductaion', 'essentially', 'faggot', 'firms', 'frustrated', 'goal', 'hanibal', 'hip-hop', 'identified', 'infoboxes', 'issue', 'kindergarten', 'lets', 'lot', \"mclaren's\", 'moderator', 'naturally', 'noticeable', 'opposing', 'pdf', 'plant', 'pretoria', 'punctuation', 'rebels', 'repetative', 'riadh', 'schulz', 'shes', 'slit', 'spoof', 'stupid', 't', 'theoretical', 'topic', 'uglyness', 'userspace', 'wanted', 'wikieditor', 'year', '←']\n"
     ]
    }
   ],
   "source": [
    "import itertools\n",
    "from collections import Counter\n",
    "\n",
    "k = 10000\n",
    "\n",
    "# task: find up to k most frequent tokens in texts_train,\n",
    "# sort them by number of occurences (highest first)\n",
    "\n",
    "vocab = [text.split() for text in texts_train]\n",
    "vocab = list(itertools.chain(*vocab))\n",
    "\n",
    "vocab_counts = dict(Counter(vocab))\n",
    "vocab_counts_sorted = dict(sorted(vocab_counts.items(), key=lambda item: -item[1]))\n",
    "\n",
    "bow_vocabulary = list(vocab_counts_sorted.keys())[:k]\n",
    "\n",
    "print(\"example features:\", sorted(bow_vocabulary)[::100])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "After we calculate frequencies for our vocabulary we need to update the k value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5707"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "k = min(k, len(bow_vocabulary))\n",
    "k"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [],
   "source": [
    "def text_to_bow(text):\n",
    "    \"\"\"convert text string to an array of token counts. Use bow_vocabulary.\"\"\"\n",
    "    bow = np.zeros(k)\n",
    "\n",
    "    for token in text.split():\n",
    "        ind = (\n",
    "            bow_vocabulary.index(token)\n",
    "            if token in bow_vocabulary\n",
    "            else -1\n",
    "        )\n",
    "        if ind != -1:\n",
    "            bow[ind] += 1\n",
    "    return bow"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_train_bow = np.stack(list(map(text_to_bow, texts_train)))\n",
    "X_test_bow = np.stack(list(map(text_to_bow, texts_test)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [],
   "source": [
    "k_max = len(set(\" \".join(texts_train).split()))\n",
    "assert X_train_bow.shape == (len(texts_train), min(k, k_max))\n",
    "assert X_test_bow.shape == (len(texts_test), min(k, k_max))\n",
    "assert np.all(\n",
    "    X_train_bow[5:10].sum(-1) == np.array([len(s.split()) for s in texts_train[5:10]])\n",
    ")\n",
    "assert len(bow_vocabulary) <= min(k, k_max)\n",
    "assert X_train_bow[6, bow_vocabulary.index(\".\")] == texts_train[6].split().count(\".\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__Naive bayes:__ perhaps the simplest model that can solve your problem is the so called Naive Bayes Classifier. \n",
    "Its a trivial linear model that assumes the independence of input features and computes the coefficients by, well, counting probabilities.\n",
    "\n",
    "If you don't remember the math behind Naive Bayes, read [this chunk](https://lena-voita.github.io/nlp_course/text_classification.html#naive_bayes) to help refresh your memory. Done? Good! Now let's implement that :)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_p_x(delta, word_ind, counts, vocab_size, sum_counts):\n",
    "    return (delta + counts[word_ind]) / (delta * vocab_size + sum_counts)\n",
    "\n",
    "\n",
    "class BinaryNaiveBayes:\n",
    "    delta = 1.0  # add this to all word counts to smoothe probabilities\n",
    "\n",
    "    def fit(self, X, y):\n",
    "        \"\"\"\n",
    "        Fit a NaiveBayes classifier for two classes\n",
    "        :param X: [batch_size, vocab_size] of bag-of-words features\n",
    "        :param y: [batch_size] of binary targets {0, 1}\n",
    "        \"\"\"\n",
    "        # first, compute marginal probabilities of every class, p(y=k) for k = 0,1\n",
    "        p_y_0 = np.count_nonzero(y == 0) / len(y)\n",
    "        p_y_1 = np.count_nonzero(y == 1) / len(y)\n",
    "        self.p_y = np.array([p_y_0, p_y_1])\n",
    "\n",
    "        # count occurences of each word in texts with label 1 and label 0 separately\n",
    "        word_counts_positive = X[y == 1].sum(axis=0)\n",
    "        word_counts_negative = X[y == 0].sum(axis=0)\n",
    "        # ^-- both must be vectors of shape [vocab_size].\n",
    "\n",
    "        # finally, lets use those counts to estimate p(x | y = k) for k = 0, 1\n",
    "\n",
    "        # <YOUR CODE HERE>\n",
    "        sum_word_counts_positive = sum(word_counts_positive)\n",
    "        sum_word_counts_negative = sum(word_counts_negative)\n",
    "\n",
    "        self.p_x_given_positive = np.array(\n",
    "            [\n",
    "                get_p_x(\n",
    "                    delta=self.delta,\n",
    "                    word_ind=word_ind,\n",
    "                    counts=word_counts_positive,\n",
    "                    vocab_size=X.shape[1],\n",
    "                    sum_counts=sum_word_counts_positive,\n",
    "                )\n",
    "                for word_ind in range(X.shape[1])\n",
    "            ]\n",
    "        )\n",
    "        self.p_x_given_negative = np.array(\n",
    "            [\n",
    "                get_p_x(\n",
    "                    delta=self.delta,\n",
    "                    word_ind=word_ind,\n",
    "                    counts=word_counts_negative,\n",
    "                    vocab_size=X.shape[1],\n",
    "                    sum_counts=sum_word_counts_negative,\n",
    "                )\n",
    "                for word_ind in range(X.shape[1])\n",
    "            ]\n",
    "        )\n",
    "        # both must be of shape [vocab_size]; and don't forget to add self.delta!\n",
    "\n",
    "        return self\n",
    "\n",
    "    def predict_scores(self, X):\n",
    "        \"\"\"\n",
    "        :param X: [batch_size, vocab_size] of bag-of-words features\n",
    "        :returns: a matrix of scores [batch_size, k] of scores for k-th class\n",
    "        \"\"\"\n",
    "        # compute scores for positive and negative classes separately.\n",
    "        # these scores should be proportional to log-probabilities of the respective target {0, 1}\n",
    "        # note: if you apply logarithm to p_x_given_*, the total log-probability can be written\n",
    "        # as a dot-product with X\n",
    "        score_negative = np.log(self.p_y[0]) + np.dot(X, np.log(self.p_x_given_negative))\n",
    "        # <YOUR CODE HERE - compute unnormalized negative log-probability>\n",
    "        score_positive = np.log(self.p_y[1]) + np.dot(X, np.log(self.p_x_given_positive))\n",
    "        # <YOUR CODE HERE - compute unnormalized positive log-probability>\n",
    "        # you can compute total p(x | y=k) with a dot product\n",
    "        return np.stack([score_negative, score_positive], axis=-1)\n",
    "\n",
    "    def predict(self, X):\n",
    "        return self.predict_scores(X).argmax(axis=-1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [],
   "source": [
    "naive_model = BinaryNaiveBayes().fit(X_train_bow, y_train)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [],
   "source": [
    "assert naive_model.p_y.shape == (2,) and naive_model.p_y.sum() == 1 and naive_model.p_y[0] > naive_model.p_y[1]\n",
    "assert naive_model.p_x_given_positive.shape == naive_model.p_x_given_negative.shape == X_train_bow.shape[1:]\n",
    "assert np.allclose(naive_model.p_x_given_positive.sum(), 1.0)\n",
    "assert np.allclose(naive_model.p_x_given_negative.sum(), 1.0)\n",
    "assert naive_model.p_x_given_negative.min() > 0, \"did you forget to add delta?\"\n",
    "\n",
    "f_index = bow_vocabulary.index('fuck')  # offensive tweets should contain more of this\n",
    "assert naive_model.p_x_given_positive[f_index] > naive_model.p_x_given_negative[f_index]\n",
    "\n",
    "g_index = bow_vocabulary.index('good')  # offensive tweets should contain less of this\n",
    "assert naive_model.p_x_given_positive[g_index] < naive_model.p_x_given_negative[g_index]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model accuracy: 0.756\n",
      "Well done!\n"
     ]
    },
    {
     "data": {
      "image/png": "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AOzbtw9/f3+OHDlCjx496Nmzp9EhiQxER0dz+fJlypUrR8GCBS3W9z8mJobChQtb5Fy2Qsp8h9aamzdvEhJirjNnN6lnelNUa50EvAFsAU4A32utjymlJiqlnkvZbRrgDvyglDqklNqQrSgEs2fPplGjRkRERLB+/XpWr15NqVKljA5LZCA8PJxy5cpRqFAheZBLWIxSikKFClGuXDnCw7NfN85SG7rWejOw+Z5149K8bpPtKwvA/D+yUgpfX1/8/f2ZOnUqRYsWNToskYnExEQKFixodBjCThUsWDBHTXkyOJdBoqOjeffddylQoACzZs2iSZMmNGnSxOiwRDZIzVxYS06/W/LovwE2b95MrVq1WLBgAS4uLjKYlhDCIiSh56KIiAh69+7Ns88+i4eHB3///TfTpk2Tmp7Ik4YMGcLHH39sdBgiGySh56Lr16+zceNGPvroIw4cOECjRg96PkuInKtYsSK///77Q53j66+/ZuzYsQ91jv79++Pi4kJYWNh96z/88MO71p0/fx6lFElJSanrVq1aha+vL+7u7pQpU4b27dvz119/ZTuOWbNmUbp0aYoUKcKrr75KQkLGPasXLlxI1apVcXd3p127dnc9xJeQkMCQIUPw9PTkkUceoVOnTqk9UgBOnDhBq1at8PDwoGrVqqxduzZ127fffou7u3tqWW7fUN+/f3+2y5MRSehWFhISwtSpU9FaU61aNYKCghg/fjz58uUzOjThwNImTWu5ceMG//vf//Dw8GDlypXZPn7mzJm89dZbvP/++1y+fJkLFy4wdOhQ1q9fn63zbNmyhcmTJ7Nt2zaCgoIIDAzko48+SnffgIAA3n//fdavX8+1a9eoVKnSXd2HZ8+eza5duzhy5AihoaEUK1aMYcOGAeb3tHPnznTs2JFr166xYMECevfuzenTpwF4+eWXiY2NJTY2lrCwMObNm0flypWpX79+tt+bjEhCtxKtNd988w0+Pj6MHz+e//77D0B6sAir69OnDxcuXKBTp064u7szderU1NrvokWLqFChAq1atQKgW7dulC5dGg8PD5o3b86xY8dSz5O2Fh0QEED58uWZMWMGpUqVokyZMixZsuSBcfzvf/+jaNGijBs3jmXLlmWrDFFRUYwbN465c+fywgsv4ObmhqurK506dWLatGnZOteyZcvw9/enVq1aFCtWjLFjx7J06dJ09/3555/p1q0btWrVIl++fIwdO5YdO3ak/v6eO3eOtm3b4unpSYECBXjppZdS37OTJ08SGhrKiBEjcHZ2plWrVjRp0oQVK1ZkGFffvn0t2uQqvVys4L///mPgwIH88ccf+Pn58c0331C1alWjwxJWNGHjMY6HRj/0eUwmE87Ozulu8ylbhI861cr0HCtWrODPP/9k4cKFtGlj7lF8/vx5ALZv386JEydwcjLX5dq3b8/ixYvJly8f7777Li+//DKHDh1K97yXLl0iKiqKkJAQtm7dyosvvsjzzz+f4Yify5Yto2fPnvTo0YO3336b/fv306BBg0zjB9i1axfx8fEPnA931apVDB06NMPtR44coUKFChw7dozOne+MJ/jYY49x+fJlrl69SvHixe87Lm0nhduvjx49SpUqVfD392f48OGEhoZStGhRvv32W9q3b59hDFprjh49et/6CxcusGPHDhYvXpzhsTkhNXQLS0pKonXr1uzbt4/58+ezbds2SeYizxg/fjxubm6pfehfffVVChcuTP78+Rk/fjyHDx8mKioq3WNdXV0ZN24crq6udOjQAXd3d06dOpXuvhcuXOCPP/6gV69eeHp60rp1a5YvX57lOK9evUqJEiVwccm4ztmrVy8iIyMz/KlQoQJgHgjLw8Mj9bjbr2NiYu47Z7t27fj+++85cuQIN2/eZOLEiSiliIszDyZbrVo1vLy8KFeuHEWKFOHEiROMG2d+JKd69eqUKlWKadOmkZiYyG+//cb27dtTj01r9erVNGvWjEqVKmX5PckKqaFbyKlTp6hSpQouLi4sW7aMKlWqUL681UYRFnlMVmrOWWHtx+C9vO4My2Qymfjggw/44YcfuHLlSmqtPSIi4q4EeFvx4sXvSrCFChUiNjY23eusWLGCmjVrUq9ePcDcfvz2228zffp0XF1dcXFxue/BmcTERJycnHBycqJ48eJERESQlJT0wKSeFe7u7kRH3/nr6fbr9N7nNm3aMGHCBLp27Up0dDRvvfUWhQsXTv1dfv3110lISODq1au4ubkxdepU2rdvz549e3B1dWXdunUMGzaMKVOm4OvrS/fu3dOdfGb16tX33RS2BKmhP6Rbt24xYcIE6tSpw9y5cwFo0aKFJHNhqIzaZdOuX7VqFevXr+f3338nKioqtVnGEs9FLF++nMDAQEqXLk3p0qUZOXIkERERbN5sfuC8QoUKqde77dy5c3h5eeHk5ETjxo3Jnz8/69aty/AaaXuNpPdz4cIFAGrVqsXhw4dTjzt8+DCenp7pNreAOWmfOXOGy5cv07VrV5KSkqhduzYAhw4don///jzyyCPkz5+fYcOGsXfvXiIiIgCoW7cu27dv5+rVq2zZsoXAwECeeOKJu86/c+dOLl26xIsvvpit9zQrJKE/hL1799KgQQPGjx9Pt27dePnll40OSQjAPN9AYGDgA/eJiYkhf/78FC9enLi4ON5//32LXHvXrl38999/7N27l0OHDnHo0CGOHj1Kr169UptdunbtyqZNm/jtt98wmUyEhoYyadIkevToAZibRSZOnMjrr7/OunXriIuLIzExkV9++YXRo0cDd/caSe/ndpNL3759WbRoEcePHycyMpJJkybRv3//dGOPj4/n6NGjaK25cOECgwYNYvjw4an3CRo2bMjy5cuJiooiMTGRefPmUbZsWUqUKAGY2+3j4+OJi4tj+vTphIWF3XetZcuW8dxzz1nnLzGttSE/DRo00DmxYvoIrT8qonVCbI6Ot5RZs2ZpJycnXa5cOb1x40arX++PP/6w+jXymrxc5uPHj1vlvNHR0RY5z7p167SXl5f28PDQ06ZN0+fOndOATkxMTN0nJiZGP/fcc9rd3V1XqFBBL1u2TAP6zJkzWmut+/Xrpz/44AOttfmzKFeu3F3X8Pb21lu3br3v2oMHD9YvvPDCfev37Nmj8+XLp69evaq11nrDhg26fv36ukiRIrpChQp61KhROi4u7q5jVq5cqRs0aKALFSqkPT09dYcOHfTOnTuz/X7MmDFDlypVShcuXFj3799fx8fHp27z8fHRK1eu1Fprff36dV2nTp3U640ZM0YnJSWl7hsREaF79eqlS5YsqT08PHSTJk30nj17UrePGjVKFy1aVLu5uel27dqlvpe33bx5U3t4eOgNGzZkGnNG3zFgn84gr0pCz6bk5GSttdY7d+7UgwcP1pGRkbly3byc3KwlL5c5ryd0WyJlTl9OErrcFM2iqKgoRo8eTcGCBfn888956qmneOqpp4wOSwghUkkbehZs3LgRHx8fFi5cSP78+WUwLSFEniQJ/QGuXLlCr169eO655yhevDi7d+9mypQpMpiWECJPkoT+AFFRUWzevJkJEyawb98+GjZsaHRIQgiRIWlDv8fFixdZuXIlY8aMoWrVqgQFBaX7kIUQQuQ1UkNPkZyczNdff02tWrWYNGlS6mA8ksyFELZCEjpw5swZWrVqxWuvvcYTTzzBv//+K+OvCCFsjsM3uSQlJfH0008TGRnJokWLeOWVV+SmpxDCJjlsQj9x4gTVqlXDxcWFFStWUKVKFcqWLWt0WEIIkWMO1+SSkJDARx99RN26dfnyyy8BaNasmSRzYVcsMQUdwNKlS2natGmOjvXz86NYsWL3Tffm5+fHwoUL71p3ewKN27TWzJkzh9q1a+Pm5kb58uXp1q0b//77b7Zi0Frz7rvvUrx4cYoXL8677777wOdIvvjiCypVqkSRIkXw9fW9a7q7WbNmUblyZYoUKULZsmUZMWLEfTM/zZ49m0qVKuHm5kbNmjVTZyuCO92gPTw8qFChglXGfnKohL57927q16/PxIkT6dmzJ3369DE6JCHs0vnz5/nzzz9RSrFhw4ZsHz98+HBmz57NnDlzuHbtGqdPn+b5559n06ZN2TrPggULWLduHYcPH+bIkSNs3LiR+fPnp7vvnj17GDNmDD/++CNRUVH4+/vTpUsXTCYTAM899xwHDhwgOjqao0ePcvjwYebMmZN6/MKFC1m0aBGbNm0iNjaWn3/+OXXQLoAXXniB0qVLc+HCBf777z9GjRqV7fclUxmNCWDtn9wey2X69OlaKaW9vLz05s2bc3RtI+XlcU2sJS+XOS+P5dK7d2+tlNIFChTQbm5uesqUKVprrXft2qUbN26sPTw8dN26de96f5csWaIrVaqk3d3ddcWKFfXKlSv18ePHdf78+bWTk5N2c3PTHh4eWY5hwoQJ+qmnntIjRozQzz777F3bWrRoob/55pvU5ejo6LsG/zp9+rR2cnK6a9CrnGrcuLGeP39+6vLChQt1o0aN0t33u+++0w0bNkxdjo2N1YAODQ29b9+IiAjdunVr/dprr2mttTaZTLp8+fL6999/T/fcW7Zs0d7e3qkDfclYLjmUnJycOr7ykCFDmDx5MkWKFDE6LGFvfhkDl7LXHJCegqYkcM7g17J0HWg/OdNzpDcFXUhICM8++ywrVqygXbt2bNu2ja5du3Ly5EkKFSrEm2++yT///EP16tUJCwvj2rVr1KxZk6+//pqFCxfe1fSQFcuXL2fkyJE0atSIJ598ksuXL+Pp6ZmlY7dt20b58uXvG0c8rcmTJzN5csbvRWRkJADHjh3jscceS13/2GOP3TVvalrt27dn6tSp7NmzB19fXxYvXky9evUoXbp06j6rVq1iyJAhxMTEUKJECWbMmAFAcHAwwcHBHD16lP79++Pi4kLfvn356KOPcHJyYvfu3VSvXp1+/frxyy+/4O3tzaxZs2jRokWW3pOsstsml8jIyNT5/wCeeuop5s2bJ8lcOKSVK1fSoUMHOnTogJOTE08//TS+vr6pE044OTlx9OhRbt68SZkyZahVK+czMP31118EBQXRvXt3GjRoQJUqVVi1alWWj7969SplypR54D5jxox54PRzt6U3/VxsbGy67eiFCxema9euNG3alPz58zNhwgQWLFhwV6+3Xr16ER0dzenTpxkyZEjqf1LBwcEA/Pbbb/z777/88ccfrF69mkWLFqVu/+2332jZsiWXLl1i2LBhdO7cOXViDEuxyxr6unXrGDp0KOHh4YwePRqttXRFFNaVhZpzVty00hR0QUFB/PDDD2zcuDF1XWJiIi1btsTNzY01a9Ywffp0/P39adKkCTNmzKBGjRo5utayZct45plnUtuPe/XqxbJlyxgxYgRAhtPPubq6Auap7sLCwnJ07XulN/2cu7t7uvlg0aJFLFmyhGPHjlG1alV+++03OnbsyMGDB+/rNFGtWjVq1arF0KFD+emnn1LnaB09ejRFixalaNGiDB48mM2bNzNw4EAKFixIxYoV8ff3B+DFF19k5syZ7Ny5864JrB+WXdXQw8PD6d69O126dMHT05O9e/fy6aefSjIXDufe77yXlxd9+vS5qxZ748YNxowZA0Dbtm3ZunUrYWFh1KhRg4EDB6Z7nszcvHmT77//nu3bt6dOPzdr1iwOHz6cOg1cRtPPeXt7A9C6dWuCg4PZt29fhtf59NNPHzj93G3pTT+X0V8fhw4domPHjjz66KM4OTnRrl07ypQpw99//53u/klJSalPlFevXp18+fLd9X6lfV23bt373ktr5CW7SujR0dFs3bqVTz75hL1791K/fn2jQxLCEPdOQde7d282btzIli1bMJlMxMfHExAQQHBwMJcvX2b9+vXcuHGD/Pnz4+7unjphtKenJ8HBwdy6dStL1123bh3Ozs4cP348dfq5EydO0KxZs9Tp51566SWWLFnC3r170Vpz5swZZs2alTr9XLVq1Rg6dCg9e/YkICCAW7duER8fz3fffZfabv7+++8/cPq52/r27cvMmTMJCQkhNDSUGTNmZDj9XMOGDdm0aROBgYFordm6dSunT59OnU904cKFhIeHA3D8+HE+++wzWrduDZgnzH7ppZeYOnUqMTExBAcHs2DBAjp27AhAly5duH79OsuWLcNkMrFu3TqCg4Np0qRJlt7XLMvobqm1fyzVyyUoKEhPmjQpdSYhe539JC/3+LCWvFzmvNzLRev7p6DTWuvdu3fr5s2b62LFiukSJUroDh066KCgIB0aGqqbN2+uixQpoj08PHSLFi30sWPHtNZaJyQk6A4dOuhixYrp4sWLZ3rdtm3b6pEjR963fs2aNdrT0zN1CrxFixZpHx8fXbhwYV2pUiX92WefaZPJlLp/cnKy/vzzz7WPj48uWLCgLlu2rO7evbs+evRott6H5ORk/c477+hixYrpYsWK6XfeeSc1V2ittZubm96xY0fqvmPHjtVeXl7a3d1d16hRQy9fvjx13/79++tSpUrpQoUKaW9vbz1q1Ch98+bN1O1RUVH6pZde0u7u7rp8+fJ6woQJd11rx44dunbt2trNzU0//vjjqdfNiNWmoAPaAaeAs8CYdLbnB9akbN8DVMzsnA+b0E03o/XcuXO1u7u7LlSo0H1z99mbvJzcrCUvlzmvJ3RbImVOX04SeqZNLkopZ2Au0B7wAXoqpXzu2c0fuK61rgrMAqZY4I+HDJ2KMOH3dDtef/11GjdunHoTQwghHFlW2tCfAM5qrQO11reA74B7b8t2BpalvP4RaK2sdCfSZEqm7co4/j16nCVLlrBlyxYqVqxojUsJIYRNyUq3xXLAxTTLwUCjjPbRWicppaKA4sBdnSyVUoOAQWC+2RIQEJDtgGNcS/BBtwaUeHokxUqVYfv27dk+hy2KjY3N0ftly/JymT08PIiJibH4eU0mk1XOm5dJmdN3+8Z1duRqP3St9QJgAYCvr6/28/PL9jn8/PwICGhKTo61ZQEBAVLmPOTEiRMZ9md+GDFW6oeel0mZ76e1pkCBAjz++OPZOm9WmlxCAK80y+VT1qW7j1LKBfAArmYrEiFsiKurKzdv3jQ6DGGnbt68mfqgVXZkJaH/A1RTSlVSSuUDegD3Dp+2AeiX8vpF4P9S7sYKYZdKlSpFSEgIcXFxDxyOVYjs0FoTFxdHSEgIpUqVyvbxmTa5pLSJvwFsAZyBxVrrY0qpiZi7z2wAFgErlFJngWuYk74Qduv2mEChoaH3Pcb+MOLj4ylQoIDFzmcLpMx3c3V1xdPTM0fjTmWpDV1rvRnYfM+6cWlexwPdsn11IWxYkSJFLD7YW0BAQLbbTW2dlNly7OrRfyGEcGSS0IUQwk5IQhdCCDshCV0IIeyEMqrLlVLqChCUw8NLcM9TqA5AyuwYpMyO4WHK7K21LpneBsMS+sNQSu3TWvsaHUdukjI7BimzY7BWmaXJRQgh7IQkdCGEsBO2mtAXGB2AAaTMjkHK7BisUmabbEMXQghxP1utoQshhLiHJHQhhLATeTqhK6XaKaVOKaXOKqXGpLM9v1JqTcr2PUqpigaEaVFZKPNIpdRxpdQRpdQ2pZS3EXFaUmZlTrNfV6WUVkrZfBe3rJRZKdU95bM+ppRaldsxWloWvtsVlFJ/KKUOpny/OxgRp6UopRYrpcKVUkcz2K6UUnNS3o8jSqn6D33RjGaPNvoH81C9/wGVgXzAYcDnnn2GAl+nvO4BrDE67lwoc0ugUMrr1xyhzCn7FQZ2ALsBX6PjzoXPuRpwECiWslzK6LhzocwLgNdSXvsA542O+yHL3ByoDxzNYHsH4BdAAU8Cex72mnm5hp6nJqfOJZmWWWv9h9Y6LmVxN+YZpGxZVj5ngI+BKUB8bgZnJVkp80Bgrtb6OoDWOjyXY7S0rJRZA7fHI/YAQnMxPovTWu/APD9ERjoDy7XZbqCoUqrMw1wzLyf09CanLpfRPlrrJOD25NS2KitlTssf8//wtizTMqf8Keqltd6Um4FZUVY+50eBR5VSO5VSu5VS7XItOuvISpnHA72VUsGY518YljuhGSa7v++ZytVJooXlKKV6A75AC6NjsSallBMwE+hvcCi5zQVzs4sf5r/Cdiil6mitI40Mysp6Aku11jOUUo0xz4JWW2udbHRgtiIv19AdcXLqrJQZpVQb4APgOa11Qi7FZi2ZlbkwUBsIUEqdx9zWuMHGb4xm5XMOBjZorRO11ueA05gTvK3KSpn9ge8BtNa7gAKYB7GyV1n6fc+OvJzQHXFy6kzLrJR6HJiPOZnbersqZFJmrXWU1rqE1rqi1roi5vsGz2mt9xkTrkVk5bu9DnPtHKVUCcxNMIG5GKOlZaXMF4DWAEqpmpgT+pVcjTJ3bQD6pvR2eRKI0lqHPdQZjb4TnMld4g6Yayb/AR+krJuI+RcazB/4D8BZYC9Q2eiYc6HMvwOXgUMpPxuMjtnaZb5n3wBsvJdLFj9nhbmp6TjwL9DD6Jhzocw+wE7MPWAOAc8YHfNDlnc1EAYkYv6Lyx8YAgxJ8xnPTXk//rXE91oe/RdCCDuRl5tchBBCZIMkdCGEsBOS0IUQwk5IQhdCCDshCV0IIeyEJHQhhLATktCFEMJO/D8K0fg3Y/PbaQAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "from sklearn.metrics import roc_auc_score, roc_curve\n",
    "\n",
    "for name, X, y, model in [\n",
    "    (\"train\", X_train_bow, y_train, naive_model),\n",
    "    (\"test \", X_test_bow, y_test, naive_model),\n",
    "]:\n",
    "    proba = model.predict_scores(X)[:, 1] - model.predict_scores(X)[:, 0]\n",
    "    auc = roc_auc_score(y, proba)\n",
    "    plt.plot(*roc_curve(y, proba)[:2], label=\"%s AUC=%.4f\" % (name, auc))\n",
    "\n",
    "plt.plot(\n",
    "    [0, 1],\n",
    "    [0, 1],\n",
    "    \"--\",\n",
    "    color=\"black\",\n",
    ")\n",
    "plt.legend(fontsize=\"large\")\n",
    "plt.grid()\n",
    "\n",
    "test_accuracy = np.mean(naive_model.predict(X_test_bow) == y_test)\n",
    "print(f\"Model accuracy: {test_accuracy:.3f}\")\n",
    "assert test_accuracy > 0.75, \"Accuracy too low. There's likely a mistake in the code.\"\n",
    "print(\"Well done!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Okay, it definitely learned *something*. Now let's figure out what exactly it learned. The simplest way to do that is by highlighting which words have a greatest ratio of positive to negative probability or vice versa. We'll go with the positive one [because reasons](https://www.urbandictionary.com/define.php?term=because%20reasons).\n",
    "\n",
    "__Your task__ is to compute top-25 words that have the __highest__ ratio of ${p(x_i | y=1)} \\over {p(x_i | y=0)}$. Enjoy!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "#0\t    hitler\t(ratio=475.47341740332655)\n",
      "#1\t      heil\t(ratio=471.80652729481756)\n",
      "#2\t   offfuck\t(ratio=441.24910972390967)\n",
      "#3\t      suck\t(ratio=314.7414009803511)\n",
      "#4\t    nigger\t(ratio=223.68029661904563)\n",
      "#5\t j.delanoy\t(ratio=220.0134065105367)\n",
      "#6\t      dick\t(ratio=187.01139553395618)\n",
      "#7\t      fggt\t(ratio=97.78373622690519)\n",
      "#8\t     bitch\t(ratio=59.89253843897943)\n",
      "#9\t      fuck\t(ratio=53.78105492479786)\n",
      "#10\t      slap\t(ratio=44.00268130210734)\n",
      "#11\t      shit\t(ratio=44.00268130210734)\n",
      "#12\t   fucking\t(ratio=31.779714273744187)\n",
      "#13\t       ass\t(ratio=26.89052746239893)\n",
      "#14\t    stupid\t(ratio=18.334450542544726)\n",
      "#15\t         =\t(ratio=17.53995768570112)\n",
      "#16\t   college\t(ratio=17.11215383970841)\n",
      "#17\t         *\t(ratio=17.11215383970841)\n",
      "#18\t   asshole\t(ratio=15.889857136872093)\n",
      "#19\t         u\t(ratio=15.278708785453937)\n",
      "#20\t   bastard\t(ratio=14.66756043403578)\n",
      "#21\t       hit\t(ratio=14.66756043403578)\n",
      "#22\t     idiot\t(ratio=13.445263731199464)\n",
      "#23\t         @\t(ratio=13.445263731199464)\n",
      "#24\tscientific\t(ratio=12.222967028363149)\n"
     ]
    }
   ],
   "source": [
    "# hint: use naive_model.p_*\n",
    "probability_ratio = naive_model.p_x_given_positive / naive_model.p_x_given_negative\n",
    "top_negative_words = np.array(bow_vocabulary)[np.argsort(-probability_ratio)[:25]]\n",
    "\n",
    "assert len(top_negative_words) == 25 and [isinstance(w, str) for w in top_negative_words]\n",
    "assert 'j.delanoy' in top_negative_words and 'college' in top_negative_words\n",
    "\n",
    "for i, word in enumerate(top_negative_words):\n",
    "    print(f\"#{i}\\t{word.rjust(10, ' ')}\\t(ratio={probability_ratio[bow_vocabulary.index(word)]})\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now lets try something less prehistoric: __Logistic Regression__. Turns out, if you're using silicon instead of an abacus, you can find model weights by optimizing the log-probability of the answer. Though, of course, you don't even need to write it by hand anymore. Let's sklearn it!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.linear_model import LogisticRegression\n",
    "from sklearn.model_selection import GridSearchCV, StratifiedKFold\n",
    "\n",
    "log_reg = LogisticRegression(solver='lbfgs', random_state=42)\n",
    "parameters = {\"C\": np.logspace(-1, 1, 300)}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "GridSearchCV(cv=StratifiedKFold(n_splits=2, random_state=None, shuffle=False),\n",
       "             estimator=LogisticRegression(random_state=42), n_jobs=-1,\n",
       "             param_grid={'C': array([ 0.1       ,  0.10155211,  0.10312832,  0.10472898,  0.1063545 ,\n",
       "        0.10800524,  0.1096816 ,  0.11138398,  0.11311279,  0.11486843,\n",
       "        0.11665131,  0.11846187,  0.12030053,  0.12216773,  0.12406392,\n",
       "        0.12598953,  0.12794503,  0.12993088,  0.1319...\n",
       "        5.92345715,  6.01539588,  6.10876161,  6.20357648,  6.29986298,\n",
       "        6.39764396,  6.4969426 ,  6.59778248,  6.7001875 ,  6.80418197,\n",
       "        6.90979055,  7.01703829,  7.12595063,  7.23655342,  7.34887289,\n",
       "        7.46293569,  7.57876886,  7.6963999 ,  7.81585671,  7.93716762,\n",
       "        8.06036141,  8.18546731,  8.31251499,  8.4415346 ,  8.57255673,\n",
       "        8.70561248,  8.8407334 ,  8.97795155,  9.11729948,  9.25881025,\n",
       "        9.40251743,  9.5484551 ,  9.69665789,  9.84716096, 10.        ])})"
      ]
     },
     "execution_count": 57,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "clf = GridSearchCV(log_reg, parameters, n_jobs=-1, cv=StratifiedKFold(2))\n",
    "clf.fit(X_train_bow, y_train)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'C': 0.19693113379074229}"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "clf.best_params_"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [],
   "source": [
    "bow_log_reg_model = clf.best_estimator_"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model accuracy: 0.772\n",
      "Well done!\n"
     ]
    },
    {
     "data": {
      "image/png": "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8OHDHD582O7Qcl18fHyOPiOeTMvsG3KrzG69Dl1EHgDCgLYZzTfGzABmAISFhZl27dplex8f7dlAbGwsqeteioff/wSgYcOGUD/728yP9u7dS40aNfD392fRokUcP36c+++/3+6w8lRERAQ5+Yx4Mi2zb8itMruS0GOAkDSvKzunXUVEOgIvA22NMZfcE54LfvkYvhtlPS8clGe7zS2JiYmMGzeON954g/HjxzNs2DDatWvnczUYT3D27FmOHz9OYmKi27YZFBTE7t273bY9T6BlvlqBAgUoV64cgYGB2d6uKwn9F6C2iFTHSuR9gKuuDRSRW4DpQBdjzPFsR3EjEhOsv0/8CqVr5umu3W3Lli2Eh4ezfft2+vTpQ9++fe0OSWXi7NmzHDt2jEqVKlGkSBG3Xft/7tw5SpQo4ZZteQot8xXGGBISEoiJserM2U3qWZ4UNcYkAU8Aq4HdwBJjzE4RGS0idzsXewcoDnwuIltFZEW2onCHMrXAg2+oef/992nevDknT55k+fLlLFq0iHLlytkdlsrE8ePHqVSpEkWLFtUbuZTbiAhFixalUqVKHD+e/bqxS23oxphVwKp000amed4x23tWwJXOtMLCwggPD2f8+PGULFnS7rBUFhITEylSpIjdYSgvVaRIkRw15XlX51we5OzZs7zwwgsULlyYSZMm0bJlS1q2bGl3WCobtGaucktOP1vedeu/h1i1ahUNGjRgxowZBAQEaGdaSim30ISeh06ePMkDDzzAP/7xD4KCgvj555955513tKan8qUhQ4bwxhtv2B2GygbPTujnT0LcNVdQ5ltnzpxh5cqVvPbaa/z22280b369+7OUyrlq1arx3Xff3dA2pk2bxquvvnpD2xgwYAABAQEcOXLkmumvvPLKVdMOHDiAiJCUlJQ6beHChYSFhVG8eHEqVKhA165d+fHHH7Mdx6RJkyhfvjyBgYE8+uijXLqU+ZXVM2fOpFatWhQvXpwuXbpcdRNfbGwsDz/8MOXKlaNcuXKMGjXqqnW3bt1K69atCQoKonLlyld9IX766acUL148tSwpJ9R//fXXbJcnM56d0D/rD1sXQMH8e3doTEwM48ePxxhD7dq1iYqKYtSoURQsWNDu0JQPS5s0c8v58+f5v//7P4KCgliwYEG213/33Xd56qmneOmllzh27BjR0dEMHTqU5cuX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d1KrVi2+/fZbunfvzu+//37NRRO1a9emQYMGDB06lC+++OKqeXPnzqVXr14UL148dVqRIkWoVq0a4eHhANx33328++67/PTTT1cNYH2jvKqGfvz4ce6//3569uxJcHAwmzdv5q233tJkrnxO+s98SEgIDz744FW12PPnz6e2SXfu3Jk1a9Zw5MgR6tWrx6BBgzLcTlYSEhJYsmQJ69atSx1+btKkSWzbti11GLjMhp+rWrUqAB06dODQoUNcb7yEt95667rDz6XIaPi5zH59bN26le7du1OnTh38/Pzo0qULFSpU4Oeff85w+aSkpNQ7ytOW//PPP+fhhx++anrjxo2vOZa5kZe8KqGfPXuWNWvW8Oabb7J582aaNm1qd0hK2SL9EHQPPPAAK1euZPXq1TgcDi5evEhERASHDh3i2LFjLF++nPPnz1OoUCGKFy+eOmB0cHAwhw4d4vLlyy7td9myZfj7+7Nr167U4ed2795N69atU4ef+9e//sUnn3zC5s2bMcawd+9eJk2alDr8XO3atRk6dCh9+/YlIiKCy5cvc/HiRT777LPUdvOXXnrpusPPpXjooYd49913iYmJ4fDhw0ycODHT4eduvfVWvvrqKyIjIzHGsGbNGv7666/U8URnzpyZeoJ0165dvP3223To0OGqbXz55ZeUKlWK9u3bXzW9Z8+enDlzhrlz5+JwOFi2bBmHDh2iZcuWLh1Xl2U2lFFuP25kCLoBb8025rVAY7Z+ZqKiosyYMWNMcnKyMcZ9Q3jlN/l5OLbckp/L7GlD0BljzMaNG02bNm1MqVKlTJkyZUy3bt1MVFSUOXz4sGnTpo0JDAw0QUFBpm3btmbnzp3GGGMuXbpkunXrZkqVKmVKly6d5X47d+5shg8ffs30xYsXm+Dg4NQh8GbNmmVCQ0NNiRIlTPXq1c3bb79tHA5H6vLJycnmvffeM6GhoaZIkSKmYsWK5v777zc7duzI1nFITk42zz33nClVqpQpVaqUee6551JzhTHGFCtWzKxfvz512VdffdWEhISY4sWLm3r16pl58+alLjtgwABTrlw5U7RoUVO1alXz7LPPmoSEhKv216lTJ/PKK69kGMv69etNw4YNTbFixcwtt9ySut/M5NoQdCLSBXgf8AdmGmPGpptfCJgHNANOAf8yxhy43jZzOgTdA9MiaHrqK566PJ1pib154YMlJCcns23bNq/ufyU/D8eWW/JzmXUIOvfRMmcsJ0PQZdnkIiL+wBSgKxAK9BWR0HSLhQNnjDG1gEnAuKy2m1PtL6zmH0c+ot2cC/z77dm0aNEi9SSGUkr5Mlfa0G8D9hljIo0xl4HPgPSnZe8BUu4eWAp0kFw6ExmQlEDnBRf4I64on8yezerVq1Mv3ldKKV/mymWLlYCDaV4fAppntowxJklE4oDSwFUXWYrIYGAwWCdb0t6p5qpzBcrwcu9mlLlrOKXKVWDdunXZ3oYnio+Pz9Hx8mT5ucxBQUGcO3fO7dt1OBy5st38TMucsZQT19mRp9ehG2NmADPAakPPSftou3btiIholW/bVnNLfm5Pzi35ucy7d+/O9HrmG6Htyb4hqzIbYyhcuHC2uwdwpcklBghJ87qyc1qGy4hIABCEdXJUKa9UoEABEhIS7A5DeamEhITUG62yw5WE/gtQW0Sqi0hBoA+Qvvu0FUDKlfT3Af8zrlw+o5SHKleuHDExMVy4cOG63bEqlR3GGC5cuEBMTAzlypXL9vpZNrk428SfAFZjXbY42xizU0RGY10PuQKYBcwXkX3Aaaykr5TXSukT6PDhw9fcxn4jLl68SOHChd22PU+gZb5agQIFCA4OzlG/Uy61oRtjVgGr0k0bmeb5RaB3tveulAcLDAx0e2dvERERudKtan6mZXYfr7r1XymlfJkmdKWU8hKa0JVSyktoQldKKS/hUudcubJjkRNAVA5XL0O6u1B9gJbZN2iZfcONlLmqMaZsRjNsS+g3QkS2ZNbbmLfSMvsGLbNvyK0ya5OLUkp5CU3oSinlJTw1oc+wOwAbaJl9g5bZN+RKmT2yDV0ppdS1PLWGrpRSKh1N6Eop5SXydUIXkS4iskdE9onIiAzmFxKRxc75m0Skmg1hupULZR4uIrtEZLuIrBWRqnbE6U5ZlTnNcr1ExIiIx1/i5kqZReR+53u9U0QW5nWM7ubCZ7uKiHwvIr87P9/d7IjTXURktogcF5EdmcwXEZnsPB7bRaTpDe/UGJMvH1hd9f4N1AAKAtuA0HTLDAWmOZ/3ARbbHXcelLk9UNT5/HFfKLNzuRLAemAjEGZ33HnwPtcGfgdKOV+XszvuPCjzDOBx5/NQ4IDdcd9gmdsATYEdmczvBnwNCHA7sOlG95mfa+j5anDqPJJlmY0x3xtjLjhfbsQaQcqTufI+A7wBjAMu5mVwucSVMg8CphhjzgAYY47ncYzu5kqZDZDSH3EQcDgP43M7Y8x6rPEhMnMPMM9YNgIlRaTCjewzPyf0jAanrpTZMsaYJCBlcGpP5UqZ0wrH+ob3ZFmW2flTNMQY81VeBpaLXHmf6wB1ROQnEdkoIl3yLLrc4UqZRwEPiMghrPEXnsyb0GyT3f/3LOXpINHKfUTkASAMaGt3LLlJRPyAd4EBNoeS1wKwml3aYf0KWy8ijYwxsXYGlcv6AnOMMRNFpAXWKGgNjTHJdgfmKfJzDd0XB6d2pcyISEfgZeBuY8ylPIott2RV5hJAQyBCRA5gtTWu8PATo668z4eAFcaYRGPMfuAvrATvqVwpcziwBMAYswEojNWJlbdy6f89O/JzQvfFwamzLLOI3AJMx0rmnt6uClmU2RgTZ4wpY4ypZoyphnXe4G5jzBZ7wnULVz7by7Bq54hIGawmmMg8jNHdXClzNNABQETqYyX0E3kaZd5aATzkvNrldiDOGHPkhrZo95ngLM4Sd8OqmfwNvOycNhrrHxqsN/xzYB+wGahhd8x5UObvgGPAVudjhd0x53aZ0y0bgYdf5eLi+yxYTU27gD+APnbHnAdlDgV+wroCZivQye6Yb7C8i4AjQCLWL65wYAgwJM17PMV5PP5wx+dab/1XSikvkZ+bXJRSSmWDJnSllPISmtCVUspLaEJXSikvoQldKaW8hCZ0pZTyEprQlVLKS/w/K/kudN1TGVcAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "from sklearn.metrics import roc_auc_score, roc_curve\n",
    "\n",
    "for name, X, y, model in [\n",
    "    ('train', X_train_bow, y_train, bow_log_reg_model),\n",
    "    ('test ', X_test_bow, y_test, bow_log_reg_model)\n",
    "]:\n",
    "    proba = model.predict_proba(X)[:, 1]\n",
    "    auc = roc_auc_score(y, proba)\n",
    "    plt.plot(*roc_curve(y, proba)[:2], label='%s AUC=%.4f' % (name, auc))\n",
    "\n",
    "plt.plot([0, 1], [0, 1], '--', color='black',)\n",
    "plt.title(\"Logistic regression + Bag-of-words\")\n",
    "plt.legend(fontsize='large')\n",
    "plt.grid()\n",
    "\n",
    "test_accuracy = np.mean(bow_log_reg_model.predict(X_test_bow) == y_test)\n",
    "print(f\"Model accuracy: {test_accuracy:.3f}\")\n",
    "assert test_accuracy > 0.77, \"Hint: tune the parameter C to improve performance\"\n",
    "print(\"Well done!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "tags": []
   },
   "source": [
    "### Task: implement TF-IDF features\n",
    "\n",
    "Not all words are equally useful. One can prioritize rare words and downscale words like \"and\"/\"or\" by using __tf-idf features__. This abbreviation stands for __text frequency/inverse document frequence__ and means exactly that:\n",
    "\n",
    "$$ feature_i = { Count(word_i \\in x) \\times { log {N \\over Count(word_i \\in D) + \\alpha} }} $$, where:\n",
    "* x is a single text\n",
    "* D is your dataset (a collection of texts)\n",
    "* N is a total number of documents\n",
    "* $\\alpha$ is a smoothing hyperparameter (typically 1)\n",
    "* $Count(word_i \\in D)$ is the number of documents where $word_i$ appears\n",
    "\n",
    "It may also be a good idea to normalize each data sample after computing tf-idf features.\n",
    "\n",
    "__Your task:__ implement tf-idf features, train a model and evaluate ROC curve. Compare it with basic BagOfWords model from above.\n",
    "\n",
    "Please don't use sklearn/nltk builtin tf-idf vectorizers in your solution :) You can still use 'em for debugging though."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "tokenized_texts_train = []\n",
    "for text in texts_train:\n",
    "    tokenized_text = [token for token in text.split() if token in bow_vocabulary]\n",
    "    tokenized_texts_train.append(tokenized_text)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_tf(tokenized_text, term):\n",
    "    return tokenized_text.count(term) / len(tokenized_text)\n",
    "\n",
    "def get_idf(tokenized_corpus, term):\n",
    "    size = len(tokenized_corpus)\n",
    "    doc_count = sum([1 if term in text else 0 for text in tokenized_corpus])\n",
    "    \n",
    "    return np.log((1 + size) / (1 + doc_count)) + 1\n",
    "\n",
    "def get_tf_idf(tf, idf):\n",
    "    return tf * idf\n",
    "\n",
    "def get_l2_norm(vector):\n",
    "    return np.sqrt(np.sum(vector**2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [],
   "source": [
    "def text_to_tf_idf(text):\n",
    "    \"\"\"convert text string to an array of token counts. Use bow_vocabulary.\"\"\"\n",
    "    vec = np.zeros(k)\n",
    "    tokenized_text = text.split()\n",
    "    tf_idf_sentence = []\n",
    "    tokenized_text_set = list(set(tokenized_text))\n",
    "    \n",
    "    for word in tokenized_text_set:\n",
    "        tf_ = get_tf(tokenized_text, word)\n",
    "        idf_ = get_idf(tokenized_texts_train, word)\n",
    "        tf_idf_sentence.append(get_tf_idf(tf_, idf_))\n",
    "\n",
    "    tf_idf_sentence = np.array(tf_idf_sentence)\n",
    "    l2_norm = get_l2_norm(tf_idf_sentence)\n",
    "    tf_idf_sentence = tf_idf_sentence / l2_norm\n",
    "    tf_idf_sentence_dict = dict(zip(tokenized_text_set, tf_idf_sentence))\n",
    "    \n",
    "    for token in tokenized_text_set:\n",
    "        ind = (\n",
    "            bow_vocabulary.index(token)\n",
    "            if token in bow_vocabulary\n",
    "            else -1\n",
    "        )\n",
    "        if ind != -1:\n",
    "            vec[ind] = tf_idf_sentence_dict[token]\n",
    "    return vec"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_train_tf_idf = np.stack(list(map(text_to_tf_idf, texts_train)))\n",
    "X_test_tf_idf = np.stack(list(map(text_to_tf_idf, texts_test)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.linear_model import LogisticRegression\n",
    "from sklearn.model_selection import GridSearchCV, StratifiedKFold\n",
    "\n",
    "log_reg_tf_idf = LogisticRegression(solver='lbfgs', random_state=42)\n",
    "parameters = {\"C\": np.logspace(-1, 1, 600)}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "GridSearchCV(cv=StratifiedKFold(n_splits=2, random_state=None, shuffle=False),\n",
       "             estimator=LogisticRegression(random_state=42), n_jobs=-1,\n",
       "             param_grid={'C': array([ 0.1       ,  0.10077177,  0.1015495 ,  0.10233323,  0.10312301,\n",
       "        0.10391889,  0.10472091,  0.10552911,  0.10634356,  0.10716429,\n",
       "        0.10799135,  0.1088248 ,  0.10966468,  0.11051104,  0.11136394,\n",
       "        0.11222341,  0.11308952,  0.11396232,  0.1148...\n",
       "        7.69976486,  7.75918954,  7.81907284,  7.8794183 ,  7.9402295 ,\n",
       "        8.00151002,  8.06326348,  8.12549354,  8.18820388,  8.2513982 ,\n",
       "        8.31508023,  8.37925375,  8.44392253,  8.50909042,  8.57476125,\n",
       "        8.64093891,  8.70762732,  8.7748304 ,  8.84255214,  8.91079654,\n",
       "        8.97956763,  9.04886948,  9.11870618,  9.18908186,  9.26000068,\n",
       "        9.33146683,  9.40348454,  9.47605806,  9.54919168,  9.62288973,\n",
       "        9.69715656,  9.77199656,  9.84741416,  9.92341381, 10.        ])})"
      ]
     },
     "execution_count": 57,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "clf_tf_idf = GridSearchCV(log_reg_tf_idf, parameters, n_jobs=-1, cv=StratifiedKFold(2))\n",
    "clf_tf_idf.fit(X_train_tf_idf, y_train)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'C': 9.771996562044086}"
      ]
     },
     "execution_count": 58,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "clf_tf_idf.best_params_"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [],
   "source": [
    "tf_idf_log_reg_model = clf_tf_idf.best_estimator_"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model accuracy: 0.790\n",
      "Well done!\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXQAAAEICAYAAABPgw/pAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/YYfK9AAAACXBIWXMAAAsTAAALEwEAmpwYAAA/HUlEQVR4nO3dd3wU1RbA8d8hoZeABEILvQZEhCAiAlFQiijwUB5V0UgRC4oFbDxEVEAQhUcVUIqoyFOKoohoRBFBuhQpAqFFIEASQgnJ5r4/ZgkhJGSTbDLZzfl+Pvthp5+7szncvTNzrxhjUEop5fny2R2AUkop99CErpRSXkITulJKeQlN6Eop5SU0oSullJfQhK6UUl5CE7qHE5HpIvJ6JrarLCKxIuKTHXHlJiLyrYg8YnccrhKR0SISKSL/ZGLbj0VkdLLpJ0TkhPNcl05l/a4icsS5/Nasxq7sJXofes4RkUPA48aYH/LSsdVVIrITqOKcLAzEAwnO6beBBcAeoIox5mQq2/fDOo93prH/j4GjxpjXRCQ/EAPcbozZlsb6fwNDjTFLM12oq/syQC1jzP6s7ktljtbQVZaIiK+b9+eRvxhEJEREwtJbzxhT3xhTzBhTDPgFeOrKtDHmbaAycDq1ZJ4JAUAhYOcN1qmSzvIc46nnPjfRhJ4LiEhBEXlfRI47X++LSMFky18SkQjnssdFxIhITeeypJ/YIuIvIl+LSJSInBGRX0Qkn4jMx0oUy50/rV8SkarO/fg6t71JRD5yHuOsiCxJI9Z+IrJWRCaKyGlgpDP+8SJy2PnzfrqIFM5A/NNEZIWInAfuEpEKIvI/ETklIgdF5Jlk+7pNRDaKSIzzWO855xcSkQUictpZ/j9EJMC5LExEHne+zycir4lIuIicFJF5IuLnXHblM3nEWZZIEXnVbSc6HSLSFlgFVHCep49d2OZWEdksIudE5HOsBI6I1Maq6QNEiciPKbYrKCKxgA+wzVlTx4XPfp3z840Qkf+KSAHnsjXO1bY5Y/+387vya4rjuv3cq2SMMfrKoRdwCGibyvxRwO9AWaAM8BvwpnNZe+AfoD5QBOsnuQFqOpd/DIx2vn8HmA7kd75acrVZ7ZpjA1Wd+/F1Tn8DfA6Ucm7bOo0y9MNqInga8MVqNpgILANuAooDy4F3MhB/NNACq4JRBNgEjAAKANWBA0A75/rrgL7O98WwmhMABjqPWwQrSTUBSjiXhWE1UwA8Bux37rcY8CUwP8Vn8qGzXLcAcUA9F85tCBCWwe9DUlwp9nP0Btv0A351vi8AhAPPOc/Zg1hNOKNTlMf3BvtLfi7ypfPZNwFud573qsBu4NnU9pUy1jSO55Zzr6+rL62h5w69gVHGmJPGmFPAG0Bf57LuwEfGmJ3GmAvAyBvsJx4oj9X+Gm+M+cU4v/03IiLlgQ7AIGPMWee2P99gk+PGmMnGmATgEjAAeM4Yc8YYcw6rLbhHBuJfaoxZa4xJBG4GyhhjRhljLhtjDmAl2Cv7iwdqioi/MSbWGPN7svmlsZKFwxizyRgTk8qxegPvGWMOGGNigZeBHnJt09EbxpiLxmp33oaV2HOj27ES+fvOc7YY+CML+2vKDT5752f6uzEmwRhzCJgBtM5aEdxy7pWTJvTcoQJWTeuKcOe8K8uOJFuW/H1K72LVPr8XkQMiMtzF4wcCZ4wxZ11cP3kMZXDWrJw/xaOA75zzwbX4k8+rgtXkEJVsf69gtQcDhAK1gb+czSqdnPPnAyuBz5xNO+PEuiiYUmqftW+y/YP1i+KKC1i1weuIyPBkMX4N3Jki7iwRkZbO5otYsS6mplQBOJbiP+3wVNZz1Q0/exGpLVaT3j8iEoP1H7d/Fo4H7jn3ykkTeu5wnKt3PoDV3n3c+T4CqJRsWWBaOzHGnDPGPG+MqQ48AAwVkTZXFt/g+EeAm0SkpIvxJt9XJHARqG+MKel8+Rnrop+r8Sff3xHgYLJ9lTTGFDfGdHSWcZ8xpidW89RYYLGIFHXWUN8wxgQBdwCdgIdTOVZqn3UCcMLFsl8N2pgxV2J0Hu/X5HFndH+p7P8Xc/WCaf1UVokAKoqIJJtXOQuHvOFnD0wD/sK6k6UEVrKVtHYGnMf6zx4AESmXyjpZPveZLaw30oSe8/I7L+BdefkCnwKviUgZEfHHakNc4Fx/EfCoiNQTkSJAmveci0gnEanp/AOPBhxAonPxCaw2yesYYyKAb4GpIlJKRPKLSCtXCuP8qfwhMFFEyjrjqCgi7TIav9MG4JyIDBORwiLiIyINRKSpc999RKSM87hRzm0SReQuEblZrDslYrB+niemsv9PgedEpJqIFMOqZX7ubD7yNOuw/jN6xnnO/gXcloX93fCzx7o+EgPEikhd4IkU26f8jm0D6otIIxEpxI2bC9M9flrnPrOF9Uaa0HPeCqwa7ZXXSGA0sBHYDvwJbHbOwxjzLTAJ+AmrOeVKu2FcKvuuBfwAxGL9sU81xvzkXPYO1n8aUSLyQirb9sVKgn8BJ4FnM1CmYVdic/4U/wGok4n4McY4sGq7jYCDWL8AZgF+zlXaAzvFukPjA6CHMeYiUA5YjJVwdgM/YzXDpDTHOX+Nc/+XsC7wehxjzGXgX1gXH88A/8a6yJvZ/aX32b8A9ALOYf0n/nmKXYwE5jq/Y92NMXuxLvj/AOwDfuUGsnDulZM+WORhRKQesAMo6Im1Sk+PX6ncTGvoHkCsx7MLikgprLbD5Z6UDD09fqU8hSZ0zzAQqxnkb6x28ZRtl7mdp8evlEfQJhellPISWkNXSikv4daOlTLC39/fVK1aNVPbnj9/nqJF89btp1rmvEHLnDdkpcybNm2KNMaUSW2ZbQm9atWqbNy4MVPbhoWFERIS4t6Acjktc96gZc4bslJmEUnzaWBtclFKKS+hCV0ppbyEJnSllPISmtCVUspLaEJXSikvkW5CF5E5Yg3VtSON5SIik0Rkv4hsF5HG7g9TKaVUelypoX+M1ctZWjpg9fJXC2vkmmlZD0sppVRGufTov4hUBb42xjRIZdkMrLEUP3VO7wFCnH1spyk4ONhk5j70hesPMzdsJyVLlszwtp4sKipKy5wHeFqZ/RNOEHLxe+SG46fcWNylSxQsVMiNUeVely7HExN7kbPl7uDVZ57M1D5EZJMxJji1Ze54sKgi1w4jddQ577qELiIDsGrxBAQEEBYWluGDzV1/kcMxDq72b583OBwOoqKi7A4jR2mZb8AYChCfoX3f7thE14QVNx5jKIOCEvcBkJjVnca6IZhc7seDCQxcfhG/gvD0EP9M5b/05OiTosaYmcBMsGromXlSatqedUAUK4d1cG9wuZw+TZc3uFTmY5vg84ch5mjmDlLtrsxtl6pKUDaIfO3fzvQevP08R0VF8eKLLzJr3ixq1qzJxFmzMMZkS5ndkdCPce04kZWc85TKG/avhn3fu2VXNY8ehYvfpr3CpWjY9unV6bYjM3aAcg2hZpv011Nu4XA4uOOOO9izZw8vvfQSI0eOpHDhwtlSOwf3JPRlwFMi8hnQDIhOr/1cKY8XsQ2WPgmOeDj1lzWvkN+Nt3FBQEICnL7Bn2WiA3wLwd2vQ/CjUCBvdWrlKU6fPs1NN92Ej48Pb731FoGBgQQHp9rs7VbpJnQR+RQIAfxF5CjwHyA/gDFmOtYYmR2xxou8ADyaXcEqZbtDayH6CBz8Bf75E2q1gzJ1IKgLNPhXlne/1subH7ydMYZPPvmEIUOGMGbMGPr370/Xrl1z7PjpJnRjTM90lhsgc5drlfIUsSdh9RuwZcHVeT4FodssKFTCvrhUrnHkyBEGDRrEihUruP3222nRokWOx2Bb97lK5UqXouHcievn71tpJfOiZeCuV6B6CBQqqclcAfDpp58ycOBAHA4H77//Pk899RQ+Pj45HocmdKWSm9Eazh5Me3n/H6Fk5ZyLR3mEUqVK0axZM2bOnEm1atVsi0MTulLJXTwDNdpAo17XLytSWpO5AiAhIYGJEydy+fJlXn31Vdq3b0+7du0QceNN/pmgCV3lHZH74eLZ1JdFhcOX/cEkWhc5b34wZ2NTHmPbtm2EhoayadMmunfvjjEGEbE9mYMmdOUNLkbB5rmQcDntdc4dh41z0t9X1ZbQ+BG3haa8R1xcHKNHj2bMmDHcdNNNfPHFF3Tr1i1XJPIrNKErz/fXN7BqhGvrth4GlW5LfVnBYhDYDHLRH6jKPfbt28fYsWPp1asX7733HqVLl7Y7pOtoQleeI3Kf1TSS3Oo3IWKr9f7ZP6F4hbS3F4F8OX/ngfJcsbGxLF26lN69e9OgQQP++usvqlevbndYadKErnKtsifWwPc/XJ3x2+S0V77/A/AL1Nq1cptVq1YxYMAAwsPDady4MfXq1cvVyRw0oavcKHIffNmfoONbrOn8RZwLBJo+Dg27X7t+6ZpQ5KYcDVF5r7Nnz/LCCy8wZ84cateuzc8//0y9evXsDsslmtCV/YyBv1dbD/WsnwFH1gNwOX9JCvReCFVz/ok7lTc5HA5atGjB3r17efnllxkxYgSFPKivdk3oyj5/LoaDP1s18sPrrl3WeQrrzpajtSZzlQMiIyOTOtN6++23qVy5Mo0be95omprQVc64cAa+Gw7hv11t5446bP1btCwU8bfawf1rQbEAKFwSk01djCp1hTGG+fPn8+yzzzJmzBgGDBhAly5d7A4r0zShq+wVdw62fALfDbs67xZnf29VWkDDf0MNdw64oJRrwsPDGThwICtXruSOO+6gVatWdoeUZZrQlfsZAz/8B86Gw64lV+cHdYZ7RkGpqnZFphQACxYs4IknnsAYw+TJkxk8eDD58uWzO6ws04Su3OvoJpjfBeJirGn/2lCgGPT8DIoH2BqaUleUKVOGFi1aMGPGDKpUqWJ3OG6jCV25V+ReK5nf2td6KrNkYPrbKJXN4uPjmTBhAvHx8bz++uu0a9eOe++9N1c9tu8OmtCVe3w7DE7tgXP/WNMtn9dkrnKFLVu2EBoaypYtW+jRo0eu6kzL3TShq9RFH4PT+yH6KCwd7JyZ1h+Aufq20m1Q5z4ocYNH8JXKAZcuXWLUqFGMGzcOf39//ve///Gvf2V9mMDcTBO6ulaiA9ZNgVWvXzu/ekjanVqB1UfKrX3Ar1K2hqeUq/bv38/48eN5+OGHmTBhAqVKlbI7pGynCV1dK2Lb1WReuz3c8Yw1snz5W7SfFJXrxcbG8tVXX9G3b18aNGjAnj17bB1BKKdpQldXbV0IS56w3vf4FOp2tDcepTJg5cqVDBgwgCNHjhAcHEy9evXyVDIHTegqMRHWjIPzp+DYJmtex/FWE4tSHuD06dMMHTqUefPmUbduXX755ReP6UzL3TSh52Xhv8HCHhAXbU0XKQ2V77B6NNTmFeUBrnSmtX//fl599VVee+01j+pMy900oecll89bo/s4nEO17f/BSuY3d4e2I8Gvoq3hKeWqU6dOUbp0aXx8fBg7dixVqlShUaNGdodlO03oecnOJcluQXQqWAI6TwHfAraEpFRGGGP4+OOPGTp0KGPGjGHgwIF07tzZ7rByDU3o3iohDk7/fe28s4esf/v/CEXLWO8LldRkrjzCoUOHGDBgAKtWraJly5bcdZd26paSJnRvceYA7Pnu6vSqEZAYn/q6parpCD/Ko8yfP58nnngCEWHq1KkMHDjQKzrTcjdN6N7i14mwed618/IXgS7Trp1XLECTufI4AQEBtGrViunTp1O5cmW7w8m1NKF7MmPgnz+ti50xx6FEJXhi7dXlBYvrKPfKI8XHxzNu3DgcDgcjRozg3nvv5d5777U7rFxPE7onO74ZPrz76nSZelC4pG3hKOUOmzdv5rHHHmPbtm306tUrqTMtlT5N6J4oMdG69fDCWWv63rcgoD6UrmlvXEplwcWLF3njjTcYP348ZcqU4auvvvLo4eDs4FJCF5H2wAeADzDLGDMmxfLKwFygpHOd4caYFe4NVSWZ2wnCkzWtVGoKlZvZF49SbnDgwAHee+89+vXrx7vvvpsnOtNyt3QTuoj4AFOAe4CjwB8isswYsyvZaq8Bi4wx00QkCFgBVM2GeBVYd7RUuBXqPWC1k1f0vNHJlQKIiYnhu+++IyQkhPr167Nv3z6vGkEop7ly389twH5jzAFjzGXgMyDlnfwGKOF87wccd1+IKlXlboaWQ+G2/uCT3+5olMqwFStW0KBBA9599112794NoMk8i1xpcqkIHEk2fRRI+ft+JPC9iDwNFAXaprYjERkADADrNqSwsLAMhgtRURdxOByZ2taTxcbGJpW5eVwcpyMi2Ovln0HyMucVeaHM0dHRTJkyhVWrVlGlShXGjh3LiRMnOHHihN2h5ZjsOs/uuijaE/jYGDNBRJoD80WkgTEmMflKxpiZwEyA4OBgExISkuEDTduzjqioKDKzrScLCwsjpMXt8MNISLxIhfLlqeDln0FYWFjePM9eXGaHw0FQUBAHDhxgxIgRvPLKK6xbt86ry5ya7DrPriT0Y0DywSErOeclFwq0BzDGrBORQoA/cNIdQeZp505AXAyFLxyFvd/B+mnWY/uBt9sdmVIuO3HiBGXKlMHHx4fx48dTpUoVGjZsaHdYXseVNvQ/gFoiUk1ECgA9gGUp1jkMtAEQkXpAIeCUOwPNcy6cgZ/egQm14b/BNNvwJHzxiLWs22y4tbe98SnlAmMMs2fPpk6dOsycOROA+++/X5N5Nkm3hm6MSRCRp4CVWLckzjHG7BSRUcBGY8wy4HngQxF5DusCaT9jjEl7rypViQ5Y8QLEnoS/vr46v+nj7Ir1IygoyHqcv0oL+2JUykUHDhygf//+/Pjjj7Ru3Zq2bVO9tKbcyKU2dOc95StSzBuR7P0uQLNMViQ6IOwd2DjHmi4bZPW78tDHULgkJ8PCCLo5xM4IlXLZ3LlzGTx4MD4+PkyfPp3+/ftrZ1o5QJ8UzS12/A/WvAvVWkH3+foIv/JoFSpU4O6772batGlUqlTJ7nDyDE3odkp0wJLBEHPMeoEmc+WRLl++zJgxY0hMTGTkyJHcc8893HPPPXaHledoQrfLwTUw9/6r05XvsB7hL1gi7W2UyoX++OMPHnvsMXbs2EHfvn21My0baUK3y5XRg1o8C3c8DUX97YxGqQy7cOECI0aMYOLEiZQvX55ly5Zx//33p7+hyjaa0HOSMbD4MTjzt3VbIliP7msyVx7o4MGDTJ48mf79+zN27Fj8/PzsDinP04SekxzxsPNLKF3L6u62xl1QvLzdUSnlsujoaL788kseffRR6tevz/79+wkMDEx/Q5UjNKFnl4tR8PNYiL9wdV6iw/r3lh7Q6gVbwlIqs7755hsGDhxIREQEzZs3p27duprMcxlN6NkhMdF6MOj3qVC4FPgUuLqseAUof4t9sSmVQadOneLZZ59l4cKFNGjQgC+//JK6devaHZZKhSb07PDjKGvQZoBHvoZyDeyNR6lMcjgc3HnnnRw8eJA33niD4cOHU6BAgfQ3VLbQhJ4dzp+CQiWh8xSrrVwpD/PPP/9QtmxZfHx8mDBhAlWrVqVBA62Y5Hb6LG52KVAU6nUCvR9XeZDExERmzJhB7dq1mTFjBgCdOnXSZO4hNKErpQDYv38/bdq0YdCgQTRt2pR27drZHZLKIE3oSik++ugjbr75ZjZv3syHH37IDz/8QPXq1e0OS2WQtqG7W9Rha1AKpTxI5cqVadeuHVOmTKFixYp2h6MySRO6u33UEaKPgH8duyNRKk1xcXG88847JCYmMmrUKNq0aUObNm3sDktlkTa5uMv5SJjX2eo1sW4n6PuV3REplar169fTpEkT3njjDQ4fPoyOReM9NKG7y6m/4EAYVLgVmg0EP/3ZqnKX8+fPM3ToUJo3b050dDRff/01H3/8sfaM6EU0obvDiZ2wfrr1vu1Ia5AKpXKZ8PBwpk6dyqBBg9i5cyf33Xef3SEpN9M2dHfYsgB2Lwe/ylCyit3RKJUkKiqKxYsX8/jjjxMUFMT+/ft1BCEvpjV0dzAGCvrBc39CKU3oKndYunQpQUFBDBo0iL/++gtAk7mX04SulJc5efIkPXr0oEuXLpQpU4bff/9dO9PKI7TJJasuxcDFM3ZHoRRgdabVokULDh8+zOjRo3nppZfInz+/3WGpHKIJPas+6ggn/oRiAXZHovKw48ePU65cOXx8fPjggw+oWrUqQUFBdoelcpg2uWTG0U3waS9Y2ANO74OqLaH3YrujUnlQYmIi06ZNo27dukyfbt1p1bFjR03meZTW0DPqf/3hz0XW+3INwb82BD8K5RvaG5fKc/bu3Uv//v1Zs2YNbdu2pUOHDnaHpGymCT2jIrZB6Zpw51C4tbfd0ag8avbs2Tz11FMUKlSIOXPm0K9fP31ASGlCz5SABprMla2qVq1Khw4dmDJlCuXL60DjyqIJXSkPEBcXx5tvvgnA6NGjtTMtlSq9KKpULvfbb7/RqFEj3nrrLSIiIrQzLZUmTegZcfk8JCbYHYXKI2JjYxkyZAh33nknFy5c4LvvvmP27NnaVq7S5FJCF5H2IrJHRPaLyPA01ukuIrtEZKeILHRvmLnA+UgYVx3O/A0++qCGyn6HDx9mxowZPPnkk+zYsUOHhFPpSrcNXUR8gCnAPcBR4A8RWWaM2ZVsnVrAy0ALY8xZESmbXQHb4vTf8OObkHAJGvWGls/bHZHyUufOnWPmzJkMGDCAoKAgDhw4QIUKFewOS3kIVy6K3gbsN8YcABCRz4DOwK5k6/QHphhjzgIYY066O9AclxBnPQUae8IagQjALxBuHwyla9gbm/JKX331FY8//jjR0dG0bt2aOnXqaDJXGeJKQq8IHEk2fRRolmKd2gAishbwAUYaY75LuSMRGQAMAAgICCAsLCzDAUdFXcThcGRq24woEHeaO45tJLpEHS6Uu5uLhctzuEp3+CsS/sreY6cmNjY228uc2+SVMp85c4ZJkybx888/U716dd555x0iIiKIiIiwO7QckVfOc3LZVWZ33bboC9QCQoBKwBoRudkYE5V8JWPMTGAmQHBwsAkJCcnwgabtWUdUVBSZ2TZDYo7DOvBrPRi/Jv0AsHMM9LCwsOwvcy6TF8rscDioW7cuR44c4e2336Zp06a0bdvW7rByVF44zyllV5ldSejHgMBk05Wc85I7Cqw3xsQDB0VkL1aC/8MtUSrlZY4ePUqFChXw8fFh0qRJVKtWjbp16+a5mqpyL1fucvkDqCUi1USkANADWJZinSVYtXNExB+rCeaA+8LMYRfOwKaP7Y5CeaHExEQmT55M3bp1mTZtGgAdOnTQ/sqVW6Sb0I0xCcBTwEpgN7DIGLNTREaJyAPO1VYCp0VkF/AT8KIx5nR2BZ3tdn4FP48F8YGSle2ORnmJv/76i1atWvHMM89w55130qlTJ7tDUl7GpTZ0Y8wKYEWKeSOSvTfAUOfLs52PhJO7rfdDd0HxcvbGo7zCrFmzeOqppyhSpAhz586lb9+++oCQcjvtyyWlZU/DnhWQzxcKFLM7GuUlatSowf33389///tfAgJ0MBSVPTShp3Q5FsrWh3/Ph4Ka0FXmXLp0iVGjRgHw9ttvc9ddd3HXXXfZHJXydtqXS2oKldCHh1SmrV27lkaNGvHOO+9w6tQp7UxL5RhN6Eq5yblz53j66adp2bIlcXFxrFy5kg8//FDbylWO0YSulJscPXqUWbNm8fTTT/Pnn39y77332h2SymO0DV2pLDh9+jSLFi3iiSeeoF69ehw4cEBHEFK20Rq6UplgjGHx4sUEBQXxzDPPsGfPHgBN5spWmtCvcCTAzBAI/w3QNk+VtoiICLp168ZDDz1EYGAgGzdupE6dOnaHpZQ2uSSJPw/Ht0BgM2jp+c9HqezhcDho2bIlx44dY9y4cTz33HP4+uqfkcod9JsIcOYgfDvMeh/UGWrdY288Ktc5cuQIFStWxMfHhylTplCtWjVq165td1hKXUObXC5Fw+a5sG8llG8ElW+3OyKVizgcDiZNmnRNZ1rt2rXTZK5yJa2hf/MC/LnIet97MRQrY288KtfYvXs3oaGhrFu3jg4dOnD//ffbHZJSN6Q19MuxUKoaDFqryVwlmTlzJo0aNWLv3r3Mnz+fb775hsqVtedNlbtpDR2sPlvKNbA7CpWL1KpVi65duzJp0iTKlvWuMc+V98rbCX3Dh3Bip9V3i8rTLl68yMiRIxERxowZo51pKY+Ud5tcEh2w4gU49w9Uamp3NMpGa9as4ZZbbmHcuHFER0drZ1rKY+XdhH5Fqxeg00S7o1A2iImJYfDgwbRu3RqHw8Hq1auZNm2adqalPJYmdJVnHT9+nI8//pihQ4eyfft27r77brtDUipL8mYb+snd8GkP54TWxvKSyMhIFi1axODBg6lbty4HDx7UEYSU18ibNfRTe+DsIajfFep3sTsalQOMMXz++ecEBQXx7LPPsnfvXgBN5sqr5M2EfkWrl8C/lt1RqGx2/PhxunTpQo8ePahSpQqbNm3SJz2VV/L+Jpf4S3BiB1y5c2HdZNi11Hovefv/s7zA4XDQqlUrjh07xvjx4xkyZIh2pqW8lvd/s38aDb9Nvn7+3a9r7dyLhYeHU6lSJXx8fJg6dSrVq1enZs2adoelVLby/oQedw4K+UG3OVfnlakNJfUxbm/kcDj44IMPeO211xg3bhxPPfWUDgWn8gzvT+gAvoWgVlu7o1DZbMeOHYSGhrJhwwY6depEly5d7A5JqRyljcjKK0yfPp3GjRtz4MABFi5cyLJly6hUqZLdYSmVozShK4925TH9evXq8dBDD7Fr1y569uypT3uqPClvNLkor3PhwgVGjBiBj48PY8eOpXXr1rRu3drusJSylXfX0Dd+BIfW2h2FcrOwsDAaNmzIhAkTiI2N1c60lHLy7oT+42iIOQbVWtkdiXKD6OhoBg4cmNSt7Y8//siUKVO0eUUpJ+9O6Bho1Au6zbI7EOUGERERLFiwgBdeeIHt27drf+VKpeBSQheR9iKyR0T2i8jwG6zXTUSMiAS7L0SVl506dYrJk60Hw+rWrcuhQ4d49913KVKkiM2RKZX7pJvQRcQHmAJ0AIKAniISlMp6xYEhwHp3B5kpp/8GR4LdUahMMsbwww8/UK9ePZ5//vmkzrTKlNFxX5VKiys19NuA/caYA8aYy8BnQOdU1nsTGAtccmN8mXNiF0xuDHHRkL+w3dGoDDpy5Aj3338/b731FjVr1mTLli3amZZSLnDltsWKwJFk00eBZslXEJHGQKAx5hsReTGtHYnIAGAAWN2WhoWFZTjgqKiLOByOG27rF7WLW4GDVXtzNF9zHJk4Tm4TGxubqc/L0zgcDh5++GHOnDnD448/To8ePTh16lSeKDvknfOcnJbZfbJ8H7qI5APeA/qlt64xZiYwEyA4ONiEhIRk+HjT9qwjKiqKG24bXhC2QrWWD1GthndcOAsLC7txmT3coUOHCAwMxMfHh7lz51K9enUOHz7s1WVOjbef59Romd3HlSaXY0BgsulKznlXFAcaAGEicgi4HVimF0aVKxISEhg/fjz16tVj6tSpALRt25bq1avbHJlSnseVGvofQC0RqYaVyHsAva4sNMZEA/5XpkUkDHjBGLPRvaEqb7N9+3ZCQ0PZuHEjnTt3plu3bnaHpJRHS7eGboxJAJ4CVgK7gUXGmJ0iMkpEHsjuAJV3mjp1Kk2aNCE8PJzPP/+cr776igoVKtgdllIezaU2dGPMCmBFinkj0lg3JOthKW9ljEFEaNCgAT169GDixIn4+/unv6FSKl3aOZfKEefPn+e1117D19eXd999l1atWtGqlXbJoJQ7efmj/yo3WL16NTfffDPvv/8+cXFx2pmWUtnE+xL6sc2wZpzdUSggKiqKxx9/nLZt2+Lr68uaNWuYNGmSdqalVDbxvoS+43/w949Q7mYdBNpmJ06c4LPPPmPYsGFs27aNli1b2h2SUl7NO9vQCxSDQb/aHUWedCWJDxkyhDp16nDo0CG96KlUDvG+GrqyhTGGBQsWEBQUxEsvvcS+ffsANJkrlYM0oassO3z4MPfddx99+/alTp06bN26lVq1tLlLqZzmnU0uKsckJCQQEhLCyZMnmTRpEoMHD8bHx8fusJTKk7wrof/0Dvy52O4o8oQDBw5QpUoVfH19+fDDD6lRowZVq1a1Oyyl8jTvanLZ9imYRGgaanckXishIYGxY8cSFBTElClTAGjTpo0mc6VyAe+qoQPUuBvuGWV3FF5p69athIaGsnnzZrp27cpDDz1kd0hKqWS8q4auss1///tfmjZtyrFjx1i8eDFffvkl5cuXtzsspVQy3pHQjYG938PlWLsj8TpXHtNv2LAhvXv3ZteuXdrNrVK5lHc0uUTug4XOn/9F9b5nd4iNjeXVV18lf/78jB8/XjvTUsoDeEcNPcE5LvV9E7T93A2+//57GjRowOTJk4mPj9fOtJTyEN6R0K8oVg7y6T3QmXX27FkeffRR2rVrR6FChVizZg0ffPCBdqallIfwroSusuTkyZMsXryYl19+ma1bt3LnnXfaHZJSKgM8vw3dkQCRe+2OwmP9888/fPrppzz33HNJnWmVLl3a7rCUUpng+TX09dPhf84HiQoUsTcWD2KMYe7cuQQFBfHyyy8ndaalyVwpz+X5CT0uxvr3ka+hWmt7Y/EQhw4don379vTr14+goCDtTEspL+H5TS5XVNPBE1yRkJDAXXfdRWRkJFOmTGHQoEHky+f5/68rpTw9oe/9Hv7+ye4oPML+/fupVq0avr6+zJkzh+rVq1OlShW7w1JKuZFnV81Wj4JjG6F8I7sjybXi4+N5++23qV+/flJnWnfddZcmc6W8kGfX0E0i1OkIPT6xO5JcafPmzYSGhrJ161Yeeugh/v3vf9sdklIqG3l2DV2ladKkSdx22238888/fPnllyxatIiAgAC7w1JKZSPPS+jGUCYxEs4eAsdlu6PJda48pn/rrbfy8MMPs2vXLrp27WpzVEqpnOBxTS6tLv7Ak3ET4APnjPINbY0ntzh37hwvv/wyBQsWZMKECbRs2ZKWLfXOH6XyEo+rofslRllvOr0PXaZB25E2RpM7fPfddzRo0ICpU6dijNHOtJTKozyuhp6kYXcoUNTuKGx1+vRphg4dyrx586hXrx5r166lefPmdoellLKJx9XQ1VWnT5/mq6++4vXXX2fLli2azJXK41xK6CLSXkT2iMh+ERmeyvKhIrJLRLaLyGoR0Zucs0lERATjx4/HGEPt2rUJDw9n1KhRFCxY0O7QlFI2Szehi4gPMAXoAAQBPUUkKMVqW4BgY0xDYDEwzt2B5nXGGObMmUO9evV4/fXX2b9/PwClSpWyOTKlVG7hSg39NmC/MeaAMeYy8BnQOfkKxpifjDEXnJO/A5XcG2bedvDgQV588UVCQ0O55ZZb2LZtm3ampZS6jisXRSsCR5JNHwWa3WD9UODb1BaIyABgAEBAQABhYWGuRZnMxYvWcHNrfvmFRJ9CGd7e0zgcDvr06UN0dDTPPfccnTp14vjx4xw/ftzu0LJdbGxspr4jnkzLnDdkV5ndepeLiPQBgoFU+7E1xswEZgIEBwebkJCQDB/j6KZlcA5atWzp1Xe57Nu3j+rVq+Pj48Onn37KyZMn6d69u91h5aiwsDAy8x3xZFrmvCG7yuxKQj8GBCabruScdw0RaQu8CrQ2xsS5J7y8Jz4+nrFjx/Lmm28ybtw4hgwZQkhISJ6rwXiCxMREjh49yvnz5922Tz8/P3bv3u22/XkCLfO18ufPT9myZSlRokSG9+tKQv8DqCUi1bASeQ+gV/IVRORWYAbQ3hhzMsNRKAA2btxIaGgo27dvp0ePHvTs2dPukNQNREZGIiLUqVPHbX3Knzt3juLFi7tlX55Cy3yVMYaLFy9y7JhVZ85oUk/3W2iMSQCeAlYCu4FFxpidIjJKRB5wrvYuUAz4QkS2isiyDEWh+OCDD2jWrBmRkZEsXbqUTz/9lLJly9odlrqBqKgoAgICdIAQ5TYiQpEiRahYsSInT2a8buxSG7oxZgWwIsW8Ecnet83wkRVg/Y8sIgQHBxMaGsq4ceMoWbKk3WEpFzgcDvLnz293GMoLFS5cmPj4+Axv57mP/nu4mJgYhg0bRqFChZg4cSItWrSgRYsWdoelMkhE7A5BeaHMfq/0t6INVqxYQf369Zk5cya+vr7amZZSyi00oeegyMhI+vTpw3333Yefnx+//fYb7777rtbyVK40aNAg3nzzTbvDUBmgCT0HnT17luXLl/Of//yHzZs306zZjZ7PUirzqlatyg8//JClfUyfPp3XX389S/vo168fvr6+REREXDf/tddeu2beoUOHEBESEhKS5i1cuJDg4GCKFStG+fLl6dChA7/++muGYtixYwft2rXD39/fpcrT1q1badKkCUWKFKFJkyZs3bo1aZkxhmHDhlG6dGlKly7NsGHDrvmF7eq2VapUuW5bd9CEns2OHTvGuHHjMMZQq1YtwsPDGTlyJAUKFLA7NJWHJU+a2eX8+fP873//w8/PjwULFmR4+/fee49nn32WV155hRMnTnD48GEGDx7M0qVLM7Sf/Pnz0717d2bPnp3uupcvX6Zz58706dOHs2fP8sgjj9C5c2cuX7ZGR5s5cyZLlixh27ZtbN++neXLlzNjxowMb7tu3bprtnWbKwMi5PSrSZMmJjPmj3/OmP+UMCYuNlPb55TExEQzc+ZMU6JECVO4cGGzb9++LO3vp59+ck9gHiS3l3nXrl1u32dMTEyW99GnTx8jIqZQoUKmaNGiZuzYsebgwYMGMLNmzTKBgYGmZcuWxhhjHnzwQRMQEGBKlChhWrZsaXbs2JG0n0ceecS8+uqrxhjrXFSsWNGMHz/elClTxpQrV87MmTPnhnHMnTvXVKpUybz//vumfv361yxLvu8rZb4SY3x8vImKijJFixY1ixYtyvLnccW+ffuMlfLStnLlSlOhQgWTmJiYNC8wMNB8++23xhhjmjdvbmbMmJG0bNasWaZZs2YZ3jYmJuaabVOT1vcL2GjSyKt6l0s2+Pvvv+nfvz8//fQTISEhfPjhh9SsWdPusFQ2e2P5TnYdj8nSPhwOBz4+PmkuD6pQgv/cX/+G+5g/fz6//PILs2bNom1b647iQ4cOAfDzzz+ze/fupHvnO3TowJw5cyhQoADDhg2jd+/e1zQTJPfPP/8QHR3NsWPHWLVqFQ8++CBdunRJs8fPuXPn0rNnT3r06MHzzz/Ppk2baNKkSTqfgGXdunVcunTphuPhLly4kMGDB6e5fPv27VSuXNml412xc+dOGjZseE3TTMOGDdm5cyft27dn586d3HLLLUnLbrnlFnbu3Jnlbd1Fm1zcLCEhgTZt2rBx40ZmzJjB6tWrNZmrXGPkyJEULVqUwoULA/DYY49RvHhxChYsyMiRI9m2bRvR0dGpbps/f35GjBhB/vz56dixI8WKFWPPnj2prnv48GF++uknevXqRUBAAG3atGHevHkux3n69Gn8/f3x9U27ztmrVy+ioqLSfGU0mYPVaZafn9818/z8/Dh37lyqy/38/IiNjcUYk6Vt3UVr6G6yZ88eatSoga+vL3PnzqVGjRpUqqS9COcl6dWcXZHdj8EHBl7tlsnhcPDqq6/yxRdfcOrUqaRae2Rk5HWJCaB06dLXJNgiRYoQGxub6nHmz59PvXr1aNSoEQC9e/fm+eefZ/z48eTPnx9fX9/rHpyJj48nX7585MuXj9KlSxMZGUlCQsINk7q7FStWjJiYa39lxcTEJJ2TlMtjYmIoVqwYIpKlbd1Fa+hZdPnyZd544w1uvvlmpkyZAkDr1q01mStbpZUkks9fuHAhS5cu5YcffiA6OjqpWcYdNcZ58+Zx4MABypUrR7ly5Rg6dCiRkZGsWGE9cF65cuWk411x8OBBAgMDyZcvH82bN6dgwYIsWbIkzWN88sknFCtWLM3X4cOHMxx3/fr12b59+zWfwfbt26lfv37S8m3btiUt27Zt2zXLMrutu2hCz4INGzbQpEkTRo4cyUMPPUTv3r3tDkkpwBpv4MCBAzdc59y5cxQsWJDSpUtz4cIFXnnlFbcce926dfz9999s2LCBrVu3snXrVnbs2EGvXr2Sml26devGN998w/fff4/D4eD48eOMHj2aHj16AFZzxKhRo3jyySdZsmQJFy5cID4+nm+//ZaXXnoJsGr9sbGxab6uNLkYY7h06VLS3SaXLl0iLi71DmFDQkLw8fFh0qRJxMXF8d///heAu+++G4CHH36Y9957j2PHjnH8+HEmTJhAv379MrxtRETENdu6TVpXS7P75el3uUycONHky5fPVKxY0Sxfvjzbj5fb7/jIDrm9zLn1LhdjjFmyZIkJDAw0fn5+5t13373mDpIrzp07Zx544AFTrFgxU7lyZTN37lwDJN2RldpdLslVqVLFrFq16rpjDxw40PzrX/+6bv769etNgQIFzOnTp40xxixbtsw0btzYlChRwlSuXNm88MIL5sKFC9dss2DBAtOkSRNTpEgRExAQYDp27GjWrl2boc/iStmTv6pUqZK0vH379uatt95Kmt68ebNp3LixKVSokLn11lvN5s2bk5YlJiaaF1980ZQqVcqUKlXKvPjii9fc1eLqtiVLlrxu25Qyc5eLJvQMunIC1q5dawYOHGiioqJy5Li5Pbllh9xe5tyc0D2Jljl1ettiNoqOjuall16icOHCvP/++9xxxx3ccccddoellFJJtA3dBcuXLycoKIhZs2ZRsGBB7UxLKZUraUK/gVOnTtGrVy8eeOABSpcuze+//87YsWO1My2lVK6kCf0GoqOjWbFiBW+88QYbN26kadOmdoeklFJp0jb0FI4cOcKCBQsYPnw4NWvWJDw8PNWHLJRSKrfRGrpTYmIi06dPp379+owePZq///4bQJO5UspjaEIH9u3bx913380TTzzBbbfdxp9//qn9ryilPE6eb3JJSEjgnnvuISoqitmzZ/Poo4/qRU+llEfKswl99+7d1KpVC19fX+bPn0+NGjWoUKGC3WEppVSm5bkml7i4OP7zn//QsGHDpL4WWrZsqclceRV3DEEH8PHHH3PnnXdmatuQkBBKlSp1Xb8pISEhzJo165p5YWFh13RoZ4xh0qRJNGjQgKJFi1KpUiUeeugh/vzzzwzFYNIZMi6lyZMnU61aNUqUKEFwcPA1w93FxcUxaNAgAgICuOmmm7j//vs5duxY0vIzZ87QtWtXihYtSpUqVVi4cGGqx3jssccoUaIE+/fvz1BZXJGnEvrvv/9O48aNGTVqFD179qRv3752h6SUVzp06BC//PILIsKyZcsyvP2QIUP44IMPmDRpEmfOnGHv3r106dKFb775JkP7udGQcSmtX7+e4cOHs3jxYqKjowkNDaVr1644HA4APvjgA9atW8f27ds5fvw4pUqV4umnn07a/sknn6RAgQKcOHGCTz75hCeeeOK6ASx+/fXXpBsuskVafQJk9yun+3IZP368ERETGBhoVqxYkalj2ym392uSHXJ7mXNrXy6pDUFnjDHr1q0zzZs3N35+fqZhw4bXfL4fffSRqVatmilWrJipWrWqWbBggdm1a5cpWLCgyZcvnylatKjx8/NzOYY33njD3HHHHea5554z99133zXLWrdubT788MOk6ZiYmGs6/9q7d6/Jly+fWb9+feY/BKcbDRmX0meffWaaNm2aNB0bG2sAc/z4cWOMMYMGDTIvvvhi0vKvv/7a1K5dO2nd/Pnzmz179iQt79Onjxk2bFjSdHx8vGnUqJHZtm3bNZ2gpUX7cklFYmJiUv/KgwYNYsyYMZQoUcLusJQ3+nY4/JOxJoGUCjsSwOcGf5blboYOY264j9SGoDt27Bj33Xcf8+fPp3379qxevZpu3brx119/UaRIEZ555hn++OMP6tSpQ0REBGfOnKFevXpMnz6dWbNmXdP04Ip58+YxdOhQmjVrxu23386JEycICAhwadvVq1dTqVIlbrvttjTXGTNmDGPGpP05REVFAWRo2LcOHTowbtw41q9fT3BwMHPmzKFRo0aUK1cOgNDQUIYMGcLx48cpWbIkn3zyCR06dABg7969+Pr6Urt27WuO9fPPPydNT5w4kVatWtGwYcP0P4RM8tqEHhUVxfPPP0+RIkWYPHmydqal8rQFCxbQsWNHOnbsCMA999xDcHAwK1as4MEHHyRfvnzs2LGDypUrU758ecqXL5/pY/3666+Eh4fTvXt3/P39qVGjBgsXLuS5555zafvTp0+ne/zhw4czfPjwdPd1o2HfUt7NVrx4cbp168add96JMYaSJUvy7bffJq1Xq1YtAgMDqVixIj4+Ptx8881J1+FiY2OvqygmH37uyJEjzJgxg02bNqX/AWSBVyb0JUuWMHjwYE6ePMlLL72U6slTyu3SqTm74mI2DUEXHh7OF198wfLly5PmxcfHc9ddd1G0aFE+//xzxo8fT2hoKC1atGDChAnUrVs3U8eaO3cu9957L/7+/oA19ufcuXOTEnpaw8/lz58fsIa6i4iIyNSxU8rIsG+zZ8/mo48+YufOndSsWZPvv/+eTp06sWXLFipUqMCTTz5JXFwcp0+fpmjRoowbN44OHTqwfv36dIefe/bZZxkxYkS2P6joVRdFT548Sffu3enatSsBAQFs2LCBt99+W5O5ynNSfucDAwPp27fvNYMonz9/PqmW265dO1atWkVERAR169alf//+qe4nPRcvXmTRokX8/PPPScPPTZw4kW3btiUNv5bW8HNVqlQBoE2bNhw9epSNGzemeZy33377hsPPXZGRYd+2bt1Kp06dqF27Nvny5aN9+/aUL1+e3377LWl5v379uOmmmyhYsCBPP/00GzZsIDIyktq1a5OQkMC+fftSPdbq1at58cUXkz4TgObNm6d5J0xmeVVCj4mJYdWqVbz11lts2LCBxo0b2x2SUrZIOQRdnz59WL58OStXrsThcHDp0iXCwsI4evQoJ06cYOnSpZw/f56CBQtSrFixpAGjAwICOHr0aNLwbelZsmQJPj4+7Nq1K2n4ud27d9OyZcuk4ef+/e9/89FHH7FhwwaMMezbt4+JEycmDT9Xq1YtBg8eTM+ePQkLC+Py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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "from sklearn.metrics import roc_auc_score, roc_curve\n",
    "\n",
    "for name, X, y, model in [\n",
    "    ('train', X_train_tf_idf, y_train, tf_idf_log_reg_model),\n",
    "    ('test ', X_test_tf_idf, y_test, tf_idf_log_reg_model)\n",
    "]:\n",
    "    proba = model.predict_proba(X)[:, 1]\n",
    "    auc = roc_auc_score(y, proba)\n",
    "    plt.plot(*roc_curve(y, proba)[:2], label='%s AUC=%.4f' % (name, auc))\n",
    "\n",
    "plt.plot([0, 1], [0, 1], '--', color='black',)\n",
    "plt.title(\"Logistic regression + Tf-Idf features\")\n",
    "plt.legend(fontsize='large')\n",
    "plt.grid()\n",
    "\n",
    "test_accuracy = np.mean(tf_idf_log_reg_model.predict(X_test_tf_idf) == y_test)\n",
    "print(f\"Model accuracy: {test_accuracy:.3f}\")\n",
    "assert test_accuracy > 0.77, \"Hint: tune the parameter C to improve performance\"\n",
    "print(\"Well done!\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "tags": []
   },
   "source": [
    "### Solving it better: word vectors\n",
    "\n",
    "Let's try another approach: instead of counting per-word frequencies, we shall map all words to pre-trained word vectors and average over them to get text features.\n",
    "\n",
    "This should give us two key advantages: (1) we now have 10^2 features instead of 10^4 and (2) our model can generalize to words that are not in training dataset.\n",
    "\n",
    "We begin with a standard approach with pre-trained word vectors. However, you may also try\n",
    "* training embeddings from scratch on relevant (unlabeled) data\n",
    "* multiplying word vectors by inverse word frequency in dataset (like tf-idf).\n",
    "* concatenating several embeddings\n",
    "    * call `gensim.downloader.info()['models'].keys()` to get a list of available models\n",
    "* clusterizing words by their word-vectors and try bag of cluster_ids\n",
    "\n",
    "__Note:__ loading pre-trained model may take a while. It's a perfect opportunity to refill your cup of tea/coffee and grab some extra cookies. Or binge-watch some tv series if you're slow on internet connection"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# !pip install gensim"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import gensim.downloader \n",
    "embeddings = gensim.downloader.load(\"fasttext-wiki-news-subwords-300\")\n",
    "\n",
    "# If you're low on RAM or download speed, use \"glove-wiki-gigaword-100\" instead. Ignore all further asserts."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 124,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def vectorize_sum(comment):\n",
    "    \"\"\"\n",
    "    implement a function that converts preprocessed comment to a sum of token vectors\n",
    "    \"\"\"\n",
    "    embedding_dim = embeddings.vectors.shape[1]\n",
    "    features = np.zeros([embedding_dim], dtype=\"float32\")\n",
    "\n",
    "    features = np.sum(\n",
    "        [\n",
    "            embeddings.get_vector(word) if word in embeddings.key_to_index else features\n",
    "            for word in comment.split()\n",
    "        ],\n",
    "        axis=0,\n",
    "    ) / len(comment.split())\n",
    "\n",
    "    return features\n",
    "\n",
    "\n",
    "assert np.allclose(\n",
    "    vectorize_sum(\"who cares anymore . they attack with impunity .\")[::70],\n",
    "    np.array([ 0.00120684,  0.00290737,  0.01539459, -0.0205673 , -0.05153336])\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 125,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "X_train_wv = np.stack([vectorize_sum(text) for text in texts_train])\n",
    "X_test_wv = np.stack([vectorize_sum(text) for text in texts_test])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 126,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.linear_model import LogisticRegression\n",
    "from sklearn.model_selection import GridSearchCV, StratifiedKFold\n",
    "\n",
    "parameters = {\"C\": np.logspace(-2, 2, 1000)}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 127,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "GridSearchCV(cv=StratifiedKFold(n_splits=2, random_state=None, shuffle=False),\n",
       "             estimator=LogisticRegression(max_iter=10000, random_state=42),\n",
       "             n_jobs=-1,\n",
       "             param_grid={'C': array([1.00000000e-02, 1.00926219e-02, 1.01861017e-02, 1.02804473e-02,\n",
       "       1.03756668e-02, 1.04717682e-02, 1.05687597e-02, 1.06666496e-02,\n",
       "       1.07654461e-02, 1.08651577e-02, 1.09657929e-02, 1.10673602e-02,...\n",
       "       8.08924349e+01, 8.16416760e+01, 8.23978568e+01, 8.31610415e+01,\n",
       "       8.39312950e+01, 8.47086827e+01, 8.54932707e+01, 8.62851257e+01,\n",
       "       8.70843150e+01, 8.78909065e+01, 8.87049689e+01, 8.95265713e+01,\n",
       "       9.03557835e+01, 9.11926760e+01, 9.20373200e+01, 9.28897872e+01,\n",
       "       9.37501502e+01, 9.46184819e+01, 9.54948564e+01, 9.63793480e+01,\n",
       "       9.72720319e+01, 9.81729841e+01, 9.90822810e+01, 1.00000000e+02])})"
      ]
     },
     "execution_count": 127,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "clf = GridSearchCV(LogisticRegression(solver='lbfgs', random_state=42, max_iter=10000), parameters, n_jobs=-1, cv=StratifiedKFold(2))\n",
    "clf.fit(X_train_wv, y_train)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 128,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'C': 40.889482262948604}"
      ]
     },
     "execution_count": 128,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "clf.best_params_"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 129,
   "metadata": {},
   "outputs": [],
   "source": [
    "wv_model = clf.best_estimator_"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 130,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "for name, X, y, model in [\n",
    "    ('bow train', X_train_bow, y_train, bow_log_reg_model),\n",
    "    ('bow test ', X_test_bow, y_test, bow_log_reg_model),\n",
    "    ('vec train', X_train_wv, y_train, wv_model),\n",
    "    ('vec test ', X_test_wv, y_test, wv_model)\n",
    "]:\n",
    "    proba = model.predict_proba(X)[:, 1]\n",
    "    auc = roc_auc_score(y, proba)\n",
    "    plt.plot(*roc_curve(y, proba)[:2], label='%s AUC=%.4f' % (name, auc))\n",
    "\n",
    "plt.plot([0, 1], [0, 1], '--', color='black',)\n",
    "plt.legend(fontsize='large')\n",
    "plt.grid()\n",
    "\n",
    "assert roc_auc_score(y_test, wv_model.predict_proba(X_test_wv)[:, 1]) > 0.92, \"something's wrong with your features\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If everything went right, you've just managed to reduce misclassification rate by a factor of two.\n",
    "This trick is very useful when you're dealing with small datasets. However, if you have hundreds of thousands of samples, there's a whole different range of methods for that. We'll get there in the second part."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "yandex_nlp",
   "language": "python",
   "name": "yandex_nlp"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}