{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# SCC0633/SCC5908 - Processamento de Linguagem Natural\n", "> **Docente:** Thiago Alexandre Salgueiro Pardo \\\\\n", "> **Estagiário PAE:** Germano Antonio Zani Jorge\n", "\n", "\n", "# Integrantes do Grupo: GPTrouxas\n", "> André Guarnier De Mitri - 11395579 \\\\\n", "> Daniel Carvalho - 10685702 \\\\\n", "> Fernando - 11795342 \\\\\n", "> Lucas Henrique Sant'Anna - 10748521 \\\\\n", "> Magaly L Fujimoto - 4890582 \\\\\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Abordagem Neural usando BERT\n", "![alt text](../imagens/BERT_TDIDF.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "###" ] }, { "cell_type": "markdown", "metadata": { "id": "6yecpJR0feeQ" }, "source": [ "## Importando bibliotecas" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "id": "FAIvyZwodEtm" }, "outputs": [], "source": [ "import torch\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "import math\n", "from tqdm.notebook import tqdm\n", "import pandas as pd" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "#!pip install transformers seaborn nltk" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Carregando dados" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 206 }, "id": "LYgXl3RIfgfo", "outputId": "eb496faf-7826-44f7-fa88-3b21fb6e7cbf" }, "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", "
reviewsentiment
0One of the other reviewers has mentioned that ...positive
1A wonderful little production. <br /><br />The...positive
2I thought this was a wonderful way to spend ti...positive
3Basically there's a family where a little boy ...negative
4Petter Mattei's \"Love in the Time of Money\" is...positive
\n", "
" ], "text/plain": [ " review sentiment\n", "0 One of the other reviewers has mentioned that ... positive\n", "1 A wonderful little production.

The... positive\n", "2 I thought this was a wonderful way to spend ti... positive\n", "3 Basically there's a family where a little boy ... negative\n", "4 Petter Mattei's \"Love in the Time of Money\" is... positive" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df_reviews = pd.read_csv('../data/imdb_reviews.csv')\n", "df_reviews.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Mapeando as classes\n", "- Sentimento positivo recebe label 1\n", "- Sentimento negativo recebe label 0" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 206 }, "id": "D-5n8XzJbWOO", "outputId": "cef630cc-b0cc-4598-c53f-d32636bfcd86" }, "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", "
reviewsentiment
0One of the other reviewers has mentioned that ...1
1A wonderful little production. <br /><br />The...1
2I thought this was a wonderful way to spend ti...1
3Basically there's a family where a little boy ...0
4Petter Mattei's \"Love in the Time of Money\" is...1
\n", "
" ], "text/plain": [ " review sentiment\n", "0 One of the other reviewers has mentioned that ... 1\n", "1 A wonderful little production.

The... 1\n", "2 I thought this was a wonderful way to spend ti... 1\n", "3 Basically there's a family where a little boy ... 0\n", "4 Petter Mattei's \"Love in the Time of Money\" is... 1" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def map_sentiments(sentiment):\n", " if sentiment == 'positive':\n", " return 1\n", " return 0\n", "\n", "df_reviews['sentiment'] = df_reviews['sentiment'].apply(map_sentiments)\n", "df_reviews.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Funções para limpeza do texto\n", "**lowercase_text(text)** Converte o texto para letras minúsculas para uniformizar o texto.\n", "\n", "\n", "**remove_html(text)** Remove quaisquer tags HTML do texto para limpar dados provenientes de fontes HTML.\n", "\n", "\n", " **remove_url(text)** Remove URLs do texto para eliminar links que podem não ser relevantes para a análise de texto.\n", "\n", "\n", "**remove_punctuations(text)** Remove pontuações do texto para simplificar a estrutura do texto, mantendo apenas palavras.\n", "\n", "**remove_emojis(text)** Remove emojis do texto para evitar caracteres não verbais que podem interferir na análise textual.\n", "\n", "**remove_stop_words(text)** Remove stop words (palavras comuns como \"e\", \"de\", \"o\") que geralmente não adicionam valor significativo à análise de texto.\n", "\n", "**stem_words(text)** Aplica stemming nas palavras do texto, reduzindo-as à sua raiz (por exemplo, \"running\" vira \"run\") para normalizar as variações das palavras.\n", "\n", "**preprocess_text(text)** Aplica todas as funções acima em sequência para pré-processar o texto de forma completa, tornando-o mais adequado para análise de texto ou modelagem.\n", "\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 241 }, "id": "PnFHO62rnWn-", "outputId": "17fb6619-fab9-4395-de5d-4c5199e7e45e" }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "[nltk_data] Downloading package stopwords to\n", "[nltk_data] C:\\Users\\andre\\AppData\\Roaming\\nltk_data...\n", "[nltk_data] Package stopwords is already up-to-date!\n" ] }, { "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", "
reviewsentiment
0one review mention watch 1 oz episod hook righ...1
1wonder littl product film techniqu unassum old...1
2thought wonder way spend time hot summer weeke...1
3basic famili littl boy jake think zombi closet...0
4petter mattei love time money visual stun film...1
\n", "
" ], "text/plain": [ " review sentiment\n", "0 one review mention watch 1 oz episod hook righ... 1\n", "1 wonder littl product film techniqu unassum old... 1\n", "2 thought wonder way spend time hot summer weeke... 1\n", "3 basic famili littl boy jake think zombi closet... 0\n", "4 petter mattei love time money visual stun film... 1" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import re\n", "import nltk\n", "from nltk.corpus import stopwords\n", "from nltk.stem import PorterStemmer\n", "\n", "\n", "def lowercase_text(text):\n", " return text.lower()\n", "\n", "def remove_html(text):\n", " return re.sub(r'<[^<]+?>', '', text)\n", "\n", "def remove_url(text):\n", " return re.sub(r'http[s]?://\\S+|www\\.\\S+', '', text)\n", "\n", "def remove_punctuations(text):\n", " tokens_list = '!\"#$%&\\'()*+,-./:;<=>?@[\\\\]^_`{|}~'\n", " for char in text:\n", " if char in tokens_list:\n", " text = text.replace(char, ' ')\n", "\n", " return text\n", "\n", "def remove_emojis(text):\n", " emojis = re.compile(\"[\"\n", " u\"\\U0001F600-\\U0001F64F\"\n", " u\"\\U0001F300-\\U0001F5FF\"\n", " u\"\\U0001F680-\\U0001F6FF\"\n", " u\"\\U0001F1E0-\\U0001F1FF\"\n", " u\"\\U00002500-\\U00002BEF\"\n", " u\"\\U00002702-\\U000027B0\"\n", " u\"\\U00002702-\\U000027B0\"\n", " u\"\\U000024C2-\\U0001F251\"\n", " u\"\\U0001f926-\\U0001f937\"\n", " u\"\\U00010000-\\U0010ffff\"\n", " u\"\\u2640-\\u2642\"\n", " u\"\\u2600-\\u2B55\"\n", " u\"\\u200d\"\n", " u\"\\u23cf\"\n", " u\"\\u23e9\"\n", " u\"\\u231a\"\n", " u\"\\ufe0f\"\n", " u\"\\u3030\"\n", " \"]+\", re.UNICODE)\n", "\n", " text = re.sub(emojis, '', text)\n", " return text\n", "\n", "def remove_stop_words(text):\n", " stop_words = stopwords.words('english')\n", " new_text = ''\n", " for word in text.split():\n", " if word not in stop_words:\n", " new_text += ''.join(f'{word} ')\n", "\n", " return new_text.strip()\n", "\n", "def stem_words(text):\n", " stemmer = PorterStemmer()\n", " new_text = ''\n", " for word in text.split():\n", " new_text += ''.join(f'{stemmer.stem(word)} ')\n", "\n", " return new_text\n", "\n", "def preprocess_text(text):\n", " text = lowercase_text(text)\n", " text = remove_html(text)\n", " text = remove_url(text)\n", " text = remove_punctuations(text)\n", " text = remove_emojis(text)\n", " text = remove_stop_words(text)\n", " text = stem_words(text)\n", "\n", " return text\n", "\n", "nltk.download('stopwords')\n", "df_reviews['review'] = df_reviews['review'].apply(preprocess_text)\n", "df_reviews.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Visualizando balancemento da classes" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 452 }, "id": "Gdi_L0HWfntv", "outputId": "bce77594-f662-4b3f-c8eb-27d8a188b4f2" }, "outputs": [ { "data": { "image/png": "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hx08eNCZL6l4v+cRERF6//33dfr0aY93cwo+coyIiLjmYwHljY+rgHJS8C/c7/+LNjs7W4sWLbrmY8TGxuqbb77xuEX6woULeuWVVzzq2rdvr8jISD333HMea2cKHDt2zPl19erVJRUOFVcycOBA5eTk6LXXXlNycrIGDBjgsb2oa83NzdVLL7101WNHRETIx8dHaWlpHuM/3Dc4OFi33367/vKXv+jIkSOFjvP9ayxK7969denSJY9b+PPy8vTiiy9etceTJ08WGit4xywnJ0dSyeb1Sv72t7/p9OnTzvO33npLR44cUa9evZyxyMhIbd26Vbm5uc7YypUrC91qXpzeevfurby8PM2dO9djfNasWfLy8vI4P/BTwzs5QDnp0aOHfH191adPHz344IM6c+aMXnnlFQUHBxf5Il2UBx98UHPnztXgwYP12GOPqV69elq8eLH8/PwkffcvdG9vby1cuFC9evVSy5YtNXz4cNWvX1/ffPON1q9fr4CAAK1YsULS/wKRJE2aNEmDBg1S1apV1adPH+eFsCg333yzmjRpokmTJiknJ8fjoyrpf7cj16pVS/Hx8frNb34jLy8v/f3vf7+mjywCAwN133336cUXX5SXl5ciIyO1cuXKItfXzJs3T507d1br1q01cuRINW7cWFlZWUpPT9fXX3+t//73v5c9T58+fdSpUydNmDBBBw4cUIsWLfT2229f03qh6dOnKy0tTXFxcYqIiNDRo0f10ksvqUGDBs7i68jISAUFBWnBggWqWbOmqlevrqioqCLXWl2L2rVrq3Pnzho+fLiysrI0e/ZsNWnSxGPR+QMPPKC33npLPXv21IABA7R371794x//8FgIXNze+vTpozvuuEOTJk3SgQMH1LZtW61du1bvvvuuxowZU+jYwE9Kxd3YBditqFvI33vvPdOmTRvj5+dnGjZsaP70pz+ZV199tdAt3BERESYuLq7I4+7bt8/ExcUZf39/U7duXfP444+bf/7zn0aS2bp1q0ft9u3bzT333GOuu+4643K5TEREhBkwYIBJTU31qHv66adN/fr1jbe39zXfTj5p0iQjyTRp0qTI7Zs3bzYdO3Y0/v7+JiwszDzxxBNmzZo1hW4P/+Ety8YYc+zYMdO/f39TrVo1U6tWLfPggw+aHTt2FHnb8969e82vfvUrExoaaqpWrWrq169v7rrrLvPWW29d9RpOnDhhhg4dagICAkxgYKAZOnSo2b59+1VvIU9NTTV33323CQsLM76+viYsLMwMHjzYfPHFFx7Hf/fdd02LFi1MlSpVPI7ZrVs307JlyyJ7utwt5K+//rqZOHGiCQ4ONv7+/iYuLs4cPHiw0P7PP/+8qV+/vnG5XKZTp07mo48+KnTMK/VW1O/H6dOnzdixY01YWJipWrWqueGGG8yzzz7r8fUExvzvFvKEhIRCPV3u1nagrHkZw2owoLKbPXu2xo4dq6+//lr169ev6HYA4CeBkANUMufPn/e4M+fChQu66aablJeXpy+++KICOwOAnxbW5ACVzD333KPrr79e7dq1U3Z2tv7xj39o165dWrx4cUW3BgA/KYQcoJKJjY3VwoULtXjxYuXl5alFixZ64403Ci3+BYCfOz6uAgAAVuJ7cgAAgJUIOQAAwEo/6zU5+fn5Onz4sGrWrFnqX8EOAADKhjFGp0+fVlhYmLy9L/9+zc865Bw+fFjh4eEV3QYAACiBr776Sg0aNLjs9p91yCn4YXNfffWVAgICKrgbAABwLdxut8LDwz1+aGxRftYhp+AjqoCAAEIOAACVzNWWmrDwGAAAWImQAwAArETIAQAAViLkAAAAKxFyAACAlQg5AADASoQcAABgJUIOAACwEiEHAABYiZADAACsVKyQM2PGDN1yyy2qWbOmgoOD1a9fP+3evduj5vbbb5eXl5fH46GHHvKoOXTokOLi4lStWjUFBwdr3LhxunTpkkfNhg0bdPPNN8vlcqlJkyZKSkoq1M+8efPUsGFD+fn5KSoqSh9++GFxLgcAAFisWCFn48aNSkhI0NatW5WSkqKLFy+qR48eOnv2rEfdyJEjdeTIEecxc+ZMZ1teXp7i4uKUm5urLVu26LXXXlNSUpKmTJni1Ozfv19xcXG64447lJGRoTFjxuiBBx7QmjVrnJqlS5cqMTFRU6dO1ccff6y2bdsqNjZWR48eLelcAAAAi3gZY0xJdz527JiCg4O1ceNGde3aVdL/3slp166dZs+eXeQ+//rXv3TXXXfp8OHDCgkJkSQtWLBA48eP17Fjx+Tr66vx48dr1apV2rFjh7PfoEGDdOrUKSUnJ0uSoqKidMstt2ju3LmSpPz8fIWHh+vRRx/VhAkTrql/t9utwMBAZWdn8wM6AQCoJK719ftHrcnJzs6WJNWuXdtjfPHixapTp45atWqliRMn6ty5c8629PR0tW7d2gk4khQbGyu3262dO3c6NTExMR7HjI2NVXp6uiQpNzdX27Zt86jx9vZWTEyMU1OUnJwcud1ujwcAALBTlZLumJ+frzFjxqhTp05q1aqVMz5kyBBFREQoLCxMn3zyicaPH6/du3fr7bffliRlZmZ6BBxJzvPMzMwr1rjdbp0/f17ffvut8vLyiqzZtWvXZXueMWOGnnrqqZJecrE0nLCqXM5Tmg48E1fRLQAALoPXleIrcchJSEjQjh07tGnTJo/xUaNGOb9u3bq16tWrp+7du2vv3r2KjIwseaelYOLEiUpMTHSeu91uhYeHV2BHAACgrJQo5IwePVorV65UWlqaGjRocMXaqKgoSdKePXsUGRmp0NDQQndBZWVlSZJCQ0Od/xaMfb8mICBA/v7+8vHxkY+PT5E1BccoisvlksvluraLBAAAlVqx1uQYYzR69Gi98847WrdunRo1anTVfTIyMiRJ9erVkyRFR0fr008/9bgLKiUlRQEBAWrRooVTk5qa6nGclJQURUdHS5J8fX3Vvn17j5r8/HylpqY6NQAA4OetWO/kJCQkaMmSJXr33XdVs2ZNZw1NYGCg/P39tXfvXi1ZskS9e/fWddddp08++URjx45V165d1aZNG0lSjx491KJFCw0dOlQzZ85UZmamJk+erISEBOddloceekhz587VE088oV//+tdat26dli1bplWrvvs8MjExUfHx8erQoYNuvfVWzZ49W2fPntXw4cNLa24AAEAlVqyQM3/+fEn/u038+xYtWqRhw4bJ19dX77//vhM4wsPD1b9/f02ePNmp9fHx0cqVK/Xwww8rOjpa1atXV3x8vKZPn+7UNGrUSKtWrdLYsWM1Z84cNWjQQAsXLlRsbKxTM3DgQB07dkxTpkxRZmam2rVrp+Tk5EKLkQEAwM/Tj/qenMquLL8nh1XwAIDSxOvKd8rle3IAAAB+qgg5AADASoQcAABgJUIOAACwEiEHAABYiZADAACsRMgBAABWIuQAAAArEXIAAICVCDkAAMBKhBwAAGAlQg4AALASIQcAAFiJkAMAAKxEyAEAAFYi5AAAACsRcgAAgJUIOQAAwEqEHAAAYCVCDgAAsBIhBwAAWImQAwAArETIAQAAViLkAAAAKxFyAACAlQg5AADASoQcAABgJUIOAACwEiEHAABYiZADAACsRMgBAABWIuQAAAArEXIAAICVCDkAAMBKhBwAAGAlQg4AALASIQcAAFiJkAMAAKxEyAEAAFYi5AAAACsRcgAAgJUIOQAAwEqEHAAAYCVCDgAAsBIhBwAAWImQAwAArETIAQAAViLkAAAAKxFyAACAlQg5AADASoQcAABgJUIOAACwEiEHAABYiZADAACsRMgBAABWIuQAAAArEXIAAICVCDkAAMBKhBwAAGAlQg4AALBSsULOjBkzdMstt6hmzZoKDg5Wv379tHv3bo+aCxcuKCEhQdddd51q1Kih/v37Kysry6Pm0KFDiouLU7Vq1RQcHKxx48bp0qVLHjUbNmzQzTffLJfLpSZNmigpKalQP/PmzVPDhg3l5+enqKgoffjhh8W5HAAAYLFihZyNGzcqISFBW7duVUpKii5evKgePXro7NmzTs3YsWO1YsUKvfnmm9q4caMOHz6se+65x9mel5enuLg45ebmasuWLXrttdeUlJSkKVOmODX79+9XXFyc7rjjDmVkZGjMmDF64IEHtGbNGqdm6dKlSkxM1NSpU/Xxxx+rbdu2io2N1dGjR3/MfAAAAEt4GWNMSXc+duyYgoODtXHjRnXt2lXZ2dmqW7eulixZonvvvVeStGvXLjVv3lzp6enq2LGj/vWvf+muu+7S4cOHFRISIklasGCBxo8fr2PHjsnX11fjx4/XqlWrtGPHDudcgwYN0qlTp5ScnCxJioqK0i233KK5c+dKkvLz8xUeHq5HH31UEyZMuKb+3W63AgMDlZ2drYCAgJJOQ5EaTlhVqscrDweeiavoFgAAl8Hryneu9fX7R63Jyc7OliTVrl1bkrRt2zZdvHhRMTExTk2zZs10/fXXKz09XZKUnp6u1q1bOwFHkmJjY+V2u7Vz506n5vvHKKgpOEZubq62bdvmUePt7a2YmBinpig5OTlyu90eDwAAYKcSh5z8/HyNGTNGnTp1UqtWrSRJmZmZ8vX1VVBQkEdtSEiIMjMznZrvB5yC7QXbrlTjdrt1/vx5HT9+XHl5eUXWFByjKDNmzFBgYKDzCA8PL/6FAwCASqHEISchIUE7duzQG2+8UZr9lKmJEycqOzvbeXz11VcV3RIAACgjVUqy0+jRo7Vy5UqlpaWpQYMGznhoaKhyc3N16tQpj3dzsrKyFBoa6tT88C6ogruvvl/zwzuysrKyFBAQIH9/f/n4+MjHx6fImoJjFMXlcsnlchX/ggEAQKVTrHdyjDEaPXq03nnnHa1bt06NGjXy2N6+fXtVrVpVqampztju3bt16NAhRUdHS5Kio6P16aefetwFlZKSooCAALVo0cKp+f4xCmoKjuHr66v27dt71OTn5ys1NdWpAQAAP2/FeicnISFBS5Ys0bvvvquaNWs6618CAwPl7++vwMBAjRgxQomJiapdu7YCAgL06KOPKjo6Wh07dpQk9ejRQy1atNDQoUM1c+ZMZWZmavLkyUpISHDeZXnooYc0d+5cPfHEE/r1r3+tdevWadmyZVq16ruV5YmJiYqPj1eHDh106623avbs2Tp79qyGDx9eWnMDAAAqsWKFnPnz50uSbr/9do/xRYsWadiwYZKkWbNmydvbW/3791dOTo5iY2P10ksvObU+Pj5auXKlHn74YUVHR6t69eqKj4/X9OnTnZpGjRpp1apVGjt2rObMmaMGDRpo4cKFio2NdWoGDhyoY8eOacqUKcrMzFS7du2UnJxcaDEyAAD4efpR35NT2fE9OZ74nhwA+OnideU75fI9OQAAAD9VhBwAAGAlQg4AALASIQcAAFiJkAMAAKxEyAEAAFYi5AAAACsRcgAAgJUIOQAAwEqEHAAAYCVCDgAAsBIhBwAAWImQAwAArETIAQAAViLkAAAAKxFyAACAlQg5AADASoQcAABgJUIOAACwEiEHAABYiZADAACsRMgBAABWIuQAAAArEXIAAICVCDkAAMBKhBwAAGAlQg4AALASIQcAAFiJkAMAAKxEyAEAAFYi5AAAACsRcgAAgJUIOQAAwEqEHAAAYCVCDgAAsBIhBwAAWImQAwAArETIAQAAViLkAAAAKxFyAACAlQg5AADASoQcAABgJUIOAACwEiEHAABYiZADAACsRMgBAABWIuQAAAArEXIAAICVCDkAAMBKhBwAAGAlQg4AALASIQcAAFiJkAMAAKxEyAEAAFYi5AAAACsRcgAAgJUIOQAAwEqEHAAAYCVCDgAAsFKxQ05aWpr69OmjsLAweXl5afny5R7bhw0bJi8vL49Hz549PWpOnjyp+++/XwEBAQoKCtKIESN05swZj5pPPvlEXbp0kZ+fn8LDwzVz5sxCvbz55ptq1qyZ/Pz81Lp1a61evbq4lwMAACxV7JBz9uxZtW3bVvPmzbtsTc+ePXXkyBHn8frrr3tsv//++7Vz506lpKRo5cqVSktL06hRo5ztbrdbPXr0UEREhLZt26Znn31W06ZN08svv+zUbNmyRYMHD9aIESO0fft29evXT/369dOOHTuKe0kAAMBCVYq7Q69evdSrV68r1rhcLoWGhha57fPPP1dycrL+85//qEOHDpKkF198Ub1799Zzzz2nsLAwLV68WLm5uXr11Vfl6+urli1bKiMjQy+88IIThubMmaOePXtq3LhxkqSnn35aKSkpmjt3rhYsWFDcywIAAJYpkzU5GzZsUHBwsJo2baqHH35YJ06ccLalp6crKCjICTiSFBMTI29vb33wwQdOTdeuXeXr6+vUxMbGavfu3fr222+dmpiYGI/zxsbGKj09/bJ95eTkyO12ezwAAICdSj3k9OzZU3/729+UmpqqP/3pT9q4caN69eqlvLw8SVJmZqaCg4M99qlSpYpq166tzMxMpyYkJMSjpuD51WoKthdlxowZCgwMdB7h4eE/7mIBAMBPVrE/rrqaQYMGOb9u3bq12rRpo8jISG3YsEHdu3cv7dMVy8SJE5WYmOg8d7vdBB0AACxV5reQN27cWHXq1NGePXskSaGhoTp69KhHzaVLl3Ty5ElnHU9oaKiysrI8agqeX63mcmuBpP+tFQoICPB4AAAAO5V5yPn666914sQJ1atXT5IUHR2tU6dOadu2bU7NunXrlJ+fr6ioKKcmLS1NFy9edGpSUlLUtGlT1apVy6lJTU31OFdKSoqio6PL+pIAAEAlUOyQc+bMGWVkZCgjI0OStH//fmVkZOjQoUM6c+aMxo0bp61bt+rAgQNKTU3V3XffrSZNmig2NlaS1Lx5c/Xs2VMjR47Uhx9+qM2bN2v06NEaNGiQwsLCJElDhgyRr6+vRowYoZ07d2rp0qWaM2eOx0dNjz32mJKTk/X8889r165dmjZtmj766CONHj26FKYFAABUdsUOOR999JFuuukm3XTTTZKkxMRE3XTTTZoyZYp8fHz0ySefqG/fvrrxxhs1YsQItW/fXv/+97/lcrmcYyxevFjNmjVT9+7d1bt3b3Xu3NnjO3ACAwO1du1a7d+/X+3bt9fjjz+uKVOmeHyXzm233aYlS5bo5ZdfVtu2bfXWW29p+fLlatWq1Y+ZDwAAYAkvY4yp6CYqitvtVmBgoLKzs0t9fU7DCatK9Xjl4cAzcRXdAgDgMnhd+c61vn7zs6sAAICVCDkAAMBKhBwAAGAlQg4AALASIQcAAFiJkAMAAKxEyAEAAFYi5AAAACsRcgAAgJUIOQAAwEqEHAAAYCVCDgAAsBIhBwAAWImQAwAArETIAQAAViLkAAAAKxFyAACAlQg5AADASoQcAABgJUIOAACwEiEHAABYiZADAACsRMgBAABWIuQAAAArEXIAAICVCDkAAMBKhBwAAGAlQg4AALASIQcAAFiJkAMAAKxEyAEAAFYi5AAAACsRcgAAgJUIOQAAwEqEHAAAYCVCDgAAsBIhBwAAWImQAwAArETIAQAAViLkAAAAKxFyAACAlQg5AADASoQcAABgJUIOAACwEiEHAABYiZADAACsRMgBAABWIuQAAAArEXIAAICVCDkAAMBKhBwAAGAlQg4AALASIQcAAFiJkAMAAKxEyAEAAFYi5AAAACsRcgAAgJUIOQAAwEqEHAAAYCVCDgAAsFKxQ05aWpr69OmjsLAweXl5afny5R7bjTGaMmWK6tWrJ39/f8XExOjLL7/0qDl58qTuv/9+BQQEKCgoSCNGjNCZM2c8aj755BN16dJFfn5+Cg8P18yZMwv18uabb6pZs2by8/NT69attXr16uJeDgAAsFSxQ87Zs2fVtm1bzZs3r8jtM2fO1J///GctWLBAH3zwgapXr67Y2FhduHDBqbn//vu1c+dOpaSkaOXKlUpLS9OoUaOc7W63Wz169FBERIS2bdumZ599VtOmTdPLL7/s1GzZskWDBw/WiBEjtH37dvXr10/9+vXTjh07intJAADAQl7GGFPinb289M4776hfv36S/vcuTlhYmB5//HH99re/lSRlZ2crJCRESUlJGjRokD7//HO1aNFC//nPf9ShQwdJUnJysnr37q2vv/5aYWFhmj9/viZNmqTMzEz5+vpKkiZMmKDly5dr165dkqSBAwfq7NmzWrlypdNPx44d1a5dOy1YsKDIfnNycpSTk+M8d7vdCg8PV3Z2tgICAko6DUVqOGFVqR6vPBx4Jq6iWwAAXAavK99xu90KDAy86ut3qa7J2b9/vzIzMxUTE+OMBQYGKioqSunp6ZKk9PR0BQUFOQFHkmJiYuTt7a0PPvjAqenatasTcCQpNjZWu3fv1rfffuvUfP88BTUF5ynKjBkzFBgY6DzCw8N//EUDAICfpFINOZmZmZKkkJAQj/GQkBBnW2ZmpoKDgz22V6lSRbVr1/aoKeoY3z/H5WoKthdl4sSJys7Odh5fffVVcS8RAABUElUquoHy5HK55HK5KroNAABQDkr1nZzQ0FBJUlZWlsd4VlaWsy00NFRHjx712H7p0iWdPHnSo6aoY3z/HJerKdgOAAB+3ko15DRq1EihoaFKTU11xtxutz744ANFR0dLkqKjo3Xq1Clt27bNqVm3bp3y8/MVFRXl1KSlpenixYtOTUpKipo2bapatWo5Nd8/T0FNwXkAAMDPW7FDzpkzZ5SRkaGMjAxJ/1tsnJGRoUOHDsnLy0tjxozR73//e7333nv69NNP9atf/UphYWHOHVjNmzdXz549NXLkSH344YfavHmzRo8erUGDBiksLEySNGTIEPn6+mrEiBHauXOnli5dqjlz5igxMdHp47HHHlNycrKef/557dq1S9OmTdNHH32k0aNH//hZAQAAlV6x1+R89NFHuuOOO5znBcEjPj5eSUlJeuKJJ3T27FmNGjVKp06dUufOnZWcnCw/Pz9nn8WLF2v06NHq3r27vL291b9/f/35z392tgcGBmrt2rVKSEhQ+/btVadOHU2ZMsXju3Ruu+02LVmyRJMnT9aTTz6pG264QcuXL1erVq1KNBEAAMAuP+p7ciq7a73PviT4PgMAQGnideU7FfI9OQAAAD8VhBwAAGAlQg4AALASIQcAAFiJkAMAAKxEyAEAAFYi5AAAACsRcgAAgJUIOQAAwEqEHAAAYCVCDgAAsBIhBwAAWImQAwAArETIAQAAViLkAAAAKxFyAACAlQg5AADASoQcAABgJUIOAACwEiEHAABYiZADAACsRMgBAABWIuQAAAArEXIAAICVCDkAAMBKhBwAAGAlQg4AALASIQcAAFiJkAMAAKxEyAEAAFYi5AAAACsRcgAAgJUIOQAAwEqEHAAAYCVCDgAAsBIhBwAAWImQAwAArETIAQAAViLkAAAAKxFyAACAlQg5AADASoQcAABgJUIOAACwEiEHAABYiZADAACsRMgBAABWIuQAAAArEXIAAICVCDkAAMBKhBwAAGAlQg4AALASIQcAAFiJkAMAAKxEyAEAAFYi5AAAACsRcgAAgJUIOQAAwEqEHAAAYCVCDgAAsFKph5xp06bJy8vL49GsWTNn+4ULF5SQkKDrrrtONWrUUP/+/ZWVleVxjEOHDikuLk7VqlVTcHCwxo0bp0uXLnnUbNiwQTfffLNcLpeaNGmipKSk0r4UAABQiZXJOzktW7bUkSNHnMemTZucbWPHjtWKFSv05ptvauPGjTp8+LDuueceZ3teXp7i4uKUm5urLVu26LXXXlNSUpKmTJni1Ozfv19xcXG64447lJGRoTFjxuiBBx7QmjVryuJyAABAJVSlTA5apYpCQ0MLjWdnZ+uvf/2rlixZojvvvFOStGjRIjVv3lxbt25Vx44dtXbtWn322Wd6//33FRISonbt2unpp5/W+PHjNW3aNPn6+mrBggVq1KiRnn/+eUlS8+bNtWnTJs2aNUuxsbFlcUkAAKCSKZN3cr788kuFhYWpcePGuv/++3Xo0CFJ0rZt23Tx4kXFxMQ4tc2aNdP111+v9PR0SVJ6erpat26tkJAQpyY2NlZut1s7d+50ar5/jIKagmNcTk5Ojtxut8cDAADYqdRDTlRUlJKSkpScnKz58+dr//796tKli06fPq3MzEz5+voqKCjIY5+QkBBlZmZKkjIzMz0CTsH2gm1XqnG73Tp//vxle5sxY4YCAwOdR3h4+I+9XAAA8BNV6h9X9erVy/l1mzZtFBUVpYiICC1btkz+/v6lfbpimThxohITE53nbreboAMAgKXK/BbyoKAg3XjjjdqzZ49CQ0OVm5urU6dOedRkZWU5a3hCQ0ML3W1V8PxqNQEBAVcMUi6XSwEBAR4PAABgpzIPOWfOnNHevXtVr149tW/fXlWrVlVqaqqzfffu3Tp06JCio6MlSdHR0fr000919OhRpyYlJUUBAQFq0aKFU/P9YxTUFBwDAACg1EPOb3/7W23cuFEHDhzQli1b9Itf/EI+Pj4aPHiwAgMDNWLECCUmJmr9+vXatm2bhg8frujoaHXs2FGS1KNHD7Vo0UJDhw7Vf//7X61Zs0aTJ09WQkKCXC6XJOmhhx7Svn379MQTT2jXrl166aWXtGzZMo0dO7a0LwcAAFRSpb4m5+uvv9bgwYN14sQJ1a1bV507d9bWrVtVt25dSdKsWbPk7e2t/v37KycnR7GxsXrppZec/X18fLRy5Uo9/PDDio6OVvXq1RUfH6/p06c7NY0aNdKqVas0duxYzZkzRw0aNNDChQu5fRwAADi8jDGmopuoKG63W4GBgcrOzi719TkNJ6wq1eOVhwPPxFV0CwCAy+B15TvX+vrNz64CAABWIuQAAAArEXIAAICVCDkAAMBKhBwAAGAlQg4AALASIQcAAFiJkAMAAKxEyAEAAFYi5AAAACsRcgAAgJUIOQAAwEqEHAAAYCVCDgAAsBIhBwAAWImQAwAArETIAQAAViLkAAAAKxFyAACAlQg5AADASoQcAABgJUIOAACwEiEHAABYiZADAACsRMgBAABWIuQAAAArEXIAAICVCDkAAMBKhBwAAGAlQg4AALASIQcAAFiJkAMAAKxEyAEAAFYi5AAAACsRcgAAgJUIOQAAwEqEHAAAYCVCDgAAsBIhBwAAWImQAwAArETIAQAAViLkAAAAKxFyAACAlQg5AADASoQcAABgJUIOAACwEiEHAABYiZADAACsRMgBAABWIuQAAAArEXIAAICVCDkAAMBKhBwAAGAlQg4AALASIQcAAFiJkAMAAKxEyAEAAFYi5AAAACsRcgAAgJUqfciZN2+eGjZsKD8/P0VFRenDDz+s6JYAAMBPQKUOOUuXLlViYqKmTp2qjz/+WG3btlVsbKyOHj1a0a0BAIAKVqlDzgsvvKCRI0dq+PDhatGihRYsWKBq1arp1VdfrejWAABABatS0Q2UVG5urrZt26aJEyc6Y97e3oqJiVF6enqR++Tk5CgnJ8d5np2dLUlyu92l3l9+zrlSP2ZZK4t5AACUDl5XCh/XGHPFukobco4fP668vDyFhIR4jIeEhGjXrl1F7jNjxgw99dRThcbDw8PLpMfKJnB2RXcAALBJWb+unD59WoGBgZfdXmlDTklMnDhRiYmJzvP8/HydPHlS1113nby8vErtPG63W+Hh4frqq68UEBBQaseFJ+a5/DDX5YN5Lh/Mc/koy3k2xuj06dMKCwu7Yl2lDTl16tSRj4+PsrKyPMazsrIUGhpa5D4ul0sul8tjLCgoqKxaVEBAAH+BygHzXH6Y6/LBPJcP5rl8lNU8X+kdnAKVduGxr6+v2rdvr9TUVGcsPz9fqampio6OrsDOAADAT0GlfSdHkhITExUfH68OHTro1ltv1ezZs3X27FkNHz68olsDAAAVrFKHnIEDB+rYsWOaMmWKMjMz1a5dOyUnJxdajFzeXC6Xpk6dWuijMZQu5rn8MNflg3kuH8xz+fgpzLOXudr9VwAAAJVQpV2TAwAAcCWEHAAAYCVCDgAAsBIhBwAAWImQAwAArETIKaF58+apYcOG8vPzU1RUlD788MMr1r/55ptq1qyZ/Pz81Lp1a61evbqcOq3cijPPr7zyirp06aJatWqpVq1aiomJuervC/6nuH+eC7zxxhvy8vJSv379yrZBixR3rk+dOqWEhATVq1dPLpdLN954I///uAbFnefZs2eradOm8vf3V3h4uMaOHasLFy6UU7eVU1pamvr06aOwsDB5eXlp+fLlV91nw4YNuvnmm+VyudSkSRMlJSWVbZMGxfbGG28YX19f8+qrr5qdO3eakSNHmqCgIJOVlVVk/ebNm42Pj4+ZOXOm+eyzz8zkyZNN1apVzaefflrOnVcuxZ3nIUOGmHnz5pnt27ebzz//3AwbNswEBgaar7/+upw7r1yKO88F9u/fb+rXr2+6dOli7r777vJptpIr7lzn5OSYDh06mN69e5tNmzaZ/fv3mw0bNpiMjIxy7rxyKe48L1682LhcLrN48WKzf/9+s2bNGlOvXj0zduzYcu68clm9erWZNGmSefvtt40k884771yxft++faZatWomMTHRfPbZZ+bFF180Pj4+Jjk5ucx6JOSUwK233moSEhKc53l5eSYsLMzMmDGjyPoBAwaYuLg4j7GoqCjz4IMPlmmflV1x5/mHLl26ZGrWrGlee+21smrRCiWZ50uXLpnbbrvNLFy40MTHxxNyrlFx53r+/PmmcePGJjc3t7xatEJx5zkhIcHceeedHmOJiYmmU6dOZdqnTa4l5DzxxBOmZcuWHmMDBw40sbGxZdYXH1cVU25urrZt26aYmBhnzNvbWzExMUpPTy9yn/T0dI96SYqNjb1sPUo2zz907tw5Xbx4UbVr1y6rNiu9ks7z9OnTFRwcrBEjRpRHm1YoyVy/9957io6OVkJCgkJCQtSqVSv98Y9/VF5eXnm1XemUZJ5vu+02bdu2zflIa9++fVq9erV69+5dLj3/XFTEa2Gl/rEOFeH48ePKy8sr9KMjQkJCtGvXriL3yczMLLI+MzOzzPqs7Eoyzz80fvx4hYWFFfpLhe+UZJ43bdqkv/71r8rIyCiHDu1Rkrnet2+f1q1bp/vvv1+rV6/Wnj179Mgjj+jixYuaOnVqebRd6ZRknocMGaLjx4+rc+fOMsbo0qVLeuihh/Tkk0+WR8s/G5d7LXS73Tp//rz8/f1L/Zy8kwMrPfPMM3rjjTf0zjvvyM/Pr6Lbscbp06c1dOhQvfLKK6pTp05Ft2O9/Px8BQcH6+WXX1b79u01cOBATZo0SQsWLKjo1qyyYcMG/fGPf9RLL72kjz/+WG+//bZWrVqlp59+uqJbw4/EOznFVKdOHfn4+CgrK8tjPCsrS6GhoUXuExoaWqx6lGyeCzz33HN65pln9P7776tNmzZl2WalV9x53rt3rw4cOKA+ffo4Y/n5+ZKkKlWqaPfu3YqMjCzbpiupkvyZrlevnqpWrSofHx9nrHnz5srMzFRubq58fX3LtOfKqCTz/Lvf/U5Dhw7VAw88IElq3bq1zp49q1GjRmnSpEny9ub9gNJwudfCgICAMnkXR+KdnGLz9fVV+/btlZqa6ozl5+crNTVV0dHRRe4THR3tUS9JKSkpl61HyeZZkmbOnKmnn35aycnJ6tChQ3m0WqkVd56bNWumTz/9VBkZGc6jb9++uuOOO5SRkaHw8PDybL9SKcmf6U6dOmnPnj1OkJSkL774QvXq1SPgXEZJ5vncuXOFgkxBsDT8DOtSUyGvhWW2pNlib7zxhnG5XCYpKcl89tlnZtSoUSYoKMhkZmYaY4wZOnSomTBhglO/efNmU6VKFfPcc8+Zzz//3EydOpVbyK9Bcef5mWeeMb6+vuatt94yR44ccR6nT5+uqEuoFIo7zz/E3VXXrrhzfejQIVOzZk0zevRos3v3brNy5UoTHBxsfv/731fUJVQKxZ3nqVOnmpo1a5rXX3/d7Nu3z6xdu9ZERkaaAQMGVNQlVAqnT58227dvN9u3bzeSzAsvvGC2b99uDh48aIwxZsKECWbo0KFOfcEt5OPGjTOff/65mTdvHreQ/1S9+OKL5vrrrze+vr7m1ltvNVu3bnW2devWzcTHx3vUL1u2zNx4443G19fXtGzZ0qxataqcO66cijPPERERRlKhx9SpU8u/8UqmuH+ev4+QUzzFnestW7aYqKgo43K5TOPGjc0f/vAHc+nSpXLuuvIpzjxfvHjRTJs2zURGRho/Pz8THh5uHnnkEfPtt9+Wf+OVyPr164v8f27B3MbHx5tu3boV2qddu3bG19fXNG7c2CxatKhMe/QyhvfiAACAfViTAwAArETIAQAAViLkAAAAKxFyAACAlQg5AADASoQcAABgJUIOAACwEiEHAABYiZADAACsRMgBAABWIuQAAAAr/T9WUkYIDg5fygAAAABJRU5ErkJggg==", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "plt.title('Target value distribution')\n", "plt.hist(df_reviews['sentiment'])\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Modelo BERT" ] }, { "cell_type": "markdown", "metadata": { "id": "EDkjlPDakskM" }, "source": [ "## Instalando Bibliotecas" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "lk7m_1xvmWvz", "outputId": "ce842053-b261-4768-d9d7-fe9c65c9f6aa" }, "outputs": [], "source": [ "#pip install transformers\n", "#pip install accelerate -U\n", "#pip install transformers[torch]\n", "#pip install datasets evaluate" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Carregando o modelo treinado e tokenizador" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "GlyrkK52zMcc", "outputId": "a938653b-92c3-4b4e-802c-eacc3f1b6ecf" }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Some weights of BertForSequenceClassification were not initialized from the model checkpoint at bert-base-uncased and are newly initialized: ['classifier.bias', 'classifier.weight']\n", "You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n" ] } ], "source": [ "from transformers import AutoTokenizer\n", "from transformers import BertForSequenceClassification\n", "\n", "pre_trained_base = \"bert-base-uncased\"\n", "tokenizer = AutoTokenizer.from_pretrained(pre_trained_base)\n", "model = BertForSequenceClassification.from_pretrained(pre_trained_base, num_labels = 2, output_attentions=False, output_hidden_states=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Tokenização das Sentenças e Cálculo do Tamanho dos Tokens" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "id": "LKEjDZCHpk4e" }, "outputs": [], "source": [ "token_lens = []\n", "\n", "for sentence in df_reviews['review']:\n", " tokens = tokenizer.encode(sentence, max_length=200, truncation=True)\n", " token_lens.append(len(tokens))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Divisão dos Dados em Conjunto de Treinamento e Validação:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "id": "H7PfXaVVp2uQ" }, "outputs": [], "source": [ "SEED=42\n", "MAX_LEN = 200\n", "from sklearn.model_selection import train_test_split\n", "df_train, df_val = train_test_split(df_reviews, test_size=0.2, random_state=SEED)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Processando os dados\n", "A função process_data recebe uma linha de um dataframe contendo uma revisão de texto e sua respectiva classificação de sentimento. Ela começa extraindo e limpando o texto da revisão, removendo quaisquer espaços extras. Em seguida, utiliza o tokenizer BERT para tokenizar o texto, aplicando padding e truncamento para garantir que todas as sequências tenham um comprimento fixo definido pela variável MAX_LEN. A função então adiciona a etiqueta de sentimento original e o texto limpo às codificações geradas, retornando um dicionário que contém os tokens do texto, a etiqueta de sentimento e o texto original." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "id": "v7EZ6wd-qDfd" }, "outputs": [], "source": [ "def process_data(row):\n", "\n", " text = row['review']\n", " text = str(text)\n", " text = ' '.join(text.split())\n", "\n", " encodings = tokenizer(text, padding=\"max_length\", truncation=True, max_length=MAX_LEN)\n", "\n", " encodings['label'] = row['sentiment']\n", " encodings['text'] = text\n", "\n", " return encodings" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "id": "d9VgrXNSqIYL" }, "outputs": [], "source": [ "# Treino\n", "processed_data_tr = []\n", "for i in range(df_train.shape[0]):\n", " processed_data_tr.append(process_data(df_train.iloc[i]))" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "id": "p0NLQxoKqJ_k" }, "outputs": [], "source": [ "# Validação\n", "processed_data_val = []\n", "for i in range(df_val.shape[0]):\n", " processed_data_val.append(process_data(df_val.iloc[i]))" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "id": "ac76Rb6fqP_G" }, "outputs": [], "source": [ "# Dataframes de Treino e Validação\n", "df_train = pd.DataFrame(processed_data_tr)\n", "df_val = pd.DataFrame(processed_data_val)" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 206 }, "id": "RdbHaVy_fd64", "outputId": "a9aed834-81b7-4223-da42-6289799c2e1e" }, "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", " \n", " \n", " \n", " \n", " \n", " \n", "
attention_maskinput_idslabeltexttoken_type_ids
0[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ...[101, 2921, 3198, 23624, 2954, 6978, 2674, 841...0kept ask mani fight scream match swear gener m...[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...
1[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ...[101, 3422, 4372, 3775, 2099, 9587, 5737, 2071...0watch entir movi could watch entir movi stop d...[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...
2[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ...[101, 3543, 2293, 2358, 10050, 2128, 25300, 11...1touch love stori reminisc ‘in mood love draw h...[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...
3[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ...[101, 3732, 2154, 11865, 15472, 2072, 8040, 73...0latter day fulci schlocker total abysm concoct...[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...
4[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ...[101, 2034, 3813, 3669, 19337, 2666, 2615, 504...0first firmli believ norwegian movi continu get...[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...
\n", "
" ], "text/plain": [ " attention_mask \\\n", "0 [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ... \n", "1 [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ... \n", "2 [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ... \n", "3 [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ... \n", "4 [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ... \n", "\n", " input_ids label \\\n", "0 [101, 2921, 3198, 23624, 2954, 6978, 2674, 841... 0 \n", "1 [101, 3422, 4372, 3775, 2099, 9587, 5737, 2071... 0 \n", "2 [101, 3543, 2293, 2358, 10050, 2128, 25300, 11... 1 \n", "3 [101, 3732, 2154, 11865, 15472, 2072, 8040, 73... 0 \n", "4 [101, 2034, 3813, 3669, 19337, 2666, 2615, 504... 0 \n", "\n", " text \\\n", "0 kept ask mani fight scream match swear gener m... \n", "1 watch entir movi could watch entir movi stop d... \n", "2 touch love stori reminisc ‘in mood love draw h... \n", "3 latter day fulci schlocker total abysm concoct... \n", "4 first firmli believ norwegian movi continu get... \n", "\n", " token_type_ids \n", "0 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... \n", "1 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... \n", "2 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... \n", "3 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... \n", "4 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... " ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df_train.head()" ] }, { "cell_type": "markdown", "metadata": { "id": "0lTWT8JwkRic" }, "source": [ "## Fine Tunning do Modelo\n", "Ajuste fino do BERT para tarefas específica de classificação de sentimento para o dataset do IMDB" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import torch\n", "import pyarrow as pa\n", "from datasets import Dataset\n", "import evaluate\n", "import numpy as np" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "kW53p7VQqUDD", "outputId": "8231f3ba-37d5-4546-c4d0-6b4ff317ecf3" }, "outputs": [ { "data": { "text/plain": [ "device(type='cuda', index=0)" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n", "device" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "id": "68OdbTv5rLrm" }, "outputs": [], "source": [ "train_hg = Dataset(pa.Table.from_pandas(df_train))\n", "valid_hg = Dataset(pa.Table.from_pandas(df_val))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Metricas de avaliação F1 Score e Acc" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`compute_metrics` calcula tanto a acurácia quanto o F1-score para avaliar um modelo de classificação. Primeiramente, são carregadas as métricas de acurácia e F1-score usando evaluate.load. Em seguida, a função compute_metrics recebe um par de arrays eval_pred, contendo as previsões do modelo e os rótulos verdadeiros. Utilizando as previsões, a função calcula a acurácia e o F1-score ponderado, onde a acurácia é obtida através da comparação das previsões com os rótulos utilizando a métrica de acurácia previamente carregada, e o F1-score é calculado utilizando a métrica de F1 previamente carregada, com ponderação \"weighted\". Os resultados de ambas as métricas são então combinados em um dicionário e retornados como um único objeto contendo as métricas de avaliação calculadas." ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "id": "lUNhDPs0ry4m" }, "outputs": [], "source": [ "# Load both accuracy and f1 metrics\n", "accuracy_metric = evaluate.load(\"accuracy\")\n", "f1_metric = evaluate.load(\"f1\")\n", "\n", "# Metric helper method\n", "def compute_metrics(eval_pred):\n", " predictions, labels = eval_pred\n", " predictions = np.argmax(predictions, axis=1)\n", "\n", " # Compute accuracy\n", " accuracy = accuracy_metric.compute(predictions=predictions, references=labels)\n", "\n", " # Compute F1 score\n", " f1 = f1_metric.compute(predictions=predictions, references=labels, average=\"weighted\")\n", "\n", " # Combine the metrics into a single dictionary\n", " combined_metrics = {\n", " 'accuracy': accuracy['accuracy'],\n", " 'f1': f1['f1']\n", " }\n", "\n", " return combined_metrics" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "9jJYTWsHjnEc", "outputId": "fe45691a-4476-4978-89b8-15f36465c37c" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Name: accelerateNote: you may need to restart the kernel to use updated packages.\n", "\n", "Version: 0.31.0\n", "Summary: Accelerate\n", "Home-page: https://github.com/huggingface/accelerate\n", "Author: The HuggingFace team\n", "Author-email: zach.mueller@huggingface.co\n", "License: Apache\n", "Location: c:\\Users\\andre\\1JUPYTER\\dt_labs\\.venv\\Lib\\site-packages\n", "Requires: huggingface-hub, numpy, packaging, psutil, pyyaml, safetensors, torch\n", "Required-by: \n", "---\n", "Name: transformers\n", "Version: 4.41.2\n", "Summary: State-of-the-art Machine Learning for JAX, PyTorch and TensorFlow\n", "Home-page: https://github.com/huggingface/transformers\n", "Author: The Hugging Face team (past and future) with the help of all our contributors (https://github.com/huggingface/transformers/graphs/contributors)\n", "Author-email: transformers@huggingface.co\n", "License: Apache 2.0 License\n", "Location: c:\\Users\\andre\\1JUPYTER\\dt_labs\\.venv\\Lib\\site-packages\n", "Requires: filelock, huggingface-hub, numpy, packaging, pyyaml, regex, requests, safetensors, tokenizers, tqdm\n", "Required-by: \n" ] } ], "source": [ "pip show accelerate transformers" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Treinamento do modelo" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "QlaLCwf7rLtp", "outputId": "7e10e82a-8bc7-478b-851e-c7b628b46c41" }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "c:\\Users\\andre\\1JUPYTER\\dt_labs\\.venv\\Lib\\site-packages\\transformers\\training_args.py:1474: FutureWarning: `evaluation_strategy` is deprecated and will be removed in version 4.46 of 🤗 Transformers. Use `eval_strategy` instead\n", " warnings.warn(\n" ] } ], "source": [ "from transformers import TrainingArguments, Trainer\n", "\n", "EPOCHS = 1\n", "\n", "training_args = TrainingArguments(output_dir=\"./result\",\n", " evaluation_strategy=\"epoch\",\n", " num_train_epochs= EPOCHS,\n", " per_device_train_batch_size=16,\n", " per_device_eval_batch_size=8\n", " )\n", "\n", "trainer = Trainer(\n", " model=model,\n", " args=training_args,\n", " train_dataset=train_hg,\n", " eval_dataset=valid_hg,\n", " tokenizer=tokenizer,\n", " compute_metrics=compute_metrics\n", ")" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CUDA available: True\n", "CUDA version: 12.1\n" ] } ], "source": [ "print(\"CUDA available: \", torch.cuda.is_available())\n", "print(\"CUDA version: \", torch.version.cuda)" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 141 }, "id": "3s6lVFz_rLwO", "outputId": "ee64e8e9-9c8c-42a8-c355-f51410cc33df" }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ " 0%| | 0/2500 [00:00\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", " \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", "
reviewsentimentbert_results
651The film is content as it is to run clever one...negativePositive
2205&#91;Has&#93; a surprising and somewhat disapp...negativePositive
362Absurdly over-rated...negativeNegative
2784A rare bird, not because of what it's like but...negativePositive
1914Lord of Illusions is also quite repulsive, as ...negativePositive
............
2230The movie is completely innocuous, passably en...negativePositive
2354A mud-simple horror trudge set in a swamp colo...negativeNegative
2404Just plain generic.negativeNegative
720Ulmer brings an enormous amount of impressioni...positiveNegative
527In their directorial debut, Britt Poulton and ...negativeNegative
\n", "

3000 rows × 3 columns

\n", "" ], "text/plain": [ " review sentiment bert_results\n", "651 The film is content as it is to run clever one... negative Positive\n", "2205 [Has] a surprising and somewhat disapp... negative Positive\n", "362 Absurdly over-rated... negative Negative\n", "2784 A rare bird, not because of what it's like but... negative Positive\n", "1914 Lord of Illusions is also quite repulsive, as ... negative Positive\n", "... ... ... ...\n", "2230 The movie is completely innocuous, passably en... negative Positive\n", "2354 A mud-simple horror trudge set in a swamp colo... negative Negative\n", "2404 Just plain generic. negative Negative\n", "720 Ulmer brings an enormous amount of impressioni... positive Negative\n", "527 In their directorial debut, Britt Poulton and ... negative Negative\n", "\n", "[3000 rows x 3 columns]" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import pandas as pd\n", "from preprocess_data import preprocess_text,get_stopwords\n", "from transformers import AutoTokenizer, pipeline\n", "\n", "df = pd.read_csv('../data/rotten_tomatos.csv')\n", "\n", "MODEL_PATH = 'danielcd99/BERT_imdb'\n", "\n", "def load_pipeline():\n", " tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH)\n", " tokenizer.model_max_length = 200\n", "\n", " pipe=pipeline(\n", " \"text-classification\",\n", " model=MODEL_PATH\n", " )\n", " return pipe\n", "\n", "pipe = load_pipeline()\n", "get_stopwords()\n", "df['preprocessed_review'] = df['review'].copy()\n", "df['preprocessed_review'] = df['preprocessed_review'].apply(preprocess_text)\n", " \n", "predictions = []\n", "for review in df['preprocessed_review']:\n", " try:\n", " label = pipe(review)[0]['label']\n", " except:\n", " print(\"Ocorreu um erro de carregamento, tente novamente!\")\n", " \n", " if label == 'LABEL_0':\n", " predictions.append(0)\n", " else:\n", " predictions.append(1)\n", "\n", "df['bert_results'] = predictions\n", "\n", "cols = ['review','sentiment', 'bert_results']\n", "df = df[cols]\n", "df" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Precision: 0.8066\n", "Recall: 0.8449\n", "F1 Score: 0.8253\n" ] }, { "data": { "image/png": "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ADgCiSTgogICAiRJFy9evKn+x44dk4eHhypWrGjXHhoaqqCgIB07dsyuvUyZMrnGKFq0qM6dO2cy4tyeeOIJ1atXT88884xKliypjh07asGCBX+ZVF6Ls1KlSrnOValSRb///rsuXbpk1/7nz1K0aFFJytNnadGihYoUKaL58+crISFBDzzwQK7v8pqcnBxNnTpVd911l6xWq4oXL64SJUpo165dunDhwk2/5x133JGnG2tef/11BQcHa8eOHZoxY4ZCQkJu+loAcAQSScBFBQQEKCwsTD///HOervvzzS43UqhQoeu2G4Zh+j2urd+7xtfXV+vWrdN3332nLl26aNeuXXriiSf0yCOP5Or7T/yTz3KN1WpVu3btNHfuXC1atOiG1UhJGj9+vAYNGqSGDRvqo48+0rfffqsVK1aoatWqN115la5+P3mxfft2JSUlSZJ2796dp2sBwBFIJAEX1qpVK/3666/auHHj3/YNDw9XTk6ODh48aNd++vRpnT9/3nYHdn4oWrSo3R3O1/y56ilJHh4eatKkiaZMmaJffvlFr776qlatWqXVq1dfd+xrce7fvz/XuX379ql48eLy8/P7Zx/gBjp16qTt27fr4sWL171B6ZrPP/9cjRs31nvvvaeOHTuqadOmioqKyvWd3GxSfzMuXbqkbt26KSIiQj179tSkSZO0efPmfBsfAMwgkQRc2PPPPy8/Pz8988wzOn36dK7zv/76q6ZPny7p6tSspFx3Vk+ZMkWS1LJly3yLq0KFCrpw4YJ27dplazt16pQWLVpk1y85OTnXtdc25v7zlkTXlCpVSjVq1NDcuXPtErOff/5Zy5cvt31OR2jcuLFeeeUVvfHGGwoNDb1hv0KFCuWqdn722Wf67bff7NquJbzXS7rzatiwYTp+/Ljmzp2rKVOmqGzZsoqNjb3h9wgAtwIbkgMurEKFCvr444/1xBNPqEqVKnZPttmwYYM+++wzde3aVZJUvXp1xcbGavbs2Tp//rwaNWqkn376SXPnzlXbtm1vuLWMGR07dtSwYcP02GOPqV+/fkpLS9OsWbN09913291sMnbsWK1bt04tW7ZUeHi4kpKS9NZbb+nOO+9U/fr1bzj+a6+9pubNmysyMlLdu3fX5cuXNXPmTAUGBmr06NH59jn+zMPDQy+99NLf9mvVqpXGjh2rbt26qW7dutq9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J07kOv/rr7+qWbNmat++vXbt2qX58+dr/fr16tu3r63PU089pZMnT2rNmjVauHChZs+eraSkJLtxPDw8NGPGDO3Zs0dz587VqlWr9Pzzz0u6Oi02bdo0BQQE6NSpUzp16pSGDBmSK5aYmBgtWbLEloBK0rfffqu0tDQ99thjkqS4uDh9+OGHio+P1549ezRw4EB17txZa9euzZfvCyhofH19lZGRoa5du2rLli368ssvtXHjRhmGoRYtWigzM1OS1KdPH6Wnp2vdunXavXu3Jk6cKH9//1zj8XsGXJwB5JPY2FijTZs2hmEYRp06dYynn37aMAzDWLRokXHtH7Xu3bsbPXv2tLvu+++/Nzw8PIzLly8be/fuNSQZmzdvtp0/ePCgIcmYOnXqDd/7s88+M4oVK2Z7/cEHHxiBgYG5+oWHh9vGyczMNIoXL258+OGHtvNPPvmk8cQTTxiGYRhXrlwxChcubGzYsMFujO7duxtPPvnkX38ZgBv4428+JyfHWLFihWG1Wo22bdsakowffvjB1vf33383fH19jQULFhiGYRjVqlUzRo8efd1xV69ebUgyzp07ZxgGv2fAlXk6NYtFgTVx4kQ9/PDDuSoHO3fu1K5du5SQkGBrMwxDOTk5OnLkiA4cOCBPT0/df//9tvMVK1ZU0aJF7cb57rvvFBcXp3379iklJUVZWVm6cuWK0tLSbnrNlKenpzp06KCEhAR16dJFly5d0n/+8x99+umnkqRDhw4pLS1NjzzyiN11GRkZuu+++/L0fQAF1dKlS+Xv76/MzEzl5OSoU6dOateunZYuXaratWvb+hUrVkyVKlXS3r17JUn9+vVT7969tXz5ckVFRal9+/a69957TcfB7xlwDhJJOETDhg0VHR2tESNGqGvXrrb21NRUPfvss+rXr1+ua8qUKaMDBw787dhHjx5Vq1at1Lt3b7366qsKDg7W+vXr1b17d2VkZORp8X1MTIwaNWqkpKQkrVixQr6+vmrWrJktVklatmyZ7rjjDrvrePYvcFXjxo01a9YseXt7KywsTJ6envryyy//9rpnnnlG0dHRWrZsmZYvX664uDhNnjxZzz33nOlY+D0Dtx6JJBxmwoQJqlGjhipVqmRru//++/XLL7+oYsWK172mUqVKysrK0vbt21WzZk1JVysJf7wLfOvWrcrJydHkyZPl4XF1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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from sklearn.metrics import confusion_matrix, precision_recall_fscore_support\n", "import matplotlib.pyplot as plt\n", "import seaborn as sns\n", "\n", "# Mapear 'Positive' para 1 e 'Negative' para 0 em 'sentiment'\n", "df['sentiment'] = df['sentiment'].map({'positive': 1, 'negative': 0})\n", "df['bert_results'] = df['bert_results'].map({'Positive': 1, 'Negative': 0})\n", "\n", "# Calcular métricas de avaliação: precision, recall, f1-score\n", "precision, recall, f1_score, _ = precision_recall_fscore_support(df['sentiment'], df['bert_results'], average='binary')\n", "\n", "print(f\"Precision: {precision:.4f}\")\n", "print(f\"Recall: {recall:.4f}\")\n", "print(f\"F1 Score: {f1_score:.4f}\")\n", "\n", "# Calcular e plotar a matriz de confusão\n", "cm = confusion_matrix(df['sentiment'], df['bert_results'])\n", "plt.figure(figsize=(8, 6))\n", "sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=['Negative', 'Positive'], yticklabels=['Negative', 'Positive'])\n", "plt.xlabel('Predicted')\n", "plt.ylabel('True')\n", "plt.title('Confusion Matrix')\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Após ajustar o modelo BERT utilizando a base de dados do IMDb, avaliada com referência aos dados do Rotten Tomatoes, obtivemos as seguintes métricas de desempenho:\n", "\n", "Precision: 0.8562 --- Recall: 0.8654 --- F1 Score: 0.8608\n", "\n", "Essas métricas indicam que o modelo ajustado conseguiu classificar de forma bastante precisa os sentimentos dos textos da base de dados IMDb, utilizando o BERT finetunado com dados do Rotten Tomatoes como referência." ] } ], "metadata": { "accelerator": "GPU", "colab": { "provenance": [] }, "gpuClass": "standard", "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.11.7" } }, "nbformat": 4, "nbformat_minor": 0 }