diff --git a/Ch4/01_OnePipeline_ManyClassifiers.ipynb b/Ch4/01_OnePipeline_ManyClassifiers.ipynb index 87ea672..b5b5122 100644 --- a/Ch4/01_OnePipeline_ManyClassifiers.ipynb +++ b/Ch4/01_OnePipeline_ManyClassifiers.ipynb @@ -1,736 +1,744 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "id": "janWv1vG5xUD" - }, - "source": [ - "# Text Classification with Naive Bayes, Logistic Regression, SVM" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "gBCjEALX5xWj" - }, - "source": [ - "**Overview:** This notebook aims to give you a brief overview of performing text classification using Naive Bayes, Logistic Regression and Support Vector Machines. We will be using a dataset called \"Economic news article tone and relevance\" from [Figure-Eight](https://github.com/practical-nlp/practical-nlp/blob/master/Ch4/Data/Full-Economic-News-DFE-839861.csv) which consists of approximately 8000 news articles, which were tagged as relevant or not relevant to the US Economy. Our goal in this notebook is to explore the process of training and testing text classifiers for this problem, using this data set and two text classification algorithms: Multinomial Naive Bayes and Logistic Regression, implemented in sklearn. \n", - "\n", - "##### Dataset Link: In the a folder called Data in folder Ch4 of this repo\n", - "

\n", - "Let's import few necessary packages before we start our work" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "janWv1vG5xUD" + }, + "source": [ + "# Text Classification with Naive Bayes, Logistic Regression, SVM" + ] }, - "id": "Mee0VQbBXDto", - "outputId": "459d0120-aa17-4536-bc9e-e2395bfa6886" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Requirement already satisfied: numpy==1.19.5 in /usr/local/lib/python3.7/dist-packages (1.19.5)\n", - "Requirement already satisfied: pandas==1.1.5 in /usr/local/lib/python3.7/dist-packages (1.1.5)\n", - "Requirement already satisfied: python-dateutil>=2.7.3 in /usr/local/lib/python3.7/dist-packages (from pandas==1.1.5) (2.8.1)\n", - "Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.7/dist-packages (from pandas==1.1.5) (2018.9)\n", - "Requirement already satisfied: numpy>=1.15.4 in /usr/local/lib/python3.7/dist-packages (from pandas==1.1.5) (1.19.5)\n", - "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.7/dist-packages (from python-dateutil>=2.7.3->pandas==1.1.5) (1.15.0)\n", - "Collecting scikit-learn==0.21.3\n", - "\u001b[?25l Downloading https://files.pythonhosted.org/packages/9f/c5/e5267eb84994e9a92a2c6a6ee768514f255d036f3c8378acfa694e9f2c99/scikit_learn-0.21.3-cp37-cp37m-manylinux1_x86_64.whl (6.7MB)\n", - "\u001b[K |████████████████████████████████| 6.7MB 3.0MB/s \n", - "\u001b[?25hRequirement already satisfied: numpy>=1.11.0 in /usr/local/lib/python3.7/dist-packages (from scikit-learn==0.21.3) (1.19.5)\n", - "Requirement already satisfied: scipy>=0.17.0 in /usr/local/lib/python3.7/dist-packages (from scikit-learn==0.21.3) (1.4.1)\n", - "Requirement already satisfied: joblib>=0.11 in /usr/local/lib/python3.7/dist-packages (from scikit-learn==0.21.3) (1.0.1)\n", - "Installing collected packages: scikit-learn\n", - " Found existing installation: scikit-learn 0.22.2.post1\n", - " Uninstalling scikit-learn-0.22.2.post1:\n", - " Successfully uninstalled scikit-learn-0.22.2.post1\n", - "Successfully installed scikit-learn-0.21.3\n", - "Requirement already satisfied: matplotlib==3.2.2 in /usr/local/lib/python3.7/dist-packages (3.2.2)\n", - "Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib==3.2.2) (1.3.1)\n", - "Requirement already satisfied: python-dateutil>=2.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib==3.2.2) (2.8.1)\n", - "Requirement already satisfied: numpy>=1.11 in /usr/local/lib/python3.7/dist-packages (from matplotlib==3.2.2) (1.19.5)\n", - "Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib==3.2.2) (2.4.7)\n", - "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.7/dist-packages (from matplotlib==3.2.2) (0.10.0)\n", - "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.7/dist-packages (from python-dateutil>=2.1->matplotlib==3.2.2) (1.15.0)\n" - ] - } - ], - "source": [ - "# To install only the requirements of this notebook, uncomment the lines below and run this cell\n", - "\n", - "# ===========================\n", - "\n", - "!pip install numpy==1.19.5\n", - "!pip install pandas==1.1.5\n", - "!pip install scikit-learn==0.21.3\n", - "!pip install matplotlib==3.2.2\n", - "\n", - "# ===========================" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "id": "n7dE_FbM1lk5" - }, - "outputs": [], - "source": [ - "# To install the requirements for the entire chapter, uncomment the lines below and run this cell\n", - "\n", - "# ===========================\n", - "\n", - "# try:\n", - "# import google.colab\n", - "# !curl https://raw.githubusercontent.com/practical-nlp/practical-nlp/master/Ch4/ch4-requirements.txt | xargs -n 1 -L 1 pip install\n", - "# except ModuleNotFoundError:\n", - "# !pip install -r \"ch4-requirements.txt\"\n", - "\n", - "# ===========================" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "id": "QBvvarqE5xWm" - }, - "outputs": [], - "source": [ - "import warnings\n", - "warnings.filterwarnings('ignore')\n", - "import numpy as np\n", - "import pandas as pd # to work with csv files\n", - "\n", - "# matplotlib imports are used to plot confusion matrices for the classifiers\n", - "import matplotlib as mpl \n", - "import matplotlib.cm as cm \n", - "import matplotlib.pyplot as plt \n", - "\n", - "# import feature extraction methods from sklearn\n", - "from sklearn.feature_extraction.text import CountVectorizer\n", - "from sklearn.feature_extraction import stop_words\n", - "\n", - "# pre-processing of text\n", - "import string\n", - "import re\n", - "\n", - "# import classifiers from sklearn\n", - "from sklearn.naive_bayes import MultinomialNB\n", - "from sklearn.linear_model import LogisticRegression\n", - "from sklearn.svm import LinearSVC\n", - "\n", - "# import different metrics to evaluate the classifiers\n", - "from sklearn.metrics import accuracy_score\n", - "\n", - "# from sklearn.model_selection import train_test_split\n", - "from sklearn.metrics import confusion_matrix \n", - "from sklearn import metrics\n", - "\n", - "# import time function from time module to track the training duration\n", - "from time import time" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "1giNRemr1lk7" - }, - "source": [ - "### Section 1: Load and explore the dataset" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" + "cell_type": "markdown", + "metadata": { + "id": "gBCjEALX5xWj" + }, + "source": [ + "**Overview:** This notebook aims to give you a brief overview of performing text classification using Naive Bayes, Logistic Regression and Support Vector Machines. We will be using a dataset called \"Economic news article tone and relevance\" from [Figure-Eight](https://github.com/practical-nlp/practical-nlp/blob/master/Ch4/Data/Full-Economic-News-DFE-839861.csv) which consists of approximately 8000 news articles, which were tagged as relevant or not relevant to the US Economy. Our goal in this notebook is to explore the process of training and testing text classifiers for this problem, using this data set and two text classification algorithms: Multinomial Naive Bayes and Logistic Regression, implemented in sklearn.\n", + "\n", + "##### Dataset Link: In the a folder called Data in folder Ch4 of this repo\n", + "

\n", + "Let's import few necessary packages before we start our work" + ] }, - "id": "fVD8N_E51lk7", - "outputId": "b5893f5e-1123-43f7-d3a5-2e4fb92bfdc9" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "--2021-07-16 08:09:13-- https://raw.githubusercontent.com/practical-nlp/practical-nlp/master/Ch4/Data/Full-Economic-News-DFE-839861.csv\n", - "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.111.133, 185.199.108.133, 185.199.110.133, ...\n", - "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.111.133|:443... connected.\n", - "HTTP request sent, awaiting response... 200 OK\n", - "Length: 12383529 (12M) [text/plain]\n", - "Saving to: ‘DATAPATH/Full-Economic-News-DFE-839861.csv’\n", - "\n", - "Full-Economic-News- 100%[===================>] 11.81M 22.9MB/s in 0.5s \n", - "\n", - "2021-07-16 08:09:14 (22.9 MB/s) - ‘DATAPATH/Full-Economic-News-DFE-839861.csv’ saved [12383529/12383529]\n", - "\n", - "total 12M\n", - "drwxr-xr-x 2 root root 4.0K Jul 16 08:09 .\n", - "drwxr-xr-x 1 root root 4.0K Jul 16 08:09 ..\n", - "-rw-r--r-- 1 root root 12M Jul 16 08:09 Full-Economic-News-DFE-839861.csv\n" - ] - } - ], - "source": [ - "try:\n", - " from google.colab import files\n", - " !wget -P DATAPATH https://raw.githubusercontent.com/practical-nlp/practical-nlp/master/Ch4/Data/Full-Economic-News-DFE-839861.csv\n", - " !ls -lah DATAPATH\n", - " our_data = pd.read_csv(\"DATAPATH/Full-Economic-News-DFE-839861.csv\" , encoding = \"ISO-8859-1\" )\n", - "\n", - "except ModuleNotFoundError:\n", - " our_data = pd.read_csv(\"Data/Full-Economic-News-DFE-839861.csv\" , encoding = \"ISO-8859-1\" )" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 102 + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Mee0VQbBXDto", + "outputId": "7ee35588-1066-4c90-dd1a-f5d30bb13f02" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Requirement already satisfied: numpy==1.23.5 in /usr/local/lib/python3.10/dist-packages (1.23.5)\n", + "Requirement already satisfied: pandas==1.5.3 in /usr/local/lib/python3.10/dist-packages (1.5.3)\n", + "Requirement already satisfied: python-dateutil>=2.8.1 in /usr/local/lib/python3.10/dist-packages (from pandas==1.5.3) (2.8.2)\n", + "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.10/dist-packages (from pandas==1.5.3) (2023.3)\n", + "Requirement already satisfied: numpy>=1.21.0 in /usr/local/lib/python3.10/dist-packages (from pandas==1.5.3) (1.23.5)\n", + "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.8.1->pandas==1.5.3) (1.16.0)\n", + "Requirement already satisfied: matplotlib==3.7.1 in /usr/local/lib/python3.10/dist-packages (3.7.1)\n", + "Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib==3.7.1) (1.1.0)\n", + "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.10/dist-packages (from matplotlib==3.7.1) (0.11.0)\n", + "Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib==3.7.1) (4.42.0)\n", + "Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib==3.7.1) (1.4.4)\n", + "Requirement already satisfied: numpy>=1.20 in /usr/local/lib/python3.10/dist-packages (from matplotlib==3.7.1) (1.23.5)\n", + "Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib==3.7.1) (23.1)\n", + "Requirement already satisfied: pillow>=6.2.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib==3.7.1) (9.4.0)\n", + "Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib==3.7.1) (3.1.1)\n", + "Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.10/dist-packages (from matplotlib==3.7.1) (2.8.2)\n", + "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.7->matplotlib==3.7.1) (1.16.0)\n", + "Requirement already satisfied: scikit-learn==1.2.2 in /usr/local/lib/python3.10/dist-packages (1.2.2)\n", + "Requirement already satisfied: numpy>=1.17.3 in /usr/local/lib/python3.10/dist-packages (from scikit-learn==1.2.2) (1.23.5)\n", + "Requirement already satisfied: scipy>=1.3.2 in /usr/local/lib/python3.10/dist-packages (from scikit-learn==1.2.2) (1.10.1)\n", + "Requirement already satisfied: joblib>=1.1.1 in /usr/local/lib/python3.10/dist-packages (from scikit-learn==1.2.2) (1.3.2)\n", + "Requirement already satisfied: threadpoolctl>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from scikit-learn==1.2.2) (3.2.0)\n" + ] + } + ], + "source": [ + "# To install only the requirements of this notebook, uncomment the lines below and run this cell\n", + "\n", + "# ===========================\n", + "\n", + "!pip install numpy==1.23.5\n", + "!pip install pandas==1.5.3\n", + "!pip install matplotlib==3.7.1\n", + "!pip install scikit-learn==1.2.2\n", + "\n", + "# ===========================" + ] }, - "id": "LbED8Q185xWu", - "outputId": "2ded8ddf-5553-4f4a-b55f-16454270648d" - }, - "outputs": [ { - "data": { - "text/plain": [ - "(8000, 15)" + "cell_type": "code", + "execution_count": 4, + "metadata": { + "id": "n7dE_FbM1lk5" + }, + "outputs": [], + "source": [ + "# To install the requirements for the entire chapter, uncomment the lines below and run this cell\n", + "\n", + "# ===========================\n", + "\n", + "# try:\n", + "# import google.colab\n", + "# !curl https://raw.githubusercontent.com/practical-nlp/practical-nlp/master/Ch4/ch4-requirements.txt | xargs -n 1 -L 1 pip install\n", + "# except ModuleNotFoundError:\n", + "# !pip install -r \"ch4-requirements.txt\"\n", + "\n", + "# ===========================" ] - }, - "metadata": { - "tags": [] - }, - "output_type": "display_data" }, { - "data": { - "text/plain": [ - "no 0.821375\n", - "yes 0.177500\n", - "not sure 0.001125\n", - "Name: relevance, dtype: float64" + "cell_type": "code", + "execution_count": 5, + "metadata": { + "id": "QBvvarqE5xWm" + }, + "outputs": [], + "source": [ + "import warnings\n", + "warnings.filterwarnings('ignore')\n", + "import numpy as np\n", + "import pandas as pd # to work with csv files\n", + "\n", + "# matplotlib imports are used to plot confusion matrices for the classifiers\n", + "import matplotlib as mpl\n", + "import matplotlib.cm as cm\n", + "import matplotlib.pyplot as plt\n", + "\n", + "# import feature extraction methods from sklearn\n", + "from sklearn.feature_extraction.text import CountVectorizer\n", + "from sklearn.feature_extraction import _stop_words # This Module has become private after sklearn 0.24 thus stop_words changed to _stop_words\n", + "\n", + "# pre-processing of text\n", + "import string\n", + "import re\n", + "\n", + "# import classifiers from sklearn\n", + "from sklearn.naive_bayes import MultinomialNB\n", + "from sklearn.linear_model import LogisticRegression\n", + "from sklearn.svm import LinearSVC\n", + "\n", + "# import different metrics to evaluate the classifiers\n", + "from sklearn.metrics import accuracy_score\n", + "\n", + "# from sklearn.model_selection import train_test_split\n", + "from sklearn.metrics import confusion_matrix\n", + "from sklearn import metrics\n", + "\n", + "# import time function from time module to track the training duration\n", + "from time import time" ] - }, - "execution_count": 5, - "metadata": { - "tags": [] - }, - "output_type": "execute_result" - } - ], - "source": [ - "display(our_data.shape) # Number of rows (instances) and columns in the dataset\n", - "our_data[\"relevance\"].value_counts()/our_data.shape[0] # Class distribution in the dataset" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "vCED1t7F5xW9" - }, - "source": [ - "There is an imbalance in the data with **not relevant** being 82% in the dataset. That is, most of the articles are not relevant to US Economy, which makes sense in a real-world scenario, as news articles discuss various topics. We should keep this class imbalance mind when interpreting the classifier performance later. Let us first convert the class labels into binary outcome variables for convenience. 1 for Yes (relevant), and 0 for No (not relevant), and ignore \"Not sure\". " - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" }, - "id": "BYW_S3585xXF", - "outputId": "b64bb281-6512-43b5-eda9-73d43becb1ae" - }, - "outputs": [ { - "data": { - "text/plain": [ - "(7991, 2)" + "cell_type": "markdown", + "metadata": { + "id": "1giNRemr1lk7" + }, + "source": [ + "### Section 1: Load and explore the dataset" ] - }, - "execution_count": 6, - "metadata": { - "tags": [] - }, - "output_type": "execute_result" - } - ], - "source": [ - "# convert label to a numerical variable\n", - "our_data = our_data[our_data.relevance != \"not sure\"] # removing the data where we don't want relevance=\"not sure\".\n", - "our_data.shape\n", - "our_data['relevance'] = our_data.relevance.map({'yes':1, 'no':0}) # relevant is 1, not-relevant is 0. \n", - "our_data = our_data[[\"text\",\"relevance\"]] # Let us take only the two columns we need.\n", - "our_data.shape" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "fOKz8xQr5xXJ" - }, - "source": [ - "### Section 2: Text Pre-processing" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "yhC5TZuL5xXK" - }, - "source": [ - "Typical steps involve tokenization, lower casing, removing, stop words, punctuation markers etc, and vectorization. Other processes such as stemming/lemmatization can also be performed. Here, we are performing the following steps: removing br tags, punctuation, numbers, and stopwords. While we are using sklearn's list of stopwords, there are several other stop word lists (e.g., from NLTK) or sometimes, custom stopword lists are needed depending on the task. " - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "id": "7MZSHdHZ5xXL" - }, - "outputs": [], - "source": [ - "stopwords = stop_words.ENGLISH_STOP_WORDS\n", - "def clean(doc): # doc is a string of text\n", - " doc = doc.replace(\"
\", \" \") # This text contains a lot of
tags.\n", - " doc = \"\".join([char for char in doc if char not in string.punctuation and not char.isdigit()])\n", - " doc = \" \".join([token for token in doc.split() if token not in stopwords])\n", - " # remove punctuation and numbers\n", - " return doc" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "3CfVm42o5xXS" - }, - "source": [ - "### Section 3: Modeling\n", - "\n", - "Now we are ready for the modelling. We are going to use algorithms from sklearn package. We will go through the following steps:\n", - "\n", - "1 Split the data into training and test sets (75% train, 25% test) \n", - "2 Extract features from the training data using CountVectorizer, which is a bag of words feature implementation. We will use the pre-processing function above in conjunction with Count Vectorizer \n", - "3 Transform the test data into the same feature vector as the training data. \n", - "4 Train the classifier \n", - "5 Evaluate the classifier " - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" }, - "id": "GimJJHhg5xYl", - "outputId": "7ed9cad8-3bd8-416d-a352-4a44fad9dc80" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "(7991,) (7991,)\n", - "(5993,) (5993,)\n", - "(1998,) (1998,)\n" - ] - } - ], - "source": [ - "import sklearn\n", - "#from sklearn.cross_validation import train_test_split\n", - "from sklearn.model_selection import train_test_split\n", - "\n", - "# Step 1: train-test split\n", - "X = our_data.text # the column text contains textual data to extract features from\n", - "y = our_data.relevance # this is the column we are learning to predict. \n", - "print(X.shape, y.shape)\n", - "# split X and y into training and testing sets. By default, it splits 75% training and 25% test\n", - "# random_state=1 for reproducibility\n", - "X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1)\n", - "print(X_train.shape, y_train.shape)\n", - "print(X_test.shape, y_test.shape)" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" + "cell_type": "code", + "execution_count": 6, + "metadata": { + "id": "fVD8N_E51lk7", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "a36f603b-afc2-475e-f6c9-46b3299db3f4" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "--2023-08-22 16:03:42-- https://raw.githubusercontent.com/practical-nlp/practical-nlp/master/Ch4/Data/Full-Economic-News-DFE-839861.csv\n", + "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.110.133, 185.199.111.133, 185.199.109.133, ...\n", + "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.110.133|:443... connected.\n", + "HTTP request sent, awaiting response... 200 OK\n", + "Length: 12383529 (12M) [text/plain]\n", + "Saving to: ‘DATAPATH/Full-Economic-News-DFE-839861.csv’\n", + "\n", + "Full-Economic-News- 100%[===================>] 11.81M 71.4MB/s in 0.2s \n", + "\n", + "2023-08-22 16:03:43 (71.4 MB/s) - ‘DATAPATH/Full-Economic-News-DFE-839861.csv’ saved [12383529/12383529]\n", + "\n", + "total 12M\n", + "drwxr-xr-x 2 root root 4.0K Aug 22 16:03 .\n", + "drwxr-xr-x 1 root root 4.0K Aug 22 16:03 ..\n", + "-rw-r--r-- 1 root root 12M Aug 22 16:03 Full-Economic-News-DFE-839861.csv\n" + ] + } + ], + "source": [ + "try:\n", + " from google.colab import files\n", + " !wget -P DATAPATH https://raw.githubusercontent.com/practical-nlp/practical-nlp/master/Ch4/Data/Full-Economic-News-DFE-839861.csv\n", + " !ls -lah DATAPATH\n", + " our_data = pd.read_csv(\"DATAPATH/Full-Economic-News-DFE-839861.csv\" , encoding = \"ISO-8859-1\" )\n", + "\n", + "except ModuleNotFoundError:\n", + " our_data = pd.read_csv(\"Data/Full-Economic-News-DFE-839861.csv\" , encoding = \"ISO-8859-1\" )" + ] }, - "id": "gsUyIBUD5xZI", - "outputId": "f4082e6a-a1e9-4b4a-c247-8b1b84c7edae" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "(5993, 49753) (1998, 49753)\n" - ] - } - ], - "source": [ - "# Step 2-3: Preprocess and Vectorize train and test data\n", - "vect = CountVectorizer(preprocessor=clean) # instantiate a vectoriezer\n", - "X_train_dtm = vect.fit_transform(X_train)# use it to extract features from training data\n", - "# transform testing data (using training data's features)\n", - "X_test_dtm = vect.transform(X_test)\n", - "print(X_train_dtm.shape, X_test_dtm.shape)\n", - "# i.e., the dimension of our feature vector is 49753!" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" + "cell_type": "code", + "execution_count": 7, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 106 + }, + "id": "LbED8Q185xWu", + "outputId": "7672d092-6fda-401a-9651-05e35794a3a0" + }, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "(8000, 15)" + ] + }, + "metadata": {} + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "no 0.821375\n", + "yes 0.177500\n", + "not sure 0.001125\n", + "Name: relevance, dtype: float64" + ] + }, + "metadata": {}, + "execution_count": 7 + } + ], + "source": [ + "display(our_data.shape) # Number of rows (instances) and columns in the dataset\n", + "our_data[\"relevance\"].value_counts()/our_data.shape[0] # Class distribution in the dataset" + ] }, - "id": "nDLwA4CL5xZq", - "outputId": "3cb119d8-3017-4ebb-89b9-86dca66e3e92" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "CPU times: user 14 ms, sys: 994 µs, total: 14.9 ms\n", - "Wall time: 15.2 ms\n" - ] - } - ], - "source": [ - "# Step 3: Train the classifier and predict for test data\n", - "nb = MultinomialNB() # instantiate a Multinomial Naive Bayes model\n", - "%time nb.fit(X_train_dtm, y_train) # train the model(timing it with an IPython \"magic command\")\n", - "y_pred_class = nb.predict(X_test_dtm) # make class predictions for X_test_dtm" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 494 + "cell_type": "markdown", + "metadata": { + "id": "vCED1t7F5xW9" + }, + "source": [ + "There is an imbalance in the data with **not relevant** being 82% in the dataset. That is, most of the articles are not relevant to US Economy, which makes sense in a real-world scenario, as news articles discuss various topics. We should keep this class imbalance mind when interpreting the classifier performance later. Let us first convert the class labels into binary outcome variables for convenience. 1 for Yes (relevant), and 0 for No (not relevant), and ignore \"Not sure\"." + ] }, - "id": "LiCHjvc75xZ3", - "outputId": "1409e48f-0ed6-4705-8688-4e6126662863" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Accuracy: 0.7822822822822822\n", - "ROC_AOC_Score: 0.7251117679464362\n" - ] + "cell_type": "code", + "execution_count": 8, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "BYW_S3585xXF", + "outputId": "a3e800a7-e175-4308-dbfe-33ef45e4ba85" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "(7991, 2)" + ] + }, + "metadata": {}, + "execution_count": 8 + } + ], + "source": [ + "# convert label to a numerical variable\n", + "our_data = our_data[our_data.relevance != \"not sure\"] # removing the data where we don't want relevance=\"not sure\".\n", + "our_data.shape\n", + "our_data['relevance'] = our_data.relevance.map({'yes':1, 'no':0}) # relevant is 1, not-relevant is 0.\n", + "our_data = our_data[[\"text\",\"relevance\"]] # Let us take only the two columns we need.\n", + "our_data.shape" + ] }, { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" + "cell_type": "code", + "source": [], + "metadata": { + "id": "JAumX0BC2lFK" + }, + "execution_count": 8, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "fOKz8xQr5xXJ" + }, + "source": [ + "### Section 2: Text Pre-processing" ] - }, - "metadata": { - "needs_background": "light", - "tags": [] - }, - "output_type": "display_data" - } - ], - "source": [ - "# Step 4: Evaluate the classifier using various measures\n", - "\n", - "# Function to plot confusion matrix. \n", - "# Ref:http://scikit-learn.org/stable/auto_examples/model_selection/plot_confusion_matrix.html\n", - "import itertools\n", - "from sklearn.metrics import roc_auc_score\n", - "\n", - "def plot_confusion_matrix(cm, classes,\n", - " normalize=False,\n", - " title='Confusion matrix',\n", - " cmap=plt.cm.Blues):\n", - " \"\"\"\n", - " This function prints and plots the confusion matrix.\n", - " Normalization can be applied by setting `normalize=True`.\n", - " \"\"\"\n", - " if normalize:\n", - " cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]\n", - "\n", - " plt.imshow(cm, interpolation='nearest', cmap=cmap)\n", - " plt.title(title)\n", - " plt.colorbar()\n", - " tick_marks = np.arange(len(classes))\n", - " plt.xticks(tick_marks, classes, rotation=45)\n", - " plt.yticks(tick_marks, classes)\n", - "\n", - " fmt = '.2f' if normalize else 'd'\n", - " thresh = cm.max() / 2.\n", - " for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):\n", - " plt.text(j, i, format(cm[i, j], fmt),\n", - " horizontalalignment=\"center\",\n", - " color=\"white\" if cm[i, j] > thresh else \"black\")\n", - "\n", - " plt.tight_layout()\n", - " plt.ylabel('True label',fontsize=15)\n", - " plt.xlabel('Predicted label',fontsize=15)\n", - " \n", - " \n", - "# Print accuracy:\n", - "print(\"Accuracy: \", accuracy_score(y_test, y_pred_class))\n", - "\n", - " \n", - "# print the confusion matrix\n", - "cnf_matrix = confusion_matrix(y_test, y_pred_class)\n", - "plt.figure(figsize=(8,6))\n", - "plot_confusion_matrix(cnf_matrix, classes=['Not Relevant','Relevant'],normalize=True,\n", - " title='Confusion matrix with all features')\n", - "\n", - "# calculate AUC: Area under the curve(AUC) gives idea about the model efficiency:\n", - "# Further information: https://en.wikipedia.org/wiki/Receiver_operating_characteristic\n", - "y_pred_prob = nb.predict_proba(X_test_dtm)[:, 1]\n", - "print(\"ROC_AOC_Score: \", roc_auc_score(y_test, y_pred_prob))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "ga5-KhYN5xaD" - }, - "source": [ - "At this point, we can notice that the classifier is doing poorly with identifying relevant articles, while it is doing well with non-relevant ones. Our large feature vector could be creating a lot of noise in the form of very rarely occurring features that are not useful for learning. Let us change the count vectorizer to take a certain number of features as maximum. " - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 511 }, - "id": "ylOI4OsD5xaE", - "outputId": "0aea4279-84d2-49d3-e979-30e7c911f814" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "CPU times: user 7.05 ms, sys: 7 µs, total: 7.06 ms\n", - "Wall time: 7.13 ms\n", - "Accuracy: 0.6876876876876877\n" - ] + "cell_type": "markdown", + "metadata": { + "id": "yhC5TZuL5xXK" + }, + "source": [ + "Typical steps involve tokenization, lower casing, removing, stop words, punctuation markers etc, and vectorization. Other processes such as stemming/lemmatization can also be performed. Here, we are performing the following steps: removing br tags, punctuation, numbers, and stopwords. While we are using sklearn's list of stopwords, there are several other stop word lists (e.g., from NLTK) or sometimes, custom stopword lists are needed depending on the task." + ] }, { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" + "cell_type": "code", + "execution_count": 9, + "metadata": { + "id": "7MZSHdHZ5xXL" + }, + "outputs": [], + "source": [ + "stopwords = _stop_words.ENGLISH_STOP_WORDS\n", + "def clean(doc): # doc is a string of text\n", + " doc = doc.replace(\"
\", \" \") # This text contains a lot of
tags.\n", + " doc = \"\".join([char for char in doc if char not in string.punctuation and not char.isdigit()])\n", + " doc = \" \".join([token for token in doc.split() if token not in stopwords])\n", + " # remove punctuation and numbers\n", + " return doc" ] - }, - "metadata": { - "needs_background": "light", - "tags": [] - }, - "output_type": "display_data" - } - ], - "source": [ - "vect = CountVectorizer(preprocessor=clean, max_features=5000) # Step-1\n", - "X_train_dtm = vect.fit_transform(X_train) # combined step 2 and 3\n", - "X_test_dtm = vect.transform(X_test)\n", - "nb = MultinomialNB() # instantiate a Multinomial Naive Bayes model\n", - "%time nb.fit(X_train_dtm, y_train) # train the model(timing it with an IPython \"magic command\")\n", - "y_pred_class = nb.predict(X_test_dtm) # make class predictions for X_test_dtm\n", - "print(\"Accuracy: \", metrics.accuracy_score(y_test, y_pred_class))\n", - "# print the confusion matrix\n", - "cnf_matrix = confusion_matrix(y_test, y_pred_class)\n", - "plt.figure(figsize=(8,6))\n", - "plot_confusion_matrix(cnf_matrix, classes=['Not Relevant','Relevant'],normalize=True,\n", - " title='Confusion matrix with max 5000 features')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "2JzJ6k7g5xaL" - }, - "source": [ - "Clearly, the performance on relevance classification got better even though the overall accuracy fell by 10%. Let us try another classification algorithm and see if the performance changes. For this experiment, we have considered logistic regression, with class_weight attribute as \"balanced\", to address the problem of class imbalance in this dataset. " - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 494 }, - "id": "0v7pM9hB5xbA", - "outputId": "292bdf0c-924b-494b-ffae-c4914f2f5db9" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Accuracy: 0.7377377377377378\n", - "AUC: 0.7251117679464362\n" - ] + "cell_type": "markdown", + "metadata": { + "id": "3CfVm42o5xXS" + }, + "source": [ + "### Section 3: Modeling\n", + "\n", + "Now we are ready for the modelling. We are going to use algorithms from sklearn package. We will go through the following steps:\n", + "\n", + "1 Split the data into training and test sets (75% train, 25% test) \n", + "2 Extract features from the training data using CountVectorizer, which is a bag of words feature implementation. We will use the pre-processing function above in conjunction with Count Vectorizer \n", + "3 Transform the test data into the same feature vector as the training data. \n", + "4 Train the classifier \n", + "5 Evaluate the classifier " + ] }, { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" + "cell_type": "code", + "execution_count": 10, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "GimJJHhg5xYl", + "outputId": "48f5d9f9-b0e3-4e65-b6cc-13cc21f874a1" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(7991,) (7991,)\n", + "(5993,) (5993,)\n", + "(1998,) (1998,)\n" + ] + } + ], + "source": [ + "import sklearn\n", + "#from sklearn.cross_validation import train_test_split\n", + "from sklearn.model_selection import train_test_split\n", + "\n", + "# Step 1: train-test split\n", + "X = our_data.text # the column text contains textual data to extract features from\n", + "y = our_data.relevance # this is the column we are learning to predict.\n", + "print(X.shape, y.shape)\n", + "# split X and y into training and testing sets. By default, it splits 75% training and 25% test\n", + "# random_state=1 for reproducibility\n", + "X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1)\n", + "print(X_train.shape, y_train.shape)\n", + "print(X_test.shape, y_test.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "gsUyIBUD5xZI", + "outputId": "6e17b2c2-d0ea-453a-e42e-308f33ed5bd2" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(5993, 49753) (1998, 49753)\n" + ] + } + ], + "source": [ + "# Step 2-3: Preprocess and Vectorize train and test data\n", + "vect = CountVectorizer(preprocessor=clean) # instantiate a vectoriezer\n", + "X_train_dtm = vect.fit_transform(X_train)# use it to extract features from training data\n", + "# transform testing data (using training data's features)\n", + "X_test_dtm = vect.transform(X_test)\n", + "print(X_train_dtm.shape, X_test_dtm.shape)\n", + "# i.e., the dimension of our feature vector is 49753!" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "nDLwA4CL5xZq", + "outputId": "c374e0f2-2026-497d-b2c8-12ad9289b865" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "CPU times: user 13.6 ms, sys: 0 ns, total: 13.6 ms\n", + "Wall time: 81.6 ms\n" + ] + } + ], + "source": [ + "# Step 3: Train the classifier and predict for test data\n", + "nb = MultinomialNB() # instantiate a Multinomial Naive Bayes model\n", + "%time nb.fit(X_train_dtm, y_train) # train the model(timing it with an IPython \"magic command\")\n", + "y_pred_class = nb.predict(X_test_dtm) # make class predictions for X_test_dtm" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 668 + }, + "id": "LiCHjvc75xZ3", + "outputId": "db90135e-8645-4e2a-f2d3-5d3f3c2ecaa4" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Accuracy: 0.7822822822822822\n", + "ROC_AOC_Score: 0.7251117679464362\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "
" + ], + "image/png": "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RoqKinI4p4uLi/3fY1siYh9ASgg3NzeHijUAADCPtPK64NSQ5pNdSfroo49UpkwZFS5c2GG8aNGiWr9+vcPY+vXr9dRTT9kTSldX1yRVeYOCgtSqVSv1799fv/zyi8qVK6ewsDA5OzsrODj4kccHBAQoZ86cOn78uEOl9r+ee+45vf7661qyZIl++OEHtW/f3r7v4MGDunz5sj766CMFBQVJut/GkFhJ/RwAAACkd+ki2S1ZsqTatWunsWPHOoy/++67qlixooYPH65WrVpp48aN+vLLLx1WPAgODtaaNWvUunVrubm5ydfXN8HXffvtt1WiRAlt27ZN9evXV9WqVdWiRQt9/PHHeuqpp3Tu3DktXLhQzz//vCpUqBDn+KFDh6pHjx7y9vbWM888o8jISG3btk1Xr15Vr169JEkeHh5q0aKFBg4cqAMHDjj09+bJk0eurq4aN26cunTpon379mn48OGJ/fQpODhYJ06c0K5du5Q7d25lyZKFKi4AABmIxfLPUpPJec70IM337MYaNmxYnD/LlytXTnPnztXs2bNVokQJDRo0SMOGDXNodxg2bJhOnjypAgUKyM/PL1HXLFasmBo2bKhBgwbJYrFo0aJFqlWrljp27KinnnpKrVu31qlTp+xP2P5X586d9e2332rq1KkqWbKkateurWnTpilfvnwO89q1a6fdu3erZs2aypMnj33cz89P06ZN07x581SsWDF99NFH+vTTTxN1D5L04osv6plnnlHdunXl5+enWbNmJfocAAAg/bqf7Cb3SyVS+64SJk2uxoDUF/v0JqsxAGkTqzEAaVdaXI0h/1s/yurmkazntkXe0vFxL6WJ+3yYdNHGAAAAgMeQAm0M6eWlEummjQEAAABILCq7AAAAJpeRlx6jsgsAAADTorILAABgciw9BgAAAJgQlV0AAACTs1otslqTtxRrJPP5UgrJLgAAgMnRxgAAAACYEJVdAAAAk2PpMQAAAMCEqOwCAACYHD27AAAAgAlR2QUAADA5enYBAAAAE6KyCwAAYHJUdgEAAAATorILAABgchl5NQaSXQAAAJOzKAXaGJQ+sl3aGAAAAGBaVHYBAABMLiO3MVDZBQAAgGlR2QUAADA5lh4DAAAATIjKLgAAgMnRswsAAACYEJVdAAAAk8vIPbskuwAAACZHGwMAAABgQlR2AQAATC4j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wgsvaM2aNcqUKZMKFSqkmJgYnT17ViVLlrSvuFC0aFH98MMPGjJkCIkukED07ALQlStXlC1bNvvH586dU/HixfXuu+86/Jn0999/1+3bt1WxYkXly5cvNUIFTOvtt9/W77//rq1btypbtmwKDw/X008/rbJly6ply5Zav369pk+frj179ih79uypHS6QbpDsAhncpk2b1K9fP5UoUUIjRoyQm5ub3NzcNHbsWM2cOVNTp05VsWLFUjtMwLRiX+t78OBBderUSZ06ddJrr70mSVq1apXatm2rLFmy6N69e5o/f77Kli2byhED6QttDEAG5+/vr5o1a2rDhg0qXbq0hg8frv3796tx48a6ceOGDh06JOn+UmQAks9ff/2lGzdu2JfsK1y4sHLlyqXvvvvOPqdOnTravHmzFi1apE2bNpHoAklAZRfIYGIXrL9+/bqio6Md/hw6ZMgQ7dixQytWrNCIESM0depU3bx5U9u2beOtTEAyOnHihFq3bq0zZ85ozJgxKlasmEqUKKFTp06pevXq6t+/v7p165baYQKmQLILZCCxie5vv/2mr776SkeOHFG5cuVUoUIF9enTR5J0/fp1/f7775o4caKOHz+u69ev69ixYwoICEjl6AHzuHfvnk6cOKEvv/xSa9askWEYatOmjVq2bKnPP/9c0dHRGjNmjNzc3HhRBPCYSHYBk4tNcGMtXLhQL730kj788EOVKFFCixcv1hdffKEVK1aobt269nlnz57VoUOHlDdvXhUoUCA1QgdMI/b78OjRo7p27Zqio6NVpUoVSdLWrVu1ZcsWDRw4UPXq1dOhQ4e0f/9+bdy4UZUrV07lyIH0j2QXyABiYmLk5OSkO3fuKCQkRGXLllW/fv0UHh6usmXL6vnnn9fYsWNTO0zAlGIT3QULFmjgwIGKiYmRxWJRtmzZtGDBAvtfTY4fP6758+dryZIlWrlypQ4ePKinnnoqlaMH0j8eUANMavLkyWrZsqUkycnJyf6/R48eVdGiRXX+/HmVKVNGzz77rD3RnTdvnjZu3JhqMQNmZLFYtGrVKrVv3149e/bU9u3b9eWXX2rjxo1atGiRJMlmsyl//vx67733tGLFCp07d45EF0gmJLuACd27d09Xr17VgQMHFBoaah+Pjo5W0aJFtX37dlWvXl2NGzfWpEmTJEnh4eFasmSJDh48KJvN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\n" + }, + "metadata": {} + } + ], + "source": [ + "# Step 4: Evaluate the classifier using various measures\n", + "\n", + "# Function to plot confusion matrix.\n", + "# Ref:http://scikit-learn.org/stable/auto_examples/model_selection/plot_confusion_matrix.html\n", + "import itertools\n", + "from sklearn.metrics import roc_auc_score\n", + "\n", + "def plot_confusion_matrix(cm, classes,\n", + " normalize=False,\n", + " title='Confusion matrix',\n", + " cmap=plt.cm.Blues):\n", + " \"\"\"\n", + " This function prints and plots the confusion matrix.\n", + " Normalization can be applied by setting `normalize=True`.\n", + " \"\"\"\n", + " if normalize:\n", + " cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]\n", + "\n", + " plt.imshow(cm, interpolation='nearest', cmap=cmap)\n", + " plt.title(title)\n", + " plt.colorbar()\n", + " tick_marks = np.arange(len(classes))\n", + " plt.xticks(tick_marks, classes, rotation=45)\n", + " plt.yticks(tick_marks, classes)\n", + "\n", + " fmt = '.2f' if normalize else 'd'\n", + " thresh = cm.max() / 2.\n", + " for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):\n", + " plt.text(j, i, format(cm[i, j], fmt),\n", + " horizontalalignment=\"center\",\n", + " color=\"white\" if cm[i, j] > thresh else \"black\")\n", + "\n", + " plt.tight_layout()\n", + " plt.ylabel('True label',fontsize=15)\n", + " plt.xlabel('Predicted label',fontsize=15)\n", + "\n", + "\n", + "# Print accuracy:\n", + "print(\"Accuracy: \", accuracy_score(y_test, y_pred_class))\n", + "\n", + "\n", + "# print the confusion matrix\n", + "cnf_matrix = confusion_matrix(y_test, y_pred_class)\n", + "plt.figure(figsize=(8,6))\n", + "plot_confusion_matrix(cnf_matrix, classes=['Not Relevant','Relevant'],normalize=True,\n", + " title='Confusion matrix with all features')\n", + "\n", + "# calculate AUC: Area under the curve(AUC) gives idea about the model efficiency:\n", + "# Further information: https://en.wikipedia.org/wiki/Receiver_operating_characteristic\n", + "y_pred_prob = nb.predict_proba(X_test_dtm)[:, 1]\n", + "print(\"ROC_AOC_Score: \", roc_auc_score(y_test, y_pred_prob))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ga5-KhYN5xaD" + }, + "source": [ + "At this point, we can notice that the classifier is doing poorly with identifying relevant articles, while it is doing well with non-relevant ones. Our large feature vector could be creating a lot of noise in the form of very rarely occurring features that are not useful for learning. Let us change the count vectorizer to take a certain number of features as maximum." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 686 + }, + "id": "ylOI4OsD5xaE", + "outputId": "cb0303c1-140f-4990-aa1c-e9ba1e3cc05b" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "CPU times: user 7.24 ms, sys: 0 ns, total: 7.24 ms\n", + "Wall time: 35.2 ms\n", + "Accuracy: 0.6876876876876877\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "
" + ], + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAsQAAAJnCAYAAACQ3UXDAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAB+OElEQVR4nO3dd3gU5dfG8Xs3vZBQ0iiB0HsvoYN0QZqCgCgEBURBVKSIhaogNhBEsFFFBUXxpyCCkd47gtK7ECC0hJaQ7Lx/8GZlSbImISFh9/vxmksy88yzZzaEnJycecZkGIYhAAAAwEmZszsAAAAAIDuREAMAAMCpkRADAADAqZEQAwAAwKmREAMAAMCpkRADAADAqZEQAwAAwKmREAMAAMCpkRADAADAqZEQAznMwYMH1aJFC/n7+8tkMmnRokWZOv+xY8dkMpk0a9asTJ3XEYSFhSkiIiJbYxg1apRMJlO6xkZHR2dxVMiohIQEDR06VKGhoTKbzerQoUN2hwQgBSTEQAoOHz6sZ599VsWKFZOnp6f8/PxUr149ffTRR7px40aWvnbPnj31559/6u2339bcuXNVo0aNLH09R/TXX39p1KhROnbsWHaHkinGjRuX6T8YPUhMJlOK2zvvvJNs7D///KPHH39cuXPnlp+fn9q3b68jR46kOO+XX36psmXLytPTUyVLltSUKVNSHJeeOe82Y8YMvffee+rUqZNmz56tl19+Oe0Xng5ff/21Jk2alCVzA87AZBiGkd1BADnJ4sWL1blzZ3l4eKhHjx6qUKGC4uPjtXbtWi1cuFARERH67LPPsuS1b9y4IW9vb73++ut66623suQ1DMNQXFyc3Nzc5OLikiWvkd2+//57de7cWStWrFDjxo3TfF5cXJzMZrPc3NyyLrj/kJCQoISEBHl6elr3+fr6qlOnTsmq+qNGjdLo0aN1/vx5BQQE3OdI7x+TyaTmzZurR48eNvurVq2q8uXLWz++evWqqlWrpitXruiVV16Rm5ubJk6cKMMwtHPnTuXLl8869tNPP1W/fv302GOPqWXLllqzZo3mzp2rd955R8OGDcvQnCnp2rWr1q5dq1OnTmXSu5GyRx55RHv27HGYHwKB+801uwMAcpKjR4+qa9euKlKkiP744w/lz5/feqx///46dOiQFi9enGWvf/78eUlS7ty5s+w1TCaTTbLl7AzD0M2bN+Xl5SUPD4/sDkeurq5ydeWf5ruVKlVKTz75pN0xn3zyiQ4ePKjNmzerZs2akqSHH35YFSpU0AcffKBx48ZJuv2D5+uvv642bdro+++/lyT16dNHFotFY8eOVd++fZUnT550zZmac+fOZenXc1a7fv26vL29szsMIOsZAKz69etnSDLWrVuXpvG3bt0yxowZYxQrVsxwd3c3ihQpYgwfPty4efOmzbgiRYoYbdq0MdasWWPUrFnT8PDwMIoWLWrMnj3bOmbkyJGGJJutSJEihmEYRs+ePa1/vlPSOXdatmyZUa9ePcPf39/w8fExSpUqZQwfPtx6/OjRo4YkY+bMmTbnRUZGGvXr1ze8vb0Nf39/o127dsZff/2V4usdPHjQ6Nmzp+Hv72/4+fkZERERxrVr1/7z/WrUqJFRvnx5Y9euXUbDhg0NLy8vo3jx4sZ3331nGIZhrFy50qhVq5bh6elplCpVyli+fLnN+ceOHTOee+45o1SpUoanp6eRN29eo1OnTsbRo0etY2bOnJnsfZRkrFixwuZzsXTpUqN69eqGh4eHMXHiROuxnj17GoZhGBaLxWjcuLEREBBgnD171jp/XFycUaFCBaNYsWLG1atXU7xOi8Vi5MuXz3j55Zet+xITEw1/f3/DbDYbly5dsu5/5513DBcXFyM2NtbmPU6S0rUkxfggfD7u5X288z3o37+/cf36dePGjRupjqtZs6ZRs2bNZPtbtGhhFC9e3Prx4sWLDUnG4sWLbcatX7/ekGTMnTs33XPeLenrLLW/h4mJicbEiRONcuXKGR4eHkZQUJDRt29f4+LFizbzLFq0yGjdurWRP39+w93d3ShWrJgxZswYIyEhwTqmUaNGqf7bkfT1cOfnxDAMY8WKFTbxJM1Tvnx5Y+vWrUaDBg0MLy8v48UXXzQMwzBu3rxpjBgxwihevLjh7u5uFCpUyBgyZEiyf+v+698fIKeihxi4w88//6xixYqpbt26aRrfu3dvjRgxQtWqVdPEiRPVqFEjjR8/Xl27dk029tChQ+rUqZOaN2+uDz74QHny5FFERIT27t0rSXr00Uc1ceJESVK3bt00d+7cdPcE7t27V4888oji4uI0ZswYffDBB2rXrp3WrVtn97zff/9dLVu21Llz5zRq1CgNGjRI69evV7169VL8Fezjjz+u2NhYjR8/Xo8//rhmzZql0aNHpynGS5cu6ZFHHlF4eLjeffddeXh4qGvXrpo/f766du2q1q1b65133tG1a9fUqVMnxcbGWs/dsmWL1q9fr65du2ry5Mnq16+fIiMj1bhxY12/fl2S1LBhQw0cOFCS9Nprr2nu3LmaO3euypYta51n//796tatm5o3b66PPvpIVapUSRanyWTSjBkzdPPmTfXr18+6f+TIkdq7d69mzpwpHx+fFK/RZDKpXr16Wr16tXXf7t27deXKFUmy+XysWbNGVatWla+vb4pzzZ07Vx4eHmrQoIH1Wp599lmbMTn583Ev7+OdZs2aJR8fH3l5ealcuXL6+uuvbY5bLBbt3r07xZ77WrVq6fDhw9bYd+zYIUnJxlavXl1ms9l6PD1z3i0wMFBz585VmTJlVKhQoWR/D5999lkNGTLEem9Cr169NG/ePLVs2VK3bt2yuW5fX18NGjRIH330kapXr64RI0bo1VdftY55/fXXVaVKFQUEBFhfJ6P9xBcuXNDDDz+sKlWqaNKkSXrooYdksVjUrl07vf/++2rbtq2mTJmiDh06aOLEierSpYv13Iz++wPkCNmdkQM5xZUrVwxJRvv27dM0fufOnYYko3fv3jb7Bw8ebEgy/vjjD+u+IkWKGJKM1atXW/edO3fO8PDwMF555RXrvqSq0nvvvWczZ1orxBMnTjQkGefPn0817pQqxFWqVDGCgoKMCxcuWPft2rXLMJvNRo8ePZK93tNPP20zZ8eOHY18+fKl+ppJkipZX3/9tXXfvn37DEmG2Ww2Nm7caN3/22+/JYvz+vXryebcsGGDIcmYM2eOdd93332XrPqVJOlzsXTp0hSPJVVfk3z66aeGJOOrr74yNm7caLi4uBgvvfTSf17re++9Z7i4uBgxMTGGYRjG5MmTjSJFihi1atUyhg0bZhjG7Sph7ty5bSrJKVX9fXx8ksV159ic/vkwjIy/j4ZhGHXr1jUmTZpk/PTTT8a0adOMChUqGJKMTz75xDrm/PnzhiRjzJgxyc6fOnWqIcnYt2+fYRiG0b9/f8PFxSXF1woMDDS6du2a7jlTk1R1vdOaNWsMSca8efNs9i9dujTZ/pTe42effdbw9va2qc62adMmxX8j0lshlmRMnz7dZuzcuXMNs9lsrFmzxmb/9OnTbX6jlpZ/f4Ccigox8P9iYmIkSbly5UrT+CVLlkiSBg0aZLP/lVdekaRkvcblypVTgwYNrB8HBgaqdOnSab5bPS2SehV/+uknWSyWNJ1z5swZ7dy5UxEREcqbN691f6VKldS8eXPrdd7pzkqfJDVo0EAXLlywvof2+Pr62lTQS5curdy5c6ts2bIKDw+37k/6853vj5eXl/XPt27d0oULF1SiRAnlzp1b27dvT8PV3la0aFG1bNkyTWP79u2rli1b6oUXXtBTTz2l4sWL/2ffqHT7PUlMTNT69esl3a4EN2jQQA0aNNCaNWskSXv27NHly5dt/l5kxIPw+cjo+yjdrqi/+OKLateunfr166dt27apQoUKeu2116yrviT9P6U+8KSe+TvHuru7p/hanp6eGZozPb777jv5+/urefPmio6Otm7Vq1eXr6+vVqxYYR1753scGxur6OhoNWjQQNevX9e+ffvS/dr/xcPDQ7169UoWb9myZVWmTBmbeJs0aSJJ1ngz8u8PkFOQEAP/z8/PT5JS/RXo3Y4fPy6z2awSJUrY7A8JCVHu3Ll1/Phxm/2FCxdONkeePHl06dKlDEacXJcuXVSvXj317t1bwcHB6tq1qxYsWGD3m1NSnKVLl052rGzZsoqOjta1a9ds9t99LUk3IKXlWgoVKpRsnV1/f3+FhoYm23f3nDdu3NCIESMUGhoqDw8PBQQEKDAwUJcvX7a2I6RF0aJF0zxWur081/Xr13Xw4EHNmjXLJklJTbVq1eTt7W1NfpMS4oYNG2rr1q26efOm9Vj9+vXTFc/dHpTPR0bex5S4u7trwIABunz5srZt2ybp38QxLi4u2fibN2/ajPHy8lJ8fHyKcyfdYJneOdPj4MGDunLlioKCghQYGGizXb16VefOnbOO3bt3rzp27Ch/f3/5+fkpMDDQenNhev7Op1XBggWT/bBw8OBB7d27N1mspUqVkiRrvBn59wfIKbiVGfh/fn5+KlCggPbs2ZOu89L6EIXUljgz0rDyYWqvkZiYaPOxl5eXVq9erRUrVmjx4sVaunSp5s+fryZNmmjZsmWZtszavVxLauemZc4XXnhBM2fO1EsvvaQ6depYH17StWvXdH3TTW8Ss3LlSmtS9Oeff6pOnTr/eY6bm5vCw8O1evVqHTp0SFFRUWrQoIGCg4N169Ytbdq0SWvWrFGZMmUUGBiYrnju9qB8PjLyPqYmKWG/ePGiJClv3rzy8PDQmTNnko1N2legQAFJUv78+ZWYmKhz584pKCjIOi4+Pl4XLlywjkvPnOlhsVgUFBSkefPmpXg86e/D5cuX1ahRI/n5+WnMmDEqXry4PD09tX37dg0bNixNf+fT+m9HkpS+NiwWiypWrKgPP/wwxXOSPhf3698fICuQEAN3eOSRR/TZZ59pw4YN//nNukiRIrJYLDp48KDNDVtnz57V5cuXVaRIkUyLK0+ePLp8+XKy/XdXoSXJbDaradOmatq0qT788EONGzdOr7/+ulasWKFmzZqleB3S7RvN7rZv3z4FBASk6aan++H7779Xz5499cEHH1j33bx5M9l7k9YfUtLizJkzeuGFF9SiRQu5u7tr8ODBatmyZZo+vw0aNNCECRP0+++/KyAgQGXKlJHJZFL58uW1Zs0arVmzRo888sh/zpOZ15OZ0vr5kO7tfUxJUutGUvJoNptVsWJFbd26NdnYTZs2qVixYtZ2qKSbKLdu3arWrVtbx23dulUWi8V6PD1zpkfx4sX1+++/q169enZ/OFu5cqUuXLigH374QQ0bNrTuP3r0aLKxqf0dSfptwd2fk5T+7bAX765du9S0adP//LuY3n9/gJyClgngDkOHDpWPj4969+6ts2fPJjt++PBhffTRR5Jk/UZ6993cSVWUNm3aZFpcxYsX15UrV7R7927rvjNnzujHH3+0GZdULbtT0jf3lH7tK92ullWpUkWzZ8+2+aa5Z88eLVu2zCZhyG4uLi7Jqp5TpkxJVu1KSuBTSszSK2l92i+//FKfffaZXF1d9cwzz6Sp+tqgQQPFxcVp0qRJql+/vjWZSFox4vTp02nqH/bx8cmUa8lsaf18SBl/H5PW5r5TbGysJk2apICAAFWvXt26v1OnTtqyZYtNArt//3798ccf6ty5s3VfkyZNlDdvXk2bNs1m3mnTpsnb29vmazetc6bH448/rsTERI0dOzbZsYSEBOvnOqmieud7FB8fr08++STZeT4+Pim2UBQvXlySbFY8SUxMTNfDhR5//HH9888/+vzzz5Mdu3HjhrWlKiP//gA5BRVi4A7FixfX119/rS5duqhs2bI2T6pbv369vvvuO0VEREiSKleurJ49e+qzzz6z/mpz8+bNmj17tjp06KCHHnoo0+Lq2rWrhg0bpo4dO2rgwIG6fv26pk2bplKl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e9j9FRERocuXL2vRokWqU6eOypUrpy+//FKLFi1Sx44dlRRy9+7ddf78eS1btsx67tChQ7V48WLt3btX0u3e4JdeekkvvfSS3dcMCwvTmTNn5Obmpri4OCUkJChv3rzatGmTSpQoobVr16pNmzY6d+6cPDw8rOeVKFFCQ4cOVd++fTVq1CgtWrRIO3futB4bO3asunXrZh3/1ltvacmSJVq/fr3Onz+vAgUK6I8//rAmwHXr1lXDhg2T9U8n2bp1q2rWrKnY2Fj5+vpq5cqVeuihh/T777+radOmkqQlS5aoTZs2unHjhjw9PZPFlZJRo0Zp9OjRyfZ7VOwjk4t7CmcAyE6XtqT8gw2A7BcTE6PgfP66cuVKmhLFrI7F399fRZ7/TmYP70yd2xJ3Xcc/6Zyu6wwPD1etWrU0ZcqU23NYLCpcuLAGDBigV199Ndn41157TV9//bWOHDli/UHyo48+0oQJE3T69OkMzZmSHNsykWTChAmaPXu2/v7772TH/v77b9WrV89mX7169XTw4EFrs3V6DBkyRDt37tQff/yh8PBwTZw4USVKlJAk7dq1S1evXlW+fPnk6+tr3Y4eParDhw8nm+vatWs6fPiwnnnmGZvxb731lnV8YGCgWrRooXnz5kmSjh49qg0bNqh79+7WebZt26a2bduqcOHCypUrlxo1aiRJOnHihM3rVapUyfrn/PnzS1K6lhsZPny4za87Tp48meZzAQAA0mLQoEH6/PPPrbndc889p2vXrqlXr9stRT169LBpuXjuued08eJFvfjiizpw4IAWL16scePGqX///mmeMy1ybMtEkoYNG6ply5YaPny4IiIisvS1AgICVKJECZUoUULfffedKlasqBo1aqhcuXK6evWq8ufPr5UrVyY7L3fu3Mn2Xb16VZL0+eefKzw83ObYnWX/7t27a+DAgZoyZYq+/vprVaxYURUrVpR0O6lu2bKlWrZsqXnz5ikwMFAnTpxQy5YtFR8fbzOnm5ub9c9J7RcWiyXN1+7h4WFT+QYAAI4jKx61nJH5unTpovPnz2vEiBGKiopSlSpVtHTpvy1FJ07YthSFhobqt99+08svv6xKlSqpYMGCevHFF23uwfqvOdMixyfEkvTOO++oSpUqKl26tM3+smXLat26dTb71q1bp1KlSlmTTnd39wxVi0NDQ9WlSxcNHz5c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hN9//13bt2/XoUOHNGXKFHl4eEiSpk2bpmHDhunFF1/U888/r/z580uS4uPj+foD0oiEGIBdSYnunRITE7V//349+eSTio+P19q1a5U7d26bxzhfvXpVhQsXzqaoAcfz+uuva/z48SpWrJhWr16tAgUKWI9NmzZNb7zxhiIiIjR48GBrUgwgbUiIAaQqKcHdsGGD1qxZo6tXr6p169aqXbu2JOnvv//WE088oVu3biVLigFkvg8++EBDhgzRBx98oD59+sjX19d67MMPP9SkSZO0bds2BQYGZmOUwIOHhBiAXT/88IP69eunChUqyMfHR4sXL9ZXX32lJ554QtLtpLhnz576559/9Ndff8nf3z+bIwYefEk/WCYmJioxMdGm9eGNN97QO++8o8mTJ6tnz57y8fGxHrt8+bJy586dDREDDzYWJgSQqg0bNuj555/XuHHj1Lt3b506dUphYWHq1auXLl68qAEDBqhs2bKaMWOG+vfvrwsXLpAQA/coKRn+7bffNGfOHB07dkzNmzfXE088oVKlSumtt96SYRgaOHCgXFxc1L17d2ulmK8/IGNIiAGkKDExUTt37lTfvn3Vu3dvnTx5UvXr19ezzz6rwMBAvfTSS/L19VVERIQqVKig5cuXcwMPkAlMJpN++uknPfXUU3riiSfUpUsXjR07VgcOHFC/fv3UsGFDvf3223JxcdFzzz0nNzc39erVSyaTiXYlIINomQCQzLlz5xQUFKS///5bV69eVYUKFfTwww+rZMmS+vTTT3X8+HFVqVJFsbGx+vTTT9WnT5/sDhlwGHv37tWjjz6ql19+Wf369ZNhGAoKCtKtW7dUu3Ztvfnmm6pXr54k6a233tJjjz2msmXLZnPUwIONCjEAG3/++aeaNGmiP//80/pN9uDBg4qJiVFERITMZrPc3d312GOPqUyZMtZvzAAyR1xcnJ544gk988wzOnXqlBo0aKDu3burV69eql27tnx9fXX9+nU1b95cb7zxRnaHCzgEEmIANipWrKiCBQvqvffe0/vvvy+TyaRLly5p586dunTpkrUqvG/fPn388cfy9vbO7pABh7B79255enqqbNmyypUrl1xcXDR06FBri4SPj4/q1KmjH374Qd7e3qpfv768vLyyO2zAIfCkOgBWCQkJslgs6tSpk3bs2KGLFy9KkmrVqqX+/furXbt2qlOnjiZPnqypU6eSDAOZwDAMnTt3To8++qhWr14tLy8vlSxZUomJifrnn39UsWJF60oSZcuW1ddff61Ro0aRDAOZiB5iALp48aLy5s1r/fj06dMqX768XnnlFZtfyf7yyy+6fv26atasqaJFi2ZHqIDDevHFF/XLL79oy5Ytyps3r6Kjo9W0aVNVrVpVnTt31rp16zR79mzt3r1b+fLly+5wAYdCQgw4uY0bN+rVV19VhQoVNG7cOHl4eMjDw0OTJ0/WvHnzNHPmTJUrVy67wwQcVtIjlvft26dnnnlGzzzzjJ5++mlJ0sqVK/XEE08oV65cunXrlhYuXKiqVatmc8SA46FlAnByQUFBatCggdavX6/KlStr7Nix2rt3r1q3bq3Y2Fjt379f0u1l2ABknr/++kuxsbHW5QpLly6tggULas6cOdYxjRs31qZNm7RkyRJt3LiRZBjIIlSIASeTtOj/lStXlJCQYPOr11GjRmn79u2KjIzUuHHjNHPmTF29elVbt27l6VdAJjp69Ki6du2qkydPatKkSSpXrpwqVKig48ePq169eho+fLj69++f3WECToOEGHAiScnwzz//rE8++UQHDx5UtWrVVKNGDQ0dOlSSdOXKFf3yyy+aPn26jhw5oitXrujw4cMKDg7O5ugBx3Hr1i0dPXpUH3/8sVavXi3DMNStWzd17txZEydOVEJCgiZNmiQPDw8etgHcByTEgINLSoKTLF68WJ06ddLbb7+tChUq6Ndff9VHH32kyMhIPfTQQ9Zx//zzj/bv368iRYqoePHi2RE64DCSvg4PHTqky5cvKyEhQbVr15YkbdmyRZs3b9abb76pJk2aaP/+/dq7d682bNig8PDwbI4ccA4kxIATSExMlIuLi27cuKGIiAhVrVpVr776qqKjo1W1alV17NhRkydPzu4wAYeUlAz/8MMPevPNN5WYmCiTyaS8efPqhx9+sP725ciRI1q4cKGWLl2qFStWaN++fSpVqlQ2Rw84B26qAxzUl19+qc6dO0uSXFxcrP8/dOiQypYtqzNnzqhKlSp6+OGHrcnwd999pw0bNmRbzIAjMplMWrlypXr06KGXX35Z27Zt08cff6wNGzZoyZIlkiSLxaJixYpp8ODBioyM1OnTp0mGgfuIhBhwQLdu3dKlS5f0999/q0+fPtb9CQkJKlu2rLZt26Z69eqpdevW+vTTTyVJ0dHRWrp0qfbt2yeLxZJdoQMPvKQH2tz5dbRp0yb16tVLvXv31vnz59W7d2/169dPvXr1kiSZzbe/HSet5hISEnKfowacGwkx4IDc3NzUr18/Pf/889qyZYt1TVNvb2/VrVtXb731lgoVKqTJkydb+4snTpyo1atXq3HjxtZvzgDSZ8GCBQoMDNS+fftkNputSfGOHTusP6g2aNBALVq00NSpUyVJM2fOtP7Z1dU122IHnBlfeYADMgxDvr6+6tGjhywWi7744gv16tVLM2fO1PPPP6/z589r7Nixeumll+Ti4qJr165p0aJFWrlyJU+gA+5B7dq11bx5czVp0kR//PGHypQpI0l67LHHNGPGDJUuXVodOnTQp59+KovFIovFoq1bt8psNuvmzZvy9PTM5isAnBNlIMABJVV9k5LiPn36aNu2bYqIiJAkjRw5UlOnTlVsbKz27dunvHnzav369apSpUr2BQ04gMKFC+vLL79U1apV1bBhQ+3bt0/S7YduXL58Wblz51bXrl0lSVevXtXIkSP1ww8/aMCAASTDQDZilQnAgSTdzX7kyBHduHFDCQkJqly5sm7duqUvvvhC06dPV7Vq1TRz5kxJ0vXr1+Xt7S2LxUKbBJCJTp06pb59+2rr1q1auXKlypUrp7Vr16p///4ymUxKTExU/vz5tWfPHi1evJgn0AHZjIQYcBBJyfCPP/6owYMHy9/fX0ePHlX79u3Vv39/VapUSV988YU+//xz1ahRQ1988UV2hww4tLNnz6pnz57atm2bVq1apXLlymnPnj06cOCA1q1bp6pVq6pu3boqVqxYdocKOD0SYsCBrF69Wm3bttWECRPUr18/ffXVV+rRo4dmzJihiIgIXb16VV999ZXGjx+vdu3aacqUKdkdMvDAS/phdOvWrfrrr7905coV1a5dWzVr1tTFixfVvXt3bd261ZoUA8h5SIgBB5D0DXnEiBE6cuSIvvrqKx09elQtWrTQQw89pM8++0zS7SWdbt68qW+++UZNmjShMgVkkoULF6pv375q0KCBTpw4IbPZrBYtWmjcuHE6deqUnn32We3cuVPLli1T+fLlsztcAHehaRB4wKS0RnDSTXRRUVGqXLmyEhMTVb9+fTVt2tS6zvD8+fP1448/ysfHR8888wzJMJBJ/vzzTw0cOFDjxo3T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u5e3V69ekqTRo0fb9FHfXS3/+++/FRERodDQUHl4eCg4OFhdu3bV3r17U4wtMTFR77//vsqUKSNPT0+FhobqxRdfVExMTKrXk15nzpzRu+++q0aNGqlgwYJyd3dXSEiIHn30UW3ZssXuuYZh6KOPPlK5cuXk6empggULauDAgbp8+XKq47/55hs1adJEefLkkaenp8qWLatRo0bp+vXrmXZNAJwLLRMAHgixsbGSJA8PD5v9FotFHTp00OrVq9WoUSNVqlRJ+fLlkySdO3dOzZs31+7duxUSEqJGjRrJMAytX79eERER2rp1q6ZMmWIz3y+//KKOHTsqISFBtWrVUrFixbRr1y41bNhQERER6Yr5zJkzat68ufbu3as8efKocePG8vDw0JEjRzR9+nSVLFlSpUqVUqtWrZSQkKB169apcuXKqlKlinWOEiVKWP+8aNEide3aVXFxcapSpYpq166tkydPasGCBfr555/166+/qmHDhjYxPPnkk/r222/l7e2tFi1ayNXVVbNnz9a6devk5uaWrutJzU8//aRhw4apdOnSqlSpkvz8/HTw4EH9+OOP+uWXX/TLL7+oRYsWKZ77wgsv6LPPPlPjxo1VsWJFrVq1SlOmTNGqVau0Zs0a+fn5WcdaLBY9+eST+uabb+Tr66saNWooT5482rp1q0aPHq1ff/1VK1eulJeXV6ZcFwAnYgBADiHJSOmfJYvFYtSpU8eQZLz++uvJxpcoUcI4depUsvNat25tSDJefPFF4+bNm9b9UVFRRo0aNQxJxq+//mrdHxMTYwQGBhqSjBkzZti8/rBhw6yvN3LkSJvX6dmzpyHJWLFihc3+pk2bGpKMxx9/3IiNjbU5dvToUWPXrl3Wj2fOnJni3HeO9/HxMXx9fY3ly5fbHPv1118NNzc3IzQ01IiLi7Pu//bbbw1JRuHChY2jR49a9589e9aoUKGC9XruPGZPajHu3r3b2LNnT7LxS5cuNdzd3Y3ixYsbFovF5liRIkUMSYafn5+xdetW6/7Y2FijSZMm1s/bnd59911DktG4cWPjzJkz1v1xcXHGM888Y0gyhg0bZnNOap8bALgTCTGAHOPuhDghIcE4cOCAERERYUgyPDw8jEOHDiUb/9133yWba8eOHYYko2bNmkZiYmKy49u3bzckGe3atbPumzFjhiHJaNiwYbLx8fHxRqFChdKcEG/atMmQZAQFBRkxMTH/ee3/lRC/+OKLhiRjypQpKR4fOHCgIcn44YcfrPsaNmyYLLlP8uuvv2ZaQmxP9+7dDUnG7t27bfYnJcSvvfZasnP27t1rmEwmw9fX17hx44ZhGIZx69YtIyAgwPDx8TGioqKSnXP9+nUjJCTEyJMnj83nm4QYQFrQQwwgx0nqn3V1dVWpUqU0a9Ys5cqVS998842KFy+ebGzbtm2TzbFs2TJJUocOHWQ2J/+nLqmnePPmzdZ9a9askSR17do12Xg3Nzd16tQpzdfw+++/S5K6deumXLlypfm81CRdz6OPPpri8QYNGkiS9Xpu3bqljRs3SpK6dOmSbHyrVq2UJ0+ee44rSVxcnH766Se9/vrr6tu3ryIiIhQREaE///xTknTw4MEUz0vpvS5XrpwqV66sq1evaseOHZKk7du3Kzo6WnXr1lVwcHCyc7y8vFS9enVdunQp1dcCgNTQQwwgx+nZs6ckyWw2y8/PTxUrVtSjjz6aYgIXFBSUrK9YkvVhE6+//rpef/31VF/r5s2b1j+fPn1aklSkSJEUx4aFhaX1Eqw3/92dwGdU0vUULFjQ7rjo6GhJ0oULFxQfH6/AwMBUV78oUqSILl26dM+x/fnnn2rXrp3dB3wk9YCnFENKwsLCtHPnTuvnJGnu5cuX/+cDPaKjo1W6dOn/DhwA/h8JMYAc5+6VFezx9PRMcb/FYpEk1a9fP9OS0uyUdD1JPyykJjw8/H6EY2UYhh5//HEdO3ZM/fr1U79+/VSsWDH5+vrKZDLptdde0/jx4+/5KYNJ11+iRAnVq1fP7tikmyoBIK1IiAE4pEKFCkm63TLxyiuvpOmc/PnzS5KOHz+e4vHU9qckNDRUknT48OE0n2NPoUKFdPjwYX3wwQdpSvjy5csnd3d3nT9/Xjdu3Ehx5YUTJ07cc1z79u3Tvn37VKNGDU2bNi3Z8SNHjtg9//jx46pYsWKK+yWpQIECkv79fJYpUyZdPzABQFrQQwzAITVv3lyS9OOPP6b5nKQ+3AULFiQ7lpCQoIULF6Z5rmbNmkmSvvnmG129evU/x7u7u1tfJyXpvR43NzdrtTil61m2bJkuXryYprnsSWq5SEpY7z62fPlyu+enFNu+ffu0c+dO+fr6Wpegq1mzpvz9/bVq1apMiRsA7kRCDMAhhYeHq3nz5lq3bp369++f4oModu3apaVLl1o/7ty5s/Lly6eVK1faPLjDMAyNHDkyXRXVWrVq6aGHHtK5c+fUt29fXbt2zeb4sWPHrDecSf9WQvfv35/ifK+88oq8vLw0ePBg/fDDD8mOx8XF6fvvv9epU6es+5577jlJShZ7dHS0hgwZkuZrsadEiRIym836448/bG5mu3nzpvr16/efyeuUKVOsN85Jtx+k8sILL8gwDPXq1cta2fbw8NDQoUMVGxurRx99NMXK8z///KO5c+dmynUBcDLZu8gFAPxLqaxDbG98kSJFUj1+9uxZo2rVqoYkI3fu3Ebjxo2NJ554wmjTpo0RGhqa4lq3ixYtMlxcXAxJRnh4uNGtWzejXLlyhpubm9GnT590rUN86tQpo3Tp0oYkI2/evEa7du2Mzp07G9WqVTPMZrMxceJE69gbN24YQUFBhiSjUaNGRq9evYxnnnnGWLdunU1s3t7e1rWX27Zta3Tt2tVo0KCB4ePjY0gyduzYYRND586dDUmGj4+P0a5dO+PRRx81cufObVSrVs2oXbt2piy7lvS+eHl5GW3atDE6depkBAcHGwEBAdYl82bOnGlzTtKya/379zfc3NyMli1bGo8//rgREhJiSDLKly9vXL582eacxMRE46mnnjIkGe7u7kZ4eLjRtWtX49FHHzXKly9vmEwmo3Llymn63ADAnagQA3BYQUFBWr9+vSZPnqxy5cppx44d+v7777V7924VK1ZM7733ngYPHmxzTvv27bVixQo99NBD2rNnjxYvXqz8+fNr1apVqlu3brpev2DBgtqyZYvGjBmjQoUKafny5fr11191/fp1Pf/883rkkUesYz09PbV48WI1b95cO3fu1KxZs/Tll19aH++cFNvu3bv1/PPPy2Qyafny5Vq8eLHOnTuntm3basGCBSpXrpxNDF9//bUmTJigggULaunSpdq4caOeeOIJ/fHHHymuzpER06ZN0wcffKCiRYsqMjJSa9asUbNmzbR169ZUV5FIMnnyZI0fP17Hjx/XTz/9JJPJpP79+2vNmjXy9/e3GWs2mzVnzhz99NNPat68uY4ePaqFCxdq7dq18vT01JAhQzRjxoxMuSYAzsVkGPd46y8AAADwAKNCDAAAAKdGQgwAAACnRkIMAAAAp0ZCDAAAAKdGQgwAAACnRkIMAAAAp0ZCDAAAAKdGQgwAAACnRkIMAAAAp0ZCDAAAAKdGQgwAAACnRkIMAAAAp/Z/8F95wrmqYi0AAAAASUVORK5CYII=\n" + }, + "metadata": {} + } + ], + "source": [ + "vect = CountVectorizer(preprocessor=clean, max_features=5000) # Step-1\n", + "X_train_dtm = vect.fit_transform(X_train) # combined step 2 and 3\n", + "X_test_dtm = vect.transform(X_test)\n", + "nb = MultinomialNB() # instantiate a Multinomial Naive Bayes model\n", + "%time nb.fit(X_train_dtm, y_train) # train the model(timing it with an IPython \"magic command\")\n", + "y_pred_class = nb.predict(X_test_dtm) # make class predictions for X_test_dtm\n", + "print(\"Accuracy: \", metrics.accuracy_score(y_test, y_pred_class))\n", + "# print the confusion matrix\n", + "cnf_matrix = confusion_matrix(y_test, y_pred_class)\n", + "plt.figure(figsize=(8,6))\n", + "plot_confusion_matrix(cnf_matrix, classes=['Not Relevant','Relevant'],normalize=True,\n", + " title='Confusion matrix with max 5000 features')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2JzJ6k7g5xaL" + }, + "source": [ + "Clearly, the performance on relevance classification got better even though the overall accuracy fell by 10%. Let us try another classification algorithm and see if the performance changes. For this experiment, we have considered logistic regression, with class_weight attribute as \"balanced\", to address the problem of class imbalance in this dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 668 + }, + "id": "0v7pM9hB5xbA", + "outputId": "e86c81ce-cb3f-4268-8ccd-7daa7c4d3a66" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Accuracy: 0.7367367367367368\n", + "AUC: 0.6584385682402464\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "
" + ], + "image/png": "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T4MHD9akSZNUsWJF7d27VyEhIXJzc1NQUJC6dOmijz/+WKNGjbIl4gsXLlS+fPkUGBgoSbp//75GjBihEiVKKDw8XH379lVwcLCWL19ud673339fY8eOlZeXl3r27KmXXnpJmzdvVocOHXTw4EGtXLlSq1evliR5eHgkiDUqKkpRUVG215GRif86HE+GRb/skWfObBr8ajN5586uA0f/1HO9PrfdtObrk8uukjtq2koZhqEhrzVXvjweirh2S8s2HNTQST/a+rzSvo4kadW0N+3OFTJ4jub+uD3tLwowiXbtOyji8mUNHzZYl8LCVK58BS39aaW8vf+6ae38+XN2VdqLFy6oetWKttfjx32i8eM+UWCduvplzTpJ0rjPJmrYkEF64/XXdDk8XHnz5VP3kFf03geDH+u1ARmRxXgCH78SHBys69ev64cfflCNGjXk7++v6dOn64cfflDr1q1tT4zp0qWLLl++rF9++fuO8v79+2vZsmU6dOivJZX8/Pz05ptv6s0333zoOf38/HTx4kU5OjoqKipKMTExypUrl7Zv365ixYpp06ZNatasmcLDw+X8j3UQixUrpv79+6tHjx4aOnSofvjhB+3bt8+2b8SIEerUqZOt/4cffqjly5dry5Ytunz5svLly6dff/3VltzWrFlTderUSTBfOd6uXbtUtWpV3bx5U9myZdO6detUv359rV69Wk8//bQkafny5WrWrJnu3r0rFxeXBHElZujQoRo2bFiCdueyIbI48FAB4Elzbeek9A4BwANERkbKO7eHbty4IXd393SPxcPDQ4Ve+05WZ9dUHTsu6o7OftHuibjOh3lipzHEGz16tGbPnq3Dhw8n2Hf48GHVqlXLrq1WrVo6fvy4YmNjk32ud955R/v27dOvv/6qgIAAffrppypWrJgkaf/+/bp165Zy586tbNmy2bbTp0/r5MmTCca6ffu2Tp48qe7du9v1//DDD239vby81LhxY82bN0+SdPr0aW3dulVduvy9lMzu3bvVokULFSxYUNmzZ1fdunUlSefOnbM7X7ly5Wx/zps3ryQ98Gk9iRk4cKBu3Lhh286fP5/kYwEAAJ5UT+w0hnh16tRRkyZNNHDgQAUHB6fpuTw9PVWsWDEVK1ZM3333ncqWLasqVarI399ft27dUt68ebVu3boEx+XIkSNB261btyRJX331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jmjSpElydXVN75ABUzhw4IBcXFxUqlQpZc+eXQ4ODurfv79t2oKbm5tq1KihJUuWyNXVVbVr11bWrFnTO2zgiccT1ADYxMTEKC4uTs8//7z27t2rq1evSpKqVaumXr16qWXLlqpRo4YmTJigzz//nEQXSAWGYSg8PFxt2rTRhg0blDVrVhUvXlyxsbH6888/VbZsWduKC6VKldI333yjoUOHkugCScScXQC6evWqcuXKZXt94cIFlS5dWm+//bbdr0l/+ukn3blzR1WrVlXhwoXTI1TAtN544w399NNP2rlzp3LlyqWIiAg9/fTTqlixotq1a6fNmzdr9uzZOnDggHLnzp3e4QIZBskukMlt27ZN7777rsqUKaORI0fK2dlZzs7OmjBhgubNm6eZM2fK398/vcMETCv+sb5HjhxR9+7d1b17d7300kuSpHXr1qlz587Knj277t+/r8WLF6tixYrpHDGQsTCNAcjk8uTJo8DAQG3ZskXly5fXiBEjdOjQITVt2lQ3b97U0aNHJf21FBmA1PP777/r5s2btiX7SpQoofz58+vrr7+29alXr562b9+u5cuXa9u2bSS6QApQ2QUymfgF62/cuKGYmBi7X4cOHTpUe/bs0Zo1azRy5EjNnDlTt27d0q5du3gqE5CKTp8+rY4dO+r8+fMaP368/P39VaZMGZ09e1a1atXSwIED1atXr/QOEzAFkl0gE4lPdH/88Ud98cUXOn78uCpVqqQqVaqof//+kqQbN27op59+0pQpU3Tq1CnduHFDJ0+elLe3dzpHD5jH/fv3dfr0aU2aNEkbNmyQYRjq1KmT2rVrp08//VQxMTEaP368nJ2deVAE8IhIdgGTi09w4y1btkzPP/+8PvroI5UpU0YrVqzQZ599pjVr1qh+/fq2fn/++aeOHj2qQoUKqWjRoukROmAa8V+HJ06c0PXr1xUTE6Pq1atLknbu3KkdO3Zo0KBBatCggY4ePapDhw5p69atCggISOfIgYyPZBfIBGJjY+Xg4KC7d+8qODhYFStW1LvvvquIiAhVrFhRrVu31oQJE9I7TMCU4hPdJUuWaNCgQYqNjZXFYlGuXLm0ZMkS229NTp06pcWLF2vlypVau3atjhw5oqeeeiqdowcyPm5QA0xq+vTpateunSTJwcHB9v8TJ06oVKlSunjxoipUqKBnn33Wluh+99132rp1a7rFDJiRxWLRunXr1LVrV7311lvavXu3Jk2apK1bt2r58uWSpLi4OBUpUkT9+vXTmjVrdOHCBRJdIJWQ7AImdP/+fV27dk2HDx9WSEiIrT0mJkalSpXS7t27VatWLTVt2lRTp06VJEVERGjlypU6cuSI4uLi0it0IMOLfxjLP7+Otm/frm7duunll1/W5cuX9fLLL6tnz57q1q2bJMlq/evbcfyqJz4+Po85asC8SHYBE3J0dFTPnj312muvaefOnbY1O11dXVWzZk19+OGHKlCggCZMmGCbz/vpp59qw4YNqlevnu0bL4Dk+fbbb+Xl5aUjR47IarXaEt69e/fafggNDAxU48aN9fnnn0uSZs6caftzlixZ0i12wKz4qgJMyDAMZcuWTV27dlVcXJymTZumbt26aebMmXrttdd0+fJljRgxQm+++aYcHBx0+/Zt/fDDD1q3bh1PRgMeQfXq1dWoUSM1aNBAv/76q0qWLClJatu2rWbMmKESJUqoVatWmjp1quLi4hQXF6ddu3bJarXq3r17cnFxSecrAMyH8g1gQvHV2viENyQkRLt371ZwcLAkaciQIfr888918+ZNHTlyRLly5dKWLVtUoUKF9AsaMIGCBQtq+vTpqlixourUqaMjR45I+uuBEdevX1eOHDnUsWNHSdKtW7c0ZMgQLVmyRL179ybRBdIIqzEAJhJ/1/epU6d09+5dxcTEqHz58rp//76mTZumKVOmqFKlSpo5c6Yk6c6dO3J1dVVcXBxTF4BU9Mcff6hHjx7atWuX1q1bJ39/f23atEm9evWSxWJRbGys8ubNq4MHD2rZsmU8GQ1IQyS7gEnEJ7rff/+9+vXrJw8PD50+fVrPPfecevXqpXLlymnatGn66quvVKVKFU2bNi29QwZM7dKlSwoKCtLu3bu1fv16+fv76+DBgzp27Jg2b96sihUrqmbNmipSpEh6hwqYGskuYCIbNmxQixYtNHr0aPXs2VNz585V165dNWPGDAUHB+vWrVuaO3euQkND1bJlS02cODG9QwYyvPgfNHft2qXff/9dN27cUPXq1VW1alVdvXpVXbp00a5du2wJL4DHi2QXMIH4b7aDBw/WqVOnNHfuXJ0+fVqNGzdW/fr19eWXX0r6a1mje/fuaf78+WrQoAEVJSCVLF68WD169FBgYKDOnTsnq9Wqxo0ba+TIkfrjjz/0yiuvaN++ffrll19UunTp9A4XyFSYpAdkMImtgRt/Q1pYWJjKly+v2NhY1a5dW08//bRtHd2FCxfq+++/l5ubm7p3706iC6SS3377TX369NHIkSP1ww8/aPr06Tp06JDt67JAgQKaPn26/Pz81Lp1a92/fz+dIwYyF5JdIAOJv5Hs7NmzmjZtmj799FPt2LHDtr906dL6+OOPlS9fPrVr106TJk2SxWKRYRhavny5NmzYoKioKNs3YQBJ96CHrRw7dkwFCxbUK6+8otOnT6t169bq2rWrPvroI0nSoUOH5OPjo8WLF2vNmjVydHR8nGEDmR7r7AIZRHyie+DAATVr1kx+fn7atm2b7RGjISEhateunTZt2qQNGzbo1VdfVZYsWXT37l2NGDFCq1at0tq1a+Xs7JzelwJkOPFff+fPn9cvv/yiuLg4lSxZUoGBgXJ0dJS3t7fOnz+vOnXqqGnTpvriiy8kSRs3btTPP/+s119/naeiAemEZBfIAOK/0f7222+qXr26+vfvr3feeUfXrl1T9erVtXjxYoWEhChfvnx66aWXdO3aNVWpUkVVq1aVYRg6evSoli1bphIlSqT3pQAZzj9/0GzZsqW8vb118uRJ5ciRQ+PGjVO5cuW0fPlyrVixQj179tRnn31mO/bbb7/VmTNnWEMXSEdMYwAygPipCzVq1NBzzz2noUOHys3NTQUKFJC/v7/27dun8+fPS5KeffZZLV68WB9//LGqVKmi9u3b25Y5ApA8/0x0a9SooU6dOmnt2rVasGCB7t69qylTpsjPz0+TJ0+WYRgqUKCAzp07p5MnT6p///6aN2+eRo0aJQ8Pj/S+FCDTYjUGIIM4c+aMAgMDVaVKFfXt21eBgYEaM2aMBgwYoGLFiqlMmTKSpAoVKqhnz57KmTMncwOBVHD+/HlVqlRJ9evX17fffmtrr1atmq5fv66dO3cqS5YsWrhwoXr16iVvb2+5urrKYrFo7ty5/KAJpDOSXSADiK8uHT16VG3btlWJEiXk5eWl7777TvPnz5efn58Mw9DMmTO1fv167du3T40bN9b8+fPl4uLC09GAR3DmzBm1b99eefPmVf/+/VWrVi2Fhobq/fffV5UqVZQ3b17lzp1bzZs3V44cOXT37l0VKlRIXl5e8vb2Tu/wgUyPZBfIIOIT3iNHjqhDhw767bff9Mknn6hv3762PvHr7c6bN081a9ZU4cKF0zFiwDyOHz+uPn36yMnJSXny5NHSpUv1xRdfqFq1atq9e7cOHjyoiRMnys3NTZUqVdLixYvTO2QA/0OyC2Qg8QnvyZMn1apVK/n5+emdd95RnTp1JEkxMTHKkoX7ToG0cOzYMfXu3VsbN27UiBEj1K9fP7v9V65c0dq1a1W+fHkVL148naIE8G8ku8ATKn5NT6vVakty49vjK7zPP/+8ChUqpIEDB6p27drpGS6QKZw8eVKvvfaaHBwc9N5779m+7u7fv88ceeAJxUQ+4AkRn9zeu3dP0l9J7vHjx21/jhef/JYsWVKLFi3Sn3/+qXfffVdbt259/EEDmUzRokU1adIkGYahDz/8UJs3b5YkEl3gCUayCzwhrFarTp06pTfffFN//vmnFi1apFKlSunQoUOJ9o1PeOfNm6e4uDgVKFAgHaIGMp/ixYtrwoQJcnR0VL9+/bRt27b0DgnAQzCNAXiCbNiwQa1atVL58uW1detWffnll+ratavtxrN/i42NlYODA79CBdLBkSNHNGjQII0dO1YFCxZM73AAPADJLvCEiE9oR48erYEDB6p69er6+uuvVaxYMbv9DzsWwOMVHR0tJyen9A4DwEMwjQF4QsTGxkqSXFxcNHjwYF26dElDhw7V3r17JUkWi0X//Nk0fo5v/D4Ajx+JLvDko7ILpLP4quy/lw375Zdf9Morr6hmzZrq37+/ypcvL0naunWratSokV7hAgCQoZDsAukoPtFds2aNvv/+e127dk3+/v4KCQlRnjx59Msvv6hnz56qVauWOnbsqD179mjIkCEKCwuTl5cXFV0AAP4DyS6Qzn744Qd16tRJL7zwgs6ePatr167p8uXL2rBhgwoWLKg1a9aoX79+iouLU2RkpBYtWqTKlSund9gAAGQIJLvAY/TvG8kiIiLUqFEjde7cWe+8844k6eDBg3r77bd1/Phx7dixQ56enjpz5owiIyPl5eWlvHnzplf4AABkONygBjwG8T9T3rlzR9LfN5fdunVLFy9eVIUKFWx9S5UqpY8//lg5c+bUggULJEl+fn4qV64ciS4AAMlEsgs8BhaLReHh4fLz89O3335reyKaj4+PfH19tX79eltfBwcHlStXTlmyZNHRo0fTK2QAAEyBZBd4TKxWq1q2bKkX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\n" + }, + "metadata": {} + } + ], + "source": [ + "from sklearn.linear_model import LogisticRegression # import\n", + "\n", + "logreg = LogisticRegression(class_weight=\"balanced\") # instantiate a logistic regression model\n", + "logreg.fit(X_train_dtm, y_train) # fit the model with training data\n", + "\n", + "# Make predictions on test data\n", + "y_pred_class = logreg.predict(X_test_dtm)\n", + "y_pred_prob = logreg.predict_proba(X_test_dtm)[:, 1]\n", + "\n", + "# calculate evaluation measures:\n", + "print(\"Accuracy: \", accuracy_score(y_test, y_pred_class))\n", + "print(\"AUC: \", roc_auc_score(y_test, y_pred_prob))\n", + "cnf_matrix = confusion_matrix(y_test, y_pred_class)\n", + "plt.figure(figsize=(8,6))\n", + "plot_confusion_matrix(cnf_matrix, classes=['Not Relevant','Relevant'],normalize=True,\n", + " title='Confusion matrix with normalization')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6v1evQyy5xbe" + }, + "source": [ + "Let us wrap this notebook by trying with one more classifier, but reducing the feature vector size to 1000." ] - }, - "metadata": { - "needs_background": "light", - "tags": [] - }, - "output_type": "display_data" - } - ], - "source": [ - "from sklearn.linear_model import LogisticRegression # import\n", - "\n", - "logreg = LogisticRegression(class_weight=\"balanced\") # instantiate a logistic regression model\n", - "logreg.fit(X_train_dtm, y_train) # fit the model with training data\n", - "\n", - "# Make predictions on test data\n", - "y_pred_class = logreg.predict(X_test_dtm)\n", - "\n", - "# calculate evaluation measures:\n", - "print(\"Accuracy: \", accuracy_score(y_test, y_pred_class))\n", - "print(\"AUC: \", roc_auc_score(y_test, y_pred_prob))\n", - "cnf_matrix = confusion_matrix(y_test, y_pred_class)\n", - "plt.figure(figsize=(8,6))\n", - "plot_confusion_matrix(cnf_matrix, classes=['Not Relevant','Relevant'],normalize=True,\n", - " title='Confusion matrix with normalization')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "6v1evQyy5xbe" - }, - "source": [ - "Let us wrap this notebook by trying with one more classifier, but reducing the feature vector size to 1000." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 494 }, - "id": "XJLKusAQ5xbf", - "outputId": "4dcdc0d5-4f4f-487a-ac44-2bc6778a0876" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Accuracy: 0.6836836836836837\n", - "AUC: 0.7251117679464362\n" - ] + "cell_type": "code", + "execution_count": 16, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 668 + }, + "id": "XJLKusAQ5xbf", + "outputId": "cea4494e-a06d-41d0-c2a9-cdad88ff4a89" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Accuracy: 0.6926926926926927\n", + "AUC: 0.6742856032997147\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "
" + ], + "image/png": "iVBORw0KGgoAAAANSUhEUgAAArsAAAJnCAYAAACXn5vWAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABx6ElEQVR4nO3dd1yV5f/H8fc5TEUBB4IDxZUjtybulaPUTCtzlWCmWZaVlWXDWa6yTCu1cqWmZpr+SjNHbs2tpLlylyLiAhwgnPv3h19OHgEFPAjcvJ497kdy3dd93Z/7HI98+HDd120xDMMQAAAAYELWzA4AAAAAyCgkuwAAADAtkl0AAACYFskuAAAATItkFwAAAKZFsgsAAADTItkFAACAaZHsAgAAwLRIdgEAAGBaJLtADnH48GG1bNlSPj4+slgsWrRokVPHP378uCwWi6ZPn+7Ucc0gKChIoaGhmRrDkCFDZLFY0tQ3MjIyg6PKXiwWi4YMGWL/evr06bJYLDp+/Ph9jSOzzgtkVyS7wH105MgRvfDCCypVqpQ8PT3l7e2t+vXr6/PPP9e1a9cy9NwhISH6888/9dFHH2nmzJmqVatWhp7PjP766y8NGTLENEnGiBEjnP5DD5yH9wdwDothGEZmBwHkBEuWLFHHjh3l4eGh7t27q1KlSoqLi9OGDRu0YMEChYaG6uuvv86Qc1+7dk25c+fWe++9pw8//DBDzmEYhmJjY+Xm5iYXF5cMOUdm+/HHH9WxY0etXr1aTZo0SfVxsbGxslqtcnNzy7jg7iI+Pl7x8fHy9PS0t+XJk0dPPfVUkmr8kCFDNHToUJ07d04FCxa8z5FmXRaLRYMHD7ZXdxMSEnTjxg15eHikumqeFim9Pxl9XsBsXDM7ACAnOHbsmDp37qwSJUro999/V+HChe37+vbtq7///ltLlizJsPOfO3dOkuTr65th57BYLA6JVE5nGIauX7+uXLlyycPDI7PDkaurq1xdzfNPfnx8vGw2m9zd3TMtBhcXl0z5wS6zzgtkV0xjAO6DMWPGKCYmRlOmTHFIdBOVKVNGr776qv3r+Ph4DR8+XKVLl5aHh4eCgoL07rvvKjY21uG4oKAgtW3bVhs2bFDt2rXl6empUqVK6bvvvrP3GTJkiEqUKCFJeuutt2SxWBQUFCRJCg0Ntf/5VsnN71yxYoUaNGggX19f5cmTR+XKldO7775r35/SnN3ff/9dDRs2lJeXl3x9ffX4449r//79yZ7v77//VmhoqHx9feXj46MePXro6tWrKb+w/9OkSRNVqlRJYWFhaty4sXLnzq0yZcroxx9/lCStXbtWwcHBypUrl8qVK6eVK1c6HH/ixAm99NJLKleunHLlyqUCBQqoY8eODtMVpk+fro4dO0qSmjZtKovFIovFojVr1kj677347bffVKtWLeXKlUuTJ0+270ucs2sYhpo2bSo/Pz9FRETYx4+Li1PlypVVunRpXblyJdnrNAxDBQsWVP/+/e1tNptNvr6+cnFx0aVLl+zto0ePlqurq2JiYhxe40QWi0VXrlzRjBkz7Ndy+7ziS5cu3dP78ddff6lp06bKnTu3ihYtqjFjxiTpGxERoZ49e8rf31+enp6qWrWqZsyY4dAn8e/WJ598onHjxtk/F4nTSiwWiw4dOqRnnnlGPj4+8vPz0wcffCDDMHTq1Ck9/vjj8vb2VkBAgMaOHeswdlxcnAYNGqSaNWvKx8dHXl5eatiwoVavXn3X67x97mxiLMltt762n3zyierVq6cCBQooV65cqlmzpv3vaqI7vT8pzdn96quv9OCDD8rDw0NFihRR3759Hf5OpPW9AcyCZBe4D37++WeVKlVK9erVS1X/559/XoMGDVKNGjX02WefqXHjxho5cqQ6d+6cpO/ff/+tp556Si1atNDYsWOVL18+hYaGat++fZKkJ554Qp999pkkqUuXLpo5c6bGjRuXpvj37duntm3bKjY2VsOGDdPYsWPVrl07bdy48Y7HrVy5Uq1atVJERISGDBmi/v37a9OmTapfv36y816ffvppRUdHa+TIkXr66ac1ffp0DR06NFUxXrx4UW3btlVwcLDGjBkjDw8Pde7cWfPmzVPnzp3VunVrjRo1SleuXNFTTz2l6Oho+7Hbtm3Tpk2b1LlzZ40fP159+vTRqlWr1KRJE3ty16hRI/Xr10+S9O6772rmzJmaOXOmKlSoYB/n4MGD6tKli1q0aKHPP/9c1apVSxKnxWLR1KlTdf36dfXp08fePnjwYO3bt0/Tpk2Tl5dXstdosVhUv359rVu3zt4WFhamy5cvS5LD+7F+/XpVr15defLkSXasmTNnysPDQw0bNrRfywsvvODQ517fj0ceeURVq1bV2LFjVb58eb399tv69ddf7X2uXbumJk2aaObMmerWrZs+/vhj+fj4KDQ0VJ9//nmSMadNm6YJEyaod+/eGjt2rPLnz2/f16lTJ9lsNo0aNUrBwcH68MMPNW7cOLVo0UJFixbV6NGjVaZMGb355psOr19UVJS+/fZbNWnSRKNHj9aQIUN07tw5tWrVSrt3707VtSZ64okn7K9l4vbaa69JkgoVKmTv9/nnn6t69eoaNmyYRowYIVdXV3Xs2NHhtzupeX9uNWTIEPXt21dFihTR2LFj9eSTT2ry5Mlq2bKlbty44dA3Ne8NYCoGgAx1+fJlQ5Lx+OOPp6r/7t27DUnG888/79D+5ptvGpKM33//3d5WokQJQ5Kxbt06e1tERITh4eFhvPHGG/a2Y8eOGZKMjz/+2GHMkJAQo0SJEkliGDx4sHHrPw+fffaZIck4d+5cinEnnmPatGn2tmrVqhmFChUyzp8/b2/bs2ePYbVaje7duyc533PPPecwZocOHYwCBQqkeM5EjRs3NiQZ33//vb3twIEDhiTDarUaf/zxh739t99+SxLn1atXk4y5efNmQ5Lx3Xff2dvmz59vSDJWr16dpH/ie7Fs2bJk94WEhDi0TZ482ZBkzJo1y/jjjz8MFxcX47XXXrvrtX788ceGi4uLERUVZRiGYYwfP94oUaKEUbt2bePtt982DMMwEhISDF9fX+P111+3H3f7e2oYhuHl5ZUkrlv73uv7cetrFxsbawQEBBhPPvmkvW3cuHH21yBRXFycUbduXSNPnjz2a0z8u+Xt7W1EREQkG2vv3r3tbfHx8UaxYsUMi8VijBo1yt5+8eJFI1euXA7XHB8fb8TGxjqMefHiRcPf3z/J9UsyBg8ebP962rRphiTj2LFjyb4O586dM4oXL25UrlzZiImJsbff/vctLi7OqFSpktGsWTOH9pTen9vPGxERYbi7uxstW7Y0EhIS7P2++OILQ5IxdepUe1tq3xvATKjsAhksKipKkpQ3b95U9V+6dKkkOfyqWpLeeOMNSUoyt7dixYpq2LCh/Ws/Pz+VK1dOR48eTXfMt0uc67t48WLZbLZUHXPmzBnt3r1boaGhDhW4KlWqqEWLFvbrvNWtlU5Jatiwoc6fP29/De8kT548DpXvcuXKydfXVxUqVFBwcLC9PfHPt74+uXLlsv/5xo0bOn/+vMqUKSNfX1/t3LkzFVd7U8mSJdWqVatU9e3du7datWqlV155Rc8++6xKly6tESNG3PW4hg0bKiEhQZs2bZJ0s4LbsGFDNWzYUOvXr5ck7d27V5cuXXL4e5Ee9/p+PPPMM/av3d3dVbt2bYfXfenSpQoICFCXLl3sbW5uburXr59iYmK0du1ahzGffPJJ+fn5JXu+559/3v5nFxcX1apVS4ZhqGfPnvZ2X1/fJJ8NFxcX+7xfm82mCxcuKD4+XrVq1UrTe3+7hIQEdenSRdHR0frpp58cqvW3/n27ePGiLl++rIYNG6b7fCtXrlRcXJxee+01Wa3/fVvv1auXvL29k/ybkZr3BjATkl0gg3l7e0uSw6/N7+TEiROyWq0qU6aMQ3tAQIB8fX114sQJh/bixYsnGSNfvny6ePFiOiNOqlOnTqpfv76ef/55+fv7q3Pnzvrhhx/umPgmxlmuXLkk+ypUqKDIyMgkc1Nvv5Z8+fJJUqqupVixYknmGfv4+CgwMDBJ2+1jXrt2TYMGDVJgYKA8PDxUsGBB+fn56dKlS/YpAqlRsmTJVPeVpClTpujq1as6fPiwpk+f7pAEpaRGjRrKnTu3PbFNTHYbNWqk7du36/r16/Z9DRo0SFM8t3P2+3H738sTJ06obNmyDgmaJPvUkNv/rt/p9b09Vh8fH3l6eiZZTcLHxydJ/DNmzFCVKlXk6empAgUKyM/PT0uWLEnTe3+7999/X7///ru+//57lS5d2mHfL7/8ojp16sjT01P58+eXn5+fJk6cmO7zpfRZc3d3V6lSpZK8jql5bwAzIdkFMpi3t7eKFCmivXv3pum41C4plNJd2UYqVhVM6RwJCQkOX+fKlUvr1q3TypUr9eyzzyosLEydOnVSixYtkvS9F/dyLSkdm5oxX3nlFX300Ud6+umn9cMPP2j58uVasWKFChQokOpKtqRUJau3WrNmjf2mwz///DNVx7i5uSk4OFjr1q3T33//rfDwcDVs2FANGjTQjRs3tGXLFq1fv17ly5dPsQqaWhnxfqTm2JTc6fVN7nypiWHWrFkKDQ1V6dKlNWXKFC1btkwrVqxQs2bN0vTe32rRokUaPXq0hg0bpkceecRh3/r169WuXTt5enrqq6++0tKlS7VixQp17dr1nl6btMiI9wbIykh2gfugbdu2OnLkiDZv3nzXviVKlJDNZtPhw4cd2s+ePatLly7ZV1Zwhnz58iW5W1tKWlGTJKvVqocffliffvqp/vrrL3300Uf6/fffU7xrPTHOgwcPJtl34MABFSxYMMUbse63H3/8USEhIRo7dqz9Zr8GDRokeW2cuabpmTNn9Morr6hly5Zq27at3nzzzWRf9+Q0bNhQW7du1cqVK1WwYEGVL19e+fPn14MPPqj169dr/fr1atSo0V3Hyew1WkuUKKHDhw8nSSoPHDhg35/RfvzxR5UqVUoLFy7Us88+q1atWql58+a6fv16usY7dOiQQkJC1L59e4fVShItWLBAnp6e+u233/Tcc8/p0UcfVfPmzZMdK7XvT0qftbi4OB07duy+vI5AVkayC9wHAwYMkJeXl55//nmdPXs2yf4jR47Y7z5v3bq1JCVZMeHTTz+VJLVp08ZpcZUuXVqXL19WWFiYve3MmTP66aefHPpduHAhybGJKw3cvhxaosKFC6tatWqaMWOGQ9K4d+9eLV++3H6dWYGLi0uSqtaECROSVK0Tk/PkfkBIq169eslms2nKlCn6+uuv5erqqp49e6aqutawYUPFxsZq3LhxatCggT0pSrxz//Tp06mar+vl5eWUa0mv1q1bKzw8XPPmzbO3xcfHa8KECcqTJ48aN26c4TEkVjlvfd23bNmSqh9MbxcTE6MOHTqoaNGi9iXDkjufxWJx+Lt1/PjxZJ+Ultr3p3nz5nJ3d9f48eMdrmPKlCm6fPmyU//NALIj86wwDmRhpUuX1vfff69OnTqpQoUKDk9Q27Rpk+bPn29fQ7Nq1aoKCQnR119/rUuXLqlx48baunWrZsyYofbt26tp06ZOi6tz5856++231aFDB/Xr109Xr17VxIkT9cADDzjcLDNs2DCtW7dObdq0UYkSJRQREaGvvvpKxYoVu+O80I8//liPPvqo6tatq549e+ratWuaMGGCfHx87E+hygratm2rmTNnysfHRxUrVtTmzZu1cuVKFShQwKFftWrV5OLiotGjR+vy5cvy8PBQs2bNHJaVSo1p06ZpyZIlmj59uooVKybpZnL9zDPPaOLEiXrppZfueHzdunXl6uqqgwcPqnfv3vb2Ro0aaeLEiZKUqmS3Zs2aWrlypT799FMVKVJEJUuWdLiZL6P17t1bkydPVmhoqHbs2KGgoCD9+OOP2rhxo8aNG5fqmzrvRdu2bbVw4UJ16NBBbdq00bFjxzRp0iRVrFjRvkZxag0dOlR//fWX3n//fS1evNhhX+nSpVW3bl21adNGn376qR555BF17dpVERER+vLLL1WmTBmHHzql1L8/fn5+GjhwoIYOHapHHnlE7dq108GDB/XVV1/poYcecrgZDciJSHaB+6Rdu3YKCwvTxx9/rMWLF2vixIny8PBQlSpVNHbsWPXq1cve99tvv1WpUqU0ffp0/fTTTwoICNDAgQM1ePBgp8ZUoEAB/fTTT+rfv78GDBigkiVLauTIkTp8+LBDstuuXTsdP35cU6dOVWRkpAoWLKjGjRtr6NCh9hu+ktO8eXMtW7ZMgwcP1qBBg+Tm5qbGjRtr9OjRab6ZKyN9/vnncnFx0ezZs3X9+nXVr1/fvkbwrQICAjRp0iSNHDlSPXv2VEJCglavXp2mZPeff/7R66+/rscee0whISH29m7dumnBggUaMGCAHn300Tu+Pl5eXqpevbq2bdvm8MNGYoIbGBiYql9df/rpp+rdu7fef/99Xbt2TSEhIfc12c2VK5fWrFmjd955RzNmzFBUVJTKlSunadOmJXnARUYJDQ1VeHi4Jk+erN9++00VK1bUrFmzNH/+fPsDQ1Ir8UmFyT2SOyQkRHXr1lWzZs00ZcoUjRo1Sq+99ppKliyp0aNH6/jx40mS3bS8P0OGDJGfn5+++OILvf7668qfP7969+6tESNGZOpjqoGswGIwIx0AAAAmxZxdAAAAmBbJLgAAAEyLZBcAAACmRbILAAAA0yLZBQAAgGmR7AIAAMC0WGcXybLZbDp9+rTy5s2b6Y8UBQAgOzEMQ9HR0SpSpIis1syvK16/fl1xcXEZMra7u7s8PT0zZGxnIdlFsk6fPq3AwMDMDgMAgGzr1KlT9qckZpbr168rV94CUvzVDBk/ICBAx44dy9IJL8kukpX4mE73iiGyuLhncjQAbndyzSeZHQKAFERHRalMycD78sjru4mLi5Pir8qjYojk7O/nCXEK/2uG4uLiSHaR/SROXbC4uJPsAlmQt7d3ZocA4C6y1DRAV0+nfz83LJk/RSM1SHYBAADMziLJ2cl3Fsrl7yR7pOQAAABAOlDZBQAAMDuL9ebm7DGzgewRJQAAAJAOVHYBAADMzmLJgDm72WPSLpVdAAAAmBaVXQAAALNjzi4AAABgPlR2AQAAzC4Hz9kl2QUAADC9DJjGkE0mCGSPKAEAAIB0oLILAABgdjl4GgOVXQAAAJgWlV0AAACzY+kxAAAAwHyo7AIAAJgdc3YBAAAA86GyCwAAYHbM2QUAAADMh8ouAACA2eXgObskuwAAAGbHNAYAAADAfKjsAgAAmJ3FkgGV3ewxjYHKLgAAAEyLyi4AAIDZWS03N2ePmQ1Q2QUAAMB98+WXXyooKEienp4KDg7W1q1bU+zbpEkTWSyWJFubNm1SfT6SXQAAALNLXI3B2VsazZs3T/3799fgwYO1c+dOVa1aVa1atVJERESy/RcuXKgzZ87Yt71798rFxUUdO3ZM9TlJdgEAAHBffPrpp+rVq5d69OihihUratKkScqdO7emTp2abP/8+fMrICDAvq1YsUK5c+cm2QUAAMAtEh8q4exNUlRUlMMWGxubbAhxcXHasWOHmjdvbm+zWq1q3ry5Nm/enKrLmDJlijp37iwvL69UXzrJLgAAgNll4DSGwMBA+fj42LeRI0cmG0JkZKQSEhLk7+/v0O7v76/w8PC7XsLWrVu1d+9ePf/882m6dFZjAAAAQLqdOnVK3t7e9q89PDwy5DxTpkxR5cqVVbt27TQdR7ILAABgdrdMO3DqmJK8vb0dkt2UFCxYUC4uLjp79qxD+9mzZxUQEHDHY69cuaK5c+dq2LBhaQ6TaQwAAADIcO7u7qpZs6ZWrVplb7PZbFq1apXq1q17x2Pnz5+v2NhYPfPMM2k+L5VdAAAAs0vnUmF3HTON+vfvr5CQENWqVUu1a9fWuHHjdOXKFfXo0UOS1L17dxUtWjTJvN8pU6aoffv2KlCgQJrPSbILAACA+6JTp046d+6cBg0apPDwcFWrVk3Lli2z37R28uRJWa2OSfTBgwe1YcMGLV++PF3nJNkFAAAwuwycs5tWL7/8sl5++eVk961ZsyZJW7ly5WQYRrrOJTFnFwAAACZGZRcAAMDsssic3cyQPaIEAAAA0oHKLgAAgNlloTm79xvJLgAAgOllwDSGbDJBIHtECQAAAKQDlV0AAACzy8HTGKjsAgAAwLSo7AIAAJidxZIBS49R2QUAAAAyFZVdAAAAs+OhEgAAAID5UNkFAAAwuxy8GgPJLgAAgNkxjQEAAAAwHyq7AAAAZpeDpzFQ2QUAAIBpUdkFAAAwO+bsAgAAAOZDZRcAAMDsmLMLAAAAmA+VXQAAAJOzWCyy5NDKLskuAACAyeXkZJdpDAAAADAtKrsAAABmZ/nf5uwxswEquwAAADAtKrsAAAAmx5xdAAAAwISo7AIAAJgclV0AAADAhKjsAgAAmByVXQAAAMCEqOwCAACYXE6u7JLsAgAAmB0PlQAAAADMh8ouAACAyeXkaQxUdgEAAGBaVHYBAABMzmJRBlR2nTtcRqGyCwAAANOisgsAAGByFmXAnN1sUtqlsgsAAADTorILAABgcjl5NQaSXQAAALPjoRIAAACA+VDZBQAAMLsMmMZgZJNpDFR2AQAAYFpUdgEAAEwuI25Qc/5SZhmDyi4AAABMi8ouAACAyVHZBQAAAEyIyi4AAIDZsc4uAAAAYD5UdgEAAEwuJ8/ZJdkFAAAwuZyc7DKNAQAAAKZFZRcAAMDkqOwCAAAAJkRlFwAAwOSo7AIAAAAmRGUXAADA7HioBAAAAGA+VHYBAABMLifP2SXZBQAAMLmcnOwyjQEAAACmRWUXAADA5KjsAgAAACZEZRcAAMDsWHoMQHbzwtONdGDJUF384zOt++5N1XqwRIp9f/vmVV3b9UWSbeH4PvY+773QWrsXvq/ITWN1eu0YLZn0sh6qlPKYAFI26asvVa5MkHzzeKphvWBt27o1xb5Tv/1GDzdpqMJ++VTYL59at2qepH8uN0uy26djP87oSwGyPZJdIBt6qmUNjX6jgz6a/Kvqdh2tsEP/6v++6iu/fHmS7d/5jW8U1Hygfavx5IeKj0/QwhW77H3+PhGh10fPV62OI/Rwj0914vQF/fzVyyqYwpgAkjf/h3l6+63+eu/9wdq8daeqVKmqdm1aKSIiItn+69au0dOdumjZitVas36zihUL1GOtW+rff/+19zl26ozDNvmbqbJYLOrQ4cn7dVnI5hLn7Dp7yw4shmEYmR0Esp6oqCj5+PjIo3IvWVzcMzsc3Gbdd29qx74Ten30fEk3/xH7e9lwTZy7Vp9MW3HX41/u2kQfvNhGJVu8p6vX45Ltk9fLUxEbPtGjL4zXmq2HnBo/7t3FbV9kdghIQcN6wapZ6yGNG3/zPbLZbCpTMlAv9n1Fbw14567HJyQkqLBfPn32+Rfq9mz3ZPt0fLK9YqKj9evyVU6NHc4RFRUl/wI+unz5sry9vTM9Fh8fHxXtPUdW99xOHdsWd1X/ft0lS1znnVDZBbIZN1cXVa8QqN+3HLS3GYah37ccVO0qJVM1Rkj7epr/284UE103Vxf1fKK+LkVf1Z+H/k22D4Ck4uLitGvnDjV7uLm9zWq1qlmz5tr6x+ZUjXH16lXduHFD+fLnT3b/2bNntWzpEoX06OmUmJEz5OTKLjeoAdlMwXx55OrqoogL0Q7tEeejVC7I/67H13qwhCqVLaIXh85Osu/RhpX03ageyu3ppvDIKLXt84XOX7ritNgBs4uMjFRCQoIKFXL8LBby99fBgwdSNcb7A99W4SJFHBLmW82aOUN58+ZV+w5P3HO8yDksyoClx7LJHWpUdp1oyJAhqlatWmaHAdxRSPu6+vPQv9q+70SSfWu3HVJw55FqGvqplm/6S7PGPJfiPGAAzvfxmFGa/8NczZv/kzw9PZPt8930qerUpVuK+wE4ypLJbmhoqCwWi0aNGuXQvmjRojT/VBIUFKRx48alql9iST537tyqXLmyvv322zSdK6siCTeXyIsxio9PUKH8eR3aCxXwVvj5qDsem9vTXR1b1dSMRcn/OvXq9TgdPRWprX8e14tDv1d8gk0hHeo5LXbA7AoWLCgXFxdFRJx1aI84e1YBAQF3PPazTz/R2DGj9PPS5apcpUqyfTZsWK9DBw+qx3PPOy1m5Aw5eRpDlkx2JcnT01OjR4/WxYsX79s5hw0bpjNnzmjv3r165pln1KtXL/3666/37fxAatyIT9Cu/afUNLicvc1isahp7Qe0NezYHY99okV1ebi7as7Sbak6l9VikYcbs52A1HJ3d1f1GjW1+vf/bhyz2WxavXqVatepm+JxYz8Zo1EfDdfiX5apZq1aKfabMXWKatSoqSpVqzo1bsDMsmyy27x5cwUEBGjkyJF37LdgwQI9+OCD8vDwUFBQkMaOHWvf16RJE504cUKvv/56qn4CyZs3rwICAlSqVCm9/fbbyp8/v1as+O/O9kuXLun555+Xn5+fvL291axZM+3Zs+eOY3777beqUKGCPD09Vb58eX311Vf2ffXq1dPbb7/t0P/cuXNyc3PTunXrJEkzZ85UrVq17LF17drVYfmaNWvWyGKxaNWqVapVq5Zy586tevXq6eDBmzcvTZ8+XUOHDtWePXvsr8H06dPvGDOyvvGzflePDvXU7bFglSvpr/HvdlLuXB76bvEfkqRvhz+rYa+0S3JcaPu6+nlNmC5cdpyHm9vTXUNffky1KwepeOF8ql4hUJMGd1ORQr5auGLnfbkmwCz6vdZf06Z8o1nfzdCB/fvVr++LunrlirqH9JAk9Qztrg/eG2jv/8nHozVs8Aea9M1UlQgKUnh4uMLDwxUTE+MwblRUlBYumK9QqrpID0sGbdlAli3ZuLi4aMSIEeratav69eunYsWKJemzY8cOPf300xoyZIg6deqkTZs26aWXXlKBAgUUGhqqhQsXqmrVqurdu7d69eqV6nPbbDb99NNPunjxotzd/1t2q2PHjsqVK5d+/fVX+fj4aPLkyXr44Yd16NAh5U/mrtnZs2dr0KBB+uKLL1S9enXt2rVLvXr1kpeXl0JCQtStWzeNGTNGo0aNsifi8+bNU5EiRdSwYUNJ0o0bNzR8+HCVK1dOERER6t+/v0JDQ7V06VKHc7333nsaO3as/Pz81KdPHz333HPauHGjOnXqpL1792rZsmVauXKlJMnHxydJrLGxsYqNjbV/HRV151+HI3P9uHynCubLo0EvtpF/gbwKO/ivHu/7pf2mtcCA/LLZHFcVLFuikOrXKKM2fZIuWZVgs6lckL+eeSxYBXy9dOHyVW3fd0LNn/tM+4+G35drAsyi49OdFHnunIYNHaSz4eGqUrWaFv+yTP7+N29aO3XqpKzW/2pN30yeqLi4OHXt9JTDOO99MFjvDxpi/3r+vLkyDENPd+5yX64DMIssuc5uaGioLl26pEWLFqlu3bqqWLGipkyZokWLFqlDhw5KDLlbt246d+6cli9fbj92wIABWrJkifbt2yfp5lzc1157Ta+99todzxkUFKQzZ87Izc1NsbGxio+PV/78+bVlyxaVKVNGGzZsUJs2bRQRESEPDw/7cWXKlNGAAQPUu3dvDRkyRIsWLdLu3bvt+4YPH64uXf77h+nDDz/U0qVLtWnTJp07d05FihTR77//bk9u69Wrp0aNGiWZr5xo+/bteuihhxQdHa08efJozZo1atq0qVauXKmHH35YkrR06VK1adNG165dk6enZ5K4kjNkyBANHTo0STvr7AJZE+vsAllXVlxnt8RL82X1cPI6u7FXdeKrjlniOu8ky05jSDR69GjNmDFD+/fvT7Jv//79ql+/vkNb/fr1dfjwYSUkJKT5XG+99ZZ2796t33//XcHBwfrss89UpkwZSdKePXsUExOjAgUKKE+ePPbt2LFjOnLkSJKxrly5oiNHjqhnz54O/T/88EN7fz8/P7Vs2VKzZ99cAurYsWPavHmzunXrZh9nx44deuyxx1S8eHHlzZtXjRs3liSdPHnS4XxVbrmZoXDhwpKU4tN6kjNw4EBdvnzZvp06dSrVxwIAAGRVWXYaQ6JGjRqpVatWGjhwoEJDQzP0XAULFlSZMmVUpkwZzZ8/X5UrV1atWrVUsWJFxcTEqHDhwlqzZk2S43x9fZO0Jc61+uabbxQcHOywz8XFxf7nbt26qV+/fpowYYK+//57Va5cWZUrV5Z0M2Fu1aqVWrVqpdmzZ8vPz08nT55Uq1atFBfn+DAANzc3+58Tp0TYbLZUX7uHh4dDxRoAAJhHRqyekF1WY8jyya4kjRo1StWqVVO5cuUc2itUqKCNGzc6tG3cuFEPPPCAPaF0d3dPV5U3MDBQnTp10sCBA7V48WLVqFFD4eHhcnV1VVBQ0F2P9/f3V5EiRXT06FGHSu3tHn/8cfXu3VvLli3T999/r+7d/3s05IEDB3T+/HmNGjVKgYGBkm5OY0ir9L4GAAAA2V22SHYrV66sbt26afz48Q7tb7zxhh566CENHz5cnTp10ubNm/XFF184rHgQFBSkdevWqXPnzvLw8FDBggVTfd5XX31VlSpV0vbt29W8eXPVrVtX7du315gxY/TAAw/o9OnTWrJkiTp06KBaySwVM3ToUPXr108+Pj565JFHFBsbq+3bt+vixYvq37+/JMnLy0vt27fXBx98oP379zvM7y1evLjc3d01YcIE9enTR3v37tXw4cPT+vIpKChIx44d0+7du1WsWDHlzZuXKi4AADmIxXJzc/aY2UGWn7ObaNiwYUl+LV+jRg398MMPmjt3ripVqqRBgwZp2LBhDtMdhg0bpuPHj6t06dLy8/NL0zkrVqyoli1batCgQbJYLFq6dKkaNWqkHj166IEHHlDnzp114sQJ+x22t3v++ef17bffatq0aapcubIaN26s6dOnq2TJkg79unXrpj179qhhw4YqXry4vd3Pz0/Tp0/X/PnzVbFiRY0aNUqffPJJmq5Bkp588kk98sgjatq0qfz8/DRnzpw0jwEAALKvm8musx8qkdlXlTpZcjUGZL7EuzdZjQHImliNAci6suJqDKVe+VFWDy+njm2LvaKjE57KEtd5J9liGgMAAADuQQZMY8guD5XINtMYAAAAgLSisgsAAGByOXnpMSq7AAAAMC0quwAAACbH0mMAAADAffDll18qKChInp6eCg4O1tatW+/Y/9KlS+rbt68KFy4sDw8PPfDAA1q6dGmqz0dlFwAAwOSsVousVueWYo10jDdv3jz1799fkyZNUnBwsMaNG6dWrVrp4MGDKlSoUJL+cXFxatGihQoVKqQff/xRRYsW1YkTJ+Tr65vqc5LsAgAAmFxWmcbw6aefqlevXurRo4ckadKkSVqyZImmTp2qd955J0n/qVOn6sKFC9q0aZPc3Nwk3XwybFowjQEAAADpFhUV5bDFxsYm2y8uLk47duxQ8+bN7W1Wq1XNmzfX5s2bkz3m//7v/1S3bl317dtX/v7+qlSpkkaMGKGEhIRUx0eyCwAAYHLOf1Twf0uZBQYGysfHx76NHDky2RgiIyOVkJAgf39/h3Z/f3+Fh4cne8zRo0f1448/KiEhQUuXLtUHH3ygsWPH6sMPP0z1tTONAQAAAOl26tQph8cFe3h4OG1sm82mQoUK6euvv5aLi4tq1qypf//9Vx9//LEGDx6cqjFIdgEAAEwuI+fsent7OyS7KSlYsKBcXFx09uxZh/azZ88qICAg2WMKFy4sNzc3ubi42NsqVKig8PBwxcXFyd3d/a7nZRoDAAAAMpy7u7tq1qypVatW2dtsNptWrVqlunXrJntM/fr19ffff8tms9nbDh06pMKFC6cq0ZVIdgEAAEwvI+fspkX//v31zTffaMaMGdq/f79efPFFXblyxb46Q/fu3TVw4EB7/xdffFEXLlzQq6++qkOHDmnJkiUaMWKE+vbtm+pzMo0BAAAA90WnTp107tw5DRo0SOHh4apWrZqWLVtmv2nt5MmTslr/q8UGBgbqt99+0+uvv64qVaqoaNGievXVV/X222+n+pwkuwAAACaX3krs3cZMj5dfflkvv/xysvvWrFmTpK1u3br6448/0nUuiWkMAAAAMDEquwAAACaXVZ6glhlIdgEAAEzOogyYxqDske0yjQEAAACmRWUXAADA5HLyNAYquwAAADAtKrsAAAAml5WWHrvfqOwCAADAtKjsAgAAmBxzdgEAAAATorILAABgcjl5zi7JLgAAgMkxjQEAAAAwISq7AAAAJpeTpzFQ2QUAAIBpUdkFAAAwuwyYs6vsUdilsgsAAADzorILAABgcszZBQAAAEyIyi4AAIDJ5eR1dkl2AQAATI5pDAAAAIAJUdkFAAAwuZw8jYHKLgAAAEyLyi4AAIDJMWcXAAAAMCEquwAAACZHZRcAAAAwISq7AAAAJsdqDAAAAIAJUdkFAAAwuZw8Z5dkFwAAwOSYxgAAAACYEJVdAAAAk8vJ0xio7AIAAMC0qOwCAACYnEUZMGfXucNlGCq7AAAAMC0quwAAACZntVhkdXJp19njZRQquwAAADAtKrsAAAAml5PX2SXZBQAAMDmWHgMAAABMiMouAACAyVktNzdnj5kdUNkFAACAaVHZBQAAMDtLBsyxzSaV3VQlu88991y6T2CxWDRlypR0Hw8AAACkV6qS3enTp6f7BCS7AAAAmYulx+5i9erVGR0HAAAA4HSpSnYbN26c0XEAAAAgg1j+95+zx8wOWI0BAAAApnVPqzHEx8dryZIl2rp1qyIjIxUcHGy/me306dOKjIxUxYoV5erKog8AAACZJSevs5vuLHTDhg165plndOrUKRmGIYvFohs3btiT3c2bN+vpp5/W/Pnz9cQTTzgtYAAAAKQNjwtOo7/++kuPPPKIzpw5o1deeUU//PCDDMNw6PPYY48pd+7cWrBggVMCBQAAANIqXZXd4cOH6/r161q6dKlatmyZbB93d3fVqFFDu3btuqcAAQAAcG9y8tJj6arsrl69WrVr104x0U1UtGhRnT59Ol2BAQAAAPcqXZXdS5cuKTAw8K79rly5ohs3bqTnFAAAAHASq8Uiq5NLsc4eL6Okq7JbqFAh/f3333ftt3///lQlxQAAAEBGSFey26xZM+3evfuOT1b76aef9Pfff6tFixbpDg4AAAD3LnHOrrO37CBdye4777wjd3d3tW/fXhMnTlR4eLh938WLFzV16lT17NlTXl5e6t+/v9OCBQAAANIiXclu+fLlNWfOHNlsNr388ssqWrSoLBaLZsyYoYIFC6pXr16KjY3V7NmzVbJkSWfHDAAAgDRIXGfX2Vt2kO7HBbdv31579+7VK6+8ovLly8vT01Pu7u4qVaqUXnjhBYWFhaldu3bOjBUAAADpkJOnMdzTc3xLlCihcePGOSkUAAAAwLnuKdkFAABA1peTlx67p2Q3NjZWCxYs0Pr16+0PjyhSpIgaNGigJ598Up6enk4JEgAAAEiPdCe7K1euVGhoqM6cOSPDMBz2ff311xowYICmT5/O0mMAAACZzPK/zdljZgfpSna3bNmitm3bKi4uTsHBwerSpYuCgoIkSSdOnNCcOXP0xx9/6LHHHtPatWsVHBzszJgBAACAVElXsvvBBx/oxo0bmjhxol544YUk+1955RV9/fXX6tOnjwYNGqTffvvtngMFAABA+mTEUmGmXnpsy5YtqlWrVrKJbqLevXvroYce0h9//JHu4AAAAIB7ka5k12q1qkyZMnftV6ZMmWyT9QMAAJiV1ZIxW3aQrmkMtWvXVlhY2F37hYWFqXbt2uk5BQAAAJyEaQxpNHz4cB0+fFiDBw+WzWZLst8wDA0ePFiHDx/W8OHD7zlIAAAAID1SVdn97rvvkrSFhIToww8/1MyZM/Xkk0+qRIkSkm6uxrBw4UIdP35cvXr10sGDB1mNAQAAIJNlk0Ks01mM2xfJTYbVak22VH3roYn7bx/OYrEoISHhXuPEfRYVFSUfHx95VO4li4t7ZocD4DYXt32R2SEASEFUVJT8C/jo8uXL8vb2zvRYfHx89PTXG+SeO49Tx467GqMfejfIEtd5J6mq7A4aNCjbzMsAAACAo5w8ZzdVye6QIUMyOAwAAADA+dL9uGAAAABkDxmxVFh2WXosXasxAAAAANnBPVV2N2zYoMWLF+vw4cOKjo5OcnOadHM+x6pVq+7lNAAAALgHzNlNI8Mw1LNnT82YMcOe4FosliSrMxiGkW1eCAAAAJhPuqYxTJo0SdOnT1fNmjW1YsUKPfHEE5KkgwcP6tdff1VoaKisVqveeustHT161KkBAwAAIG0sGbRlB+mq7E6fPl1eXl769ddfVaBAAc2aNUuSVLZsWZUtW1atWrVS69at1alTJ9WrV8/+wAkAAADcf1aLRVYn/7bd2eNllHRVdvfv36969eqpQIECkv6bs3HrwyOeeuop1axZU5988okTwgQAAADSLl3Jrs1msye6kpQ7d25J0sWLFx36lS1bVn/++ec9hAcAAIB7ZbFkzJYdpCvZLVq0qE6fPm3/OnGawq5duxz6HTp0SK6uLOULAACAzJGuZLdGjRr666+/7NMWWrZsKcMwNGDAAB04cEDR0dH6+OOPtWPHDlWvXt2pAQMAACBtEpcec/aWHaQr2W3Xrp0iIyO1ZMkSSVLVqlXVuXNn7dmzRw8++KB8fX31zjvvyNXVVR999JFTAwYAAABSK11zDLp06aInnnjCYYrCjBkzVKVKFS1atEgXL17UAw88oAEDBqh27dpOCxYAAABplxFzbLNJYTf9T1Dz8PBw+NrNzU3vvPOO3nnnnXsOCgAAAHAG7h4DAAAwOdbZBQAAgGllpaXHvvzySwUFBcnT01PBwcHaunVrin2nT5+e5KY4T0/PNJ0vVZXdUqVKpWnQW1ksFh05ciTdxwMAAMAc5s2bp/79+2vSpEkKDg7WuHHj1KpVKx08eFCFChVK9hhvb28dPHjQ/nVaV4FIVbJ7/PjxNA0KAACArCMjlgpLz3iffvqpevXqpR49ekiSJk2apCVLlmjq1Kkp3vdlsVgUEBCQ7jhTlezabLZ0nwDZW+g7veSeO09mhwHgNv0X/5XZIQBIQdzVmMwO4b6Kiopy+NrDwyPJQgaSFBcXpx07dmjgwIH2NqvVqubNm2vz5s0pjh8TE6MSJUrIZrOpRo0aGjFihB588MFUx8ecXQAAAJOzZtAmSYGBgfLx8bFvI0eOTDaGyMhIJSQkyN/f36Hd399f4eHhyR5Trlw5TZ06VYsXL9asWbNks9lUr149/fPPP6m+dlZjAAAAQLqdOnVK3t7e9q+Tq+qmV926dVW3bl371/Xq1VOFChU0efJkDR8+PFVjkOwCAACYXEbO2fX29nZIdlNSsGBBubi46OzZsw7tZ8+eTfWcXDc3N1WvXl1///13quNkGgMAAAAynLu7u2rWrKlVq1bZ22w2m1atWuVQvb2ThIQE/fnnnypcuHCqz0tlFwAAwOQsFsmaBR4X3L9/f4WEhKhWrVqqXbu2xo0bpytXrthXZ+jevbuKFi1qn/c7bNgw1alTR2XKlNGlS5f08ccf68SJE3r++edTfU6SXQAAANwXnTp10rlz5zRo0CCFh4erWrVqWrZsmf2mtZMnT8pq/W/iwcWLF9WrVy+Fh4crX758qlmzpjZt2qSKFSum+pwWwzAMp18Jsr2oqCj5+PjohdlbWXoMyILiE/inG8iq4q7GaEr3YF2+fDlVc1kzUuL385fmbJOHk7+fx16N0VddHsoS13knVHYBAABMLqs8VCIzOCXZPXz4sCIjI1WgQAE98MADzhgSAAAAuGfpXo0hNjZW7777rgoWLKjy5curQYMGGjVqlH3/rFmzVKNGDe3evdsZcQIAACCdrJaM2bKDdCW7165dU5MmTTR69Gi5u7urdevWun3qb7NmzbRnzx798MMPTgkUAAAASKt0JbtjxozRli1b9Nxzz+no0aP6+eefk/QpUqSIKlasqJUrV95zkAAAAEg/iyVjtuwgXcnuvHnzVLx4cU2cOFGenp4p9itXrpxOnTqV7uAAAACAe5GuG9SOHTumNm3ayNX1zoe7u7vr4sWL6QoMAAAAzmG1WGR1cinW2eNllHRVdnPlypWqJPbYsWPKly9fek4BAAAA3LN0JbvVqlXT9u3bde7cuRT7HDt2TLt27dJDDz2U7uAAAABw76wZtGUH6YqzV69eio6OVpcuXRQZGZlk/6VLl/Tcc8/pxo0b6t279z0HCQAAgPTLyTeopWvObpcuXfTzzz9r7ty5KlWqlOrVqydJ2rhxox5//HGtXbtWUVFR6t69u9q2bevUgAEAAIDUSncFevbs2Ro9erQ8PT21fPlySTefpPbzzz/LYrHoo48+0rRp05wWKAAAANLHKov9JjWnbcoepd10Py7YYrHorbfeUv/+/bVz504dP35cNptNxYoV00MPPSR3d3dnxgkAAACkWbqT3UQuLi566KGHuBENAAAgi8qIObbZZc5udrmRDgAAAEizdFV2n3vuuVT3tVgsmjJlSnpOAwAAACewWm5uzh4zO0hXsjt9+vS79rFYLDIMg2QXAAAAmSZdye7q1auTbbfZbDp16pSWL1+uuXPn6vXXX9djjz12TwECAADg3lgszn+8b3aZs5uuZLdx48Z33N+9e3e1adNGISEhateuXboCAwAAgHNwg1oG6NKlix588EENGTIko04BAAAA3FGGrsZQtmxZbd++PSNPAQAAgLtIvEHN2Vt2kGHJrs1mU1hYmKxWVjcDAABA5nB6Jnr16lXt3r1bXbp00eHDh+86vxcAAAAZy5JB/2UH6bpBzcXF5a59DMOQn5+fPv744/ScAgAAALhn6Up2AwMDZUnhFjx3d3cVLlxYjRs3Vt++fVWoUKF7ChAAAAD3hodKpNHx48edHAYAAADgfOlKdv/v//5Pbm5uevTRR50dDwAAAJwsJ1d203WDWocOHTR+/HhnxwIAAAA4Vboqu35+fsqXL5+zYwEAAEAGsFgsKd5vdS9jZgfpSnabNGmirVu3yjCMbHOhAAAAORXTGNJo+PDhioyM1Ouvv67r1687OyYAAADAKdJV2Z0zZ45at26tCRMmaO7cuWrevLmKFy8uT0/PJH0tFos++OCDew4UAAAA6WOx3NycPWZ2kKpkt1SpUurYsaNGjx4tSRoyZIgsFosMw1BERIS+//77FI8l2QUAAEBmSVWye/z4cZ07d87+9bRp0zIsIAAAADiX1WKR1cmlWGePl1HSNY0hJCTE2XEAAAAATpeuZBcAAADZB6sxAAAAACaU6sru7t27NWzYsHSdZNCgQek6DgAAAE6QAasxKJtUdlOd7O7Zs0d79uxJ0+CJD50g2QUAAMg8VllkdXJ26uzxMkqqk93SpUurfv36GRkLAAAA4FSpTnYbNGigqVOnZmQsAAAAyAA5+aES3KAGAAAA02LpMQAAAJNj6TEAAADAhKjsAgAAmByPC74Lm82W0XEAAAAATkdlFwAAwORYjQEAAAAwISq7AAAAJmdVBszZNdsT1AAAAJA9MY0BAAAAMCEquwAAACZnlfMrnNmlYppd4gQAAADSjMouAACAyVksFlmcPMnW2eNlFCq7AAAAMC0quwAAACZn+d/m7DGzAyq7AAAAMC0quwAAACZntWTAQyWyyZxdkl0AAIAcIHukps7HNAYAAACYFpVdAAAAk+NxwQAAAIAJUdkFAAAwOR4qAQAAAJgQlV0AAACTs8r5Fc7sUjHNLnECAAAAaUZlFwAAwORy8pxdkl0AAACTs8j5D5XIHqku0xgAAABgYlR2AQAATC4nT2OgsgsAAADTorILAABgciw9BgAAAJgQlV0AAACTY84uAAAAYEJUdgEAAEyOdXYBAAAAE6KyCwAAYHIWy83N2WNmByS7AAAAJmeVRVYnTzxw9ngZhWkMAAAAMC0quwAAACaXk6cxUNkFAACAaVHZBQAAMDnL//5z9pjZAZVdAAAAmBaVXQAAAJNjzi4AAABgQlR2AQAATM6SAevsZpc5uyS7AAAAJsc0BgAAAMCESHYBAABMLrGy6+wtPb788ksFBQXJ09NTwcHB2rp1a6qOmzt3riwWi9q3b5+m85HsAgAA4L6YN2+e+vfvr8GDB2vnzp2qWrWqWrVqpYiIiDsed/z4cb355ptq2LBhms9JsgsAAGBylgz6L60+/fRT9erVSz169FDFihU1adIk5c6dW1OnTk3xmISEBHXr1k1Dhw5VqVKl0nxOkl0AAACkW1RUlMMWGxubbL+4uDjt2LFDzZs3t7dZrVY1b95cmzdvTnH8YcOGqVChQurZs2e64iPZBQAAMDmrJWM2SQoMDJSPj499GzlyZLIxREZGKiEhQf7+/g7t/v7+Cg8PT/aYDRs2aMqUKfrmm2/Sfe0sPQYAAIB0O3XqlLy9ve1fe3h4OGXc6OhoPfvss/rmm29UsGDBdI9DsgsAAGBy6Z1je7cxJcnb29sh2U1JwYIF5eLiorNnzzq0nz17VgEBAUn6HzlyRMePH9djjz1mb7PZbJIkV1dXHTx4UKVLl77reZnGAAAAgAzn7u6umjVratWqVfY2m82mVatWqW7dukn6ly9fXn/++ad2795t39q1a6emTZtq9+7dCgwMTNV5qewCAACYXFZ5glr//v0VEhKiWrVqqXbt2ho3bpyuXLmiHj16SJK6d++uokWLauTIkfL09FSlSpUcjvf19ZWkJO13QrILAABgchYpA6YxpF2nTp107tw5DRo0SOHh4apWrZqWLVtmv2nt5MmTslqdO/GAZBcAAAD3zcsvv6yXX3452X1r1qy547HTp09P8/lIdoFsqmHJfGpWNr+8PVz17+VY/RgWrpOXrifbt3ZxHz1To4hD240Em974+aBDW+vyBVU3KJ9yuVl17Pw1/bDnjM5duZFh1wCYVaNS+dTigQLy9nTVP5dj9cPuMzpxMfnPZ50SPupeq6hD240Em15ddMChrW1FP9UP8lUudxcdPX9Vc3aF61xMXIZdA8zl1qXCnDlmdkCyC2RD1YvmVYdKhTRvT7hOXLymxqXz66V6xfXhyiOKiUtI9phrNxL04cojKY7ZvGwBNSqdX7N3nNb5qzfUpoKfXqxXXCNWHVW8zcioSwFMp2Yxbz1ZxV9zdp3R8QvX1KxsAb3SoISGLP9bMbEpfz6H/va3/evbP3EtHiigJqXz67vt/+r8lRtq+2AhvdKguIYtP8LnE7gLVmMAsqGmpQto04lL2nLyssKj4/TD7nDFJdhUp4RviscYkqJjExy2WzUunV/LD0bqz/AYnY6K1cwdp+Xj6aoqhfNm7MUAJtOsbAFtPH5Jf5y4+fmcs/OM4hJsqnenz6chRcUm2LfbP5/NyuTXsgORCjsTo3+jYjVj27/y8XRV1SJ8PpE6WeVxwZmByi6QzbhYpEBfT604HGlvMyQdPHdFJfPnSvE4DxerhrQsI4ukfy5f189/RSg8+uavQAvkdpOPp6sOnrti73893qYTF68pKH8u7fw3KqMuBzAVF4tU3NdTyw86fj4PRFxRyQK5JZ1P9jgPV6uGP1JGVotFJy9d1//tjdCZ6JuPXC3g5SafXG46EBFj73893qbjF66pVIFc2vEPn0/gTqjsOsmaNWtksVh06dKlzA4FJufl4SoXq0XR1x0rP9GxCcrrkfzPrxHRcfp+1xl9s+WUZu44LYtFer1RkHw9b/b3/t//kxvTO4UxASSV53+fz6jr8Q7t0dfj7Z+z252NjtOsHac1efMpTd/2r6wW6c2mQfLNdbO/z/8+g1G3VXujYuP5fCLVEpcec/aWHZDs/k9oaKgsFossFovc3NxUsmRJDRgwQNevJ39DQXZBEg5JOn7xmraduqx/L8fq7/NX9e2WfxQTm6B6JfNldmhAjnfswjVtOXlZ/1yO1eHIq5q8+ZSiYxPUgM8n4BT8SHiLRx55RNOmTdONGze0Y8cOhYSEyGKxaPTo0ZkdGmB3JTZeCTZDeT1dHNrzergoOjY+haMc2YybUxn8vNwkyV6Fyuvpoqhbxsjr4aJ/Lsc6KXLA/GL+9/m8vYqb19M1SbU3JTZD+ufSdfnlcZckXf7fZ9Lbw8VhDG8PV/1zOXsXZHD/WJS+dXHvNmZ2QGX3Fh4eHgoICFBgYKDat2+v5s2ba8WKFZJuPs5u5MiRKlmypHLlyqWqVavqxx9/vON4GzZsUMOGDZUrVy4FBgaqX79+unLl5pzId999V8HBwUmOqVq1qoYNGyZJ2rZtm1q0aKGCBQvKx8dHjRs31s6dOx36WywWffvtt+rQoYNy586tsmXL6v/+7/8kScePH1fTpk0lSfny5ZPFYlFoaOg9vUbIfAmGdOrSdT3g52Vvs0gq5+elYxeupWoMi6Qi3h72b5znr97Q5evxDmN6ulpVIl8uHU/lmABufj5PXrqucsl9Ps9fTdUYFklFfG75fF65ocvXbqhcIcfPZ1D+XDp6ns8nUscqi6wWJ2/ZJN0l2U3B3r17tWnTJrm73/zJeuTIkfruu+80adIk7du3T6+//rqeeeYZrV27Ntnjjxw5okceeURPPvmkwsLCNG/ePG3YsMG+iHK3bt20detWHTny31JQ+/btU1hYmLp27SpJio6OVkhIiDZs2KA//vhDZcuWVevWrRUdHe1wrqFDh+rpp59WWFiYWrdurW7duunChQsKDAzUggULJEkHDx7UmTNn9Pnnnycbb2xsrKKiohw2ZF2rj5xXvRK+qh3oI/887nq6aoDcXazacvKSJOmZGoX1WEU/e/9HyhVUeT8vFcjtpmI+nupeq4jy5XbT5hOX7H3WHrmgVg8UVKWAPCrs7aFnahbR5evxCjsTLQCp9/vh86pf0lfBxX0UkNddnasXloer1f55C6lVRI8/WMje/9HyBVWhkJcKeLkp0NdTobWLKn9uN208dvG/Mf++oEfL+6ly4Twq4u2hkIdufj73nObzCdwN0xhu8csvvyhPnjyKj49XbGysrFarvvjiC8XGxmrEiBFauXKl6tatK0kqVaqUNmzYoMmTJ6tx48ZJxho5cqS6deum1157TZJUtmxZjR8/Xo0bN9bEiRP14IMPqmrVqvr+++/1wQcfSJJmz56t4OBglSlTRpLUrFkzhzG//vpr+fr6au3atWrbtq29PTQ0VF26dJEkjRgxQuPHj9fWrVv1yCOPKH/+/JKkQoUK2Z8nnZyRI0dq6NCh6XvhcN/t+jdaedwj1LqCn7z/N9Vg4uaT9uWK8uV2c1inM5ebVZ2rF5a3h4uu3rDp1KXrGrfuuH01Bklaefi83F0s6lytsHK5WXX0/DVN3HSKNTyBNNrxT5TyeLiobUU/+0Mlvtjg+Pm89WOV291FXWsUlren683P58Vr+mS14+dzxaHz8nC1qmuNIsrtZtWR81f1xYaTfD6Rajl5GgPJ7i2aNm2qiRMn6sqVK/rss8/k6uqqJ598Uvv27dPVq1fVokULh/5xcXGqXr16smPt2bNHYWFhmj17tr3NMAzZbDYdO3ZMFSpUULdu3TR16lR98MEHMgxDc+bMUf/+/e39z549q/fff19r1qxRRESEEhISdPXqVZ08edLhXFWqVLH/2cvLS97e3oqIiEjTtQ8cONDh3FFRUQoMDEzTGLi/1h+7qPW3VH5uNWGD49+Rn/ZG6Ke9d/87sfRApJYeiLxrPwB3tvbIRa09kvznc9y6Ew5fLwg7qwVhZ+865i9/ndMvf51zSnxATkKyewsvLy97VXXq1KmqWrWqpkyZokqVKkmSlixZoqJFHR/p6OHhkexYMTExeuGFF9SvX78k+4oXLy5J6tKli95++23t3LlT165d06lTp9SpUyd7v5CQEJ0/f16ff/65SpQoIQ8PD9WtW1dxcY6Ph3Rzc3P42mKxyGazpenaPTw8UrwWAACQzeXg0i7JbgqsVqveffdd9e/fX4cOHZKHh4dOnjyZ7JSF5NSoUUN//fWXPXlOTrFixdS4cWPNnj1b165dU4sWLVSo0H/zuDZu3KivvvpKrVu3liSdOnVKkZFpq7olzjlOSEj+EZUAAABmxg1qd9CxY0e5uLho8uTJevPNN/X6669rxowZOnLkiHbu3KkJEyZoxowZyR779ttva9OmTXr55Ze1e/duHT58WIsXL7bfoJaoW7dumjt3rubPn69u3bo57Ctbtqxmzpyp/fv3a8uWLerWrZty5Ur5CVnJKVGihCwWi3755RedO3dOMTExdz8IAACYSk5+XDDJ7h24urrq5Zdf1pgxYzRw4EB98MEHGjlypCpUqKBHHnlES5YsUcmSJZM9tkqVKlq7dq0OHTqkhg0bqnr16ho0aJCKFCni0O+pp57S+fPndfXqVbVv395h35QpU3Tx4kXVqFFDzz77rPr16+dQ+U2NokWLaujQoXrnnXfk7++fJNkGAAAwM4thGNzKiSSioqLk4+OjF2ZvlXvuPJkdDoDbxCfwTzeQVcVdjdGU7sG6fPmyvL29MzWWxO/nq3afVJ68zo0lJjpKD1crniWu806YswsAAGByOfj+NKYxAAAAwLyo7AIAAJhdDi7tUtkFAACAaVHZBQAAMLmMWCqMpccAAACATEZlFwAAwOQslpubs8fMDqjsAgAAwLSo7AIAAJhcDl6MgcouAAAAzIvKLgAAgNnl4NIuyS4AAIDJsfQYAAAAYEJUdgEAAEyOpccAAAAAE6KyCwAAYHI5+P40KrsAAAAwLyq7AAAAZpeDS7tUdgEAAGBaVHYBAABMLievs0uyCwAAYHIsPQYAAACYEJVdAAAAk8vB96dR2QUAAIB5UdkFAAAwuxxc2qWyCwAAANOisgsAAGByOXnpMSq7AAAAMC0quwAAACbHOrsAAACACVHZBQAAMLkcvBgDyS4AAIDp5eBsl2kMAAAAMC0quwAAACbH0mMAAACACVHZBQAAMDmWHgMAAABMiMouAACAyeXgxRio7AIAAMC8qOwCAACYXQ4u7ZLsAgAAmBxLjwEAAAAmRGUXAADA7DJg6bFsUtilsgsAAADzorILAABgcjn4/jQquwAAADAvKrsAAABml4NLu1R2AQAAYFpUdgEAAEwuJ6+zS7ILAABgcpYMWHrM6UuZZRCmMQAAAMC0qOwCAACYXA6+P43KLgAAAMyLyi4AAIDZ5eDSLpVdAAAAmBaVXQAAAJPLyUuPUdkFAACAaVHZBQAAMDmLMmCdXecOl2Go7AIAAMC0qOwCAACYXA5ejIFkFwAAwOx4XDAAAABgQlR2AQAATC/nTmSgsgsAAADTorILAABgcszZBQAAAEyIyi4AAIDJ5dwZu1R2AQAAYGJUdgEAAEwuJ8/ZJdkFAAAwOcv//nP2mNkB0xgAAABgWlR2AQAAzC4H36FGZRcAAACmRbILAABgcpYM2tLjyy+/VFBQkDw9PRUcHKytW7em2HfhwoWqVauWfH195eXlpWrVqmnmzJlpOh/JLgAAAO6LefPmqX///ho8eLB27typqlWrqlWrVoqIiEi2f/78+fXee+9p8+bNCgsLU48ePdSjRw/99ttvqT4nyS4AAIDJJS495uwtrT799FP16tVLPXr0UMWKFTVp0iTlzp1bU6dOTbZ/kyZN1KFDB1WoUEGlS5fWq6++qipVqmjDhg2pPifJLgAAANItKirKYYuNjU22X1xcnHbs2KHmzZvb26xWq5o3b67Nmzff9TyGYWjVqlU6ePCgGjVqlOr4SHYBAABMzpJB/0lSYGCgfHx87NvIkSOTjSEyMlIJCQny9/d3aPf391d4eHiKsV++fFl58uSRu7u72rRpowkTJqhFixapvnaWHgMAAEC6nTp1St7e3vavPTw8nDp+3rx5tXv3bsXExGjVqlXq37+/SpUqpSZNmqTqeJJdAAAAs8vAdXa9vb0dkt2UFCxYUC4uLjp79qxD+9mzZxUQEJDicVarVWXKlJEkVatWTfv379fIkSNTnewyjQEAAMDkssLSY+7u7qpZs6ZWrVplb7PZbFq1apXq1q2b6nFsNluK84KTQ2UXAAAA90X//v0VEhKiWrVqqXbt2ho3bpyuXLmiHj16SJK6d++uokWL2uf9jhw5UrVq1VLp0qUVGxurpUuXaubMmZo4cWKqz0myCwAAYHLpXSrsbmOmVadOnXTu3DkNGjRI4eHhqlatmpYtW2a/ae3kyZOyWv+beHDlyhW99NJL+ueff5QrVy6VL19es2bNUqdOnVIfp2EYRtpDhdlFRUXJx8dHL8zeKvfceTI7HAC3iU/gn24gq4q7GqMp3YN1+fLlVM1lzUiJ38+PnT6vvE6OJToqSiWLFMgS13knVHYBAABM77+lwpw5ZnbADWoAAAAwLSq7AAAAJpdV5uxmBiq7AAAAMC2SXQAAAJgW0xgAAABMjmkMAAAAgAlR2QUAADA5SwYsPeb8pcwyBpVdAAAAmBaVXQAAAJNjzi4AAABgQlR2AQAATM4i5z/cN5sUdqnsAgAAwLyo7AIAAJhdDi7tkuwCAACYHEuPAQAAACZEZRcAAMDkWHoMAAAAMCEquwAAACaXg+9Po7ILAAAA86KyCwAAYHY5uLRLZRcAAACmRWUXAADA5FhnFwAAADAhKrtIlmEYkqS4qzGZHAmA5MQnGJkdAoAUxF27+b0z8XtpVhAdHeX0dXGjo6OcO2AGIdlFsqKjoyVJ03o1y+RIAADInqKjo+Xj45OpMbi7uysgIEBlSwZmyPgBAQFyd3fPkLGdxWJkpR87kGXYbDadPn1aefPmlSW7PCIFKYqKilJgYKBOnTolb2/vzA4HwC34fJqPYRiKjo5WkSJFZLVm/ozR69evKy4uLkPGdnd3l6enZ4aM7SxUdpEsq9WqYsWKZXYYcDJvb2++mQJZFJ9Pc8nsiu6tPD09s3xCmpEy/8cNAAAAIIOQ7AIAAMC0SHaBHMDDw0ODBw+Wh4dHZocC4DZ8PoGMxQ1qAAAAMC0quwAAADAtkl0AAACYFskuAAAATItkFwAAAKZFsgsAAADTItkFcM9uXdQlISEhEyMBAMARyS6Ae2IYhiwWiy5cuCBJcnFx0aZNm7R9+/ZMjgwAAJJdAPfIYrHo3Llzat26tb788kv98ssvatCggWJiYjI7NMDUbDab/c/x8fFJ2gDcRLIL4J5dvXpVDz/8sEaNGqWOHTtq7ty5atKkCVMagAxktVp16tQp3bhxQ66urvr555/10UcfkfACtyHZBXDPSpQoofr16+vff/+Vt7e3zp8/L+nmlAa+8QIZ49q1a3r88cfVoEEDzZkzR48//rjKlSsnq5Vv7cCteFwwgHtis9lktVoVFhamw4cPKywsTHPnztULL7yg/v37O/QB4FxHjx5VnTp1FB0drS+//FLPPfecEhIS5OLiktmhAVmGa2YHACB7Srwx7dy5c8qVK5cefPBBValSRVWqVNH169c1efJkWa1Wvfbaa7JarVqwYIFKliypGjVqZHbogGl4eHgoJiZGHh4emj17tp599lm5ubnxAyZwCyq7ANJt0aJFGjBggDw9PeXt7a0FCxbI399fx44d0+TJk/XTTz+pTZs2ypMnjz788EMdOXJEJUuWzOywAVM5duyYrl27ptatWysoKEgrVqxwSHjj4+Pl6kptCzkXyS6ANEms6P7111+qX7++Bg4cqNy5c2vevHk6cuSIfvvtN1WuXFknTpzQvHnzNGvWLHl6emry5MmqXr16ZocPZGuJn7/9+/fr7NmzKlasmMqUKSNJ2rZtmzp27KhSpUrpt99+k5ubm7744gtFRUVp4MCBslgsmRw9kDlIdgGk2R9//KHo6Ght2rRJgwcPliRFRkbq2Wef1Z49e7R8+XJVqlRJ8fHxio+P17Vr15QvX75Mjhowh4ULFyo0NFR+fn46duyYxowZo9DQUBUsWFDbtm1T586dZbFYVKdOHc2bN087d+5U5cqVMztsINMwoQdAmly5ckUvvfSSWrVqpWPHjtnbCxYsqJkzZ6pq1apq06aN9uzZI1dXV3l6epLoAvcosS516tQpjRw5Uh9//LHWrFmjTz75REOGDNHnn3+uiIgIPfTQQ1q3bp0aNWqkXLlyadeuXSS6yPGo7AJIs7CwMA0YMEAHDx7Uli1bVKhQIfuvV8+fP6/HHntMly5d0u7du+Xu7p7Z4QKmsHLlSu3cuVN///23JkyYIA8PD0nSxIkT9fbbb+vVV1/VSy+9pMKFC0uS4uLi+PwBItkFcBeJSeytEhISdPDgQT3zzDOKi4vThg0b5Ovr6/Do4JiYGBUvXjyTogbM57333tPIkSNVqlQprVu3TkWKFLHvmzhxot5//32FhobqzTfftCe8AEh2AdxBYvK6efNmrV+/XjExMWrdurXq1KkjSdq/f7+6du2qGzduJEl4ATjf2LFj9dZbb2ns2LHq1auX8uTJY9/36aefaty4cdqxY4f8/PwyMUogayHZBXBHCxcuVJ8+fVSpUiV5eXlpyZIlmjVrlrp27SrpZsIbEhKif//9V3/99Zd8fHwyOWIg+0v8oTEhIUEJCQkO0xHef/99jRo1SuPHj1dISIi8vLzs+y5duiRfX99MiBjIulh4D0CKNm/erJdeekkjRozQ888/r3/++UdBQUHq0aOHLly4oJdfflkVKlTQ1KlT1bdvX50/f55kF7hHiYnub7/9pu+++07Hjx9XixYt1LVrVz3wwAP68MMPZRiG+vXrJxcXF3Xr1s1e4eXzByRFsgsgWQkJCdq9e7d69+6t559/XqdOnVKDBg30wgsvyM/PT6+99pry5Mmj0NBQVapUSStWrOBmGMAJLBaLFi9erGeffVZdu3ZVp06dNHz4cB06dEh9+vRRo0aN9NFHH8nFxUUvvvii3Nzc1KNHD1ksFqYQAclgGgOAJCIiIlSoUCHt379fMTExqlSpkh599FGVLVtWkydP1okTJ1StWjVFR0dr8uTJ6tWrV2aHDJjGvn379MQTT+j1119Xnz59ZBiGChUqpBs3bqhOnTr64IMPVL9+fUnShx9+qCeffFIVKlTI5KiBrIvKLgAHf/75p5o1a6Y///zT/g308OHDioqKUmhoqKxWq9zd3fXkk0+qfPny9m+6AJwjNjZWXbt2Vc+ePfXPP/+oYcOG6tatm3r06KE6deooT548unr1qlq0aKH3338/s8MFsjySXQAOKleurKJFi+rjjz/WJ598IovFoosXL2r37t26ePGivZp74MABffHFF8qdO3dmhwyYQlhYmDw9PVWhQgXlzZtXLi4uGjBggH3agpeXl+rWrauFCxcqd+7catCggXLlypXZYQNZHk9QA2AXHx8vm82mp556Srt27dKFCxckSbVr11bfvn3Vrl071a1bV+PHj9eXX35Jogs4gWEYioiI0BNPPKF169YpV65cKlu2rBISEvTvv/+qcuXK9hUXKlSooO+//15Dhgwh0QVSiTm7AHThwgXlz5/f/vXp06f14IMP6o033nD4Nekvv/yiq1ev6qGHHlLJkiUzI1TAtF599VX98ssv2rZtm/Lnz6/IyEg9/PDDql69ujp27KiNGzdqxowZCgsLU4ECBTI7XCDbINkFcrg//vhD77zzjipVqqQRI0bIw8NDHh4eGj9+vGbPnq1p06apYsWKmR0mYFqJj/U9cOCAevbsqZ49e+q5556TJK1Zs0Zdu3ZV3rx5dePGDS1YsEDVq1fP5IiB7IVpDEAOV6hQITVs2FCbNm1S1apVNXz4cO3bt0+tW7dWdHS0Dh48KOnmUmQAnOevv/5SdHS0fcm+cuXKqWjRovruu+/sfZo0aaItW7Zo6dKl+uOPP0h0gXSgsgvkMIkL1l++fFnx8fEOvw4dMmSIdu7cqVWrVmnEiBGaNm2aYmJitH37dp7KBDjRsWPH1LlzZ506dUrjxo1TxYoVValSJZ04cUL169fXwIED1bdv38wOEzAFkl0gB0lMdH/++Wd99dVXOnz4sGrUqKFatWppwIABkqTLly/rl19+0aRJk3T06FFdvnxZR44ckb+/fyZHD5jHjRs3dOzYMX3xxRdat26dDMNQly5d1LFjR3322WeKj4/XuHHj5OHhwYMigHtEsguYXGKCm2jJkiV66qmn9NFHH6lSpUr69ddf9fnnn2vVqlVq2rSpvd+///6rgwcPqkSJEipdunRmhA6YRuLn8O+//9alS5cUHx+vOnXqSJK2bdumrVu36oMPPlCzZs108OBB7du3T5s3b1ZwcHAmRw5kfyS7QA6QkJAgFxcXXbt2TaGhoapevbreeecdRUZGqnr16urQoYPGjx+f2WECppSY6C5cuFAffPCBEhISZLFYlD9/fi1cuND+W5OjR49qwYIFWrZsmVavXq0DBw7ogQceyOTogeyPG9QAk5oyZYo6duwoSXJxcbH//++//1aFChV05swZVatWTY8++qg90Z0/f742b96caTEDZmSxWLRmzRp1795dr7/+unbs2KEvvvhCmzdv1tKlSyVJNptNpUqV0ptvvqlVq1bp9OnTJLqAk5DsAiZ048YNXbx4Ufv371evXr3s7fHx8apQoYJ27Nih+vXrq3Xr1po8ebIkKTIyUsuWLdOBAwdks9kyK3Qg20t8GMutn6MtW7aoR48eev7553Xu3Dk9//zz6tOnj3r06CFJslpvfjtOXPUkICDgPkcNmBfJLmBCbm5u6tOnj1566SVt27bNvmZn7ty5Va9ePX344YcqVqyYxo8fb5/P+9lnn2ndunVq0qSJ/RsvgLT54Ycf5OfnpwMHDshqtdoT3l27dtl/CG3YsKFatmypL7/8UpI0bdo0+59dXV0zLXbArPhUASZkGIby5Mmj7t27y2az6dtvv1WPHj00bdo0vfTSSzp37pyGDx+u1157TS4uLrpy5YoWLVqkNWvW8GQ04B7UqVNHLVq0ULNmzfT777+rfPnykqQnn3xSU6dOVbly5dS+fXtNnjxZNptNNptN27dvl9Vq1fXr1+Xp6ZnJVwCYD+UbwIQSq7WJCW+vXr20Y8cOhYaGSpIGDx6sL7/8UtHR0Tpw4IDy58+vTZs2qVq1apkXNGACxYsX15QpU1S9enU1atRIBw4ckHTzgRGXLl2Sr6+vOnfuLEmKiYnR4MGDtXDhQr388sskukAGYTUGwEQS7/o+evSorl27pvj4eFWtWlU3btzQt99+q0mTJqlGjRqaNm2aJOnq1avKnTu3bDYbUxcAJ/rnn3/Uu3dvbd++XWvWrFHFihW1YcMG9e3bVxaLRQkJCSpcuLD27t2rJUuW8GQ0IAOR7AImkZjo/vTTT3rzzTfl4+OjY8eO6fHHH1ffvn1VpUoVffvtt/rmm29Uq1Ytffvtt5kdMmBqZ8+eVUhIiHbs2KG1a9eqYsWK2rt3rw4dOqSNGzeqevXqqlevnkqVKpXZoQKmRrILmMi6dev02GOPafTo0erTp49mzZql7t27a+rUqQoNDVVMTIxmzZqlkSNHql27dpowYUJmhwxke4k/aG7fvl1//fWXLl++rDp16uihhx7ShQsX1K1bN23fvt2e8AK4v0h2ARNI/GY7aNAgHT16VLNmzdKxY8fUsmVLNW3aVF9//bWkm8saXb9+XXPmzFGzZs2oKAFOsmDBAvXu3VsNGzbUyZMnZbVa1bJlS40YMUL//POPXnjhBe3evVvLly/Xgw8+mNnhAjkKk/SAbCa5NXATb0gLDw9X1apVlZCQoAYNGujhhx+2r6M7b948/fTTT/Ly8lLPnj1JdAEn+fPPP9WvXz+NGDFCixYt0pQpU7Rv3z7757JYsWKaMmWKgoKC1KFDB924cSOTIwZyFpJdIBtJvJHsxIkT+vbbb/XZZ59p69at9v0PPvigxowZoyJFiqhjx4764osvZLFYZBiGli5dqnXr1ik2Ntb+TRhA6qX0sJVDhw6pePHieuGFF3Ts2DF16NBB3bt310cffSRJ2rdvnwICArRgwQKtWrVKbm5u9zNsIMdjnV0gm0hMdMPCwtSmTRsFBQXpjz/+sD9itFevXurYsaM2bNigdevW6cUXX5Srq6uuXbum4cOHa8WKFVq9erU8PDwy+1KAbCfx83fq1CktX75cNptN5cuXV8OGDeXm5iZ/f3+dOnVKjRo1UuvWrfXVV19JktavX6/ffvtNr7zyCk9FAzIJyS6QDSR+o/3zzz9Vp04dDRgwQG+99ZYuXryoOnXqaMGCBerVq5eKFCmi5557ThcvXlStWrX00EMPyTAMHTx4UEuWLFG5cuUy+1KAbOfWHzTbtWsnf39/HTlyRL6+vvr0009VpUoVLV26VL/++qv69Omjzz//3H7sDz/8oOPHj7OGLpCJmMYAZAOJUxfq1q2rxx9/XEOGDJGXl5eKFSumihUravfu3Tp16pQk6dFHH9WCBQs0ZswY1apVS08//bR9mSMAaXNrolu3bl116dJFq1ev1ty5c3Xt2jVNmjRJQUFBmjhxogzDULFixXTy5EkdOXJEAwYM0OzZszVq1Cj5+Phk9qUAORarMQDZxPHjx9WwYUPVqlVL/fv3V8OGDfXxxx/r7bffVpkyZVSpUiVJUrVq1dSnTx/ly5ePuYGAE5w6dUo1atRQ06ZN9cMPP9jba9eurUuXLmnbtm1ydXXVvHnz1LdvX/n7+yt37tyyWCyaNWsWP2gCmYxkF8gGEqtLBw8e1JNPPqly5crJz89P8+fP15w5cxQUFCTDMDRt2jStXbtWu3fvVsuWLTVnzhx5enrydDTgHhw/flxPP/20ChcurAEDBqh+/foaOXKk3nvvPdWqVUuFCxdWgQIF1LZtW/n6+uratWsqUaKE/Pz85O/vn9nhAzkeyS6QTSQmvAcOHFCnTp30559/6pNPPlH//v3tfRLX2509e7bq1aunkiVLZmLEgHkcPnxY/fr1k7u7uwoVKqTFixfrq6++Uu3atbVjxw7t3btXEyZMkJeXl2rUqKEFCxZkdsgA/odkF8hGEhPeI0eOqH379goKCtJbb72lRo0aSZLi4+Pl6sp9p0BGOHTokF5++WWtX79ew4cP15tvvumw//z581q9erWqVq2qsmXLZlKUAG5HsgtkUYlrelqtVnuSm9ieWOF96qmnVKJECQ0cOFANGjTIzHCBHOHIkSN66aWX5OLionfffdf+ubtx4wZz5IEsiol8QBaRmNxev35d0s0k9/Dhw/Y/J0pMfsuXL68ff/xR//77r9555x1t3rz5/gcN5DClS5fWF198IcMw9OGHH2rjxo2SRKILZGEku0AWYbVadfToUb322mv6999/9eOPP6pChQrat29fsn0TE97Zs2fLZrOpWLFimRA1kPOULVtW48ePl5ubm95880398ccfmR0SgDtgGgOQhaxbt07t27dX1apVtXnzZn399dfq3r27/caz2yUkJMjFxYVfoQKZ4MCBA/rggw80duxYFS9ePLPDAZACkl0gi0hMaEePHq2BAweqTp06+u6771SmTBmH/Xc6FsD9FRcXJ3d398wOA8AdMI0ByCISEhIkSZ6enho0aJDOnj2rIUOGaNeuXZIki8WiW382TZzjm7gPwP1HogtkfVR2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xKXH+vbta7i5uRmtWrUynn76aSMgIMCQZDz44IPGpUuXHI5JSEgwnn32WUOS4e7ubgQHBxudO3c2nnjiCePBBx80LBaLUbVq1VS9NwByDiq7AEyrUKFC2rRpk8aPH6+KFStq165d+vHHHxUWFqZSpUrp448/1ptvvulwzOOPP67Vq1eradOm2rt3r5YsWaLChQtr7dq1qlevXprOX7RoUW3btk3Dhg1TsWLFtGLFCv3666+6evWqXnrpJbVt29be19PTU0uWLFGLFi20e/duTZ8+XVOmTLE/UjgxtrCwML300kuyWCxasWKFlixZooiICD322GP64YcfVLFiRYcYvv/+e40ePVpFixbVsmXL9Mcff6hr1676/fffk13FIj0mTpyosWPHqmTJklq1apXWr1+v5s2ba/v27SmutpBo/PjxGjlypE6cOKHFixfLYrGob9++Wr9+vXx8fBz6Wq1Wfffdd1q8eLFatGihY8eOacGCBdqwYYM8PT311ltvaerUqU65JgDmYTGMe7xFFgAAAMiiqOwCAADAtEh2AQAAYFokuwAAADAtkl0AAACYFskuAAAATItkFwAAAKZFsgsAAADTItkFAACAaZHsAgAAwLRIdgEAAGBaJLsAAAAwLZJdAAAAmNb/A0QaHiFLM1O5AAAAAElFTkSuQmCC\n" + }, + "metadata": {} + } + ], + "source": [ + "from sklearn.svm import LinearSVC\n", + "\n", + "vect = CountVectorizer(preprocessor=clean, max_features=1000) # Step-1\n", + "X_train_dtm = vect.fit_transform(X_train) # combined step 2 and 3\n", + "X_test_dtm = vect.transform(X_test)\n", + "\n", + "classifier = LinearSVC(class_weight='balanced') # instantiate a Linear Support Vector Machine model\n", + "classifier.fit(X_train_dtm, y_train) # fit the model with training data\n", + "\n", + "# Make predictions on test data\n", + "y_pred_class = classifier.predict(X_test_dtm)\n", + "\n", + "# Like other Sklearn models, LinearSVC doesn't have implement .predict_proba, but we can get the same results\n", + "# by using .decision_function (predicts the confidence scores) and then applying softmax on the output\n", + "\n", + "# Softmax Function\n", + "def softmax(x):\n", + " e_x = np.exp(x - np.max(x))\n", + " return e_x / e_x.sum(axis=0)\n", + "\n", + "y_prob_intermediate = classifier.decision_function(X_test_dtm) ## Predicts the Confidence Scores\n", + "y_pred_prob = softmax(y_prob_intermediate)\n", + "\n", + "# calculate evaluation measures:\n", + "print(\"Accuracy: \", accuracy_score(y_test, y_pred_class))\n", + "print(\"AUC: \", roc_auc_score(y_test, y_pred_prob))\n", + "cnf_matrix = confusion_matrix(y_test, y_pred_class)\n", + "plt.figure(figsize=(8,6))\n", + "plot_confusion_matrix(cnf_matrix, classes=['Not Relevant','Relevant'],normalize=True,\n", + " title='Confusion matrix with normalization')" + ] }, { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" + "cell_type": "markdown", + "metadata": { + "id": "Fd_-M70F5xbl" + }, + "source": [ + "So, how do we choose whats the best? If we look at overall accuracy alone, we should be choosing the very first classifier in this notebook. However, that is also doing poorly with identifying \"relevant\" articles. If we choose purely based on how good it is doing with \"relevant\" category, we should choose the second one we built. If we choose purely based on how good it is doing with \"irrelevant\" category, surely, nothing beats not building any classifier and just calling everything irrelevant! So, what to choose as the best among these depends on what we are looking for in our usecase!" ] - }, - "metadata": { - "needs_background": "light", - "tags": [] - }, - "output_type": "display_data" + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "id": "iMJlTrJvLrS2" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.17" } - ], - "source": [ - "from sklearn.svm import LinearSVC\n", - "\n", - "vect = CountVectorizer(preprocessor=clean, max_features=1000) # Step-1\n", - "X_train_dtm = vect.fit_transform(X_train) # combined step 2 and 3\n", - "X_test_dtm = vect.transform(X_test)\n", - "\n", - "classifier = LinearSVC(class_weight='balanced') # instantiate a logistic regression model\n", - "classifier.fit(X_train_dtm, y_train) # fit the model with training data\n", - "\n", - "# Make predictions on test data\n", - "y_pred_class = classifier.predict(X_test_dtm)\n", - "\n", - "# calculate evaluation measures:\n", - "print(\"Accuracy: \", accuracy_score(y_test, y_pred_class))\n", - "print(\"AUC: \", roc_auc_score(y_test, y_pred_prob))\n", - "cnf_matrix = confusion_matrix(y_test, y_pred_class)\n", - "plt.figure(figsize=(8,6))\n", - "plot_confusion_matrix(cnf_matrix, classes=['Not Relevant','Relevant'],normalize=True,\n", - " title='Confusion matrix with normalization')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Fd_-M70F5xbl" - }, - "source": [ - "So, how do we choose whats the best? If we look at overall accuracy alone, we should be choosing the very first classifier in this notebook. However, that is also doing poorly with identifying \"relevant\" articles. If we choose purely based on how good it is doing with \"relevant\" category, we should choose the second one we built. If we choose purely based on how good it is doing with \"irrelevant\" category, surely, nothing beats not building any classifier and just calling everything irrelevant! So, what to choose as the best among these depends on what we are looking for in our usecase! " - ] - } - ], - "metadata": { - "colab": { - "collapsed_sections": [], - "name": "01_OnePipeline_ManyClassifiers.ipynb", - "provenance": [] - }, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.4" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/Ch4/02_Doc2Vec_Example.ipynb b/Ch4/02_Doc2Vec_Example.ipynb index c1a371e..c6c63c7 100644 --- a/Ch4/02_Doc2Vec_Example.ipynb +++ b/Ch4/02_Doc2Vec_Example.ipynb @@ -1,531 +1,653 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "id": "LCgVnQopb6TI" - }, - "source": [ - "# Doc2Vec demonstration \n", - "\n", - "In this notebook, let us take a look at how to \"learn\" document embeddings and use them for text classification. We will be using the dataset of \"Sentiment and Emotion in Text\" from [Kaggle](https://www.kaggle.com/c/sa-emotions/data).\n", - "\n", - "\"In a variation on the popular task of sentiment analysis, this dataset contains labels for the emotional content (such as happiness, sadness, and anger) of texts. Hundreds to thousands of examples across 13 labels. A subset of this data is used in an experiment we uploaded to Microsoft’s Cortana Intelligence Gallery.\"\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "LCgVnQopb6TI" + }, + "source": [ + "# Doc2Vec demonstration\n", + "\n", + "In this notebook, let us take a look at how to \"learn\" document embeddings and use them for text classification. We will be using the dataset of \"Sentiment and Emotion in Text\" from [Kaggle](https://www.kaggle.com/c/sa-emotions/data).\n", + "\n", + "\"In a variation on the popular task of sentiment analysis, this dataset contains labels for the emotional content (such as happiness, sadness, and anger) of texts. Hundreds to thousands of examples across 13 labels. A subset of this data is used in an experiment we uploaded to Microsoft’s Cortana Intelligence Gallery.\"\n" + ] }, - "id": "KX5dKXdcaENd", - "outputId": "956f503d-1a2c-4af1-aad5-a5da021ae29b" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Collecting nltk==3.5\n", - "\u001b[?25l Downloading https://files.pythonhosted.org/packages/92/75/ce35194d8e3022203cca0d2f896dbb88689f9b3fce8e9f9cff942913519d/nltk-3.5.zip (1.4MB)\n", - "\u001b[K |████████████████████████████████| 1.4MB 5.1MB/s \n", - "\u001b[?25hRequirement already satisfied: click in /usr/local/lib/python3.7/dist-packages (from nltk==3.5) (7.1.2)\n", - "Requirement already satisfied: joblib in /usr/local/lib/python3.7/dist-packages (from nltk==3.5) (1.0.1)\n", - "Requirement already satisfied: regex in /usr/local/lib/python3.7/dist-packages (from nltk==3.5) (2019.12.20)\n", - "Requirement already satisfied: tqdm in /usr/local/lib/python3.7/dist-packages (from nltk==3.5) (4.41.1)\n", - "Building wheels for collected packages: nltk\n", - " Building wheel for nltk (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for nltk: filename=nltk-3.5-cp37-none-any.whl size=1434691 sha256=a68222bfb8c06405a2c5f264264ffa3daf49f5d73637541f961a024360751028\n", - " Stored in directory: /root/.cache/pip/wheels/ae/8c/3f/b1fe0ba04555b08b57ab52ab7f86023639a526d8bc8d384306\n", - "Successfully built nltk\n", - "Installing collected packages: nltk\n", - " Found existing installation: nltk 3.2.5\n", - " Uninstalling nltk-3.2.5:\n", - " Successfully uninstalled nltk-3.2.5\n", - "Successfully installed nltk-3.5\n", - "Requirement already satisfied: pandas==1.1.5 in /usr/local/lib/python3.7/dist-packages (1.1.5)\n", - "Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.7/dist-packages (from pandas==1.1.5) (2018.9)\n", - "Requirement already satisfied: python-dateutil>=2.7.3 in /usr/local/lib/python3.7/dist-packages (from pandas==1.1.5) (2.8.1)\n", - "Requirement already satisfied: numpy>=1.15.4 in /usr/local/lib/python3.7/dist-packages (from pandas==1.1.5) (1.19.5)\n", - "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.7/dist-packages (from python-dateutil>=2.7.3->pandas==1.1.5) (1.15.0)\n", - "Collecting gensim==3.8.3\n", - "\u001b[?25l Downloading https://files.pythonhosted.org/packages/5c/4e/afe2315e08a38967f8a3036bbe7e38b428e9b7a90e823a83d0d49df1adf5/gensim-3.8.3-cp37-cp37m-manylinux1_x86_64.whl (24.2MB)\n", - "\u001b[K |████████████████████████████████| 24.2MB 1.3MB/s \n", - "\u001b[?25hRequirement already satisfied: numpy>=1.11.3 in /usr/local/lib/python3.7/dist-packages (from gensim==3.8.3) (1.19.5)\n", - "Requirement already satisfied: scipy>=0.18.1 in /usr/local/lib/python3.7/dist-packages (from gensim==3.8.3) (1.4.1)\n", - "Requirement already satisfied: smart-open>=1.8.1 in /usr/local/lib/python3.7/dist-packages (from gensim==3.8.3) (5.1.0)\n", - "Requirement already satisfied: six>=1.5.0 in /usr/local/lib/python3.7/dist-packages (from gensim==3.8.3) (1.15.0)\n", - "Installing collected packages: gensim\n", - " Found existing installation: gensim 3.6.0\n", - " Uninstalling gensim-3.6.0:\n", - " Successfully uninstalled gensim-3.6.0\n", - "Successfully installed gensim-3.8.3\n", - "Collecting scikit-learn==0.21.3\n", - "\u001b[?25l Downloading https://files.pythonhosted.org/packages/9f/c5/e5267eb84994e9a92a2c6a6ee768514f255d036f3c8378acfa694e9f2c99/scikit_learn-0.21.3-cp37-cp37m-manylinux1_x86_64.whl (6.7MB)\n", - "\u001b[K |████████████████████████████████| 6.7MB 5.1MB/s \n", - "\u001b[?25hRequirement already satisfied: joblib>=0.11 in /usr/local/lib/python3.7/dist-packages (from scikit-learn==0.21.3) (1.0.1)\n", - "Requirement already satisfied: numpy>=1.11.0 in /usr/local/lib/python3.7/dist-packages (from scikit-learn==0.21.3) (1.19.5)\n", - "Requirement already satisfied: scipy>=0.17.0 in /usr/local/lib/python3.7/dist-packages (from scikit-learn==0.21.3) (1.4.1)\n", - "Installing collected packages: scikit-learn\n", - " Found existing installation: scikit-learn 0.22.2.post1\n", - " Uninstalling scikit-learn-0.22.2.post1:\n", - " Successfully uninstalled scikit-learn-0.22.2.post1\n", - "Successfully installed scikit-learn-0.21.3\n" - ] - } - ], - "source": [ - "# To install only the requirements of this notebook, uncomment the lines below and run this cell\n", - "\n", - "# ===========================\n", - "\n", - "!pip install nltk==3.5\n", - "!pip install pandas==1.1.5\n", - "!pip install gensim==3.8.3\n", - "!pip install scikit-learn==0.21.3\n", - "\n", - "# ===========================" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "id": "CIlwQe1S4EpL" - }, - "outputs": [], - "source": [ - "# To install the requirements for the entire chapter, uncomment the lines below and run this cell\n", - "\n", - "# ===========================\n", - "\n", - "# try:\n", - "# import google.colab\n", - "# !curl https://raw.githubusercontent.com/practical-nlp/practical-nlp/master/Ch4/ch4-requirements.txt | xargs -n 1 -L 1 pip install\n", - "# except ModuleNotFoundError:\n", - "# !pip install -r \"ch4-requirements.txt\"\n", - "\n", - "# ===========================" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "KX5dKXdcaENd", + "outputId": "c18e98fa-df2d-49a9-baf5-4ac23d66297c" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Requirement already satisfied: nltk==3.8.1 in /usr/local/lib/python3.10/dist-packages (3.8.1)\n", + "Requirement already satisfied: click in /usr/local/lib/python3.10/dist-packages (from nltk==3.8.1) (8.1.7)\n", + "Requirement already satisfied: joblib in /usr/local/lib/python3.10/dist-packages (from nltk==3.8.1) (1.3.2)\n", + "Requirement already satisfied: regex>=2021.8.3 in /usr/local/lib/python3.10/dist-packages (from nltk==3.8.1) (2023.6.3)\n", + "Requirement already satisfied: tqdm in /usr/local/lib/python3.10/dist-packages (from nltk==3.8.1) (4.66.1)\n", + "Requirement already satisfied: pandas==1.5.3 in /usr/local/lib/python3.10/dist-packages (1.5.3)\n", + "Requirement already satisfied: python-dateutil>=2.8.1 in /usr/local/lib/python3.10/dist-packages (from pandas==1.5.3) (2.8.2)\n", + "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.10/dist-packages (from pandas==1.5.3) (2023.3)\n", + "Requirement already satisfied: numpy>=1.21.0 in /usr/local/lib/python3.10/dist-packages (from pandas==1.5.3) (1.23.5)\n", + "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.8.1->pandas==1.5.3) (1.16.0)\n", + "Requirement already satisfied: gensim==4.3.1 in /usr/local/lib/python3.10/dist-packages (4.3.1)\n", + "Requirement already satisfied: numpy>=1.18.5 in /usr/local/lib/python3.10/dist-packages (from gensim==4.3.1) (1.23.5)\n", + "Requirement already satisfied: scipy>=1.7.0 in /usr/local/lib/python3.10/dist-packages (from gensim==4.3.1) (1.10.1)\n", + "Requirement already satisfied: smart-open>=1.8.1 in /usr/local/lib/python3.10/dist-packages (from gensim==4.3.1) (6.3.0)\n", + "Requirement already satisfied: scikit-learn==1.2.2 in /usr/local/lib/python3.10/dist-packages (1.2.2)\n", + "Requirement already satisfied: numpy>=1.17.3 in /usr/local/lib/python3.10/dist-packages (from scikit-learn==1.2.2) (1.23.5)\n", + "Requirement already satisfied: scipy>=1.3.2 in /usr/local/lib/python3.10/dist-packages (from scikit-learn==1.2.2) (1.10.1)\n", + "Requirement already satisfied: joblib>=1.1.1 in /usr/local/lib/python3.10/dist-packages (from scikit-learn==1.2.2) (1.3.2)\n", + "Requirement already satisfied: threadpoolctl>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from scikit-learn==1.2.2) (3.2.0)\n" + ] + } + ], + "source": [ + "# To install only the requirements of this notebook, uncomment the lines below and run this cell\n", + "\n", + "# ===========================\n", + "\n", + "!pip install nltk==3.8.1\n", + "!pip install pandas==1.5.3\n", + "!pip install gensim==4.3.1\n", + "!pip install scikit-learn==1.2.2\n", + "\n", + "# ===========================" + ] }, - "id": "hSB6W1seb6TJ", - "outputId": "e93459c9-fd82-4d22-852b-819faeb430a6" - }, - "outputs": [ { - "name": "stderr", - "output_type": "stream", - "text": [ - "[nltk_data] Downloading package stopwords to /root/nltk_data...\n", - "[nltk_data] Unzipping corpora/stopwords.zip.\n" - ] - } - ], - "source": [ - "import warnings\n", - "warnings.filterwarnings('ignore')\n", - "import pandas as pd\n", - "import nltk\n", - "nltk.download('stopwords')\n", - "from nltk.tokenize import TweetTokenizer\n", - "from nltk.corpus import stopwords\n", - "from sklearn.model_selection import train_test_split\n", - "from gensim.models.doc2vec import Doc2Vec, TaggedDocument" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" + "cell_type": "code", + "execution_count": 4, + "metadata": { + "id": "CIlwQe1S4EpL" + }, + "outputs": [], + "source": [ + "# To install the requirements for the entire chapter, uncomment the lines below and run this cell\n", + "\n", + "# ===========================\n", + "\n", + "# try:\n", + "# import google.colab\n", + "# !curl https://raw.githubusercontent.com/practical-nlp/practical-nlp/master/Ch4/ch4-requirements.txt | xargs -n 1 -L 1 pip install\n", + "# except ModuleNotFoundError:\n", + "# !pip install -r \"ch4-requirements.txt\"\n", + "\n", + "# ===========================" + ] }, - "id": "NGAFbmrA4EpM", - "outputId": "f78def1c-c291-4fba-dd41-f24f1456757c" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "--2021-07-16 08:27:55-- https://raw.githubusercontent.com/practical-nlp/practical-nlp/master/Ch4/Data/Sentiment%20and%20Emotion%20in%20Text/train_data.csv\n", - "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133, 185.199.109.133, 185.199.110.133, ...\n", - "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443... connected.\n", - "HTTP request sent, awaiting response... 200 OK\n", - "Length: 2479133 (2.4M) [text/plain]\n", - "Saving to: ‘DATAPATH/train_data.csv’\n", - "\n", - "train_data.csv 100%[===================>] 2.36M --.-KB/s in 0.1s \n", - "\n", - "2021-07-16 08:27:55 (22.4 MB/s) - ‘DATAPATH/train_data.csv’ saved [2479133/2479133]\n", - "\n", - "--2021-07-16 08:27:55-- https://raw.githubusercontent.com/practical-nlp/practical-nlp/master/Ch4/Data/Sentiment%20and%20Emotion%20in%20Text/test_data.csv\n", - "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133, 185.199.111.133, 185.199.109.133, ...\n", - "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443... connected.\n", - "HTTP request sent, awaiting response... 200 OK\n", - "Length: 783640 (765K) [text/plain]\n", - "Saving to: ‘DATAPATH/test_data.csv’\n", - "\n", - "test_data.csv 100%[===================>] 765.27K --.-KB/s in 0.05s \n", - "\n", - "2021-07-16 08:27:55 (15.4 MB/s) - ‘DATAPATH/test_data.csv’ saved [783640/783640]\n", - "\n", - "total 3.2M\n", - "drwxr-xr-x 2 root root 4.0K Jul 16 08:27 .\n", - "drwxr-xr-x 1 root root 4.0K Jul 16 08:27 ..\n", - "-rw-r--r-- 1 root root 766K Jul 16 08:27 test_data.csv\n", - "-rw-r--r-- 1 root root 2.4M Jul 16 08:27 train_data.csv\n" - ] - } - ], - "source": [ - "#Load the dataset and explore.\n", - "try:\n", - " from google.colab import files\n", - " !wget -P DATAPATH https://raw.githubusercontent.com/practical-nlp/practical-nlp/master/Ch4/Data/Sentiment%20and%20Emotion%20in%20Text/train_data.csv\n", - " !wget -P DATAPATH https://raw.githubusercontent.com/practical-nlp/practical-nlp/master/Ch4/Data/Sentiment%20and%20Emotion%20in%20Text/test_data.csv\n", - " !ls -lah DATAPATH\n", - " filepath = \"DATAPATH/train_data.csv\"\n", - "except ModuleNotFoundError:\n", - " filepath = \"Data/Sentiment and Emotion in Text/train_data.csv\"" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 221 + "cell_type": "code", + "execution_count": 13, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "hSB6W1seb6TJ", + "outputId": "9e34f468-dc75-4555-9522-4fea208d6a00" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "[nltk_data] Downloading package stopwords to /root/nltk_data...\n", + "[nltk_data] Package stopwords is already up-to-date!\n" + ] + } + ], + "source": [ + "import warnings\n", + "warnings.filterwarnings('ignore')\n", + "import pandas as pd\n", + "import nltk\n", + "nltk.download('stopwords')\n", + "from nltk.tokenize import TweetTokenizer\n", + "from nltk.corpus import stopwords\n", + "from sklearn.model_selection import train_test_split\n", + "from gensim.models.doc2vec import Doc2Vec, TaggedDocument" + ] }, - "id": "lSvnHBYPb6TQ", - "outputId": "b992755a-470e-470b-eb59-e4225711f252" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "(30000, 2)\n" - ] + "cell_type": "code", + "execution_count": 14, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "NGAFbmrA4EpM", + "outputId": "947b9250-7fd2-4cc7-c74c-4c88ecfdd4fa" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "--2023-08-22 16:12:41-- https://raw.githubusercontent.com/practical-nlp/practical-nlp/master/Ch4/Data/Sentiment%20and%20Emotion%20in%20Text/train_data.csv\n", + "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133, 185.199.109.133, 185.199.110.133, ...\n", + "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443... connected.\n", + "HTTP request sent, awaiting response... 200 OK\n", + "Length: 2479133 (2.4M) [text/plain]\n", + "Saving to: ‘DATAPATH/train_data.csv.1’\n", + "\n", + "\rtrain_data.csv.1 0%[ ] 0 --.-KB/s \rtrain_data.csv.1 100%[===================>] 2.36M --.-KB/s in 0.02s \n", + "\n", + "2023-08-22 16:12:42 (131 MB/s) - ‘DATAPATH/train_data.csv.1’ saved [2479133/2479133]\n", + "\n", + "--2023-08-22 16:12:42-- https://raw.githubusercontent.com/practical-nlp/practical-nlp/master/Ch4/Data/Sentiment%20and%20Emotion%20in%20Text/test_data.csv\n", + "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133, 185.199.109.133, 185.199.110.133, ...\n", + "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443... connected.\n", + "HTTP request sent, awaiting response... 200 OK\n", + "Length: 783640 (765K) [text/plain]\n", + "Saving to: ‘DATAPATH/test_data.csv.1’\n", + "\n", + "test_data.csv.1 100%[===================>] 765.27K --.-KB/s in 0.009s \n", + "\n", + "2023-08-22 16:12:42 (78.7 MB/s) - ‘DATAPATH/test_data.csv.1’ saved [783640/783640]\n", + "\n", + "total 6.3M\n", + "drwxr-xr-x 2 root root 4.0K Aug 22 16:12 .\n", + "drwxr-xr-x 1 root root 4.0K Aug 22 16:10 ..\n", + "-rw-r--r-- 1 root root 766K Aug 22 16:08 test_data.csv\n", + "-rw-r--r-- 1 root root 766K Aug 22 16:12 test_data.csv.1\n", + "-rw-r--r-- 1 root root 2.4M Aug 22 16:08 train_data.csv\n", + "-rw-r--r-- 1 root root 2.4M Aug 22 16:12 train_data.csv.1\n" + ] + } + ], + "source": [ + "#Load the dataset and explore.\n", + "try:\n", + " from google.colab import files\n", + " !wget -P DATAPATH https://raw.githubusercontent.com/practical-nlp/practical-nlp/master/Ch4/Data/Sentiment%20and%20Emotion%20in%20Text/train_data.csv\n", + " !wget -P DATAPATH https://raw.githubusercontent.com/practical-nlp/practical-nlp/master/Ch4/Data/Sentiment%20and%20Emotion%20in%20Text/test_data.csv\n", + " !ls -lah DATAPATH\n", + " filepath = \"DATAPATH/train_data.csv\"\n", + "except ModuleNotFoundError:\n", + " filepath = \"Data/Sentiment and Emotion in Text/train_data.csv\"" + ] }, { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
sentimentcontent
0empty@tiffanylue i know i was listenin to bad habi...
1sadnessLayin n bed with a headache ughhhh...waitin o...
2sadnessFuneral ceremony...gloomy friday...
3enthusiasmwants to hang out with friends SOON!
4neutral@dannycastillo We want to trade with someone w...
\n", - "
" + "cell_type": "code", + "execution_count": 15, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 224 + }, + "id": "lSvnHBYPb6TQ", + "outputId": "aaff8fc3-c5fd-457e-f0dc-19d4b412a8ee" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(30000, 2)\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " sentiment content\n", + "0 empty @tiffanylue i know i was listenin to bad habi...\n", + "1 sadness Layin n bed with a headache ughhhh...waitin o...\n", + "2 sadness Funeral ceremony...gloomy friday...\n", + "3 enthusiasm wants to hang out with friends SOON!\n", + "4 neutral @dannycastillo We want to trade with someone w..." + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sentimentcontent
0empty@tiffanylue i know i was listenin to bad habi...
1sadnessLayin n bed with a headache ughhhh...waitin o...
2sadnessFuneral ceremony...gloomy friday...
3enthusiasmwants to hang out with friends SOON!
4neutral@dannycastillo We want to trade with someone w...
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 15 + } ], - "text/plain": [ - " sentiment content\n", - "0 empty @tiffanylue i know i was listenin to bad habi...\n", - "1 sadness Layin n bed with a headache ughhhh...waitin o...\n", - "2 sadness Funeral ceremony...gloomy friday...\n", - "3 enthusiasm wants to hang out with friends SOON!\n", - "4 neutral @dannycastillo We want to trade with someone w..." + "source": [ + "df = pd.read_csv(filepath)\n", + "print(df.shape)\n", + "df.head()" ] - }, - "execution_count": 5, - "metadata": { - "tags": [] - }, - "output_type": "execute_result" - } - ], - "source": [ - "df = pd.read_csv(filepath)\n", - "print(df.shape)\n", - "df.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" }, - "id": "5JEI6SH7b6TU", - "outputId": "7c4bccf9-3c39-4e43-cde8-3989a7a002d0" - }, - "outputs": [ { - "data": { - "text/plain": [ - "worry 7433\n", - "neutral 6340\n", - "sadness 4828\n", - "happiness 2986\n", - "love 2068\n", - "surprise 1613\n", - "hate 1187\n", - "fun 1088\n", - "relief 1021\n", - "empty 659\n", - "enthusiasm 522\n", - "boredom 157\n", - "anger 98\n", - "Name: sentiment, dtype: int64" + "cell_type": "code", + "execution_count": 16, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "5JEI6SH7b6TU", + "outputId": "c3c1034c-6a1c-4e87-9d21-3fdc9f58c9dd" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "worry 7433\n", + "neutral 6340\n", + "sadness 4828\n", + "happiness 2986\n", + "love 2068\n", + "surprise 1613\n", + "hate 1187\n", + "fun 1088\n", + "relief 1021\n", + "empty 659\n", + "enthusiasm 522\n", + "boredom 157\n", + "anger 98\n", + "Name: sentiment, dtype: int64" + ] + }, + "metadata": {}, + "execution_count": 16 + } + ], + "source": [ + "df['sentiment'].value_counts()" ] - }, - "execution_count": 6, - "metadata": { - "tags": [] - }, - "output_type": "execute_result" - } - ], - "source": [ - "df['sentiment'].value_counts()" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" }, - "id": "CHajyKpmb6TY", - "outputId": "bbb05164-f107-4b7c-fedb-145a3b2d1ca3" - }, - "outputs": [ { - "data": { - "text/plain": [ - "(16759, 2)" + "cell_type": "code", + "execution_count": 17, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "CHajyKpmb6TY", + "outputId": "9c28cf4f-87f4-4261-bd32-4e2abfd9435f" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "(16759, 2)" + ] + }, + "metadata": {}, + "execution_count": 17 + } + ], + "source": [ + "#Let us take the top 3 categories and leave out the rest.\n", + "shortlist = ['neutral', \"happiness\", \"worry\"]\n", + "df_subset = df[df['sentiment'].isin(shortlist)]\n", + "df_subset.shape" ] - }, - "execution_count": 7, - "metadata": { - "tags": [] - }, - "output_type": "execute_result" - } - ], - "source": [ - "#Let us take the top 3 categories and leave out the rest.\n", - "shortlist = ['neutral', \"happiness\", \"worry\"]\n", - "df_subset = df[df['sentiment'].isin(shortlist)]\n", - "df_subset.shape" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "m2oiZzU5b6Tf" - }, - "source": [ - "# Text pre-processing:\n", - "Tweets are different. Somethings to consider:\n", - "- Removing @mentions, and urls perhaps?\n", - "- using NLTK Tweet tokenizer instead of a regular one\n", - "- stopwords, numbers as usual." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" }, - "id": "Rl-FfMdLb6Th", - "outputId": "818e0510-afdb-4732-fe69-c6119ca695c1" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "16759 16759\n" - ] - } - ], - "source": [ - "#strip_handles removes personal information such as twitter handles, which don't\n", - "#contribute to emotion in the tweet. preserve_case=False converts everything to lowercase.\n", - "tweeter = TweetTokenizer(strip_handles=True,preserve_case=False)\n", - "mystopwords = set(stopwords.words(\"english\"))\n", - "\n", - "#Function to tokenize tweets, remove stopwords and numbers. \n", - "#Keeping punctuations and emoticon symbols could be relevant for this task!\n", - "def preprocess_corpus(texts):\n", - " def remove_stops_digits(tokens):\n", - " #Nested function that removes stopwords and digits from a list of tokens\n", - " return [token for token in tokens if token not in mystopwords and not token.isdigit()]\n", - " #This return statement below uses the above function to process twitter tokenizer output further. \n", - " return [remove_stops_digits(tweeter.tokenize(content)) for content in texts]\n", - "\n", - "#df_subset contains only the three categories we chose. \n", - "mydata = preprocess_corpus(df_subset['content'])\n", - "mycats = df_subset['sentiment']\n", - "print(len(mydata), len(mycats))" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" + "cell_type": "markdown", + "metadata": { + "id": "m2oiZzU5b6Tf" + }, + "source": [ + "# Text pre-processing:\n", + "Tweets are different. Somethings to consider:\n", + "- Removing @mentions, and urls perhaps?\n", + "- using NLTK Tweet tokenizer instead of a regular one\n", + "- stopwords, numbers as usual." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Rl-FfMdLb6Th", + "outputId": "df2382f7-5823-4831-f356-85cf93d23ab6" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "16759 16759\n" + ] + } + ], + "source": [ + "#strip_handles removes personal information such as twitter handles, which don't\n", + "#contribute to emotion in the tweet. preserve_case=False converts everything to lowercase.\n", + "tweeter = TweetTokenizer(strip_handles=True,preserve_case=False)\n", + "mystopwords = set(stopwords.words(\"english\"))\n", + "\n", + "#Function to tokenize tweets, remove stopwords and numbers.\n", + "#Keeping punctuations and emoticon symbols could be relevant for this task!\n", + "def preprocess_corpus(texts):\n", + " def remove_stops_digits(tokens):\n", + " #Nested function that removes stopwords and digits from a list of tokens\n", + " return [token for token in tokens if token not in mystopwords and not token.isdigit()]\n", + " #This return statement below uses the above function to process twitter tokenizer output further.\n", + " return [remove_stops_digits(tweeter.tokenize(content)) for content in texts]\n", + "\n", + "#df_subset contains only the three categories we chose.\n", + "mydata = preprocess_corpus(df_subset['content'])\n", + "mycats = df_subset['sentiment']\n", + "print(len(mydata), len(mycats))" + ] }, - "id": "rsGwfVebb6Tl", - "outputId": "c19bc96f-513c-45b6-d476-b95899ab7eca" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Model Saved\n" - ] + "cell_type": "code", + "execution_count": 19, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "rsGwfVebb6Tl", + "outputId": "0329a297-b6d9-4be1-d4c7-afa61ef786d3" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Model Saved\n" + ] + } + ], + "source": [ + "#Split data into train and test, following the usual process\n", + "train_data, test_data, train_cats, test_cats = train_test_split(mydata,mycats,random_state=1234)\n", + "\n", + "#prepare training data in doc2vec format:\n", + "train_doc2vec = [TaggedDocument((d), tags=[str(i)]) for i, d in enumerate(train_data)]\n", + "\n", + "#Train a doc2vec model to learn tweet representations. Use only training data!!\n", + "model = Doc2Vec(vector_size=50, alpha=0.025, min_count=5, dm =1, epochs=100)\n", + "model.build_vocab(train_doc2vec)\n", + "model.train(train_doc2vec, total_examples=model.corpus_count, epochs=model.epochs)\n", + "model.save(\"d2v.model\")\n", + "print(\"Model Saved\")" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "hTqo26Vsb6Ts", + "outputId": "5308af1c-a3a4-45d4-cf3d-b442fef129b7" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + " precision recall f1-score support\n", + "\n", + " happiness 0.32 0.51 0.39 713\n", + " neutral 0.45 0.53 0.49 1595\n", + " worry 0.60 0.38 0.47 1882\n", + "\n", + " accuracy 0.46 4190\n", + " macro avg 0.46 0.47 0.45 4190\n", + "weighted avg 0.50 0.46 0.46 4190\n", + "\n" + ] + } + ], + "source": [ + "#Infer the feature representation for training and test data using the trained model\n", + "model= Doc2Vec.load(\"d2v.model\")\n", + "\n", + "#infer in multiple steps to get a stable representation.\n", + "train_vectors = [model.infer_vector(list_of_tokens, epochs=50) for list_of_tokens in train_data]\n", + "test_vectors = [model.infer_vector(list_of_tokens, epochs=50) for list_of_tokens in test_data]\n", + "\n", + "#Use any regular classifier like logistic regression\n", + "from sklearn.linear_model import LogisticRegression\n", + "\n", + "myclass = LogisticRegression(class_weight=\"balanced\") #because classes are not balanced.\n", + "myclass.fit(train_vectors, train_cats)\n", + "\n", + "preds = myclass.predict(test_vectors)\n", + "from sklearn.metrics import classification_report, confusion_matrix\n", + "print(classification_report(test_cats, preds))\n", + "\n", + "#print(confusion_matrix(test_cats,preds))" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "id": "qcRNGUJFAA1w" + }, + "outputs": [], + "source": [] } - ], - "source": [ - "#Split data into train and test, following the usual process\n", - "train_data, test_data, train_cats, test_cats = train_test_split(mydata,mycats,random_state=1234)\n", - "\n", - "#prepare training data in doc2vec format:\n", - "train_doc2vec = [TaggedDocument((d), tags=[str(i)]) for i, d in enumerate(train_data)]\n", - "#Train a doc2vec model to learn tweet representations. Use only training data!!\n", - "model = Doc2Vec(vector_size=50, alpha=0.025, min_count=5, dm =1, epochs=100)\n", - "model.build_vocab(train_doc2vec)\n", - "model.train(train_doc2vec, total_examples=model.corpus_count, epochs=model.epochs)\n", - "model.save(\"d2v.model\")\n", - "print(\"Model Saved\")" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { + ], + "metadata": { + "accelerator": "GPU", "colab": { - "base_uri": "https://localhost:8080/" + "provenance": [] }, - "id": "hTqo26Vsb6Ts", - "outputId": "cd16346c-ca81-4dc7-c269-d9ccf83a774d" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " precision recall f1-score support\n", - "\n", - " happiness 0.37 0.45 0.41 713\n", - " neutral 0.46 0.53 0.49 1595\n", - " worry 0.58 0.46 0.51 1882\n", - "\n", - " accuracy 0.48 4190\n", - " macro avg 0.47 0.48 0.47 4190\n", - "weighted avg 0.50 0.48 0.49 4190\n", - "\n" - ] + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.17" } - ], - "source": [ - "#Infer the feature representation for training and test data using the trained model\n", - "model= Doc2Vec.load(\"d2v.model\")\n", - "#infer in multiple steps to get a stable representation. \n", - "train_vectors = [model.infer_vector(list_of_tokens, steps=50) for list_of_tokens in train_data]\n", - "test_vectors = [model.infer_vector(list_of_tokens, steps=50) for list_of_tokens in test_data]\n", - "\n", - "#Use any regular classifier like logistic regression\n", - "from sklearn.linear_model import LogisticRegression\n", - "\n", - "myclass = LogisticRegression(class_weight=\"balanced\") #because classes are not balanced. \n", - "myclass.fit(train_vectors, train_cats)\n", - "\n", - "preds = myclass.predict(test_vectors)\n", - "from sklearn.metrics import classification_report, confusion_matrix\n", - "print(classification_report(test_cats, preds))\n", - "\n", - "#print(confusion_matrix(test_cats,preds))" - ] - } - ], - "metadata": { - "accelerator": "GPU", - "colab": { - "name": "02_Doc2Vec_Example.ipynb", - "provenance": [] - }, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.4" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/Ch4/03_Word2Vec_Example.ipynb b/Ch4/03_Word2Vec_Example.ipynb index 20ec9b5..7ac82f1 100644 --- a/Ch4/03_Word2Vec_Example.ipynb +++ b/Ch4/03_Word2Vec_Example.ipynb @@ -1,31 +1,4 @@ { - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { - "accelerator": "GPU", - "colab": { - "name": "03_Word2Vec_Example.ipynb", - "provenance": [], - "collapsed_sections": [] - }, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.4" - } - }, "cells": [ { "cell_type": "markdown", @@ -52,100 +25,126 @@ }, { "cell_type": "code", + "source": [ + "import pkg_resources\n", + "\n", + "def get_library_versions(library_list):\n", + " frozen_list = []\n", + "\n", + " for library in library_list:\n", + " try:\n", + " version = pkg_resources.get_distribution(library).version\n", + " frozen_list.append(f\"{library}=={version}\")\n", + " except pkg_resources.DistributionNotFound:\n", + " print(f\"Error: {library} not found or could not retrieve version.\")\n", + "\n", + " return frozen_list\n", + "\n", + "# List of library names\n", + "libraries = [\"numpy\", \"pandas\", \"gensim\", \"nltk\", \"scikit-learn\", \"gdown\"]\n", + "\n", + "# Get frozen list of library versions\n", + "frozen_versions = get_library_versions(libraries)\n", + "\n", + "# Print the frozen list\n", + "for item in frozen_versions:\n", + " print(item)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "KN4IKdaFCH7c", + "outputId": "ad4e3a11-17ce-4049-8ce9-53972fe41bfb" + }, + "execution_count": 2, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "numpy==1.23.5\n", + "pandas==1.5.3\n", + "gensim==4.3.1\n", + "nltk==3.8.1\n", + "scikit-learn==1.2.2\n", + "gdown==4.6.6\n" + ] + } + ] + }, + { + "cell_type": "code", + "execution_count": 3, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "77UP8YyEdS2W", - "outputId": "1bb0a097-0232-42fd-ec29-b2e96ce857f5" + "outputId": "c7203e6a-e19d-4e9a-f577-ae936a2e1a4d" }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Requirement already satisfied: numpy==1.23.5 in /usr/local/lib/python3.10/dist-packages (1.23.5)\n", + "Requirement already satisfied: pandas==1.5.3 in /usr/local/lib/python3.10/dist-packages (1.5.3)\n", + "Requirement already satisfied: python-dateutil>=2.8.1 in /usr/local/lib/python3.10/dist-packages (from pandas==1.5.3) (2.8.2)\n", + "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.10/dist-packages (from pandas==1.5.3) (2023.3)\n", + "Requirement already satisfied: numpy>=1.21.0 in /usr/local/lib/python3.10/dist-packages (from pandas==1.5.3) (1.23.5)\n", + "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.8.1->pandas==1.5.3) (1.16.0)\n", + "Requirement already satisfied: gensim==4.3.1 in /usr/local/lib/python3.10/dist-packages (4.3.1)\n", + "Requirement already satisfied: numpy>=1.18.5 in /usr/local/lib/python3.10/dist-packages (from gensim==4.3.1) (1.23.5)\n", + "Requirement already satisfied: scipy>=1.7.0 in /usr/local/lib/python3.10/dist-packages (from gensim==4.3.1) (1.10.1)\n", + "Requirement already satisfied: smart-open>=1.8.1 in /usr/local/lib/python3.10/dist-packages (from gensim==4.3.1) (6.3.0)\n", + "Requirement already satisfied: nltk==3.8.1 in /usr/local/lib/python3.10/dist-packages (3.8.1)\n", + "Requirement already satisfied: click in /usr/local/lib/python3.10/dist-packages (from nltk==3.8.1) (8.1.7)\n", + "Requirement already satisfied: joblib in /usr/local/lib/python3.10/dist-packages (from nltk==3.8.1) (1.3.2)\n", + "Requirement already satisfied: regex>=2021.8.3 in /usr/local/lib/python3.10/dist-packages (from nltk==3.8.1) (2023.6.3)\n", + "Requirement already satisfied: tqdm in /usr/local/lib/python3.10/dist-packages (from nltk==3.8.1) (4.66.1)\n", + "Requirement already satisfied: scikit-learn==1.2.2 in /usr/local/lib/python3.10/dist-packages (1.2.2)\n", + "Requirement already satisfied: numpy>=1.17.3 in /usr/local/lib/python3.10/dist-packages (from scikit-learn==1.2.2) (1.23.5)\n", + "Requirement already satisfied: scipy>=1.3.2 in /usr/local/lib/python3.10/dist-packages (from scikit-learn==1.2.2) (1.10.1)\n", + "Requirement already satisfied: joblib>=1.1.1 in /usr/local/lib/python3.10/dist-packages (from scikit-learn==1.2.2) (1.3.2)\n", + "Requirement already satisfied: threadpoolctl>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from scikit-learn==1.2.2) (3.2.0)\n", + "Requirement already satisfied: gdown==4.6.6 in /usr/local/lib/python3.10/dist-packages (4.6.6)\n", + "Requirement already satisfied: filelock in /usr/local/lib/python3.10/dist-packages (from gdown==4.6.6) (3.12.2)\n", + "Requirement already satisfied: requests[socks] in /usr/local/lib/python3.10/dist-packages (from gdown==4.6.6) (2.31.0)\n", + "Requirement already satisfied: six in /usr/local/lib/python3.10/dist-packages (from gdown==4.6.6) (1.16.0)\n", + "Requirement already satisfied: tqdm in /usr/local/lib/python3.10/dist-packages (from gdown==4.6.6) (4.66.1)\n", + "Requirement already satisfied: beautifulsoup4 in /usr/local/lib/python3.10/dist-packages (from gdown==4.6.6) (4.11.2)\n", + "Requirement already satisfied: soupsieve>1.2 in /usr/local/lib/python3.10/dist-packages (from beautifulsoup4->gdown==4.6.6) (2.4.1)\n", + "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests[socks]->gdown==4.6.6) (3.2.0)\n", + "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests[socks]->gdown==4.6.6) (3.4)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests[socks]->gdown==4.6.6) (2.0.4)\n", + "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests[socks]->gdown==4.6.6) (2023.7.22)\n", + "Requirement already satisfied: PySocks!=1.5.7,>=1.5.6 in /usr/local/lib/python3.10/dist-packages (from requests[socks]->gdown==4.6.6) (1.7.1)\n" + ] + } + ], "source": [ "# To install only the requirements of this notebook, uncomment the lines below and run this cell\n", "\n", "# ===========================\n", "\n", - "!pip install numpy==1.19.5\n", - "!pip install pandas==1.1.5\n", - "!pip install gensim==3.8.3\n", - "!pip install wget==3.2\n", - "!pip install nltk==3.5\n", - "!pip install scikit-learn==0.21.3\n", + "!pip install numpy==1.23.5\n", + "!pip install pandas==1.5.3\n", + "!pip install gensim==4.3.1\n", + "!pip install nltk==3.8.1\n", + "!pip install scikit-learn==1.2.2\n", + "!pip install gdown==4.6.6\n", "\n", "# ===========================" - ], - "execution_count": 1, - "outputs": [ - { - "output_type": "stream", - "text": [ - "Requirement already satisfied: numpy==1.19.5 in /usr/local/lib/python3.7/dist-packages (1.19.5)\n", - "Requirement already satisfied: pandas==1.1.5 in /usr/local/lib/python3.7/dist-packages (1.1.5)\n", - "Requirement already satisfied: python-dateutil>=2.7.3 in /usr/local/lib/python3.7/dist-packages (from pandas==1.1.5) (2.8.1)\n", - "Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.7/dist-packages (from pandas==1.1.5) (2018.9)\n", - "Requirement already satisfied: numpy>=1.15.4 in /usr/local/lib/python3.7/dist-packages (from pandas==1.1.5) (1.19.5)\n", - "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.7/dist-packages (from python-dateutil>=2.7.3->pandas==1.1.5) (1.15.0)\n", - "Collecting gensim==3.8.3\n", - " Downloading gensim-3.8.3-cp37-cp37m-manylinux1_x86_64.whl (24.2 MB)\n", - "\u001b[K |████████████████████████████████| 24.2 MB 84.7 MB/s \n", - "\u001b[?25hRequirement already satisfied: numpy>=1.11.3 in /usr/local/lib/python3.7/dist-packages (from gensim==3.8.3) (1.19.5)\n", - "Requirement already satisfied: six>=1.5.0 in /usr/local/lib/python3.7/dist-packages (from gensim==3.8.3) (1.15.0)\n", - "Requirement already satisfied: scipy>=0.18.1 in /usr/local/lib/python3.7/dist-packages (from gensim==3.8.3) (1.4.1)\n", - "Requirement already satisfied: smart-open>=1.8.1 in /usr/local/lib/python3.7/dist-packages (from gensim==3.8.3) (5.1.0)\n", - "Installing collected packages: gensim\n", - " Attempting uninstall: gensim\n", - " Found existing installation: gensim 3.6.0\n", - " Uninstalling gensim-3.6.0:\n", - " Successfully uninstalled gensim-3.6.0\n", - "Successfully installed gensim-3.8.3\n", - "Collecting wget==3.2\n", - " Downloading wget-3.2.zip (10 kB)\n", - "Building wheels for collected packages: wget\n", - " Building wheel for wget (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for wget: filename=wget-3.2-py3-none-any.whl size=9673 sha256=4877de9e41ccfba395a6bc044ccad7ba2ea4f6324ca63bbf9da41b644eb8efea\n", - " Stored in directory: /root/.cache/pip/wheels/a1/b6/7c/0e63e34eb06634181c63adacca38b79ff8f35c37e3c13e3c02\n", - "Successfully built wget\n", - "Installing collected packages: wget\n", - "Successfully installed wget-3.2\n", - "Collecting nltk==3.5\n", - " Downloading nltk-3.5.zip (1.4 MB)\n", - "\u001b[K |████████████████████████████████| 1.4 MB 14.8 MB/s \n", - "\u001b[?25hRequirement already satisfied: click in /usr/local/lib/python3.7/dist-packages (from nltk==3.5) (7.1.2)\n", - "Requirement already satisfied: joblib in /usr/local/lib/python3.7/dist-packages (from nltk==3.5) (1.0.1)\n", - "Requirement already satisfied: regex in /usr/local/lib/python3.7/dist-packages (from nltk==3.5) (2019.12.20)\n", - "Requirement already satisfied: tqdm in /usr/local/lib/python3.7/dist-packages (from nltk==3.5) (4.41.1)\n", - "Building wheels for collected packages: nltk\n", - " Building wheel for nltk (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for nltk: filename=nltk-3.5-py3-none-any.whl size=1434691 sha256=efc90917aca010ac50551beb55d51252d0b46e103b87d83a9b66c70d6b6fd4ba\n", - " Stored in directory: /root/.cache/pip/wheels/45/6c/46/a1865e7ba706b3817f5d1b2ff7ce8996aabdd0d03d47ba0266\n", - "Successfully built nltk\n", - "Installing collected packages: nltk\n", - " Attempting uninstall: nltk\n", - " Found existing installation: nltk 3.2.5\n", - " Uninstalling nltk-3.2.5:\n", - " Successfully uninstalled nltk-3.2.5\n", - "Successfully installed nltk-3.5\n", - "Collecting scikit-learn==0.21.3\n", - " Downloading scikit_learn-0.21.3-cp37-cp37m-manylinux1_x86_64.whl (6.7 MB)\n", - "\u001b[K |████████████████████████████████| 6.7 MB 14.8 MB/s \n", - "\u001b[?25hRequirement already satisfied: joblib>=0.11 in /usr/local/lib/python3.7/dist-packages (from scikit-learn==0.21.3) (1.0.1)\n", - "Requirement already satisfied: scipy>=0.17.0 in /usr/local/lib/python3.7/dist-packages (from scikit-learn==0.21.3) (1.4.1)\n", - "Requirement already satisfied: numpy>=1.11.0 in /usr/local/lib/python3.7/dist-packages (from scikit-learn==0.21.3) (1.19.5)\n", - "Installing collected packages: scikit-learn\n", - " Attempting uninstall: scikit-learn\n", - " Found existing installation: scikit-learn 0.22.2.post1\n", - " Uninstalling scikit-learn-0.22.2.post1:\n", - " Successfully uninstalled scikit-learn-0.22.2.post1\n", - "Successfully installed scikit-learn-0.21.3\n" - ], - "name": "stdout" - } ] }, { "cell_type": "code", + "execution_count": 4, "metadata": { "id": "URLGvBLv9T0M" }, + "outputs": [], "source": [ "# To install the requirements for the entire chapter, uncomment the lines below and run this cell\n", "\n", @@ -158,25 +157,35 @@ "# !pip install -r \"ch4-requirements.txt\"\n", "\n", "# ===========================" - ], - "execution_count": 2, - "outputs": [] + ] }, { "cell_type": "code", + "execution_count": 5, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "JQX8DAmBb_Hr", - "outputId": "a89dcee7-f76f-4bd9-ba60-8642b88ab50c" + "outputId": "65dc3618-5f7b-41a9-88bf-a5090b32f270" }, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "[nltk_data] Downloading package stopwords to /root/nltk_data...\n", + "[nltk_data] Unzipping corpora/stopwords.zip.\n", + "[nltk_data] Downloading package punkt to /root/nltk_data...\n", + "[nltk_data] Unzipping tokenizers/punkt.zip.\n" + ] + } + ], "source": [ "#basic imports\n", "import warnings\n", "warnings.filterwarnings('ignore')\n", "import os\n", - "import wget\n", "import gzip\n", "import shutil\n", "from time import time\n", @@ -194,57 +203,30 @@ "from gensim.models import Word2Vec, KeyedVectors\n", "from sklearn.linear_model import LogisticRegression\n", "from sklearn.model_selection import train_test_split\n", - "from sklearn.metrics import classification_report" - ], - "execution_count": 3, - "outputs": [ - { - "output_type": "stream", - "text": [ - "[nltk_data] Downloading package stopwords to /root/nltk_data...\n", - "[nltk_data] Unzipping corpora/stopwords.zip.\n", - "[nltk_data] Downloading package punkt to /root/nltk_data...\n", - "[nltk_data] Unzipping tokenizers/punkt.zip.\n" - ], - "name": "stderr" - } + "from sklearn.metrics import classification_report\n", + "\n", + "#google-drive download imports\n", + "import gdown" ] }, { "cell_type": "code", + "execution_count": 9, "metadata": { - "colab": { - "resources": { - "http://localhost:8080/nbextensions/google.colab/files.js": { - "data": "Ly8gQ29weXJpZ2h0IDIwMTcgR29vZ2xlIExMQwovLwovLyBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgIkxpY2Vuc2UiKTsKLy8geW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLgovLyBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXQKLy8KLy8gICAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjAKLy8KLy8gVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZQovLyBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiAiQVMgSVMiIEJBU0lTLAovLyBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC4KLy8gU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZAovLyBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS4KCi8qKgogKiBAZmlsZW92ZXJ2aWV3IEhlbHBlcnMgZm9yIGdvb2dsZS5jb2xhYiBQeXRob24gbW9kdWxlLgogKi8KKGZ1bmN0aW9uKHNjb3BlKSB7CmZ1bmN0aW9uIHNwYW4odGV4dCwgc3R5bGVBdHRyaWJ1dGVzID0ge30pIHsKICBjb25zdCBlbGVtZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3BhbicpOwogIGVsZW1lbnQudGV4dENvbnRlbnQgPSB0ZXh0OwogIGZvciAoY29uc3Qga2V5IG9mIE9iamVjdC5rZXlzKHN0eWxlQXR0cmlidXRlcykpIHsKICAgIGVsZW1lbnQuc3R5bGVba2V5XSA9IHN0eWxlQXR0cmlidXRlc1trZXldOwogIH0KICByZXR1cm4gZWxlbWVudDsKfQoKLy8gTWF4IG51bWJlciBvZiBieXRlcyB3aGljaCB3aWxsIGJlIHVwbG9hZGVkIGF0IGEgdGltZS4KY29uc3QgTUFYX1BBWUxPQURfU0laRSA9IDEwMCAqIDEwMjQ7CgpmdW5jdGlvbiBfdXBsb2FkRmlsZXMoaW5wdXRJZCwgb3V0cHV0SWQpIHsKICBjb25zdCBzdGVwcyA9IHVwbG9hZEZpbGVzU3RlcChpbnB1dElkLCBvdXRwdXRJZCk7CiAgY29uc3Qgb3V0cHV0RWxlbWVudCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKG91dHB1dElkKTsKICAvLyBDYWNoZSBzdGVwcyBvbiB0aGUgb3V0cHV0RWxlbWVudCB0byBtYWtlIGl0IGF2YWlsYWJsZSBmb3IgdGhlIG5leHQgY2FsbAogIC8vIHRvIHVwbG9hZEZpbGVzQ29udGludWUgZnJvbSBQeXRob24uCiAgb3V0cHV0RWxlbWVudC5zdGVwcyA9IHN0ZXBzOwoKICByZXR1cm4gX3VwbG9hZEZpbGVzQ29udGludWUob3V0cHV0SWQpOwp9CgovLyBUaGlzIGlzIHJvdWdobHkgYW4gYXN5bmMgZ2VuZXJhdG9yIChub3Qgc3VwcG9ydGVkIGluIHRoZSBicm93c2VyIHlldCksCi8vIHdoZXJlIHRoZXJlIGFyZSBtdWx0aXBsZSBhc3luY2hyb25vdXMgc3RlcHMgYW5kIHRoZSBQeXRob24gc2lkZSBpcyBnb2luZwovLyB0byBwb2xsIGZvciBjb21wbGV0aW9uIG9mIGVhY2ggc3RlcC4KLy8gVGhpcyB1c2VzIGEgUHJvbWlzZSB0byBibG9jayB0aGUgcHl0aG9uIHNpZGUgb24gY29tcGxldGlvbiBvZiBlYWNoIHN0ZXAsCi8vIHRoZW4gcGFzc2VzIHRoZSByZXN1bHQgb2YgdGhlIHByZXZpb3VzIHN0ZXAgYXMgdGhlIGlucHV0IHRvIHRoZSBuZXh0IHN0ZXAuCmZ1bmN0aW9uIF91cGxvYWRGaWxlc0NvbnRpbnVlKG91dHB1dElkKSB7CiAgY29uc3Qgb3V0cHV0RWxlbWVudCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKG91dHB1dElkKTsKICBjb25zdCBzdGVwcyA9IG91dHB1dEVsZW1lbnQuc3RlcHM7CgogIGNvbnN0IG5leHQgPSBzdGVwcy5uZXh0KG91dHB1dEVsZW1lbnQubGFzdFByb21pc2VWYWx1ZSk7CiAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShuZXh0LnZhbHVlLnByb21pc2UpLnRoZW4oKHZhbHVlKSA9PiB7CiAgICAvLyBDYWNoZSB0aGUgbGFzdCBwcm9taXNlIHZhbHVlIHRvIG1ha2UgaXQgYXZhaWxhYmxlIHRvIHRoZSBuZXh0CiAgICAvLyBzdGVwIG9mIHRoZSBnZW5lcmF0b3IuCiAgICBvdXRwdXRFbGVtZW50Lmxhc3RQcm9taXNlVmFsdWUgPSB2YWx1ZTsKICAgIHJldHVybiBuZXh0LnZhbHVlLnJlc3BvbnNlOwogIH0pOwp9CgovKioKICogR2VuZXJhdG9yIGZ1bmN0aW9uIHdoaWNoIGlzIGNhbGxlZCBiZXR3ZWVuIGVhY2ggYXN5bmMgc3RlcCBvZiB0aGUgdXBsb2FkCiAqIHByb2Nlc3MuCiAqIEBwYXJhbSB7c3RyaW5nfSBpbnB1dElkIEVsZW1lbnQgSUQgb2YgdGhlIGlucHV0IGZpbGUgcGlja2VyIGVsZW1lbnQuCiAqIEBwYXJhbSB7c3RyaW5nfSBvdXRwdXRJZCBFbGVtZW50IElEIG9mIHRoZSBvdXRwdXQgZGlzcGxheS4KICogQHJldHVybiB7IUl0ZXJhYmxlPCFPYmplY3Q+fSBJdGVyYWJsZSBvZiBuZXh0IHN0ZXBzLgogKi8KZnVuY3Rpb24qIHVwbG9hZEZpbGVzU3RlcChpbnB1dElkLCBvdXRwdXRJZCkgewogIGNvbnN0IGlucHV0RWxlbWVudCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKGlucHV0SWQpOwogIGlucHV0RWxlbWVudC5kaXNhYmxlZCA9IGZhbHNlOwoKICBjb25zdCBvdXRwdXRFbGVtZW50ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQob3V0cHV0SWQpOwogIG91dHB1dEVsZW1lbnQuaW5uZXJIVE1MID0gJyc7CgogIGNvbnN0IHBpY2tlZFByb21pc2UgPSBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4gewogICAgaW5wdXRFbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ2NoYW5nZScsIChlKSA9PiB7CiAgICAgIHJlc29sdmUoZS50YXJnZXQuZmlsZXMpOwogICAgfSk7CiAgfSk7CgogIGNvbnN0IGNhbmNlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2J1dHRvbicpOwogIGlucHV0RWxlbWVudC5wYXJlbnRFbGVtZW50LmFwcGVuZENoaWxkKGNhbmNlbCk7CiAgY2FuY2VsLnRleHRDb250ZW50ID0gJ0NhbmNlbCB1cGxvYWQnOwogIGNvbnN0IGNhbmNlbFByb21pc2UgPSBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4gewogICAgY2FuY2VsLm9uY2xpY2sgPSAoKSA9PiB7CiAgICAgIHJlc29sdmUobnVsbCk7CiAgICB9OwogIH0pOwoKICAvLyBXYWl0IGZvciB0aGUgdXNlciB0byBwaWNrIHRoZSBmaWxlcy4KICBjb25zdCBmaWxlcyA9IHlpZWxkIHsKICAgIHByb21pc2U6IFByb21pc2UucmFjZShbcGlja2VkUHJvbWlzZSwgY2FuY2VsUHJvbWlzZV0pLAogICAgcmVzcG9uc2U6IHsKICAgICAgYWN0aW9uOiAnc3RhcnRpbmcnLAogICAgfQogIH07CgogIGNhbmNlbC5yZW1vdmUoKTsKCiAgLy8gRGlzYWJsZSB0aGUgaW5wdXQgZWxlbWVudCBzaW5jZSBmdXJ0aGVyIHBpY2tzIGFyZSBub3QgYWxsb3dlZC4KICBpbnB1dEVsZW1lbnQuZGlzYWJsZWQgPSB0cnVlOwoKICBpZiAoIWZpbGVzKSB7CiAgICByZXR1cm4gewogICAgICByZXNwb25zZTogewogICAgICAgIGFjdGlvbjogJ2NvbXBsZXRlJywKICAgICAgfQogICAgfTsKICB9CgogIGZvciAoY29uc3QgZmlsZSBvZiBmaWxlcykgewogICAgY29uc3QgbGkgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdsaScpOwogICAgbGkuYXBwZW5kKHNwYW4oZmlsZS5uYW1lLCB7Zm9udFdlaWdodDogJ2JvbGQnfSkpOwogICAgbGkuYXBwZW5kKHNwYW4oCiAgICAgICAgYCgke2ZpbGUudHlwZSB8fCAnbi9hJ30pIC0gJHtmaWxlLnNpemV9IGJ5dGVzLCBgICsKICAgICAgICBgbGFzdCBtb2RpZmllZDogJHsKICAgICAgICAgICAgZmlsZS5sYXN0TW9kaWZpZWREYXRlID8gZmlsZS5sYXN0TW9kaWZpZWREYXRlLnRvTG9jYWxlRGF0ZVN0cmluZygpIDoKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ24vYSd9IC0gYCkpOwogICAgY29uc3QgcGVyY2VudCA9IHNwYW4oJzAlIGRvbmUnKTsKICAgIGxpLmFwcGVuZENoaWxkKHBlcmNlbnQpOwoKICAgIG91dHB1dEVsZW1lbnQuYXBwZW5kQ2hpbGQobGkpOwoKICAgIGNvbnN0IGZpbGVEYXRhUHJvbWlzZSA9IG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7CiAgICAgIGNvbnN0IHJlYWRlciA9IG5ldyBGaWxlUmVhZGVyKCk7CiAgICAgIHJlYWRlci5vbmxvYWQgPSAoZSkgPT4gewogICAgICAgIHJlc29sdmUoZS50YXJnZXQucmVzdWx0KTsKICAgICAgfTsKICAgICAgcmVhZGVyLnJlYWRBc0FycmF5QnVmZmVyKGZpbGUpOwogICAgfSk7CiAgICAvLyBXYWl0IGZvciB0aGUgZGF0YSB0byBiZSByZWFkeS4KICAgIGxldCBmaWxlRGF0YSA9IHlpZWxkIHsKICAgICAgcHJvbWlzZTogZmlsZURhdGFQcm9taXNlLAogICAgICByZXNwb25zZTogewogICAgICAgIGFjdGlvbjogJ2NvbnRpbnVlJywKICAgICAgfQogICAgfTsKCiAgICAvLyBVc2UgYSBjaHVua2VkIHNlbmRpbmcgdG8gYXZvaWQgbWVzc2FnZSBzaXplIGxpbWl0cy4gU2VlIGIvNjIxMTU2NjAuCiAgICBsZXQgcG9zaXRpb24gPSAwOwogICAgZG8gewogICAgICBjb25zdCBsZW5ndGggPSBNYXRoLm1pbihmaWxlRGF0YS5ieXRlTGVuZ3RoIC0gcG9zaXRpb24sIE1BWF9QQVlMT0FEX1NJWkUpOwogICAgICBjb25zdCBjaHVuayA9IG5ldyBVaW50OEFycmF5KGZpbGVEYXRhLCBwb3NpdGlvbiwgbGVuZ3RoKTsKICAgICAgcG9zaXRpb24gKz0gbGVuZ3RoOwoKICAgICAgY29uc3QgYmFzZTY0ID0gYnRvYShTdHJpbmcuZnJvbUNoYXJDb2RlLmFwcGx5KG51bGwsIGNodW5rKSk7CiAgICAgIHlpZWxkIHsKICAgICAgICByZXNwb25zZTogewogICAgICAgICAgYWN0aW9uOiAnYXBwZW5kJywKICAgICAgICAgIGZpbGU6IGZpbGUubmFtZSwKICAgICAgICAgIGRhdGE6IGJhc2U2NCwKICAgICAgICB9LAogICAgICB9OwoKICAgICAgbGV0IHBlcmNlbnREb25lID0gZmlsZURhdGEuYnl0ZUxlbmd0aCA9PT0gMCA/CiAgICAgICAgICAxMDAgOgogICAgICAgICAgTWF0aC5yb3VuZCgocG9zaXRpb24gLyBmaWxlRGF0YS5ieXRlTGVuZ3RoKSAqIDEwMCk7CiAgICAgIHBlcmNlbnQudGV4dENvbnRlbnQgPSBgJHtwZXJjZW50RG9uZX0lIGRvbmVgOwoKICAgIH0gd2hpbGUgKHBvc2l0aW9uIDwgZmlsZURhdGEuYnl0ZUxlbmd0aCk7CiAgfQoKICAvLyBBbGwgZG9uZS4KICB5aWVsZCB7CiAgICByZXNwb25zZTogewogICAgICBhY3Rpb246ICdjb21wbGV0ZScsCiAgICB9CiAgfTsKfQoKc2NvcGUuZ29vZ2xlID0gc2NvcGUuZ29vZ2xlIHx8IHt9OwpzY29wZS5nb29nbGUuY29sYWIgPSBzY29wZS5nb29nbGUuY29sYWIgfHwge307CnNjb3BlLmdvb2dsZS5jb2xhYi5fZmlsZXMgPSB7CiAgX3VwbG9hZEZpbGVzLAogIF91cGxvYWRGaWxlc0NvbnRpbnVlLAp9Owp9KShzZWxmKTsK", - "ok": true, - "headers": [ - [ - "content-type", - "application/javascript" - ] - ], - "status": 200, - "status_text": "" - } - }, - "base_uri": "https://localhost:8080/", - "height": 140 - }, - "id": "S8RM8c6AS8AX", - "outputId": "0b366a76-49b0-4170-dce6-33572a37a929" + "id": "S8RM8c6AS8AX" }, + "outputs": [], "source": [ "try:\n", " from google.colab import files\n", - " \n", + "\n", " # upload 'amazon_cells_labelled.txt', 'imdb_labelled.txt' and 'yelp_labelled.txt' present in \"sentiment labelled sentences\" folder\n", " uploaded = files.upload()\n", - " \n", + "\n", " !mkdir DATAPATH\n", " !mv -t DATAPATH amazon_cells_labelled.txt imdb_labelled.txt yelp_labelled.txt\n", " !cat DATAPATH/amazon_cells_labelled.txt DATAPATH/imdb_labelled.txt DATAPATH/yelp_labelled.txt > DATAPATH/sentiment_sentences.txt\n", - " \n", + "\n", "except ModuleNotFoundError:\n", "\n", " fil = 'sentiment_sentences.txt'\n", @@ -252,7 +234,7 @@ " if not os.path.exists(\"Data/sentiment_sentences.txt\"):\n", " file = open(os.path.join(path, fil), 'w')\n", " file.close()\n", - " \n", + "\n", " # combined the three files to make sentiment_sentences.txt\n", " filenames = ['amazon_cells_labelled.txt', 'imdb_labelled.txt', 'yelp_labelled.txt']\n", "\n", @@ -263,88 +245,95 @@ " print(\"File created\")\n", " else:\n", " print(\"File already exists\")" - ], - "execution_count": 4, - "outputs": [ - { - "output_type": "display_data", - "data": { - "text/html": [ - "\n", - " \n", - " \n", - " Upload widget is only available when the cell has been executed in the\n", - " current browser session. Please rerun this cell to enable.\n", - " \n", - " " - ], - "text/plain": [ - "" - ] - }, - "metadata": { - "tags": [] - } - }, - { - "output_type": "stream", - "text": [ - "Saving amazon_cells_labelled.txt to amazon_cells_labelled.txt\n", - "Saving imdb_labelled.txt to imdb_labelled.txt\n", - "Saving yelp_labelled.txt to yelp_labelled.txt\n" - ], - "name": "stdout" - } ] }, { "cell_type": "code", + "execution_count": 20, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "COUGXAxcb_H5", - "scrolled": true, - "outputId": "b88ee64f-6c36-412e-ce57-f9387eec3051" + "outputId": "640ac771-0389-4640-ab79-a11f26bd2c29", + "scrolled": true }, + "outputs": [ + { + "metadata": { + "tags": null + }, + "name": "stdout", + "output_type": "stream", + "text": [ + "Downloading...\n", + "From: https://drive.google.com/u/0/uc?id=0B7XkCwpI5KDYNlNUTTlSS21pQmM\n", + "To: /content/DATAPATH/GoogleNews-vectors-negative300.bin.gz\n", + "100% 1.65G/1.65G [00:15<00:00, 103MB/s]\n", + "CPU times: user 28.1 s, sys: 3.92 s, total: 32.1 s\n", + "Wall time: 34.4 s\n", + "done loading Word2Vec\n" + ] + } + ], "source": [ "#Load the pre-trained word2vec model and the dataset\n", + "\n", + "def check_if_file_exists(filename: str, locations: list) -> str :\n", + " for location in locations:\n", + " if os.path.exists(os.path.join(location, filename)):\n", + " return location\n", + " return None\n", + "\n", + "def extract_data(location: str) -> None:\n", + " with gzip.open(os.path.join(location, 'GoogleNews-vectors-negative300.bin.gz'), 'rb') as f_in:\n", + " with open(os.path.join('./Data', './GoogleNews-vectors-negative300.bin'), 'wb') as f_out:\n", + " shutil.copyfileobj(f_in, f_out)\n", + "\n", "try:\n", - " \n", " from google.colab import files\n", " data_path= \"DATAPATH\"\n", - " !wget -P DATAPATH https://s3.amazonaws.com/dl4j-distribution/GoogleNews-vectors-negative300.bin.gz\n", - " !gunzip DATAPATH/GoogleNews-vectors-negative300.bin.gz \n", + " !gdown -O DATAPATH/ https://drive.google.com/u/0/uc?id=0B7XkCwpI5KDYNlNUTTlSS21pQmM&export=download\n", + " !gunzip DATAPATH/GoogleNews-vectors-negative300.bin.gz\n", " path_to_model = 'DATAPATH/GoogleNews-vectors-negative300.bin'\n", " training_data_path = \"DATAPATH/sentiment_sentences.txt\"\n", - " \n", + "\n", "except ModuleNotFoundError:\n", - " \n", - " data_path= \"Data\"\n", - " \n", - " if not os.path.exists('GoogleNews-vectors-negative300.bin'):\n", - " if not os.path.exists('../Ch2/GoogleNews-vectors-negative300.bin'):\n", - " if not os.path.exists('../Ch3/GoogleNews-vectors-negative300.bin'):\n", - " wget.download(\"https://s3.amazonaws.com/dl4j-distribution/GoogleNews-vectors-negative300.bin.gz\")\n", - "\n", - " with gzip.open('GoogleNews-vectors-negative300.bin.gz', 'rb') as f_in:\n", - " with open('GoogleNews-vectors-negative300.bin', 'wb') as f_out:\n", - " shutil.copyfileobj(f_in, f_out)\n", - "\n", - " path_to_model = 'GoogleNews-vectors-negative300.bin'\n", - " else:\n", - " path_to_model = '../Ch3/GoogleNews-vectors-negative300.bin'\n", "\n", - " else:\n", - " path_to_model = '../Ch2/GoogleNews-vectors-negative300.bin'\n", + " data_path = './Data/'\n", + " compressed_file_name = 'GoogleNews-vectors-negative300.bin.gz'\n", + " extracted_file_name = 'GoogleNews-vectors-negative300.bin'\n", + "\n", + " # Check if Extracted File exists\n", + " location_of_extracted_file = check_if_file_exists(extracted_file_name, ['./Data','../Ch2/Data','../Ch3/Data'])\n", + "\n", + " if location_of_extracted_file:\n", + " # Extracted File exists\n", + " path_to_model = os.path.join(location_of_extracted_file, extracted_file_name)\n", + "\n", " else:\n", - " path_to_model = 'GoogleNews-vectors-negative300.bin'\n", - " \n", + " location_of_compressed_file = check_if_file_exists(compressed_file_name, ['./Data','../Ch2/Data','../Ch3/Data'])\n", + "\n", + " if location_of_compressed_file:\n", + " # Compressed File exists\n", + " extract_data(os.path.join(location_of_compressed_file))\n", + " path_to_model = os.path.join(data_path, extracted_file_name)\n", + "\n", + " else:\n", + " # Download File\n", + " output_path = './Data/'\n", + " gdown.download(\"https://drive.google.com/u/0/uc?id=0B7XkCwpI5KDYNlNUTTlSS21pQmM&export=download\", output=output_path)\n", + "\n", + " # Extract File\n", + " extract_data(output_path)\n", + "\n", + " path_to_model = os.path.join(data_path, extracted_file_name)\n", + "\n", + " print(f\"Data Present at location : {path_to_model}\")\n", " training_data_path = os.path.join(data_path, \"sentiment_sentences.txt\")\n", - " \n", - " \n", - "#Load W2V model. This will take some time. \n", + "\n", + "\n", + "#Load W2V model. This will take some time.\n", "%time w2v_model = KeyedVectors.load_word2vec_format(path_to_model, binary=True)\n", "print('done loading Word2Vec')\n", "\n", @@ -357,95 +346,85 @@ " text, sentiment = line.split(\"\\t\")\n", " texts.append(text)\n", " cats.append(sentiment)" - ], - "execution_count": 5, - "outputs": [ - { - "output_type": "stream", - "text": [ - "--2021-07-20 08:36:30-- https://s3.amazonaws.com/dl4j-distribution/GoogleNews-vectors-negative300.bin.gz\n", - "Resolving s3.amazonaws.com (s3.amazonaws.com)... 52.217.130.248\n", - "Connecting to s3.amazonaws.com (s3.amazonaws.com)|52.217.130.248|:443... connected.\n", - "HTTP request sent, awaiting response... 200 OK\n", - "Length: 1647046227 (1.5G) [application/x-gzip]\n", - "Saving to: ‘DATAPATH/GoogleNews-vectors-negative300.bin.gz’\n", - "\n", - "GoogleNews-vectors- 100%[===================>] 1.53G 35.4MB/s in 46s \n", - "\n", - "2021-07-20 08:37:16 (34.1 MB/s) - ‘DATAPATH/GoogleNews-vectors-negative300.bin.gz’ saved [1647046227/1647046227]\n", - "\n", - "CPU times: user 19.6 s, sys: 3.11 s, total: 22.7 s\n", - "Wall time: 35.2 s\n", - "done loading Word2Vec\n" - ], - "name": "stdout" - } ] }, { "cell_type": "code", + "execution_count": 21, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "m-WjFyC6b_IE", - "outputId": "87270b42-96b9-4420-f22a-6f13160e5cbe" + "outputId": "7cb1a092-d3fa-4bf4-e437-6d079da7ed74" }, - "source": [ - "#Inspect the model\n", - "word2vec_vocab = w2v_model.vocab.keys()\n", - "word2vec_vocab_lower = [item.lower() for item in word2vec_vocab]\n", - "print(len(word2vec_vocab))" - ], - "execution_count": 6, "outputs": [ { "output_type": "stream", + "name": "stdout", "text": [ "3000000\n" - ], - "name": "stdout" + ] } + ], + "source": [ + "#Inspect the model\n", + "word2vec_vocab = w2v_model.key_to_index.keys()\n", + "word2vec_vocab_lower = [item.lower() for item in word2vec_vocab]\n", + "print(len(word2vec_vocab))" ] }, { "cell_type": "code", + "execution_count": 22, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "XEz30Jztb_IP", - "outputId": "18794f4b-828f-4c7c-9708-b9af3143d700" + "outputId": "7c37e0e1-9f2e-411b-cdac-b89ecc39a0ea" }, - "source": [ - "#Inspect the dataset\n", - "print(len(cats), len(texts))\n", - "print(texts[1])\n", - "print(cats[1])" - ], - "execution_count": 7, "outputs": [ { "output_type": "stream", + "name": "stdout", "text": [ "3000 3000\n", "Good case, Excellent value.\n", "1\n", "\n" - ], - "name": "stdout" + ] } + ], + "source": [ + "#Inspect the dataset\n", + "print(len(cats), len(texts))\n", + "print(texts[1])\n", + "print(cats[1])" ] }, { "cell_type": "code", + "execution_count": 23, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "MFOGaDTwb_Ig", - "outputId": "b9983e21-f00e-4c3e-ebe4-e2c8be738398" + "outputId": "4e50a4a9-1f40-429c-c7b3-e445e42cae6f" }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "3000 3000\n", + "['good', 'case', 'excellent', 'value']\n", + "1\n", + "\n" + ] + } + ], "source": [ "#preprocess the text.\n", "def preprocess_corpus(texts):\n", @@ -454,37 +433,34 @@ " #Nested function that lowercases, removes stopwords and digits from a list of tokens\n", " return [token.lower() for token in tokens if token.lower() not in mystopwords and not token.isdigit()\n", " and token not in punctuation]\n", - " #This return statement below uses the above function to process twitter tokenizer output further. \n", + " #This return statement below uses the above function to process twitter tokenizer output further.\n", " return [remove_stops_digits(word_tokenize(text)) for text in texts]\n", "\n", "texts_processed = preprocess_corpus(texts)\n", "print(len(cats), len(texts_processed))\n", "print(texts_processed[1])\n", "print(cats[1])" - ], - "execution_count": 8, - "outputs": [ - { - "output_type": "stream", - "text": [ - "3000 3000\n", - "['good', 'case', 'excellent', 'value']\n", - "1\n", - "\n" - ], - "name": "stdout" - } ] }, { "cell_type": "code", + "execution_count": 24, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "fXRiGtY1b_Iq", - "outputId": "fdba211b-e6bd-453e-b70d-79546d6ef005" + "outputId": "1f5eaad6-939e-46fc-cf27-21798f78e18f" }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "3000\n" + ] + } + ], "source": [ "# Creating a feature vector by averaging all embeddings for all sentences\n", "def embedding_feats(list_of_lists):\n", @@ -493,13 +469,13 @@ " feats = []\n", " for tokens in list_of_lists:\n", " feat_for_this = np.zeros(DIMENSION)\n", - " count_for_this = 0 + 1e-5 # to avoid divide-by-zero \n", + " count_for_this = 0 + 1e-5 # to avoid divide-by-zero\n", " for token in tokens:\n", " if token in w2v_model:\n", " feat_for_this += w2v_model[token]\n", " count_for_this +=1\n", " if(count_for_this!=0):\n", - " feats.append(feat_for_this/count_for_this) \n", + " feats.append(feat_for_this/count_for_this)\n", " else:\n", " feats.append(zero_vector)\n", " return feats\n", @@ -507,56 +483,46 @@ "\n", "train_vectors = embedding_feats(texts_processed)\n", "print(len(train_vectors))" - ], - "execution_count": 9, - "outputs": [ - { - "output_type": "stream", - "text": [ - "3000\n" - ], - "name": "stdout" - } ] }, { "cell_type": "code", + "execution_count": 25, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "mr9IaQppb_Ix", - "outputId": "2c372ab4-38d8-4884-99dc-9bb3bbba16d0" + "outputId": "c74d84ea-6586-4d68-c8f7-e2e36b7f915d" }, - "source": [ - "#Take any classifier (LogisticRegression here, and train/test it like before.\n", - "classifier = LogisticRegression(random_state=1234)\n", - "train_data, test_data, train_cats, test_cats = train_test_split(train_vectors, cats)\n", - "classifier.fit(train_data, train_cats)\n", - "print(\"Accuracy: \", classifier.score(test_data, test_cats))\n", - "preds = classifier.predict(test_data)\n", - "print(classification_report(test_cats, preds))" - ], - "execution_count": 10, "outputs": [ { "output_type": "stream", + "name": "stdout", "text": [ - "Accuracy: 0.8453333333333334\n", + "Accuracy: 0.8013333333333333\n", " precision recall f1-score support\n", "\n", " 0\n", - " 0.87 0.83 0.85 388\n", + " 0.77 0.83 0.80 353\n", " 1\n", - " 0.82 0.86 0.84 362\n", + " 0.84 0.78 0.81 397\n", "\n", - " accuracy 0.85 750\n", - " macro avg 0.85 0.85 0.85 750\n", - "weighted avg 0.85 0.85 0.85 750\n", + " accuracy 0.80 750\n", + " macro avg 0.80 0.80 0.80 750\n", + "weighted avg 0.80 0.80 0.80 750\n", "\n" - ], - "name": "stdout" + ] } + ], + "source": [ + "#Take any classifier (LogisticRegression here, and train/test it like before.\n", + "classifier = LogisticRegression(random_state=1234)\n", + "train_data, test_data, train_cats, test_cats = train_test_split(train_vectors, cats)\n", + "classifier.fit(train_data, train_cats)\n", + "print(\"Accuracy: \", classifier.score(test_data, test_cats))\n", + "preds = classifier.predict(test_data)\n", + "print(classification_report(test_cats, preds))" ] }, { @@ -565,8 +531,42 @@ "id": "k7wjLB8rb_JB" }, "source": [ - "Not bad. With little efforts we got 81% accuracy. Thats a great starting model to have!!" + "Not bad. With little efforts we got 80% accuracy. Thats a great starting model to have!!" ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "id": "h4lF7mkPCAuy" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.17" } - ] + }, + "nbformat": 4, + "nbformat_minor": 0 } \ No newline at end of file diff --git a/Ch4/04_FastText_Example.ipynb b/Ch4/04_FastText_Example.ipynb index b04f9d4..219a8ae 100644 --- a/Ch4/04_FastText_Example.ipynb +++ b/Ch4/04_FastText_Example.ipynb @@ -1,571 +1,729 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "id": "6FIToZHAhz2O" - }, - "source": [ - "In this notebook we will demonstrate using the fastText library to perform text classificatoin on the dbpedie data which can we downloaded from [here](https://github.com/le-scientifique/torchDatasets/raw/master/dbpedia_csv.tar.gz).
fastText is a library for learning of word embeddings and text classification created by Facebook's AI Research (FAIR) lab. The model allows to create an unsupervised learning or supervised learning algorithm for obtaining vector representations for words. Facebook makes available pretrained models for 294 languages(source: [wiki](https://en.wikipedia.org/wiki/FastText)).
\n", - "**Note**: This notebook uses an older version of fasttext." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "6FIToZHAhz2O" + }, + "source": [ + "In this notebook we will demonstrate using the fastText library to perform text classificatoin on the dbpedie data which can we downloaded from [here](https://github.com/le-scientifique/torchDatasets/raw/master/dbpedia_csv.tar.gz).
fastText is a library for learning of word embeddings and text classification created by Facebook's AI Research (FAIR) lab. The model allows to create an unsupervised learning or supervised learning algorithm for obtaining vector representations for words. Facebook makes available pretrained models for 294 languages(source: [wiki](https://en.wikipedia.org/wiki/FastText)).
\n", + "**Note**: This notebook uses an older version of fasttext." + ] }, - "id": "UBnT5t_LiCU2", - "outputId": "ca0bcea9-75a7-4237-e58e-154c3d72e89f" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Requirement already satisfied: pandas==1.1.5 in /usr/local/lib/python3.7/dist-packages (1.1.5)\n", - "Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.7/dist-packages (from pandas==1.1.5) (2018.9)\n", - "Requirement already satisfied: python-dateutil>=2.7.3 in /usr/local/lib/python3.7/dist-packages (from pandas==1.1.5) (2.8.1)\n", - "Requirement already satisfied: numpy>=1.15.4 in /usr/local/lib/python3.7/dist-packages (from pandas==1.1.5) (1.19.5)\n", - "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.7/dist-packages (from python-dateutil>=2.7.3->pandas==1.1.5) (1.15.0)\n", - "Collecting wget==3.2\n", - " Downloading https://files.pythonhosted.org/packages/47/6a/62e288da7bcda82b935ff0c6cfe542970f04e29c756b0e147251b2fb251f/wget-3.2.zip\n", - "Building wheels for collected packages: wget\n", - " Building wheel for wget (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for wget: filename=wget-3.2-cp37-none-any.whl size=9675 sha256=0e1e014b6bf086637aea4bfe15707b7d8d825e7280cd2f9c6ec1943ef00e80c7\n", - " Stored in directory: /root/.cache/pip/wheels/40/15/30/7d8f7cea2902b4db79e3fea550d7d7b85ecb27ef992b618f3f\n", - "Successfully built wget\n", - "Installing collected packages: wget\n", - "Successfully installed wget-3.2\n", - "Collecting fasttext==0.9.2\n", - "\u001b[?25l Downloading https://files.pythonhosted.org/packages/f8/85/e2b368ab6d3528827b147fdb814f8189acc981a4bc2f99ab894650e05c40/fasttext-0.9.2.tar.gz (68kB)\n", - "\u001b[K |████████████████████████████████| 71kB 6.8MB/s \n", - "\u001b[?25hRequirement already satisfied: pybind11>=2.2 in /usr/local/lib/python3.7/dist-packages (from fasttext==0.9.2) (2.6.2)\n", - "Requirement already satisfied: setuptools>=0.7.0 in /usr/local/lib/python3.7/dist-packages (from fasttext==0.9.2) (57.0.0)\n", - "Requirement already satisfied: numpy in /usr/local/lib/python3.7/dist-packages (from fasttext==0.9.2) (1.19.5)\n", - "Building wheels for collected packages: fasttext\n", - " Building wheel for fasttext (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for fasttext: filename=fasttext-0.9.2-cp37-cp37m-linux_x86_64.whl size=3091748 sha256=f30effec512519a72b11f0eaf7aa8a6b57df1643345f8e51bf7b1cb010552792\n", - " Stored in directory: /root/.cache/pip/wheels/98/ba/7f/b154944a1cf5a8cee91c154b75231136cc3a3321ab0e30f592\n", - "Successfully built fasttext\n", - "Installing collected packages: fasttext\n", - "Successfully installed fasttext-0.9.2\n" - ] - } - ], - "source": [ - "# To install only the requirements of this notebook, uncomment the lines below and run this cell\n", - "\n", - "# ===========================\n", - "\n", - "!pip install pandas==1.1.5\n", - "!pip install wget==3.2\n", - "!pip install fasttext==0.9.2\n", - "\n", - "# ===========================" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "id": "zrBi6bvbiCU4" - }, - "outputs": [], - "source": [ - "# To install the requirements for the entire chapter, uncomment the lines below and run this cell\n", - "\n", - "# ===========================\n", - "\n", - "# try:\n", - "# import google.colab\n", - "# !curl https://raw.githubusercontent.com/practical-nlp/practical-nlp/master/Ch4/ch4-requirements.txt | xargs -n 1 -L 1 pip install\n", - "# except ModuleNotFoundError:\n", - "# !pip install -r \"ch4-requirements.txt\"\n", - "\n", - "# ===========================" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "id": "YKgZXvTGb61z" - }, - "outputs": [], - "source": [ - "#necessary imports\n", - "import os\n", - "import pandas as pd\n", - "import wget\n", - "import tarfile" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "UBnT5t_LiCU2", + "outputId": "c63a8ae7-5816-486c-b161-1a597cff909f" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Requirement already satisfied: pandas==1.5.3 in /usr/local/lib/python3.10/dist-packages (1.5.3)\n", + "Requirement already satisfied: python-dateutil>=2.8.1 in /usr/local/lib/python3.10/dist-packages (from pandas==1.5.3) (2.8.2)\n", + "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.10/dist-packages (from pandas==1.5.3) (2023.3)\n", + "Requirement already satisfied: numpy>=1.21.0 in /usr/local/lib/python3.10/dist-packages (from pandas==1.5.3) (1.23.5)\n", + "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.8.1->pandas==1.5.3) (1.16.0)\n", + "Requirement already satisfied: gdown==4.6.6 in /usr/local/lib/python3.10/dist-packages (4.6.6)\n", + "Requirement already satisfied: filelock in /usr/local/lib/python3.10/dist-packages (from gdown==4.6.6) (3.12.2)\n", + "Requirement already satisfied: requests[socks] in /usr/local/lib/python3.10/dist-packages (from gdown==4.6.6) (2.31.0)\n", + "Requirement already satisfied: six in /usr/local/lib/python3.10/dist-packages (from gdown==4.6.6) (1.16.0)\n", + "Requirement already satisfied: tqdm in /usr/local/lib/python3.10/dist-packages (from gdown==4.6.6) (4.66.1)\n", + "Requirement already satisfied: beautifulsoup4 in /usr/local/lib/python3.10/dist-packages (from gdown==4.6.6) (4.11.2)\n", + "Requirement already satisfied: soupsieve>1.2 in /usr/local/lib/python3.10/dist-packages (from beautifulsoup4->gdown==4.6.6) (2.4.1)\n", + "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests[socks]->gdown==4.6.6) (3.2.0)\n", + "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests[socks]->gdown==4.6.6) (3.4)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests[socks]->gdown==4.6.6) (2.0.4)\n", + "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests[socks]->gdown==4.6.6) (2023.7.22)\n", + "Requirement already satisfied: PySocks!=1.5.7,>=1.5.6 in /usr/local/lib/python3.10/dist-packages (from requests[socks]->gdown==4.6.6) (1.7.1)\n", + "Requirement already satisfied: fasttext==0.9.2 in /usr/local/lib/python3.10/dist-packages (0.9.2)\n", + "Requirement already satisfied: pybind11>=2.2 in /usr/local/lib/python3.10/dist-packages (from fasttext==0.9.2) (2.11.1)\n", + "Requirement already satisfied: setuptools>=0.7.0 in /usr/local/lib/python3.10/dist-packages (from fasttext==0.9.2) (67.7.2)\n", + "Requirement already satisfied: numpy in /usr/local/lib/python3.10/dist-packages (from fasttext==0.9.2) (1.23.5)\n" + ] + } + ], + "source": [ + "# To install only the requirements of this notebook, uncomment the lines below and run this cell\n", + "\n", + "# ===========================\n", + "\n", + "!pip install pandas==1.5.3\n", + "!pip install gdown==4.6.6\n", + "!pip install fasttext==0.9.2\n", + "\n", + "# ===========================" + ] }, - "id": "l6CfW7C3L4EB", - "outputId": "debf3639-77d2-4a2c-8aa1-3ff8438b9585" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "--2021-07-16 08:57:35-- https://github.com/le-scientifique/torchDatasets/raw/master/dbpedia_csv.tar.gz\n", - "Resolving github.com (github.com)... 140.82.121.4\n", - "Connecting to github.com (github.com)|140.82.121.4|:443... connected.\n", - "HTTP request sent, awaiting response... 301 Moved Permanently\n", - "Location: https://github.com/srhrshr/torchDatasets/raw/master/dbpedia_csv.tar.gz [following]\n", - "--2021-07-16 08:57:35-- https://github.com/srhrshr/torchDatasets/raw/master/dbpedia_csv.tar.gz\n", - "Reusing existing connection to github.com:443.\n", - "HTTP request sent, awaiting response... 302 Found\n", - "Location: https://raw.githubusercontent.com/srhrshr/torchDatasets/master/dbpedia_csv.tar.gz [following]\n", - "--2021-07-16 08:57:35-- https://raw.githubusercontent.com/srhrshr/torchDatasets/master/dbpedia_csv.tar.gz\n", - "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133, 185.199.109.133, 185.199.110.133, ...\n", - "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443... connected.\n", - "HTTP request sent, awaiting response... 200 OK\n", - "Length: 68431223 (65M) [application/octet-stream]\n", - "Saving to: ‘DATAPATH/dbpedia_csv.tar.gz’\n", - "\n", - "dbpedia_csv.tar.gz 100%[===================>] 65.26M 206MB/s in 0.3s \n", - "\n", - "2021-07-16 08:57:42 (206 MB/s) - ‘DATAPATH/dbpedia_csv.tar.gz’ saved [68431223/68431223]\n", - "\n", - "dbpedia_csv/\n", - "dbpedia_csv/test.csv\n", - "dbpedia_csv/classes.txt\n", - "dbpedia_csv/train.csv\n", - "dbpedia_csv/readme.txt\n", - "total 66M\n", - "drwxr-xr-x 3 root root 4.0K Jul 16 08:57 .\n", - "drwxr-xr-x 1 root root 4.0K Jul 16 08:57 ..\n", - "drwxrwxr-x 2 1000 1000 4.0K Mar 29 2015 dbpedia_csv\n", - "-rw-r--r-- 1 root root 66M Jul 16 08:57 dbpedia_csv.tar.gz\n" - ] - } - ], - "source": [ - "try :\n", - " \n", - " from google.colab import files\n", - " \n", - " # downloading the data\n", - " !wget -P DATAPATH https://github.com/le-scientifique/torchDatasets/raw/master/dbpedia_csv.tar.gz\n", - "\n", - " # untaring the required file\n", - " !tar -xvf DATAPATH/dbpedia_csv.tar.gz -C DATAPATH\n", - "\n", - " # sneek peek in the folder structure\n", - " !ls -lah DATAPATH\n", - " \n", - " # specifying the data_path\n", - " data_path = 'DATAPATH'\n", - " \n", - "except ModuleNotFoundError:\n", - " \n", - " if not os.path.exists(os.getcwd()+'\\\\Data\\\\dbpedia_csv') :\n", - " # downloading the data\n", - " url=\"https://github.com/le-scientifique/torchDatasets/raw/master/dbpedia_csv.tar.gz\"\n", - " path=os.getcwd()+'\\Data'\n", - " wget.download(url,path)\n", - "\n", - " # untaring the required file\n", - " temp=path+'\\dbpedia_csv.tar.gz'\n", - " tar = tarfile.open(temp, \"r:gz\")\n", - " tar.extractall(path) \n", - " tar.close()\n", - " \n", - " # specifying the data_path\n", - " data_path='Data'" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" + "cell_type": "code", + "execution_count": 2, + "metadata": { + "id": "zrBi6bvbiCU4" + }, + "outputs": [], + "source": [ + "# To install the requirements for the entire chapter, uncomment the lines below and run this cell\n", + "\n", + "# ===========================\n", + "\n", + "# try:\n", + "# import google.colab\n", + "# !curl https://raw.githubusercontent.com/practical-nlp/practical-nlp/master/Ch4/ch4-requirements.txt | xargs -n 1 -L 1 pip install\n", + "# except ModuleNotFoundError:\n", + "# !pip install -r \"ch4-requirements.txt\"\n", + "\n", + "# ===========================" + ] }, - "id": "lMoRw3oQb62I", - "outputId": "744d1cb7-4966-4db1-b176-c2020975ed94" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Train:(560000, 3) Test:(70000, 3)\n" - ] - } - ], - "source": [ - "# Loading train data\n", - "train_file = data_path + '/dbpedia_csv/train.csv'\n", - "df = pd.read_csv(train_file, header=None, names=['class','name','description'])\n", - "# Loading test data\n", - "test_file = data_path + '/dbpedia_csv/test.csv'\n", - "df_test = pd.read_csv(test_file, header=None, names=['class','name','description'])\n", - "# Data we have\n", - "print(\"Train:{} Test:{}\".format(df.shape,df_test.shape))\n" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 204 + "cell_type": "code", + "execution_count": 3, + "metadata": { + "id": "YKgZXvTGb61z" + }, + "outputs": [], + "source": [ + "#necessary imports\n", + "import os\n", + "import pandas as pd\n", + "import tarfile\n", + "import gdown" + ] }, - "id": "gaz226vXb62W", - "outputId": "a7e5ab41-732e-4a94-def6-5e62124d6bd5" - }, - "outputs": [ { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
classnamedescriptionclass_name
01E. D. Abbott LtdAbbott of Farnham E D Abbott Limited was a Br...Company
11Schwan-StabiloSchwan-STABILO is a German maker of pens for ...Company
21Q-workshopQ-workshop is a Polish company located in Poz...Company
31Marvell Software Solutions IsraelMarvell Software Solutions Israel known as RA...Company
41Bergan Mercy Medical CenterBergan Mercy Medical Center is a hospital loc...Company
\n", - "
" + "cell_type": "code", + "execution_count": 6, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "l6CfW7C3L4EB", + "outputId": "6e30eb61-0cdc-4616-d14e-46888017cac9" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Downloading...\n", + "From: https://drive.google.com/uc?export=download&id=0Bz8a_Dbh9QhbQ2Vic1kxMmZZQ1k\n", + "To: /content/DATAPATH/dbpedia_csv.tar.gz\n", + "100% 68.3M/68.3M [00:00<00:00, 164MB/s]\n", + "dbpedia_csv/\n", + "dbpedia_csv/classes.txt\n", + "dbpedia_csv/test.csv\n", + "dbpedia_csv/train.csv\n", + "dbpedia_csv/readme.txt\n", + "total 66M\n", + "drwxr-xr-x 3 root root 4.0K Aug 22 18:17 .\n", + "drwxr-xr-x 1 root root 4.0K Aug 22 18:17 ..\n", + "drwxrwxr-x 2 3666 11555 4.0K Sep 9 2015 dbpedia_csv\n", + "-rw-r--r-- 1 root root 66M Aug 22 18:17 dbpedia_csv.tar.gz\n" + ] + } ], - "text/plain": [ - " class ... class_name\n", - "0 1 ... Company\n", - "1 1 ... Company\n", - "2 1 ... Company\n", - "3 1 ... Company\n", - "4 1 ... Company\n", - "\n", - "[5 rows x 4 columns]" + "source": [ + "def check_if_file_exists(filename: str, locations: list) -> str :\n", + " for location in locations:\n", + " if os.path.exists(os.path.join(location, filename)):\n", + " return location\n", + " return None\n", + "\n", + "def extract_tar_file(file_path: str, extraction_path: str) -> None:\n", + " tar = tarfile.open(file_path, \"r:gz\")\n", + " tar.extractall(extraction_path)\n", + " tar.close()\n", + "\n", + "try :\n", + "\n", + " from google.colab import files\n", + "\n", + " # specifying the data_path\n", + " data_path = \"./DATAPATH\"\n", + "\n", + " !mkdir ./DATAPATH\n", + "\n", + " # downloading the data\n", + " !gdown -O ./DATAPATH/dbpedia_csv.tar.gz \"https://drive.google.com/uc?export=download&id=0Bz8a_Dbh9QhbQ2Vic1kxMmZZQ1k\"\n", + "\n", + " # untaring the required file\n", + " !tar -xvf ./DATAPATH/dbpedia_csv.tar.gz --directory ./DATAPATH\n", + "\n", + " # sneek peek in the folder structure\n", + " !ls -lah ./DATAPATH\n", + "\n", + "except ModuleNotFoundError:\n", + " data_path = './Data/'\n", + " compressed_file_name = 'dbpedia_csv.tar.gz'\n", + " extracted_file_name = 'dbpedia_csv'\n", + "\n", + " # Check if Extracted File exists\n", + " location_of_extracted_file = check_if_file_exists(extracted_file_name, ['./Data'])\n", + "\n", + " if location_of_extracted_file:\n", + " # Extracted File exists\n", + " path_to_model = os.path.join(location_of_extracted_file, extracted_file_name)\n", + "\n", + " else:\n", + " location_of_compressed_file = check_if_file_exists(compressed_file_name, ['./Data'])\n", + "\n", + " if location_of_compressed_file:\n", + " # Compressed File exists\n", + " extract_tar_file(os.path.join(location_of_compressed_file, compressed_file_name), data_path)\n", + " path_to_model = os.path.join(data_path, extracted_file_name)\n", + "\n", + " else:\n", + " # Download File\n", + " os.makedirs(\"./Data\", exist_ok=True)\n", + " output_path = './Data/'\n", + " gdown.download(\"https://drive.google.com/uc?export=download&id=0Bz8a_Dbh9QhbQ2Vic1kxMmZZQ1k\", output=output_path)\n", + "\n", + " # Extract File\n", + " extract_tar_file(os.path.join(data_path, compressed_file_name), output_path)\n", + "\n", + " path_to_model = os.path.join(data_path, extracted_file_name)\n", + "\n", + " print(f\"Data Present at location : {path_to_model}\")" ] - }, - "execution_count": 6, - "metadata": { - "tags": [] - }, - "output_type": "execute_result" - } - ], - "source": [ - "# Since we have no clue about the classes lets build one\n", - "# Mapping from class number to class name\n", - "class_dict={\n", - " 1:'Company',\n", - " 2:'EducationalInstitution',\n", - " 3:'Artist',\n", - " 4:'Athlete',\n", - " 5:'OfficeHolder',\n", - " 6:'MeanOfTransportation',\n", - " 7:'Building',\n", - " 8:'NaturalPlace',\n", - " 9:'Village',\n", - " 10:'Animal',\n", - " 11:'Plant',\n", - " 12:'Album',\n", - " 13:'Film',\n", - " 14:'WrittenWork'\n", - " }\n", - "\n", - "# Mapping the classes\n", - "df['class_name'] = df['class'].map(class_dict)\n", - "df.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" }, - "id": "si7VC_Rub62a", - "outputId": "a1f7d406-0e9c-4adf-eaee-fc09572f27bf" - }, - "outputs": [ { - "data": { - "text/plain": [ - "Athlete 40000\n", - "MeanOfTransportation 40000\n", - "Film 40000\n", - "Artist 40000\n", - "Building 40000\n", - "Company 40000\n", - "Plant 40000\n", - "Album 40000\n", - "NaturalPlace 40000\n", - "Village 40000\n", - "EducationalInstitution 40000\n", - "Animal 40000\n", - "WrittenWork 40000\n", - "OfficeHolder 40000\n", - "Name: class_name, dtype: int64" + "cell_type": "code", + "execution_count": 7, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "lMoRw3oQb62I", + "outputId": "61ef0d52-044d-4829-db25-796f9ae2d562" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Train:(560000, 3) Test:(70000, 3)\n" + ] + } + ], + "source": [ + "# Loading train data\n", + "train_file = data_path + '/dbpedia_csv/train.csv'\n", + "df = pd.read_csv(train_file, header=None, names=['class','name','description'])\n", + "# Loading test data\n", + "test_file = data_path + '/dbpedia_csv/test.csv'\n", + "df_test = pd.read_csv(test_file, header=None, names=['class','name','description'])\n", + "# Data we have\n", + "print(\"Train:{} Test:{}\".format(df.shape,df_test.shape))" ] - }, - "execution_count": 7, - "metadata": { - "tags": [] - }, - "output_type": "execute_result" - } - ], - "source": [ - "df[\"class_name\"].value_counts()" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "id": "Sn-3kIqMb62d" - }, - "outputs": [], - "source": [ - "# Lets do some cleaning of this text\n", - "def clean_it(text,normalize=True):\n", - " # Replacing possible issues with data. We can add or reduce the replacemtent in this chain\n", - " s = str(text).replace(',',' ').replace('\"','').replace('\\'',' \\' ').replace('.',' . ').replace('(',' ( ').\\\n", - " replace(')',' ) ').replace('!',' ! ').replace('?',' ? ').replace(':',' ').replace(';',' ').lower()\n", - " \n", - " # normalizing / encoding the text\n", - " if normalize:\n", - " s = s.normalize('NFKD').str.encode('ascii','ignore').str.decode('utf-8')\n", - " \n", - " return s\n", - "\n", - "# Now lets define a small function where we can use above cleaning on datasets\n", - "def clean_df(data, cleanit= False, shuffleit=False, encodeit=False, label_prefix='__class__'):\n", - " # Defining the new data\n", - " df = data[['name','description']].copy(deep=True)\n", - " df['class'] = label_prefix + data['class'].astype(str) + ' '\n", - " \n", - " # cleaning it\n", - " if cleanit:\n", - " df['name'] = df['name'].apply(lambda x: clean_it(x,encodeit))\n", - " df['description'] = df['description'].apply(lambda x: clean_it(x,encodeit))\n", - " \n", - " # shuffling it\n", - " if shuffleit:\n", - " df.sample(frac=1).reset_index(drop=True)\n", - " \n", - " return df" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" }, - "id": "r_DRvdFcb62m", - "outputId": "d3fc1348-fcb2-4f50-c090-067e5ca66301" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "CPU times: user 4.38 s, sys: 193 ms, total: 4.57 s\n", - "Wall time: 4.63 s\n" - ] - } - ], - "source": [ - "%%time\n", - "# Transform the datasets using the above clean functions\n", - "df_train_cleaned = clean_df(df, True, True)\n", - "df_test_cleaned = clean_df(df_test, True, True)" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "id": "imMZ9-Bkb62t" - }, - "outputs": [], - "source": [ - "# Write files to disk as fastText classifier API reads files from disk.\n", - "train_file = data_path + '/dbpedia_train.csv'\n", - "df_train_cleaned.to_csv(train_file, header=None, index=False, columns=['class','name','description'] )\n", - "\n", - "test_file = data_path + '/dbpedia_test.csv'\n", - "df_test_cleaned.to_csv(test_file, header=None, index=False, columns=['class','name','description'] )\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "bWZTSzd9b62x" - }, - "source": [ - "Now that we have the train and test files written into disk in a format fastText wants, we are ready to use it for text classification!" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" + "cell_type": "code", + "execution_count": 8, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 206 + }, + "id": "gaz226vXb62W", + "outputId": "ba8cecca-4c1b-41ae-f726-27f6bbef243f" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " class name \\\n", + "0 1 E. D. Abbott Ltd \n", + "1 1 Schwan-Stabilo \n", + "2 1 Q-workshop \n", + "3 1 Marvell Software Solutions Israel \n", + "4 1 Bergan Mercy Medical Center \n", + "\n", + " description class_name \n", + "0 Abbott of Farnham E D Abbott Limited was a Br... Company \n", + "1 Schwan-STABILO is a German maker of pens for ... Company \n", + "2 Q-workshop is a Polish company located in Poz... Company \n", + "3 Marvell Software Solutions Israel known as RA... Company \n", + "4 Bergan Mercy Medical Center is a hospital loc... Company " + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
classnamedescriptionclass_name
01E. D. Abbott LtdAbbott of Farnham E D Abbott Limited was a Br...Company
11Schwan-StabiloSchwan-STABILO is a German maker of pens for ...Company
21Q-workshopQ-workshop is a Polish company located in Poz...Company
31Marvell Software Solutions IsraelMarvell Software Solutions Israel known as RA...Company
41Bergan Mercy Medical CenterBergan Mercy Medical Center is a hospital loc...Company
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 8 + } + ], + "source": [ + "# Since we have no clue about the classes lets build one\n", + "# Mapping from class number to class name\n", + "class_dict={\n", + " 1:'Company',\n", + " 2:'EducationalInstitution',\n", + " 3:'Artist',\n", + " 4:'Athlete',\n", + " 5:'OfficeHolder',\n", + " 6:'MeanOfTransportation',\n", + " 7:'Building',\n", + " 8:'NaturalPlace',\n", + " 9:'Village',\n", + " 10:'Animal',\n", + " 11:'Plant',\n", + " 12:'Album',\n", + " 13:'Film',\n", + " 14:'WrittenWork'\n", + " }\n", + "\n", + "# Mapping the classes\n", + "df['class_name'] = df['class'].map(class_dict)\n", + "df.head()" + ] }, - "id": "a-H1wouCb62x", - "outputId": "3d7c130a-fd3b-472c-8585-2e965017763f" - }, - "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "CPU times: user 1h 3min 10s, sys: 12.8 s, total: 1h 3min 23s\n", - "Wall time: 32min 17s\n" - ] + "cell_type": "code", + "execution_count": 9, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "si7VC_Rub62a", + "outputId": "dd8c0c6e-fce9-4362-abab-45d9c751858c" + }, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "Company 40000\n", + "EducationalInstitution 40000\n", + "Artist 40000\n", + "Athlete 40000\n", + "OfficeHolder 40000\n", + "MeanOfTransportation 40000\n", + "Building 40000\n", + "NaturalPlace 40000\n", + "Village 40000\n", + "Animal 40000\n", + "Plant 40000\n", + "Album 40000\n", + "Film 40000\n", + "WrittenWork 40000\n", + "Name: class_name, dtype: int64" + ] + }, + "metadata": {}, + "execution_count": 9 + } + ], + "source": [ + "df[\"class_name\"].value_counts()" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "id": "Sn-3kIqMb62d" + }, + "outputs": [], + "source": [ + "# Lets do some cleaning of this text\n", + "def clean_it(text,normalize=True):\n", + " # Replacing possible issues with data. We can add or reduce the replacemtent in this chain\n", + " s = str(text).replace(',',' ').replace('\"','').replace('\\'',' \\' ').replace('.',' . ').replace('(',' ( ').\\\n", + " replace(')',' ) ').replace('!',' ! ').replace('?',' ? ').replace(':',' ').replace(';',' ').lower()\n", + "\n", + " # normalizing / encoding the text\n", + " if normalize:\n", + " s = s.normalize('NFKD').str.encode('ascii','ignore').str.decode('utf-8')\n", + "\n", + " return s\n", + "\n", + "# Now lets define a small function where we can use above cleaning on datasets\n", + "def clean_df(data, cleanit= False, shuffleit=False, encodeit=False, label_prefix='__class__'):\n", + " # Defining the new data\n", + " df = data[['name','description']].copy(deep=True)\n", + " df['class'] = label_prefix + data['class'].astype(str) + ' '\n", + "\n", + " # cleaning it\n", + " if cleanit:\n", + " df['name'] = df['name'].apply(lambda x: clean_it(x,encodeit))\n", + " df['description'] = df['description'].apply(lambda x: clean_it(x,encodeit))\n", + "\n", + " # shuffling it\n", + " if shuffleit:\n", + " df.sample(frac=1).reset_index(drop=True)\n", + "\n", + " return df" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "r_DRvdFcb62m", + "outputId": "75d06ea5-e04f-4c03-a6c4-37fdc4e10442" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "CPU times: user 3.78 s, sys: 220 ms, total: 4 s\n", + "Wall time: 4.15 s\n" + ] + } + ], + "source": [ + "%%time\n", + "# Transform the datasets using the above clean functions\n", + "df_train_cleaned = clean_df(df, True, True)\n", + "df_test_cleaned = clean_df(df_test, True, True)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "id": "imMZ9-Bkb62t" + }, + "outputs": [], + "source": [ + "# Write files to disk as fastText classifier API reads files from disk.\n", + "train_file = data_path + '/dbpedia_train.csv'\n", + "df_train_cleaned.to_csv(train_file, header=None, index=False, columns=['class','name','description'] )\n", + "\n", + "test_file = data_path + '/dbpedia_test.csv'\n", + "df_test_cleaned.to_csv(test_file, header=None, index=False, columns=['class','name','description'] )\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "bWZTSzd9b62x" + }, + "source": [ + "Now that we have the train and test files written into disk in a format fastText wants, we are ready to use it for text classification!" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "id": "a-H1wouCb62x", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "bdb18719-1b63-4c76-f1cb-58e93717fbd2" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "CPU times: user 1h, sys: 19 s, total: 1h 19s\n", + "Wall time: 33min 36s\n" + ] + } + ], + "source": [ + "%%time\n", + "## Using fastText for feature extraction and training\n", + "from fasttext import train_supervised\n", + "\"\"\"fastText expects and training file (csv), a model name as input arguments.\n", + "label_prefix refers to the prefix before label string in the dataset.\n", + "default is __label__. In our dataset, it is __class__.\n", + "There are several other parameters which can be seen in:\n", + "https://pypi.org/project/fasttext/\n", + "\"\"\"\n", + "model = train_supervised(input=train_file, label=\"__class__\", lr=1.0, epoch=75, loss='ova', wordNgrams=2, dim=200, thread=2, verbose=100)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "id": "sAyN3ZDbQFq-", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "7aec83c4-251d-402e-e10d-1f926de4c64f" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Test Samples: 70000 Precision@1 : 91.5214 Recall@1 : 91.5214\n", + "Test Samples: 70000 Precision@2 : 47.6493 Recall@2 : 95.2986\n", + "Test Samples: 70000 Precision@3 : 31.9848 Recall@3 : 95.9543\n", + "Test Samples: 70000 Precision@4 : 24.2014 Recall@4 : 96.8057\n", + "Test Samples: 70000 Precision@5 : 19.4149 Recall@5 : 97.0743\n" + ] + } + ], + "source": [ + "for k in range(1,6):\n", + " results = model.test(test_file,k=k)\n", + " print(f\"Test Samples: {results[0]} Precision@{k} : {results[1]*100:2.4f} Recall@{k} : {results[2]*100:2.4f}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "nrxSYRs3b621" + }, + "source": [ + "Try training a classifier on this dataset with, say, LogisticRegression to realize how fast fastText is! 90% Precision and Recall are hard numbers to beat, too!" + ] + }, + { + "cell_type": "code", + "source": [], + "metadata": { + "id": "AHSqt1rLd-R0" + }, + "execution_count": null, + "outputs": [] } - ], - "source": [ - "%%time\n", - "## Using fastText for feature extraction and training\n", - "from fasttext import train_supervised \n", - "\"\"\"fastText expects and training file (csv), a model name as input arguments.\n", - "label_prefix refers to the prefix before label string in the dataset.\n", - "default is __label__. In our dataset, it is __class__. \n", - "There are several other parameters which can be seen in: \n", - "https://pypi.org/project/fasttext/\n", - "\"\"\"\n", - "model = train_supervised(input=train_file, label=\"__class__\", lr=1.0, epoch=75, loss='ova', wordNgrams=2, dim=200, thread=2, verbose=100)" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { + ], + "metadata": { + "accelerator": "GPU", "colab": { - "base_uri": "https://localhost:8080/" + "provenance": [] }, - "id": "sAyN3ZDbQFq-", - "outputId": "13acbc62-48d9-469c-dfb1-d3e5446b8530" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Test Samples: 70000 Precision@1 : 92.2486 Recall@1 : 92.2486\n", - "Test Samples: 70000 Precision@2 : 48.5014 Recall@2 : 97.0029\n", - "Test Samples: 70000 Precision@3 : 32.5619 Recall@3 : 97.6857\n", - "Test Samples: 70000 Precision@4 : 24.4968 Recall@4 : 97.9871\n", - "Test Samples: 70000 Precision@5 : 19.6420 Recall@5 : 98.2100\n" - ] + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.17" } - ], - "source": [ - "for k in range(1,6):\n", - " results = model.test(test_file,k=k)\n", - " print(f\"Test Samples: {results[0]} Precision@{k} : {results[1]*100:2.4f} Recall@{k} : {results[2]*100:2.4f}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "nrxSYRs3b621" - }, - "source": [ - "Try training a classifier on this dataset with, say, LogisticRegression to realize how fast fastText is! 93% Precision and Recall are hard numbers to beat, too!" - ] - } - ], - "metadata": { - "accelerator": "GPU", - "colab": { - "name": "04_FastText_Example.ipynb", - "provenance": [] - }, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.4" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/Ch4/05_DeepNN_Example.ipynb b/Ch4/05_DeepNN_Example.ipynb index a9c7abb..7cbdaee 100644 --- a/Ch4/05_DeepNN_Example.ipynb +++ b/Ch4/05_DeepNN_Example.ipynb @@ -1,31 +1,4 @@ { - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { - "accelerator": "GPU", - "colab": { - "name": "05_DeepNN_Example.ipynb", - "provenance": [], - "collapsed_sections": [] - }, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.13" - } - }, "cells": [ { "cell_type": "markdown", @@ -33,98 +6,35 @@ "id": "aLNg_Puse6EX" }, "source": [ - "In this notebook we will demonstrate different text classification models trained using the IMDB reviews dataset. " + "In this notebook we will demonstrate different text classification models trained using the IMDB reviews dataset." ] }, { "cell_type": "code", + "execution_count": 6, "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "eOJLveJqtEO3", - "outputId": "067a74b2-c5df-464d-a3fa-3f4517a9090a" + "id": "eOJLveJqtEO3" }, + "outputs": [], "source": [ "# To install only the requirements of this notebook, uncomment the lines below and run this cell\n", "\n", "# ===========================\n", "\n", - "!pip install numpy==1.19.5\n", - "!pip install wget==3.2\n", - "!pip install tensorflow==1.14.0\n", + "# !pip install numpy==1.23.5\n", + "# !pip install wget==3.2\n", + "# !pip install tensorflow==2.12.0\n", "\n", "# ===========================" - ], - "execution_count": 1, - "outputs": [ - { - "output_type": "stream", - "text": [ - "Requirement already satisfied: numpy==1.19.5 in /usr/local/lib/python3.7/dist-packages (1.19.5)\n", - "Collecting wget==3.2\n", - " Downloading https://files.pythonhosted.org/packages/47/6a/62e288da7bcda82b935ff0c6cfe542970f04e29c756b0e147251b2fb251f/wget-3.2.zip\n", - "Building wheels for collected packages: wget\n", - " Building wheel for wget (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for wget: filename=wget-3.2-cp37-none-any.whl size=9675 sha256=0590de33e3a5654cc81a0a21cf66fa3e8af32bf31e65c5a543d101b6d3fba858\n", - " Stored in directory: /root/.cache/pip/wheels/40/15/30/7d8f7cea2902b4db79e3fea550d7d7b85ecb27ef992b618f3f\n", - "Successfully built wget\n", - "Installing collected packages: wget\n", - "Successfully installed wget-3.2\n", - "Collecting tensorflow==1.14.0\n", - "\u001b[?25l Downloading https://files.pythonhosted.org/packages/f4/28/96efba1a516cdacc2e2d6d081f699c001d414cc8ca3250e6d59ae657eb2b/tensorflow-1.14.0-cp37-cp37m-manylinux1_x86_64.whl (109.3MB)\n", - "\u001b[K |████████████████████████████████| 109.3MB 104kB/s \n", - "\u001b[?25hRequirement already satisfied: wrapt>=1.11.1 in /usr/local/lib/python3.7/dist-packages (from tensorflow==1.14.0) (1.12.1)\n", - "Requirement already satisfied: grpcio>=1.8.6 in /usr/local/lib/python3.7/dist-packages (from tensorflow==1.14.0) (1.34.1)\n", - "Collecting tensorflow-estimator<1.15.0rc0,>=1.14.0rc0\n", - "\u001b[?25l Downloading https://files.pythonhosted.org/packages/3c/d5/21860a5b11caf0678fbc8319341b0ae21a07156911132e0e71bffed0510d/tensorflow_estimator-1.14.0-py2.py3-none-any.whl (488kB)\n", - "\u001b[K |████████████████████████████████| 491kB 49.6MB/s \n", - "\u001b[?25hRequirement already satisfied: termcolor>=1.1.0 in /usr/local/lib/python3.7/dist-packages (from tensorflow==1.14.0) (1.1.0)\n", - "Requirement already satisfied: numpy<2.0,>=1.14.5 in /usr/local/lib/python3.7/dist-packages (from tensorflow==1.14.0) (1.19.5)\n", - "Collecting tensorboard<1.15.0,>=1.14.0\n", - "\u001b[?25l Downloading https://files.pythonhosted.org/packages/91/2d/2ed263449a078cd9c8a9ba50ebd50123adf1f8cfbea1492f9084169b89d9/tensorboard-1.14.0-py3-none-any.whl (3.1MB)\n", - "\u001b[K |████████████████████████████████| 3.2MB 33.6MB/s \n", - "\u001b[?25hRequirement already satisfied: keras-preprocessing>=1.0.5 in /usr/local/lib/python3.7/dist-packages (from tensorflow==1.14.0) (1.1.2)\n", - "Requirement already satisfied: astor>=0.6.0 in /usr/local/lib/python3.7/dist-packages (from tensorflow==1.14.0) (0.8.1)\n", - "Requirement already satisfied: google-pasta>=0.1.6 in /usr/local/lib/python3.7/dist-packages (from tensorflow==1.14.0) (0.2.0)\n", - "Requirement already satisfied: absl-py>=0.7.0 in /usr/local/lib/python3.7/dist-packages (from tensorflow==1.14.0) (0.12.0)\n", - "Requirement already satisfied: six>=1.10.0 in /usr/local/lib/python3.7/dist-packages (from tensorflow==1.14.0) (1.15.0)\n", - "Requirement already satisfied: protobuf>=3.6.1 in /usr/local/lib/python3.7/dist-packages (from tensorflow==1.14.0) (3.17.3)\n", - "Collecting keras-applications>=1.0.6\n", - "\u001b[?25l Downloading https://files.pythonhosted.org/packages/71/e3/19762fdfc62877ae9102edf6342d71b28fbfd9dea3d2f96a882ce099b03f/Keras_Applications-1.0.8-py3-none-any.whl (50kB)\n", - "\u001b[K |████████████████████████████████| 51kB 8.4MB/s \n", - "\u001b[?25hRequirement already satisfied: gast>=0.2.0 in /usr/local/lib/python3.7/dist-packages (from tensorflow==1.14.0) (0.4.0)\n", - "Requirement already satisfied: wheel>=0.26 in /usr/local/lib/python3.7/dist-packages (from tensorflow==1.14.0) (0.36.2)\n", - "Requirement already satisfied: setuptools>=41.0.0 in /usr/local/lib/python3.7/dist-packages (from tensorboard<1.15.0,>=1.14.0->tensorflow==1.14.0) (57.0.0)\n", - "Requirement already satisfied: markdown>=2.6.8 in /usr/local/lib/python3.7/dist-packages (from tensorboard<1.15.0,>=1.14.0->tensorflow==1.14.0) (3.3.4)\n", - "Requirement already satisfied: werkzeug>=0.11.15 in /usr/local/lib/python3.7/dist-packages (from tensorboard<1.15.0,>=1.14.0->tensorflow==1.14.0) (1.0.1)\n", - "Requirement already satisfied: h5py in /usr/local/lib/python3.7/dist-packages (from keras-applications>=1.0.6->tensorflow==1.14.0) (3.1.0)\n", - "Requirement already satisfied: importlib-metadata; python_version < \"3.8\" in /usr/local/lib/python3.7/dist-packages (from markdown>=2.6.8->tensorboard<1.15.0,>=1.14.0->tensorflow==1.14.0) (4.6.1)\n", - "Requirement already satisfied: cached-property; python_version < \"3.8\" in /usr/local/lib/python3.7/dist-packages (from h5py->keras-applications>=1.0.6->tensorflow==1.14.0) (1.5.2)\n", - "Requirement already satisfied: zipp>=0.5 in /usr/local/lib/python3.7/dist-packages (from importlib-metadata; python_version < \"3.8\"->markdown>=2.6.8->tensorboard<1.15.0,>=1.14.0->tensorflow==1.14.0) (3.5.0)\n", - "Requirement already satisfied: typing-extensions>=3.6.4; python_version < \"3.8\" in /usr/local/lib/python3.7/dist-packages (from importlib-metadata; python_version < \"3.8\"->markdown>=2.6.8->tensorboard<1.15.0,>=1.14.0->tensorflow==1.14.0) (3.7.4.3)\n", - "\u001b[31mERROR: kapre 0.3.5 has requirement tensorflow>=2.0.0, but you'll have tensorflow 1.14.0 which is incompatible.\u001b[0m\n", - "Installing collected packages: tensorflow-estimator, tensorboard, keras-applications, tensorflow\n", - " Found existing installation: tensorflow-estimator 2.5.0\n", - " Uninstalling tensorflow-estimator-2.5.0:\n", - " Successfully uninstalled tensorflow-estimator-2.5.0\n", - " Found existing installation: tensorboard 2.5.0\n", - " Uninstalling tensorboard-2.5.0:\n", - " Successfully uninstalled tensorboard-2.5.0\n", - " Found existing installation: tensorflow 2.5.0\n", - " Uninstalling tensorflow-2.5.0:\n", - " Successfully uninstalled tensorflow-2.5.0\n", - "Successfully installed keras-applications-1.0.8 tensorboard-1.14.0 tensorflow-1.14.0 tensorflow-estimator-1.14.0\n" - ], - "name": "stdout" - } ] }, { "cell_type": "code", + "execution_count": 7, "metadata": { "id": "Ixb_5zcYtEO5" }, + "outputs": [], "source": [ "# To install the requirements for the entire chapter, uncomment the lines below and run this cell\n", "\n", @@ -137,15 +47,15 @@ "# !pip install -r \"ch4-requirements.txt\"\n", "\n", "# ===========================" - ], - "execution_count": 2, - "outputs": [] + ] }, { "cell_type": "code", + "execution_count": 8, "metadata": { "id": "xqUcb7NBb5--" }, + "outputs": [], "source": [ "#Make the necessary imports\n", "import os\n", @@ -154,7 +64,7 @@ "import tarfile\n", "import wget\n", "import warnings\n", - "warnings.filterwarnings(\"ignore\") \n", + "warnings.filterwarnings(\"ignore\")\n", "from zipfile import ZipFile\n", "from tensorflow.keras.preprocessing.text import Tokenizer\n", "from tensorflow.keras.preprocessing.sequence import pad_sequences\n", @@ -163,9 +73,7 @@ "from tensorflow.keras.layers import Conv1D, MaxPooling1D, Embedding, LSTM\n", "from tensorflow.keras.models import Model, Sequential\n", "from tensorflow.keras.initializers import Constant" - ], - "execution_count": 3, - "outputs": [] + ] }, { "cell_type": "markdown", @@ -178,84 +86,83 @@ }, { "cell_type": "code", + "execution_count": 9, "metadata": { - "id": "HUKTqLHud7fo", - "scrolled": false + "id": "HUKTqLHud7fo" }, + "outputs": [], "source": [ "%%capture\n", "try:\n", - " \n", + "\n", " from google.colab import files\n", - " \n", + "\n", " !wget -P DATAPATH http://nlp.stanford.edu/data/glove.6B.zip\n", " !unzip DATAPATH/glove.6B.zip -d DATAPATH/glove.6B\n", - " \n", + "\n", " !wget -P DATAPATH http://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz\n", " !tar -xvf DATAPATH/aclImdb_v1.tar.gz -C DATAPATH\n", - " \n", + "\n", " BASE_DIR = 'DATAPATH'\n", - " \n", + "\n", "except ModuleNotFoundError:\n", - " \n", + "\n", " if not os.path.exists('Data/glove.6B'):\n", " os.mkdir('Data/glove.6B')\n", - " \n", - " url='http://nlp.stanford.edu/data/glove.6B.zip' \n", - " wget.download(url,'Data') \n", - " \n", - " temp='Data/glove.6B.zip' \n", - " file = ZipFile(temp) \n", - " file.extractall('Data/glove.6B') \n", + "\n", + " url='http://nlp.stanford.edu/data/glove.6B.zip'\n", + " wget.download(url,'Data')\n", + "\n", + " temp='Data/glove.6B.zip'\n", + " file = ZipFile(temp)\n", + " file.extractall('Data/glove.6B')\n", " file.close()\n", - " \n", - " \n", - " \n", + "\n", + "\n", + "\n", " if not os.path.exists('Data/aclImdb'):\n", - " \n", - " url='http://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz' \n", + "\n", + " url='http://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz'\n", " wget.download(url,'Data')\n", - " \n", - " temp='Data/aclImdb_v1.tar.gz' \n", + "\n", + " temp='Data/aclImdb_v1.tar.gz'\n", " tar = tarfile.open(temp, \"r:gz\")\n", - " tar.extractall('Data') \n", + " tar.extractall('Data')\n", " tar.close()\n", - " \n", + "\n", " BASE_DIR = 'Data'" - ], - "execution_count": 4, - "outputs": [] + ] }, { "cell_type": "code", + "execution_count": 10, "metadata": { "id": "qvl1qb78fUib" }, + "outputs": [], "source": [ "GLOVE_DIR = os.path.join(BASE_DIR, 'glove.6B')\n", "TRAIN_DATA_DIR = os.path.join(BASE_DIR, 'aclImdb/train')\n", "TEST_DATA_DIR = os.path.join(BASE_DIR, 'aclImdb/test')" - ], - "execution_count": 5, - "outputs": [] + ] }, { "cell_type": "code", + "execution_count": 11, "metadata": { "id": "Yu9xmAZEd7fp" }, + "outputs": [], "source": [ - "#Within these, I only have a pos/ and a neg/ folder containing text files \n", + "#Within these, I only have a pos/ and a neg/ folder containing text files\n", "MAX_SEQUENCE_LENGTH = 1000\n", - "MAX_NUM_WORDS = 20000 \n", - "EMBEDDING_DIM = 100 \n", + "MAX_NUM_WORDS = 20000\n", + "EMBEDDING_DIM = 100\n", "VALIDATION_SPLIT = 0.2\n", "\n", "#started off from: https://github.com/keras-team/keras/blob/master/examples/pretrained_word_embeddings.py\n", "#and from: https://github.com/keras-team/keras/blob/master/examples/imdb_lstm.py" - ], - "execution_count": 6, - "outputs": [] + ] }, { "cell_type": "markdown", @@ -264,14 +171,16 @@ }, "source": [ "### Loading and Preprocessing\n", - " " + "" ] }, { "cell_type": "code", + "execution_count": 12, "metadata": { "id": "WI4O1usEb5_O" }, + "outputs": [], "source": [ "#Function to load the data from the dataset into the notebook. Will be called twice - for train and test.\n", "def get_data(data_dir):\n", @@ -292,56 +201,64 @@ "\n", "train_texts, train_labels = get_data(TRAIN_DATA_DIR)\n", "test_texts, test_labels = get_data(TEST_DATA_DIR)\n", - "labels_index = {'pos':1, 'neg':0} \n", + "labels_index = {'pos':1, 'neg':0}\n", "\n", - "#Just to see how the data looks like. \n", + "#Just to see how the data looks like.\n", "#print(train_texts[0])\n", "#print(train_labels[0])\n", "#print(test_texts[24999])\n", "#print(test_labels[24999])" - ], - "execution_count": 7, - "outputs": [] + ] }, { "cell_type": "code", + "execution_count": 13, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "QhhqM0Jdd7fs", - "outputId": "9b5b394e-bc52-4779-d85d-a0383446051d" + "outputId": "9e16478c-8111-4aaf-e73b-b89359dd114f" }, - "source": [ - "#Vectorize these text samples into a 2D integer tensor using Keras Tokenizer \n", - "#Tokenizer is fit on training data only, and that is used to tokenize both train and test data. \n", - "tokenizer = Tokenizer(num_words=MAX_NUM_WORDS) \n", - "tokenizer.fit_on_texts(train_texts) \n", - "train_sequences = tokenizer.texts_to_sequences(train_texts) #Converting text to a vector of word indexes \n", - "test_sequences = tokenizer.texts_to_sequences(test_texts) \n", - "word_index = tokenizer.word_index \n", - "print('Found %s unique tokens.' % len(word_index))" - ], - "execution_count": 8, "outputs": [ { "output_type": "stream", + "name": "stdout", "text": [ "Found 88582 unique tokens.\n" - ], - "name": "stdout" + ] } + ], + "source": [ + "#Vectorize these text samples into a 2D integer tensor using Keras Tokenizer\n", + "#Tokenizer is fit on training data only, and that is used to tokenize both train and test data.\n", + "tokenizer = Tokenizer(num_words=MAX_NUM_WORDS)\n", + "tokenizer.fit_on_texts(train_texts)\n", + "train_sequences = tokenizer.texts_to_sequences(train_texts) #Converting text to a vector of word indexes\n", + "test_sequences = tokenizer.texts_to_sequences(test_texts)\n", + "word_index = tokenizer.word_index\n", + "print('Found %s unique tokens.' % len(word_index))" ] }, { "cell_type": "code", + "execution_count": 14, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "_e0V1-bBb5_d", - "outputId": "d866429d-5bb6-43a7-c66e-ed5abbafc4cd" + "outputId": "94d409aa-5ac2-4b4a-809d-fc91d4563285" }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Splitting the train data into train and valid is done\n" + ] + } + ], "source": [ "#Converting this to sequences to be fed into neural network. Max seq. len is 1000 as set earlier\n", "#initial padding of 0s, until vector is of size MAX_SEQUENCE_LENGTH\n", @@ -362,27 +279,29 @@ "y_val = trainvalid_labels[-num_validation_samples:]\n", "#This is the data we will use for CNN and RNN training\n", "print('Splitting the train data into train and valid is done')" - ], - "execution_count": 9, - "outputs": [ - { - "output_type": "stream", - "text": [ - "Splitting the train data into train and valid is done\n" - ], - "name": "stdout" - } ] }, { "cell_type": "code", + "execution_count": 15, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "WUHqg2vvb5_l", - "outputId": "8387eda1-18f0-4254-9819-e63191b8fc04" + "outputId": "0b0bc141-184a-4d99-bf55-360d0e6a0212" }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Preparing embedding matrix.\n", + "Found 400000 word vectors in Glove embeddings.\n", + "Preparing of embedding matrix is done\n" + ] + } + ], "source": [ "print('Preparing embedding matrix.')\n", "\n", @@ -418,18 +337,6 @@ " input_length=MAX_SEQUENCE_LENGTH,\n", " trainable=False)\n", "print(\"Preparing of embedding matrix is done\")" - ], - "execution_count": 10, - "outputs": [ - { - "output_type": "stream", - "text": [ - "Preparing embedding matrix.\n", - "Found 400000 word vectors in Glove embeddings.\n", - "Preparing of embedding matrix is done\n" - ], - "name": "stdout" - } ] }, { @@ -443,13 +350,26 @@ }, { "cell_type": "code", + "execution_count": 16, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "TTY-4K-Ob5_t", - "outputId": "836681ca-936e-400a-8973-0754759bb7cd" + "outputId": "834682a9-9371-4769-a967-cc2b2c24eaa7" }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Define a 1D CNN model.\n", + "157/157 [==============================] - 139s 878ms/step - loss: 0.6694 - acc: 0.6158 - val_loss: 0.5212 - val_acc: 0.7606\n", + "782/782 [==============================] - 56s 71ms/step - loss: 0.5251 - acc: 0.7537\n", + "Test accuracy with CNN: 0.7536799907684326\n" + ] + } + ], "source": [ "print('Define a 1D CNN model.')\n", "\n", @@ -467,30 +387,13 @@ "cnnmodel.compile(loss='categorical_crossentropy',\n", " optimizer='rmsprop',\n", " metrics=['acc'])\n", - "#Train the model. Tune to validation set. \n", + "#Train the model. Tune to validation set.\n", "cnnmodel.fit(x_train, y_train,\n", " batch_size=128,\n", " epochs=1, validation_data=(x_val, y_val))\n", "#Evaluate on test set:\n", "score, acc = cnnmodel.evaluate(test_data, test_labels)\n", "print('Test accuracy with CNN:', acc)" - ], - "execution_count": 11, - "outputs": [ - { - "output_type": "stream", - "text": [ - "Define a 1D CNN model.\n", - "WARNING:tensorflow:From /usr/local/lib/python3.7/dist-packages/tensorflow/python/ops/init_ops.py:1251: calling VarianceScaling.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.\n", - "Instructions for updating:\n", - "Call initializer instance with the dtype argument instead of passing it to the constructor\n", - "Train on 20000 samples, validate on 5000 samples\n", - "20000/20000 [==============================] - 156s 8ms/sample - loss: 0.6706 - acc: 0.5972 - val_loss: 0.5116 - val_acc: 0.7512\n", - "25000/25000 [==============================] - 67s 3ms/sample - loss: 0.5239 - acc: 0.7415\n", - "Test accuracy with CNN: 0.74152\n" - ], - "name": "stdout" - } ] }, { @@ -504,13 +407,26 @@ }, { "cell_type": "code", + "execution_count": 17, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "zI0bISwRb5_w", - "outputId": "d7697504-dacb-415c-b131-b89d6b10c771" + "outputId": "1c2285f4-edd4-4142-8f9e-7da2f9a91dc7" }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Defining and training a CNN model, training embedding layer on the fly instead of using pre-trained embeddings\n", + "157/157 [==============================] - 216s 1s/step - loss: 0.6921 - acc: 0.5152 - val_loss: 0.6671 - val_acc: 0.6168\n", + "782/782 [==============================] - 66s 85ms/step - loss: 0.6667 - acc: 0.6200\n", + "Test accuracy with CNN: 0.6200399994850159\n" + ] + } + ], "source": [ "print(\"Defining and training a CNN model, training embedding layer on the fly instead of using pre-trained embeddings\")\n", "cnnmodel = Sequential()\n", @@ -527,30 +443,13 @@ "cnnmodel.compile(loss='categorical_crossentropy',\n", " optimizer='rmsprop',\n", " metrics=['acc'])\n", - "#Train the model. Tune to validation set. \n", + "#Train the model. Tune to validation set.\n", "cnnmodel.fit(x_train, y_train,\n", " batch_size=128,\n", " epochs=1, validation_data=(x_val, y_val))\n", "#Evaluate on test set:\n", "score, acc = cnnmodel.evaluate(test_data, test_labels)\n", "print('Test accuracy with CNN:', acc)" - ], - "execution_count": 12, - "outputs": [ - { - "output_type": "stream", - "text": [ - "Defining and training a CNN model, training embedding layer on the fly instead of using pre-trained embeddings\n", - "WARNING:tensorflow:From /usr/local/lib/python3.7/dist-packages/tensorflow/python/keras/initializers.py:119: calling RandomUniform.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.\n", - "Instructions for updating:\n", - "Call initializer instance with the dtype argument instead of passing it to the constructor\n", - "Train on 20000 samples, validate on 5000 samples\n", - "20000/20000 [==============================] - 234s 12ms/sample - loss: 0.5323 - acc: 0.6927 - val_loss: 0.3179 - val_acc: 0.8644\n", - "25000/25000 [==============================] - 84s 3ms/sample - loss: 0.3409 - acc: 0.8495\n", - "Test accuracy with CNN: 0.84948\n" - ], - "name": "stdout" - } ] }, { @@ -559,18 +458,32 @@ "id": "6GwhXpmSgt4H" }, "source": [ - "### LSTM Model with training your own embedding " + "### LSTM Model with training your own embedding" ] }, { "cell_type": "code", + "execution_count": 18, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "SvBt2Brib5_4", - "outputId": "008fe9fa-13bf-4127-ba46-67916426ddbe" + "outputId": "434183cf-f713-4911-b403-100223907162" }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Defining and training an LSTM model, training embedding layer on the fly\n", + "Training the RNN\n", + "625/625 [==============================] - 1315s 2s/step - loss: 0.4609 - accuracy: 0.7807 - val_loss: 0.3932 - val_accuracy: 0.8286\n", + "782/782 [==============================] - 191s 245ms/step - loss: 0.4004 - accuracy: 0.8236\n", + "Test accuracy with RNN: 0.8235999941825867\n" + ] + } + ], "source": [ "print(\"Defining and training an LSTM model, training embedding layer on the fly\")\n", "\n", @@ -591,24 +504,6 @@ "score, acc = rnnmodel.evaluate(test_data, test_labels,\n", " batch_size=32)\n", "print('Test accuracy with RNN:', acc)" - ], - "execution_count": 13, - "outputs": [ - { - "output_type": "stream", - "text": [ - "Defining and training an LSTM model, training embedding layer on the fly\n", - "WARNING:tensorflow:From /usr/local/lib/python3.7/dist-packages/tensorflow/python/ops/nn_impl.py:180: add_dispatch_support..wrapper (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.\n", - "Instructions for updating:\n", - "Use tf.where in 2.0, which has the same broadcast rule as np.where\n", - "Training the RNN\n", - "Train on 20000 samples, validate on 5000 samples\n", - "20000/20000 [==============================] - 1365s 68ms/sample - loss: 0.4997 - acc: 0.7506 - val_loss: 0.3839 - val_acc: 0.8403\n", - "25000/25000 [==============================] - 198s 8ms/sample - loss: 0.3962 - acc: 0.8300\n", - "Test accuracy with RNN: 0.82998\n" - ], - "name": "stdout" - } ] }, { @@ -622,13 +517,27 @@ }, { "cell_type": "code", + "execution_count": 19, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "Eymx0IyCb5_-", - "outputId": "da0fa303-a4c4-4b92-ff42-54f1a1d51e45" + "outputId": "2c6c182a-0dee-442c-f978-ac16e840b51f" }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Defining and training an LSTM model, using pre-trained embedding layer\n", + "Training the RNN\n", + "625/625 [==============================] - 1075s 2s/step - loss: 0.6050 - accuracy: 0.6728 - val_loss: 0.4578 - val_accuracy: 0.7916\n", + "782/782 [==============================] - 183s 234ms/step - loss: 0.4554 - accuracy: 0.7917\n", + "Test accuracy with RNN: 0.7916799783706665\n" + ] + } + ], "source": [ "print(\"Defining and training an LSTM model, using pre-trained embedding layer\")\n", "\n", @@ -648,22 +557,39 @@ "score, acc = rnnmodel2.evaluate(test_data, test_labels,\n", " batch_size=32)\n", "print('Test accuracy with RNN:', acc)" - ], - "execution_count": 14, - "outputs": [ - { - "output_type": "stream", - "text": [ - "Defining and training an LSTM model, using pre-trained embedding layer\n", - "Training the RNN\n", - "Train on 20000 samples, validate on 5000 samples\n", - "20000/20000 [==============================] - 1156s 58ms/sample - loss: 0.6122 - acc: 0.6602 - val_loss: 0.4538 - val_acc: 0.8017\n", - "25000/25000 [==============================] - 200s 8ms/sample - loss: 0.4666 - acc: 0.7930\n", - "Test accuracy with RNN: 0.793\n" - ], - "name": "stdout" - } ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Tb81rafef3Wl" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "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.9.17" } - ] + }, + "nbformat": 4, + "nbformat_minor": 0 } \ No newline at end of file diff --git a/Ch4/06_BERT_IMDB_Sentiment_Classification.ipynb b/Ch4/06_BERT_IMDB_Sentiment_Classification.ipynb index 30eb9b3..a82ddd6 100644 --- a/Ch4/06_BERT_IMDB_Sentiment_Classification.ipynb +++ b/Ch4/06_BERT_IMDB_Sentiment_Classification.ipynb @@ -24,91 +24,21 @@ "id": "MK-POIlJE0Eu", "outputId": "490a8c7e-e8b3-4522-e448-37b50ef91109" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Requirement already satisfied: numpy==1.19.5 in /usr/local/lib/python3.7/dist-packages (1.19.5)\n", - "Requirement already satisfied: tensorflow==1.14.0 in /usr/local/lib/python3.7/dist-packages (1.14.0)\n", - "Requirement already satisfied: absl-py>=0.7.0 in /usr/local/lib/python3.7/dist-packages (from tensorflow==1.14.0) (0.12.0)\n", - "Requirement already satisfied: tensorflow-estimator<1.15.0rc0,>=1.14.0rc0 in /usr/local/lib/python3.7/dist-packages (from tensorflow==1.14.0) (1.14.0)\n", - "Requirement already satisfied: termcolor>=1.1.0 in /usr/local/lib/python3.7/dist-packages (from tensorflow==1.14.0) (1.1.0)\n", - "Requirement already satisfied: wrapt>=1.11.1 in /usr/local/lib/python3.7/dist-packages (from tensorflow==1.14.0) (1.12.1)\n", - "Requirement already satisfied: wheel>=0.26 in /usr/local/lib/python3.7/dist-packages (from tensorflow==1.14.0) (0.36.2)\n", - "Requirement already satisfied: tensorboard<1.15.0,>=1.14.0 in /usr/local/lib/python3.7/dist-packages (from tensorflow==1.14.0) (1.14.0)\n", - "Requirement already satisfied: six>=1.10.0 in /usr/local/lib/python3.7/dist-packages (from tensorflow==1.14.0) (1.15.0)\n", - "Requirement already satisfied: google-pasta>=0.1.6 in /usr/local/lib/python3.7/dist-packages (from tensorflow==1.14.0) (0.2.0)\n", - "Requirement already satisfied: grpcio>=1.8.6 in /usr/local/lib/python3.7/dist-packages (from tensorflow==1.14.0) (1.34.1)\n", - "Requirement already satisfied: numpy<2.0,>=1.14.5 in /usr/local/lib/python3.7/dist-packages (from tensorflow==1.14.0) (1.19.5)\n", - "Requirement already satisfied: gast>=0.2.0 in /usr/local/lib/python3.7/dist-packages (from tensorflow==1.14.0) (0.4.0)\n", - "Requirement already satisfied: keras-applications>=1.0.6 in /usr/local/lib/python3.7/dist-packages (from tensorflow==1.14.0) (1.0.8)\n", - "Requirement already satisfied: protobuf>=3.6.1 in /usr/local/lib/python3.7/dist-packages (from tensorflow==1.14.0) (3.17.3)\n", - "Requirement already satisfied: astor>=0.6.0 in /usr/local/lib/python3.7/dist-packages (from tensorflow==1.14.0) (0.8.1)\n", - "Requirement already satisfied: keras-preprocessing>=1.0.5 in /usr/local/lib/python3.7/dist-packages (from tensorflow==1.14.0) (1.1.2)\n", - "Requirement already satisfied: setuptools>=41.0.0 in /usr/local/lib/python3.7/dist-packages (from tensorboard<1.15.0,>=1.14.0->tensorflow==1.14.0) (57.2.0)\n", - "Requirement already satisfied: markdown>=2.6.8 in /usr/local/lib/python3.7/dist-packages (from tensorboard<1.15.0,>=1.14.0->tensorflow==1.14.0) (3.3.4)\n", - "Requirement already satisfied: werkzeug>=0.11.15 in /usr/local/lib/python3.7/dist-packages (from tensorboard<1.15.0,>=1.14.0->tensorflow==1.14.0) (1.0.1)\n", - "Requirement already satisfied: h5py in /usr/local/lib/python3.7/dist-packages (from keras-applications>=1.0.6->tensorflow==1.14.0) (3.1.0)\n", - "Requirement already satisfied: importlib-metadata; python_version < \"3.8\" in /usr/local/lib/python3.7/dist-packages (from markdown>=2.6.8->tensorboard<1.15.0,>=1.14.0->tensorflow==1.14.0) (4.6.1)\n", - "Requirement already satisfied: cached-property; python_version < \"3.8\" in /usr/local/lib/python3.7/dist-packages (from h5py->keras-applications>=1.0.6->tensorflow==1.14.0) (1.5.2)\n", - "Requirement already satisfied: zipp>=0.5 in /usr/local/lib/python3.7/dist-packages (from importlib-metadata; python_version < \"3.8\"->markdown>=2.6.8->tensorboard<1.15.0,>=1.14.0->tensorflow==1.14.0) (3.5.0)\n", - "Requirement already satisfied: typing-extensions>=3.6.4; python_version < \"3.8\" in /usr/local/lib/python3.7/dist-packages (from importlib-metadata; python_version < \"3.8\"->markdown>=2.6.8->tensorboard<1.15.0,>=1.14.0->tensorflow==1.14.0) (3.7.4.3)\n", - "Requirement already satisfied: torch==1.9.0 in /usr/local/lib/python3.7/dist-packages (1.9.0+cu102)\n", - "Requirement already satisfied: typing-extensions in /usr/local/lib/python3.7/dist-packages (from torch==1.9.0) (3.7.4.3)\n", - "Requirement already satisfied: scikit-learn==0.21.3 in /usr/local/lib/python3.7/dist-packages (0.21.3)\n", - "Requirement already satisfied: scipy>=0.17.0 in /usr/local/lib/python3.7/dist-packages (from scikit-learn==0.21.3) (1.4.1)\n", - "Requirement already satisfied: numpy>=1.11.0 in /usr/local/lib/python3.7/dist-packages (from scikit-learn==0.21.3) (1.19.5)\n", - "Requirement already satisfied: joblib>=0.11 in /usr/local/lib/python3.7/dist-packages (from scikit-learn==0.21.3) (1.0.1)\n", - "Requirement already satisfied: pytorch_pretrained_bert==0.6.2 in /usr/local/lib/python3.7/dist-packages (0.6.2)\n", - "Requirement already satisfied: pytorch-nlp==0.5.0 in /usr/local/lib/python3.7/dist-packages (0.5.0)\n", - "Requirement already satisfied: regex in /usr/local/lib/python3.7/dist-packages (from pytorch_pretrained_bert==0.6.2) (2019.12.20)\n", - "Requirement already satisfied: numpy in /usr/local/lib/python3.7/dist-packages (from pytorch_pretrained_bert==0.6.2) (1.19.5)\n", - "Requirement already satisfied: requests in /usr/local/lib/python3.7/dist-packages (from pytorch_pretrained_bert==0.6.2) (2.23.0)\n", - "Requirement already satisfied: tqdm in /usr/local/lib/python3.7/dist-packages (from pytorch_pretrained_bert==0.6.2) (4.41.1)\n", - "Requirement already satisfied: torch>=0.4.1 in /usr/local/lib/python3.7/dist-packages (from pytorch_pretrained_bert==0.6.2) (1.9.0+cu102)\n", - "Requirement already satisfied: boto3 in /usr/local/lib/python3.7/dist-packages (from pytorch_pretrained_bert==0.6.2) (1.18.1)\n", - "Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /usr/local/lib/python3.7/dist-packages (from requests->pytorch_pretrained_bert==0.6.2) (1.24.3)\n", - "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.7/dist-packages (from requests->pytorch_pretrained_bert==0.6.2) (2021.5.30)\n", - "Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.7/dist-packages (from requests->pytorch_pretrained_bert==0.6.2) (2.10)\n", - "Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from requests->pytorch_pretrained_bert==0.6.2) (3.0.4)\n", - "Requirement already satisfied: typing-extensions in /usr/local/lib/python3.7/dist-packages (from torch>=0.4.1->pytorch_pretrained_bert==0.6.2) (3.7.4.3)\n", - "Requirement already satisfied: jmespath<1.0.0,>=0.7.1 in /usr/local/lib/python3.7/dist-packages (from boto3->pytorch_pretrained_bert==0.6.2) (0.10.0)\n", - "Requirement already satisfied: s3transfer<0.6.0,>=0.5.0 in /usr/local/lib/python3.7/dist-packages (from boto3->pytorch_pretrained_bert==0.6.2) (0.5.0)\n", - "Requirement already satisfied: botocore<1.22.0,>=1.21.1 in /usr/local/lib/python3.7/dist-packages (from boto3->pytorch_pretrained_bert==0.6.2) (1.21.1)\n", - "Requirement already satisfied: python-dateutil<3.0.0,>=2.1 in /usr/local/lib/python3.7/dist-packages (from botocore<1.22.0,>=1.21.1->boto3->pytorch_pretrained_bert==0.6.2) (2.8.1)\n", - "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.7/dist-packages (from python-dateutil<3.0.0,>=2.1->botocore<1.22.0,>=1.21.1->boto3->pytorch_pretrained_bert==0.6.2) (1.15.0)\n", - "Requirement already satisfied: tqdm==4.41.1 in /usr/local/lib/python3.7/dist-packages (4.41.1)\n", - "Requirement already satisfied: pandas==1.1.5 in /usr/local/lib/python3.7/dist-packages (1.1.5)\n", - "Requirement already satisfied: numpy>=1.15.4 in /usr/local/lib/python3.7/dist-packages (from pandas==1.1.5) (1.19.5)\n", - "Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.7/dist-packages (from pandas==1.1.5) (2018.9)\n", - "Requirement already satisfied: python-dateutil>=2.7.3 in /usr/local/lib/python3.7/dist-packages (from pandas==1.1.5) (2.8.1)\n", - "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.7/dist-packages (from python-dateutil>=2.7.3->pandas==1.1.5) (1.15.0)\n", - "Requirement already satisfied: matplotlib==3.2.2 in /usr/local/lib/python3.7/dist-packages (3.2.2)\n", - "Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib==3.2.2) (1.3.1)\n", - "Requirement already satisfied: python-dateutil>=2.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib==3.2.2) (2.8.1)\n", - "Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib==3.2.2) (2.4.7)\n", - "Requirement already satisfied: numpy>=1.11 in /usr/local/lib/python3.7/dist-packages (from matplotlib==3.2.2) (1.19.5)\n", - "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.7/dist-packages (from matplotlib==3.2.2) (0.10.0)\n", - "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.7/dist-packages (from python-dateutil>=2.1->matplotlib==3.2.2) (1.15.0)\n", - "Requirement already satisfied: beautifulsoup4==4.6.3 in /usr/local/lib/python3.7/dist-packages (4.6.3)\n" - ] - } - ], + "outputs": [], "source": [ "# To install only the requirements of this notebook, uncomment the lines below and run this cell\n", "\n", "# ===========================\n", "\n", - "!pip install numpy==1.19.5\n", - "!pip install tensorflow==1.14.0\n", - "!pip install torch==1.9.0\n", - "!pip install scikit-learn==0.21.3\n", - "!pip install pytorch_pretrained_bert==0.6.2 pytorch-nlp==0.5.0 \n", - "!pip install tqdm==4.41.1\n", - "!pip install pandas==1.1.5\n", - "!pip install matplotlib==3.2.2\n", - "!pip install beautifulsoup4==4.6.3\n", + "# !pip install numpy==1.19.5\n", + "# !pip install tensorflow==1.14.0\n", + "# !pip install torch==1.9.0\n", + "# !pip install scikit-learn==0.21.3\n", + "# !pip install pytorch_pretrained_bert==0.6.2 pytorch-nlp==0.5.0 \n", + "# !pip install tqdm==4.41.1\n", + "# !pip install pandas==1.1.5\n", + "# !pip install matplotlib==3.2.2\n", + "# !pip install beautifulsoup4==4.6.3\n", "\n", "# ===========================" ] @@ -140,7 +70,17 @@ "metadata": { "id": "TtokjlkCQbiw" }, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2023-08-15 11:05:48.226287: I tensorflow/tsl/cuda/cudart_stub.cc:28] Could not find cuda drivers on your machine, GPU will not be used.\n", + "2023-08-15 11:05:48.661080: I tensorflow/tsl/cuda/cudart_stub.cc:28] Could not find cuda drivers on your machine, GPU will not be used.\n", + "2023-08-15 11:05:50.154202: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Could not find TensorRT\n" + ] + } + ], "source": [ "#importing a few necessary packages and setting the DATA directory\n", "DATA_DIR=\".\"\n", @@ -151,8 +91,6 @@ "import pickle\n", "import tensorflow as tf\n", "\n", - "\n", - "\n", "# BERT imports\n", "import torch\n", "from torch.utils.data import TensorDataset, DataLoader, RandomSampler, SequentialSampler\n", @@ -199,36 +137,7 @@ "id": "BI8AvyFZRAha", "outputId": "b254d1da-f187-4c77-f1e0-748a5e6a8e90" }, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " \n", - " Upload widget is only available when the cell has been executed in the\n", - " current browser session. Please rerun this cell to enable.\n", - " \n", - " " - ], - "text/plain": [ - "" - ] - }, - "metadata": { - "tags": [] - }, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saving IMDB Dataset.csv to IMDB Dataset.csv\n" - ] - } - ], + "outputs": [], "source": [ "# uploading and reading the dataset\n", "# source for dataset: https://www.kaggle.com/lakshmi25npathi/imdb-dataset-of-50k-movie-reviews\n", @@ -242,7 +151,7 @@ "except ModuleNotFoundError :\n", " \n", " # After downnloading the dataset, put the IMDB Dataset.csv file in Data folder.\n", - " df = pd.read_csv(\"Data/IMDB Dataset.csv\",engine='python', error_bad_lines=False)" + " df = pd.read_csv(\"Data/IMDB Dataset.csv\",engine='python', on_bad_lines='warn')" ] }, { @@ -322,9 +231,7 @@ ] }, "execution_count": 5, - "metadata": { - "tags": [] - }, + "metadata": {}, "output_type": "execute_result" } ], @@ -361,15 +268,14 @@ { "data": { "text/plain": [ + "sentiment\n", "1 25000\n", "0 25000\n", - "Name: sentiment, dtype: int64" + "Name: count, dtype: int64" ] }, "execution_count": 7, - "metadata": { - "tags": [] - }, + "metadata": {}, "output_type": "execute_result" } ], @@ -476,9 +382,7 @@ ] }, "execution_count": 9, - "metadata": { - "tags": [] - }, + "metadata": {}, "output_type": "execute_result" } ], @@ -526,17 +430,12 @@ "outputs": [ { "data": { - "application/vnd.google.colaboratory.intrinsic+json": { - "type": "string" - }, "text/plain": [ "\"[CLS] One of the other reviewers has mentioned that after watching just 1 Oz episode you'll be hooked They are right, as this is exactly what happened with meThe first thing that struck me about Oz was its brutality and unflinching scenes of violence, which set in right from the word GO Trust me, this is not a show for the faint hearted or timid This show pulls no punches with regards to drugs, sex or violence Its is hardcore, in the classic use of the wordIt is called OZ as that is the nickname given to the Oswald Maximum Security State Penitentary It focuses mainly on Emerald City, an experimental section of the prison where all the cells have glass fronts and face inwards, so privacy is not high on the agenda Em City is home to manyAryans, Muslims, gangstas, Latinos, Christians, Italians, Irish and moreso scuffles, death stares, dodgy dealings and shady agreements are never far awayI would say the main appeal of the show is due to the fact that it goes where other shows wouldn't dare Forget pretty pictures painted for mainstream audiences, forget charm, forget romanceOZ doesn't mess around The first episode I ever saw struck me as so nasty it was surreal, I couldn't say I was ready for it, but as I watched more, I developed a taste for Oz, and got accustomed to the high levels of graphic violence Not just violence, but injustice crooked guards who'll be sold out for a nickel, inmates who'll kill on order and get away with it, well mannered, middle class inmates being turned into prison bitches due to their lack of street skills or prison experience Watching Oz, you may become comfortable with what is uncomfortable viewingthats if you can get in touch with your darker side [SEP]\"" ] }, "execution_count": 11, - "metadata": { - "tags": [] - }, + "metadata": {}, "output_type": "execute_result" } ], @@ -564,13 +463,6 @@ "outputId": "e80ff8c7-991d-45a4-9caf-600f9e694998" }, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 231508/231508 [00:00<00:00, 312015.27B/s]\n" - ] - }, { "name": "stdout", "output_type": "stream", @@ -718,13 +610,6 @@ "outputId": "d36884cd-ea8b-4954-ad2d-303d065f0ea0" }, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 407873900/407873900 [00:34<00:00, 11838492.74B/s]\n" - ] - }, { "name": "stdout", "output_type": "stream", @@ -740,260 +625,7 @@ " )\n", " (encoder): BertEncoder(\n", " (layer): ModuleList(\n", - " (0): BertLayer(\n", - " (attention): BertAttention(\n", - " (self): BertSelfAttention(\n", - " (query): Linear(in_features=768, out_features=768, bias=True)\n", - " (key): Linear(in_features=768, out_features=768, bias=True)\n", - " (value): Linear(in_features=768, out_features=768, bias=True)\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " (output): BertSelfOutput(\n", - " (dense): Linear(in_features=768, out_features=768, bias=True)\n", - " (LayerNorm): BertLayerNorm()\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " )\n", - " (intermediate): BertIntermediate(\n", - " (dense): Linear(in_features=768, out_features=3072, bias=True)\n", - " )\n", - " (output): BertOutput(\n", - " (dense): Linear(in_features=3072, out_features=768, bias=True)\n", - " (LayerNorm): BertLayerNorm()\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " )\n", - " (1): BertLayer(\n", - " (attention): BertAttention(\n", - " (self): BertSelfAttention(\n", - " (query): Linear(in_features=768, out_features=768, bias=True)\n", - " (key): Linear(in_features=768, out_features=768, bias=True)\n", - " (value): Linear(in_features=768, out_features=768, bias=True)\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " (output): BertSelfOutput(\n", - " (dense): Linear(in_features=768, out_features=768, bias=True)\n", - " (LayerNorm): BertLayerNorm()\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " )\n", - " (intermediate): BertIntermediate(\n", - " (dense): Linear(in_features=768, out_features=3072, bias=True)\n", - " )\n", - " (output): BertOutput(\n", - " (dense): Linear(in_features=3072, out_features=768, bias=True)\n", - " (LayerNorm): BertLayerNorm()\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " )\n", - " (2): BertLayer(\n", - " (attention): BertAttention(\n", - " (self): BertSelfAttention(\n", - " (query): Linear(in_features=768, out_features=768, bias=True)\n", - " (key): Linear(in_features=768, out_features=768, bias=True)\n", - " (value): Linear(in_features=768, out_features=768, bias=True)\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " (output): BertSelfOutput(\n", - " (dense): Linear(in_features=768, out_features=768, bias=True)\n", - " (LayerNorm): BertLayerNorm()\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " )\n", - " (intermediate): BertIntermediate(\n", - " (dense): Linear(in_features=768, out_features=3072, bias=True)\n", - " )\n", - " (output): BertOutput(\n", - " (dense): Linear(in_features=3072, out_features=768, bias=True)\n", - " (LayerNorm): BertLayerNorm()\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " )\n", - " (3): BertLayer(\n", - " (attention): BertAttention(\n", - " (self): BertSelfAttention(\n", - " (query): Linear(in_features=768, out_features=768, bias=True)\n", - " (key): Linear(in_features=768, out_features=768, bias=True)\n", - " (value): Linear(in_features=768, out_features=768, bias=True)\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " (output): BertSelfOutput(\n", - " (dense): Linear(in_features=768, out_features=768, bias=True)\n", - " (LayerNorm): BertLayerNorm()\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " )\n", - " (intermediate): BertIntermediate(\n", - " (dense): Linear(in_features=768, out_features=3072, bias=True)\n", - " )\n", - " (output): BertOutput(\n", - " (dense): Linear(in_features=3072, out_features=768, bias=True)\n", - " (LayerNorm): BertLayerNorm()\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " )\n", - " (4): BertLayer(\n", - " (attention): BertAttention(\n", - " (self): BertSelfAttention(\n", - " (query): Linear(in_features=768, out_features=768, bias=True)\n", - " (key): Linear(in_features=768, out_features=768, bias=True)\n", - " (value): Linear(in_features=768, out_features=768, bias=True)\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " (output): BertSelfOutput(\n", - " (dense): Linear(in_features=768, out_features=768, bias=True)\n", - " (LayerNorm): BertLayerNorm()\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " )\n", - " (intermediate): BertIntermediate(\n", - " (dense): Linear(in_features=768, out_features=3072, bias=True)\n", - " )\n", - " (output): BertOutput(\n", - " (dense): Linear(in_features=3072, out_features=768, bias=True)\n", - " (LayerNorm): BertLayerNorm()\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " )\n", - " (5): BertLayer(\n", - " (attention): BertAttention(\n", - " (self): BertSelfAttention(\n", - " (query): Linear(in_features=768, out_features=768, bias=True)\n", - " (key): Linear(in_features=768, out_features=768, bias=True)\n", - " (value): Linear(in_features=768, out_features=768, bias=True)\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " (output): BertSelfOutput(\n", - " (dense): Linear(in_features=768, out_features=768, bias=True)\n", - " (LayerNorm): BertLayerNorm()\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " )\n", - " (intermediate): BertIntermediate(\n", - " (dense): Linear(in_features=768, out_features=3072, bias=True)\n", - " )\n", - " (output): BertOutput(\n", - " (dense): Linear(in_features=3072, out_features=768, bias=True)\n", - " (LayerNorm): BertLayerNorm()\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " )\n", - " (6): BertLayer(\n", - " (attention): BertAttention(\n", - " (self): BertSelfAttention(\n", - " (query): Linear(in_features=768, out_features=768, bias=True)\n", - " (key): Linear(in_features=768, out_features=768, bias=True)\n", - " (value): Linear(in_features=768, out_features=768, bias=True)\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " (output): BertSelfOutput(\n", - " (dense): Linear(in_features=768, out_features=768, bias=True)\n", - " (LayerNorm): BertLayerNorm()\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " )\n", - " (intermediate): BertIntermediate(\n", - " (dense): Linear(in_features=768, out_features=3072, bias=True)\n", - " )\n", - " (output): BertOutput(\n", - " (dense): Linear(in_features=3072, out_features=768, bias=True)\n", - " (LayerNorm): BertLayerNorm()\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " )\n", - " (7): BertLayer(\n", - " (attention): BertAttention(\n", - " (self): BertSelfAttention(\n", - " (query): Linear(in_features=768, out_features=768, bias=True)\n", - " (key): Linear(in_features=768, out_features=768, bias=True)\n", - " (value): Linear(in_features=768, out_features=768, bias=True)\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " (output): BertSelfOutput(\n", - " (dense): Linear(in_features=768, out_features=768, bias=True)\n", - " (LayerNorm): BertLayerNorm()\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " )\n", - " (intermediate): BertIntermediate(\n", - " (dense): Linear(in_features=768, out_features=3072, bias=True)\n", - " )\n", - " (output): BertOutput(\n", - " (dense): Linear(in_features=3072, out_features=768, bias=True)\n", - " (LayerNorm): BertLayerNorm()\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " )\n", - " (8): BertLayer(\n", - " (attention): BertAttention(\n", - " (self): BertSelfAttention(\n", - " (query): Linear(in_features=768, out_features=768, bias=True)\n", - " (key): Linear(in_features=768, out_features=768, bias=True)\n", - " (value): Linear(in_features=768, out_features=768, bias=True)\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " (output): BertSelfOutput(\n", - " (dense): Linear(in_features=768, out_features=768, bias=True)\n", - " (LayerNorm): BertLayerNorm()\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " )\n", - " (intermediate): BertIntermediate(\n", - " (dense): Linear(in_features=768, out_features=3072, bias=True)\n", - " )\n", - " (output): BertOutput(\n", - " (dense): Linear(in_features=3072, out_features=768, bias=True)\n", - " (LayerNorm): BertLayerNorm()\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " )\n", - " (9): BertLayer(\n", - " (attention): BertAttention(\n", - " (self): BertSelfAttention(\n", - " (query): Linear(in_features=768, out_features=768, bias=True)\n", - " (key): Linear(in_features=768, out_features=768, bias=True)\n", - " (value): Linear(in_features=768, out_features=768, bias=True)\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " (output): BertSelfOutput(\n", - " (dense): Linear(in_features=768, out_features=768, bias=True)\n", - " (LayerNorm): BertLayerNorm()\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " )\n", - " (intermediate): BertIntermediate(\n", - " (dense): Linear(in_features=768, out_features=3072, bias=True)\n", - " )\n", - " (output): BertOutput(\n", - " (dense): Linear(in_features=3072, out_features=768, bias=True)\n", - " (LayerNorm): BertLayerNorm()\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " )\n", - " (10): BertLayer(\n", - " (attention): BertAttention(\n", - " (self): BertSelfAttention(\n", - " (query): Linear(in_features=768, out_features=768, bias=True)\n", - " (key): Linear(in_features=768, out_features=768, bias=True)\n", - " (value): Linear(in_features=768, out_features=768, bias=True)\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " (output): BertSelfOutput(\n", - " (dense): Linear(in_features=768, out_features=768, bias=True)\n", - " (LayerNorm): BertLayerNorm()\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " )\n", - " (intermediate): BertIntermediate(\n", - " (dense): Linear(in_features=768, out_features=3072, bias=True)\n", - " )\n", - " (output): BertOutput(\n", - " (dense): Linear(in_features=3072, out_features=768, bias=True)\n", - " (LayerNorm): BertLayerNorm()\n", - " (dropout): Dropout(p=0.1, inplace=False)\n", - " )\n", - " )\n", - " (11): BertLayer(\n", + " (0-11): 12 x BertLayer(\n", " (attention): BertAttention(\n", " (self): BertSelfAttention(\n", " (query): Linear(in_features=768, out_features=768, bias=True)\n", @@ -1058,7 +690,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -1073,97 +705,8 @@ "output_type": "stream", "text": [ "t_total value of -1 results in schedule not being applied\n", - "Epoch: 0%| | 0/4 [00:00" - ] - }, - "metadata": { - "needs_background": "light", - "tags": [] - }, - "output_type": "display_data" } ], "source": [ @@ -1257,6 +800,20 @@ "plt.plot(train_loss_set)\n", "plt.show()" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -1267,7 +824,7 @@ "provenance": [] }, "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -1281,9 +838,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.4" + "version": "3.9.17" } }, "nbformat": 4, - "nbformat_minor": 1 + "nbformat_minor": 4 } diff --git a/Ch4/07_BERT_Sentiment_Classification_IMDB_ktrain.ipynb b/Ch4/07_BERT_Sentiment_Classification_IMDB_ktrain.ipynb index ffed776..83eed16 100644 --- a/Ch4/07_BERT_Sentiment_Classification_IMDB_ktrain.ipynb +++ b/Ch4/07_BERT_Sentiment_Classification_IMDB_ktrain.ipynb @@ -11,7 +11,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -19,170 +19,22 @@ "id": "TF5qfV_flTbr", "outputId": "b536d10d-767d-4a8d-9cd6-2ea607550b1b" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Requirement already satisfied: numpy==1.19.5 in /usr/local/lib/python3.7/dist-packages (1.19.5)\n", - "Requirement already satisfied: pandas==1.1.5 in /usr/local/lib/python3.7/dist-packages (1.1.5)\n", - "Requirement already satisfied: python-dateutil>=2.7.3 in /usr/local/lib/python3.7/dist-packages (from pandas==1.1.5) (2.8.1)\n", - "Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.7/dist-packages (from pandas==1.1.5) (2018.9)\n", - "Requirement already satisfied: numpy>=1.15.4 in /usr/local/lib/python3.7/dist-packages (from pandas==1.1.5) (1.19.5)\n", - "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.7/dist-packages (from python-dateutil>=2.7.3->pandas==1.1.5) (1.15.0)\n", - "Collecting ktrain==0.26.3\n", - "\u001b[?25l Downloading https://files.pythonhosted.org/packages/4c/88/10d29578f47d0d140bf669d5598e9f5a50465ddc423b32031c65e840d003/ktrain-0.26.3.tar.gz (25.3MB)\n", - "\u001b[K |████████████████████████████████| 25.3MB 1.6MB/s \n", - "\u001b[?25hCollecting scikit-learn==0.23.2\n", - "\u001b[?25l Downloading https://files.pythonhosted.org/packages/f4/cb/64623369f348e9bfb29ff898a57ac7c91ed4921f228e9726546614d63ccb/scikit_learn-0.23.2-cp37-cp37m-manylinux1_x86_64.whl (6.8MB)\n", - "\u001b[K |████████████████████████████████| 6.8MB 41.4MB/s \n", - "\u001b[?25hRequirement already satisfied: matplotlib>=3.0.0 in /usr/local/lib/python3.7/dist-packages (from ktrain==0.26.3) (3.2.2)\n", - "Requirement already satisfied: pandas>=1.0.1 in /usr/local/lib/python3.7/dist-packages (from ktrain==0.26.3) (1.1.5)\n", - "Requirement already satisfied: fastprogress>=0.1.21 in /usr/local/lib/python3.7/dist-packages (from ktrain==0.26.3) (1.0.0)\n", - "Requirement already satisfied: requests in /usr/local/lib/python3.7/dist-packages (from ktrain==0.26.3) (2.23.0)\n", - "Requirement already satisfied: joblib in /usr/local/lib/python3.7/dist-packages (from ktrain==0.26.3) (1.0.1)\n", - "Requirement already satisfied: packaging in /usr/local/lib/python3.7/dist-packages (from ktrain==0.26.3) (21.0)\n", - "Requirement already satisfied: ipython in /usr/local/lib/python3.7/dist-packages (from ktrain==0.26.3) (5.5.0)\n", - "Collecting langdetect\n", - "\u001b[?25l Downloading https://files.pythonhosted.org/packages/0e/72/a3add0e4eec4eb9e2569554f7c70f4a3c27712f40e3284d483e88094cc0e/langdetect-1.0.9.tar.gz (981kB)\n", - "\u001b[K |████████████████████████████████| 983kB 43.3MB/s \n", - "\u001b[?25hRequirement already satisfied: jieba in /usr/local/lib/python3.7/dist-packages (from ktrain==0.26.3) (0.42.1)\n", - "Collecting cchardet\n", - "\u001b[?25l Downloading https://files.pythonhosted.org/packages/80/72/a4fba7559978de00cf44081c548c5d294bf00ac7dcda2db405d2baa8c67a/cchardet-2.1.7-cp37-cp37m-manylinux2010_x86_64.whl (263kB)\n", - "\u001b[K |████████████████████████████████| 266kB 50.8MB/s \n", - "\u001b[?25hCollecting syntok\n", - " Downloading https://files.pythonhosted.org/packages/8c/76/a49e73a04b3e3a14ce232e8e28a1587f8108baa665644fe8c40e307e792e/syntok-1.3.1.tar.gz\n", - "Collecting seqeval==0.0.19\n", - " Downloading https://files.pythonhosted.org/packages/93/e5/b7705156a77f742cfe4fc6f22d0c71591edb2d243328dff2f8fc0f933ab6/seqeval-0.0.19.tar.gz\n", - "Collecting transformers<=4.3.3,>=4.0.0\n", - "\u001b[?25l Downloading https://files.pythonhosted.org/packages/f9/54/5ca07ec9569d2f232f3166de5457b63943882f7950ddfcc887732fc7fb23/transformers-4.3.3-py3-none-any.whl (1.9MB)\n", - "\u001b[K |████████████████████████████████| 1.9MB 37.5MB/s \n", - "\u001b[?25hCollecting sentencepiece\n", - "\u001b[?25l Downloading https://files.pythonhosted.org/packages/ac/aa/1437691b0c7c83086ebb79ce2da16e00bef024f24fec2a5161c35476f499/sentencepiece-0.1.96-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.2MB)\n", - "\u001b[K |████████████████████████████████| 1.2MB 41.8MB/s \n", - "\u001b[?25hCollecting keras_bert>=0.86.0\n", - " Downloading https://files.pythonhosted.org/packages/6a/e4/3b2e2927c15c22f44005cb0ab0eaf2f7e623ea2b6488e4b7c5aca6c162c2/keras-bert-0.88.0.tar.gz\n", - "Requirement already satisfied: networkx>=2.3 in /usr/local/lib/python3.7/dist-packages (from ktrain==0.26.3) (2.5.1)\n", - "Collecting whoosh\n", - "\u001b[?25l Downloading https://files.pythonhosted.org/packages/ba/19/24d0f1f454a2c1eb689ca28d2f178db81e5024f42d82729a4ff6771155cf/Whoosh-2.7.4-py2.py3-none-any.whl (468kB)\n", - "\u001b[K |████████████████████████████████| 471kB 34.8MB/s \n", - "\u001b[?25hRequirement already satisfied: scipy>=0.19.1 in /usr/local/lib/python3.7/dist-packages (from scikit-learn==0.23.2->ktrain==0.26.3) (1.4.1)\n", - "Requirement already satisfied: numpy>=1.13.3 in /usr/local/lib/python3.7/dist-packages (from scikit-learn==0.23.2->ktrain==0.26.3) (1.19.5)\n", - "Collecting threadpoolctl>=2.0.0\n", - " Downloading https://files.pythonhosted.org/packages/c6/e8/c216b9b60cbba4642d3ca1bae7a53daa0c24426f662e0e3ce3dc7f6caeaa/threadpoolctl-2.2.0-py3-none-any.whl\n", - "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.7/dist-packages (from matplotlib>=3.0.0->ktrain==0.26.3) (0.10.0)\n", - "Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib>=3.0.0->ktrain==0.26.3) (1.3.1)\n", - "Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib>=3.0.0->ktrain==0.26.3) (2.4.7)\n", - "Requirement already satisfied: python-dateutil>=2.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib>=3.0.0->ktrain==0.26.3) (2.8.1)\n", - "Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.7/dist-packages (from pandas>=1.0.1->ktrain==0.26.3) (2018.9)\n", - "Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from requests->ktrain==0.26.3) (3.0.4)\n", - "Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.7/dist-packages (from requests->ktrain==0.26.3) (2.10)\n", - "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.7/dist-packages (from requests->ktrain==0.26.3) (2021.5.30)\n", - "Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /usr/local/lib/python3.7/dist-packages (from requests->ktrain==0.26.3) (1.24.3)\n", - "Requirement already satisfied: simplegeneric>0.8 in /usr/local/lib/python3.7/dist-packages (from ipython->ktrain==0.26.3) (0.8.1)\n", - "Requirement already satisfied: traitlets>=4.2 in /usr/local/lib/python3.7/dist-packages (from ipython->ktrain==0.26.3) (5.0.5)\n", - "Requirement already satisfied: setuptools>=18.5 in /usr/local/lib/python3.7/dist-packages (from ipython->ktrain==0.26.3) (57.2.0)\n", - "Requirement already satisfied: decorator in /usr/local/lib/python3.7/dist-packages (from ipython->ktrain==0.26.3) (4.4.2)\n", - "Requirement already satisfied: pexpect; sys_platform != \"win32\" in /usr/local/lib/python3.7/dist-packages (from ipython->ktrain==0.26.3) (4.8.0)\n", - "Requirement already satisfied: prompt-toolkit<2.0.0,>=1.0.4 in /usr/local/lib/python3.7/dist-packages (from ipython->ktrain==0.26.3) (1.0.18)\n", - "Requirement already satisfied: pickleshare in /usr/local/lib/python3.7/dist-packages (from ipython->ktrain==0.26.3) (0.7.5)\n", - "Requirement already satisfied: pygments in /usr/local/lib/python3.7/dist-packages (from ipython->ktrain==0.26.3) (2.6.1)\n", - "Requirement already satisfied: six in /usr/local/lib/python3.7/dist-packages (from langdetect->ktrain==0.26.3) (1.15.0)\n", - "Requirement already satisfied: regex in /usr/local/lib/python3.7/dist-packages (from syntok->ktrain==0.26.3) (2019.12.20)\n", - "Requirement already satisfied: Keras>=2.2.4 in /usr/local/lib/python3.7/dist-packages (from seqeval==0.0.19->ktrain==0.26.3) (2.4.3)\n", - "Collecting sacremoses\n", - "\u001b[?25l Downloading https://files.pythonhosted.org/packages/75/ee/67241dc87f266093c533a2d4d3d69438e57d7a90abb216fa076e7d475d4a/sacremoses-0.0.45-py3-none-any.whl (895kB)\n", - "\u001b[K |████████████████████████████████| 901kB 41.4MB/s \n", - "\u001b[?25hCollecting tokenizers<0.11,>=0.10.1\n", - "\u001b[?25l Downloading https://files.pythonhosted.org/packages/d4/e2/df3543e8ffdab68f5acc73f613de9c2b155ac47f162e725dcac87c521c11/tokenizers-0.10.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl (3.3MB)\n", - "\u001b[K |████████████████████████████████| 3.3MB 37.9MB/s \n", - "\u001b[?25hRequirement already satisfied: tqdm>=4.27 in /usr/local/lib/python3.7/dist-packages (from transformers<=4.3.3,>=4.0.0->ktrain==0.26.3) (4.41.1)\n", - "Requirement already satisfied: filelock in /usr/local/lib/python3.7/dist-packages (from transformers<=4.3.3,>=4.0.0->ktrain==0.26.3) (3.0.12)\n", - "Requirement already satisfied: importlib-metadata; python_version < \"3.8\" in /usr/local/lib/python3.7/dist-packages (from transformers<=4.3.3,>=4.0.0->ktrain==0.26.3) (4.6.1)\n", - "Collecting keras-transformer>=0.39.0\n", - " Downloading https://files.pythonhosted.org/packages/8a/35/6b079e920fe09a9349028bc2f209447e5636d90e29c5cf060bcc3177803a/keras-transformer-0.39.0.tar.gz\n", - "Requirement already satisfied: ipython-genutils in /usr/local/lib/python3.7/dist-packages (from traitlets>=4.2->ipython->ktrain==0.26.3) (0.2.0)\n", - "Requirement already satisfied: ptyprocess>=0.5 in /usr/local/lib/python3.7/dist-packages (from pexpect; sys_platform != \"win32\"->ipython->ktrain==0.26.3) (0.7.0)\n", - "Requirement already satisfied: wcwidth in /usr/local/lib/python3.7/dist-packages (from prompt-toolkit<2.0.0,>=1.0.4->ipython->ktrain==0.26.3) (0.2.5)\n", - "Requirement already satisfied: pyyaml in /usr/local/lib/python3.7/dist-packages (from Keras>=2.2.4->seqeval==0.0.19->ktrain==0.26.3) (3.13)\n", - "Requirement already satisfied: h5py in /usr/local/lib/python3.7/dist-packages (from Keras>=2.2.4->seqeval==0.0.19->ktrain==0.26.3) (3.1.0)\n", - "Requirement already satisfied: click in /usr/local/lib/python3.7/dist-packages (from sacremoses->transformers<=4.3.3,>=4.0.0->ktrain==0.26.3) (7.1.2)\n", - "Requirement already satisfied: zipp>=0.5 in /usr/local/lib/python3.7/dist-packages (from importlib-metadata; python_version < \"3.8\"->transformers<=4.3.3,>=4.0.0->ktrain==0.26.3) (3.5.0)\n", - "Requirement already satisfied: typing-extensions>=3.6.4; python_version < \"3.8\" in /usr/local/lib/python3.7/dist-packages (from importlib-metadata; python_version < \"3.8\"->transformers<=4.3.3,>=4.0.0->ktrain==0.26.3) (3.7.4.3)\n", - "Collecting keras-pos-embd>=0.12.0\n", - " Downloading https://files.pythonhosted.org/packages/d8/d2/1cc072ea68b573f366e08936177a33e237e66fa7d5338289d4bee64696cf/keras-pos-embd-0.12.0.tar.gz\n", - "Collecting keras-multi-head>=0.28.0\n", - " Downloading https://files.pythonhosted.org/packages/a5/e6/a83f26b2e1582de237b125f595874d808e40698f31d44d5903e872d5b64d/keras-multi-head-0.28.0.tar.gz\n", - "Collecting keras-layer-normalization>=0.15.0\n", - " Downloading https://files.pythonhosted.org/packages/33/e1/0da586d544a0940a56a2f4aa704b7dbd95eaa8ceda6168b48f5ac95e6608/keras-layer-normalization-0.15.0.tar.gz\n", - "Collecting keras-position-wise-feed-forward>=0.7.0\n", - " Downloading https://files.pythonhosted.org/packages/58/02/cd3e7e51cf45d3825818384a2f7d9c340b60c9bf55a5682b7318e1c16eab/keras-position-wise-feed-forward-0.7.0.tar.gz\n", - "Collecting keras-embed-sim>=0.9.0\n", - " Downloading https://files.pythonhosted.org/packages/2d/48/78f6d134f1ede597d91186819c9e428ada51cd8d9ea28e5faf37ed2ee602/keras-embed-sim-0.9.0.tar.gz\n", - "Requirement already satisfied: cached-property; python_version < \"3.8\" in /usr/local/lib/python3.7/dist-packages (from h5py->Keras>=2.2.4->seqeval==0.0.19->ktrain==0.26.3) (1.5.2)\n", - "Collecting keras-self-attention>=0.50.0\n", - " Downloading https://files.pythonhosted.org/packages/ea/75/e6bc5b43ee968fef714f2f10a2a1674639ec85d2428cc47b2fe1f9af0115/keras-self-attention-0.50.0.tar.gz\n", - "Building wheels for collected packages: ktrain, langdetect, syntok, seqeval, keras-bert, keras-transformer, keras-pos-embd, keras-multi-head, keras-layer-normalization, keras-position-wise-feed-forward, keras-embed-sim, keras-self-attention\n", - " Building wheel for ktrain (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for ktrain: filename=ktrain-0.26.3-cp37-none-any.whl size=25282390 sha256=0f129e50aaa4d78ab674e5f6b95d1c66df8f4fa6b62a1ac02b1867c70bbbdecd\n", - " Stored in directory: /root/.cache/pip/wheels/16/05/be/d6e659b3349016b1059e19fa028f165af4eeae2c196f329112\n", - " Building wheel for langdetect (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for langdetect: filename=langdetect-1.0.9-cp37-none-any.whl size=993242 sha256=489499c000ae032ae91b31fdedc7ec2d0ef1fccbd3b997508abed818cd45e520\n", - " Stored in directory: /root/.cache/pip/wheels/7e/18/13/038c34057808931c7ddc6c92d3aa015cf1a498df5a70268996\n", - " Building wheel for syntok (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for syntok: filename=syntok-1.3.1-cp37-none-any.whl size=20919 sha256=d2ed41e31e9075584cdf09f7dcd35228826294d924d63d47ea163b49411be409\n", - " Stored in directory: /root/.cache/pip/wheels/51/c6/a4/be1920586c49469846bcd2888200bdecfe109ec421dab9be2d\n", - " Building wheel for seqeval (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for seqeval: filename=seqeval-0.0.19-cp37-none-any.whl size=9932 sha256=0f0d22a626859918451e3439effefce4ee362409cc6a0afe0d953ebb60ab7e3b\n", - " Stored in directory: /root/.cache/pip/wheels/8d/1f/bf/1198beceed805a2099060975f6281d1b01046dd279e19c97be\n", - " Building wheel for keras-bert (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for keras-bert: filename=keras_bert-0.88.0-cp37-none-any.whl size=34206 sha256=847f5cdc7a31d9961b28e9c07b757d07c882be271c96a892b97c68b0ce425518\n", - " Stored in directory: /root/.cache/pip/wheels/7f/d8/86/b4d91b941f6f3256c487b258d5e4268a3301203b717dd11f11\n", - " Building wheel for keras-transformer (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for keras-transformer: filename=keras_transformer-0.39.0-cp37-none-any.whl size=12841 sha256=ddc1c3d23d4f739bf6269455f093aeac6995c0ec9a6d38ac3af3e5744d012e57\n", - " Stored in directory: /root/.cache/pip/wheels/77/42/35/d33c5907bca04ac5742e9eceefb644b680286de26728506a70\n", - " Building wheel for keras-pos-embd (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for keras-pos-embd: filename=keras_pos_embd-0.12.0-cp37-none-any.whl size=7471 sha256=c3a4694a4c7002edf0f340cc03c67b10a1d2b07aa9a6d8cadd4c417d56be27dd\n", - " Stored in directory: /root/.cache/pip/wheels/36/d8/36/06ed09215806dca9ff504d8c0dda5da68d7f2c67d34a231d82\n", - " Building wheel for keras-multi-head (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for keras-multi-head: filename=keras_multi_head-0.28.0-cp37-none-any.whl size=15559 sha256=8bdc034bc047b17f8a5b6c5f9e22e5a9d500f0d7c6068048d8549ce75d8a0237\n", - " Stored in directory: /root/.cache/pip/wheels/ec/92/bd/b3407bc29501f7e28eb970a6c425a9a375485c5d8197df6a8f\n", - " Building wheel for keras-layer-normalization (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for keras-layer-normalization: filename=keras_layer_normalization-0.15.0-cp37-none-any.whl size=5224 sha256=1f6ae80fd5d9dfe471270c411aff14452b56bcad9abb0ada2e955ee9de5ad0b4\n", - " Stored in directory: /root/.cache/pip/wheels/de/ea/db/833c8a9b8326e703e9f8a78c0d4153294e6a1b1f97a1836397\n", - " Building wheel for keras-position-wise-feed-forward (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for keras-position-wise-feed-forward: filename=keras_position_wise_feed_forward-0.7.0-cp37-none-any.whl size=5542 sha256=8794e806be2e654221719710e19e5219aa71df372998360a12e73281735ff2b8\n", - " Stored in directory: /root/.cache/pip/wheels/d2/d2/f6/58ce0aae0055dbccba8b40e62a6c22ab997105ad8c431a9e80\n", - " Building wheel for keras-embed-sim (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for keras-embed-sim: filename=keras_embed_sim-0.9.0-cp37-none-any.whl size=4505 sha256=1167638915a16210f6e8f52ed29f112b1ff7abfb096bbb3c25f9b9c3d0ab52c6\n", - " Stored in directory: /root/.cache/pip/wheels/c1/d5/7d/bef5ee93c88bc6150294cc74cbb081647c505bf816918dd7ff\n", - " Building wheel for keras-self-attention (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for keras-self-attention: filename=keras_self_attention-0.50.0-cp37-none-any.whl size=19416 sha256=40ca0b3fc31fcb65f163ad549b188cedd452b68d48d21e636b467155b7e43d73\n", - " Stored in directory: /root/.cache/pip/wheels/29/93/0d/891573db60f74d0e43bd7db1496c3ef898f8b5946a4c24cbda\n", - "Successfully built ktrain langdetect syntok seqeval keras-bert keras-transformer keras-pos-embd keras-multi-head keras-layer-normalization keras-position-wise-feed-forward keras-embed-sim keras-self-attention\n", - "Installing collected packages: threadpoolctl, scikit-learn, langdetect, cchardet, syntok, seqeval, sacremoses, tokenizers, transformers, sentencepiece, keras-pos-embd, keras-self-attention, keras-multi-head, keras-layer-normalization, keras-position-wise-feed-forward, keras-embed-sim, keras-transformer, keras-bert, whoosh, ktrain\n", - " Found existing installation: scikit-learn 0.22.2.post1\n", - " Uninstalling scikit-learn-0.22.2.post1:\n", - " Successfully uninstalled scikit-learn-0.22.2.post1\n", - "Successfully installed cchardet-2.1.7 keras-bert-0.88.0 keras-embed-sim-0.9.0 keras-layer-normalization-0.15.0 keras-multi-head-0.28.0 keras-pos-embd-0.12.0 keras-position-wise-feed-forward-0.7.0 keras-self-attention-0.50.0 keras-transformer-0.39.0 ktrain-0.26.3 langdetect-1.0.9 sacremoses-0.0.45 scikit-learn-0.23.2 sentencepiece-0.1.96 seqeval-0.0.19 syntok-1.3.1 threadpoolctl-2.2.0 tokenizers-0.10.3 transformers-4.3.3 whoosh-2.7.4\n" - ] - } - ], + "outputs": [], "source": [ "# To install only the requirements of this notebook, uncomment the lines below and run this cell\n", "\n", "# ===========================\n", "\n", - "!pip install numpy==1.19.5\n", - "!pip install pandas==1.1.5\n", - "!pip install ktrain==0.26.3\n", + "# !pip install numpy==1.19.5\n", + "# !pip install pandas==1.1.5\n", + "# !pip install ktrain==0.26.3\n", "\n", "# ===========================" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "metadata": { "id": "_UN7tuqnlTbs" }, @@ -203,7 +55,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -211,91 +63,28 @@ "id": "58WB13Jx3rQm", "outputId": "9af6cd3f-771e-4807-d041-bb8a3290bea1" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Collecting tensorflow==2.4.0\n", - "\u001b[?25l Downloading https://files.pythonhosted.org/packages/94/0a/012cc33c643d844433d13001dd1db179e7020b05ddbbd0a9dc86c38a8efa/tensorflow-2.4.0-cp37-cp37m-manylinux2010_x86_64.whl (394.7MB)\n", - "\u001b[K |████████████████████████████████| 394.7MB 41kB/s \n", - "\u001b[?25hCollecting h5py~=2.10.0\n", - "\u001b[?25l Downloading https://files.pythonhosted.org/packages/3f/c0/abde58b837e066bca19a3f7332d9d0493521d7dd6b48248451a9e3fe2214/h5py-2.10.0-cp37-cp37m-manylinux1_x86_64.whl (2.9MB)\n", - "\u001b[K |████████████████████████████████| 2.9MB 45.8MB/s \n", - "\u001b[?25hRequirement already satisfied: termcolor~=1.1.0 in /usr/local/lib/python3.7/dist-packages (from tensorflow==2.4.0) (1.1.0)\n", - "Requirement already satisfied: absl-py~=0.10 in /usr/local/lib/python3.7/dist-packages (from tensorflow==2.4.0) (0.12.0)\n", - "Requirement already satisfied: numpy~=1.19.2 in /usr/local/lib/python3.7/dist-packages (from tensorflow==2.4.0) (1.19.5)\n", - "Requirement already satisfied: six~=1.15.0 in /usr/local/lib/python3.7/dist-packages (from tensorflow==2.4.0) (1.15.0)\n", - "Requirement already satisfied: wrapt~=1.12.1 in /usr/local/lib/python3.7/dist-packages (from tensorflow==2.4.0) (1.12.1)\n", - "Collecting tensorflow-estimator<2.5.0,>=2.4.0rc0\n", - "\u001b[?25l Downloading https://files.pythonhosted.org/packages/74/7e/622d9849abf3afb81e482ffc170758742e392ee129ce1540611199a59237/tensorflow_estimator-2.4.0-py2.py3-none-any.whl (462kB)\n", - "\u001b[K |████████████████████████████████| 471kB 39.7MB/s \n", - "\u001b[?25hCollecting grpcio~=1.32.0\n", - "\u001b[?25l Downloading https://files.pythonhosted.org/packages/06/54/1c8be62beafe7fb1548d2968e518ca040556b46b0275399d4f3186c56d79/grpcio-1.32.0-cp37-cp37m-manylinux2014_x86_64.whl (3.8MB)\n", - "\u001b[K |████████████████████████████████| 3.8MB 37.5MB/s \n", - "\u001b[?25hRequirement already satisfied: opt-einsum~=3.3.0 in /usr/local/lib/python3.7/dist-packages (from tensorflow==2.4.0) (3.3.0)\n", - "Requirement already satisfied: typing-extensions~=3.7.4 in /usr/local/lib/python3.7/dist-packages (from tensorflow==2.4.0) (3.7.4.3)\n", - "Requirement already satisfied: flatbuffers~=1.12.0 in /usr/local/lib/python3.7/dist-packages (from tensorflow==2.4.0) (1.12)\n", - "Requirement already satisfied: protobuf>=3.9.2 in /usr/local/lib/python3.7/dist-packages (from tensorflow==2.4.0) (3.17.3)\n", - "Requirement already satisfied: tensorboard~=2.4 in /usr/local/lib/python3.7/dist-packages (from tensorflow==2.4.0) (2.5.0)\n", - "Collecting gast==0.3.3\n", - " Downloading https://files.pythonhosted.org/packages/d6/84/759f5dd23fec8ba71952d97bcc7e2c9d7d63bdc582421f3cd4be845f0c98/gast-0.3.3-py2.py3-none-any.whl\n", - "Requirement already satisfied: wheel~=0.35 in /usr/local/lib/python3.7/dist-packages (from tensorflow==2.4.0) (0.36.2)\n", - "Requirement already satisfied: astunparse~=1.6.3 in /usr/local/lib/python3.7/dist-packages (from tensorflow==2.4.0) (1.6.3)\n", - "Requirement already satisfied: google-pasta~=0.2 in /usr/local/lib/python3.7/dist-packages (from tensorflow==2.4.0) (0.2.0)\n", - "Requirement already satisfied: keras-preprocessing~=1.1.2 in /usr/local/lib/python3.7/dist-packages (from tensorflow==2.4.0) (1.1.2)\n", - "Requirement already satisfied: google-auth-oauthlib<0.5,>=0.4.1 in /usr/local/lib/python3.7/dist-packages (from tensorboard~=2.4->tensorflow==2.4.0) (0.4.4)\n", - "Requirement already satisfied: google-auth<2,>=1.6.3 in /usr/local/lib/python3.7/dist-packages (from tensorboard~=2.4->tensorflow==2.4.0) (1.32.1)\n", - "Requirement already satisfied: tensorboard-plugin-wit>=1.6.0 in /usr/local/lib/python3.7/dist-packages (from tensorboard~=2.4->tensorflow==2.4.0) (1.8.0)\n", - "Requirement already satisfied: werkzeug>=0.11.15 in /usr/local/lib/python3.7/dist-packages (from tensorboard~=2.4->tensorflow==2.4.0) (1.0.1)\n", - "Requirement already satisfied: requests<3,>=2.21.0 in /usr/local/lib/python3.7/dist-packages (from tensorboard~=2.4->tensorflow==2.4.0) (2.23.0)\n", - "Requirement already satisfied: setuptools>=41.0.0 in /usr/local/lib/python3.7/dist-packages (from tensorboard~=2.4->tensorflow==2.4.0) (57.2.0)\n", - "Requirement already satisfied: markdown>=2.6.8 in /usr/local/lib/python3.7/dist-packages (from tensorboard~=2.4->tensorflow==2.4.0) (3.3.4)\n", - "Requirement already satisfied: tensorboard-data-server<0.7.0,>=0.6.0 in /usr/local/lib/python3.7/dist-packages (from tensorboard~=2.4->tensorflow==2.4.0) (0.6.1)\n", - "Requirement already satisfied: requests-oauthlib>=0.7.0 in /usr/local/lib/python3.7/dist-packages (from google-auth-oauthlib<0.5,>=0.4.1->tensorboard~=2.4->tensorflow==2.4.0) (1.3.0)\n", - "Requirement already satisfied: pyasn1-modules>=0.2.1 in /usr/local/lib/python3.7/dist-packages (from google-auth<2,>=1.6.3->tensorboard~=2.4->tensorflow==2.4.0) (0.2.8)\n", - "Requirement already satisfied: cachetools<5.0,>=2.0.0 in /usr/local/lib/python3.7/dist-packages (from google-auth<2,>=1.6.3->tensorboard~=2.4->tensorflow==2.4.0) (4.2.2)\n", - "Requirement already satisfied: rsa<5,>=3.1.4; python_version >= \"3.6\" in /usr/local/lib/python3.7/dist-packages (from google-auth<2,>=1.6.3->tensorboard~=2.4->tensorflow==2.4.0) (4.7.2)\n", - "Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /usr/local/lib/python3.7/dist-packages (from requests<3,>=2.21.0->tensorboard~=2.4->tensorflow==2.4.0) (1.24.3)\n", - "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.7/dist-packages (from requests<3,>=2.21.0->tensorboard~=2.4->tensorflow==2.4.0) (2021.5.30)\n", - "Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from requests<3,>=2.21.0->tensorboard~=2.4->tensorflow==2.4.0) (3.0.4)\n", - "Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.7/dist-packages (from requests<3,>=2.21.0->tensorboard~=2.4->tensorflow==2.4.0) (2.10)\n", - "Requirement already satisfied: importlib-metadata; python_version < \"3.8\" in /usr/local/lib/python3.7/dist-packages (from markdown>=2.6.8->tensorboard~=2.4->tensorflow==2.4.0) (4.6.1)\n", - "Requirement already satisfied: oauthlib>=3.0.0 in /usr/local/lib/python3.7/dist-packages (from requests-oauthlib>=0.7.0->google-auth-oauthlib<0.5,>=0.4.1->tensorboard~=2.4->tensorflow==2.4.0) (3.1.1)\n", - "Requirement already satisfied: pyasn1<0.5.0,>=0.4.6 in /usr/local/lib/python3.7/dist-packages (from pyasn1-modules>=0.2.1->google-auth<2,>=1.6.3->tensorboard~=2.4->tensorflow==2.4.0) (0.4.8)\n", - "Requirement already satisfied: zipp>=0.5 in /usr/local/lib/python3.7/dist-packages (from importlib-metadata; python_version < \"3.8\"->markdown>=2.6.8->tensorboard~=2.4->tensorflow==2.4.0) (3.5.0)\n", - "Installing collected packages: h5py, tensorflow-estimator, grpcio, gast, tensorflow\n", - " Found existing installation: h5py 3.1.0\n", - " Uninstalling h5py-3.1.0:\n", - " Successfully uninstalled h5py-3.1.0\n", - " Found existing installation: tensorflow-estimator 2.5.0\n", - " Uninstalling tensorflow-estimator-2.5.0:\n", - " Successfully uninstalled tensorflow-estimator-2.5.0\n", - " Found existing installation: grpcio 1.34.1\n", - " Uninstalling grpcio-1.34.1:\n", - " Successfully uninstalled grpcio-1.34.1\n", - " Found existing installation: gast 0.4.0\n", - " Uninstalling gast-0.4.0:\n", - " Successfully uninstalled gast-0.4.0\n", - " Found existing installation: tensorflow 2.5.0\n", - " Uninstalling tensorflow-2.5.0:\n", - " Successfully uninstalled tensorflow-2.5.0\n", - "Successfully installed gast-0.3.3 grpcio-1.32.0 h5py-2.10.0 tensorflow-2.4.0 tensorflow-estimator-2.4.0\n" - ] - } - ], + "outputs": [], "source": [ "# use tensorflow 2.4.0 for this notebook\n", - "!pip install tensorflow==2.4.0" + "# !pip install tensorflow==2.4.0" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": { "id": "KN6N85ah8VXf" }, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/usr/local/lib/python3.9/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" + ] + } + ], "source": [ "#Importing\n", "import ktrain\n", @@ -304,7 +93,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -312,16 +101,7 @@ "id": "Mr1YXudk8Vti", "outputId": "4634f5ee-9c9d-4a32-9118-1845b6c43b7f" }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Downloading data from http://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz\n", - "84131840/84125825 [==============================] - 6s 0us/step\n" - ] - } - ], + "outputs": [], "source": [ "##obtain the dataset\n", "import os\n", @@ -335,7 +115,7 @@ " )\n", " IMDB_DATADIR = os.path.join(os.path.dirname(dataset), \"aclImdb\")\n", "except ModuleNotFoundError :\n", - " if not os.path.exists(os.getcwd()+\"\\\\Data\\\\aclImdb\") :\n", + " if not os.path.exists(os.getcwd()+\"/Data/aclImdb\") :\n", " import tensorflow as tf\n", " dataset = tf.keras.utils.get_file(\n", " fname=\"aclImdb.tar.gz\", \n", @@ -348,7 +128,7 @@ " else :\n", "\n", " # set path to dataset\n", - " IMDB_DATADIR=os.getcwd()+\"\\\\Data\\\\aclImdb\"" + " IMDB_DATADIR=os.getcwd()+\"/Data/aclImdb\"" ] }, { @@ -363,7 +143,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -378,14 +158,6 @@ "output_type": "stream", "text": [ "detected encoding: utf-8\n", - "downloading pretrained BERT model (uncased_L-12_H-768_A-12.zip)...\n", - "[██████████████████████████████████████████████████]\n", - "extracting pretrained BERT model...\n", - "done.\n", - "\n", - "cleanup downloaded zip...\n", - "done.\n", - "\n", "preprocessing train...\n", "language: en\n" ] @@ -393,15 +165,40 @@ { "data": { "text/html": [ - "done." + "\n", + "\n" ], "text/plain": [ "" ] }, - "metadata": { - "tags": [] + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "done." + ], + "text/plain": [ + "" + ] }, + "metadata": {}, "output_type": "display_data" }, { @@ -416,15 +213,40 @@ { "data": { "text/html": [ - "done." + "\n", + "\n" ], "text/plain": [ "" ] }, - "metadata": { - "tags": [] + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "done." + ], + "text/plain": [ + "" + ] }, + "metadata": {}, "output_type": "display_data" } ], @@ -448,7 +270,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -462,7 +284,21 @@ "output_type": "stream", "text": [ "Is Multi-Label? False\n", - "maxlen is 500\n", + "maxlen is 500\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/usr/local/lib/python3.9/site-packages/keras/src/initializers/initializers.py:120: UserWarning: The initializer GlorotNormal is unseeded and being called multiple times, which will return identical values each time (even if the initializer is unseeded). Please update your code to provide a seed to the initializer, or avoid using the same initializer instance more than once.\n", + " warnings.warn(\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ "done.\n" ] } @@ -483,7 +319,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -499,32 +335,20 @@ "\n", "\n", "begin training using onecycle policy with max lr of 2e-05...\n", - "Epoch 1/4\n", - "4167/4167 [==============================] - 2358s 561ms/step - loss: 0.3327 - accuracy: 0.8504 - val_loss: 0.1843 - val_accuracy: 0.9311\n", - "Epoch 2/4\n", - "4167/4167 [==============================] - 2325s 558ms/step - loss: 0.1542 - accuracy: 0.9423 - val_loss: 0.2223 - val_accuracy: 0.9138\n", - "Epoch 3/4\n", - "4167/4167 [==============================] - 2323s 557ms/step - loss: 0.0899 - accuracy: 0.9677 - val_loss: 0.1847 - val_accuracy: 0.9350\n", - "Epoch 4/4\n", - "4167/4167 [==============================] - 2322s 557ms/step - loss: 0.0247 - accuracy: 0.9934 - val_loss: 0.2330 - val_accuracy: 0.9416\n" + "Epoch 1/4\n" ] - }, - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 8, - "metadata": { - "tags": [] - }, - "output_type": "execute_result" } ], "source": [ "learner.fit_onecycle(2e-5, 4)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -535,7 +359,7 @@ "provenance": [] }, "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -549,9 +373,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.4" + "version": "3.9.17" } }, "nbformat": 4, - "nbformat_minor": 1 + "nbformat_minor": 4 }