{ "cells": [ { "cell_type": "markdown", "id": "023d00e4", "metadata": {}, "source": [ "# This a sentiment analysis task that design and optimization of the model, as well as potentially utilizing advanced NLP techniques such as fine-tuning pre-trained models like BERT. In a local Python environment where you can use TensorFlow and Keras, I took an optimized approach using a more complex LSTM model, incorporating some advanced techniques that help improve the accuracy." ] }, { "cell_type": "markdown", "id": "cb52f311", "metadata": {}, "source": [ "## Import Libraries:\n", "\n", "#### numpy and tensorflow are foundational libraries for numerical operations and machine learning.\n", "#### Classes imported from tensorflow.keras are used to build neural network models:\n", "- Sequential for linear stacking of layers.\n", "- Embedding, LSTM, Dense, Dropout, and Bidirectional for different types of neural network layers.\n", "- Tokenizer and pad_sequences are utilities for text processing.\n", "- train_test_split from sklearn is used for splitting data into training and test sets." ] }, { "cell_type": "code", "execution_count": 1, "id": "214a844b", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import tensorflow as tf\n", "from tensorflow.keras.models import Sequential\n", "from tensorflow.keras.layers import Embedding, LSTM, Dense, Dropout, Bidirectional\n", "from tensorflow.keras.preprocessing.text import Tokenizer\n", "from tensorflow.keras.preprocessing.sequence import pad_sequences\n", "from tensorflow.keras.initializers import Constant\n", "from sklearn.model_selection import train_test_split" ] }, { "cell_type": "code", "execution_count": 2, "id": "c01d5192", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2.10.0\n" ] } ], "source": [ "print(tf.__version__)" ] }, { "cell_type": "markdown", "id": "c7ad5ab0", "metadata": {}, "source": [ "## Load and Preprocess Data:\n", "\n", "### Download the NLP dataset Amazon Sentiment Review form Kaggle \n", "#### https://www.kaggle.com/datasets/bittlingmayer/amazonreviews\n", "\n", "- Reads each line, splits it to separate the label from the review, assigns binary labels (1 for positive, 0 for negative), and stores the results in lists.\n", "\n", "- Converts the label list to a NumPy array for further processing.\n" ] }, { "cell_type": "code", "execution_count": 3, "id": "992745e5", "metadata": {}, "outputs": [], "source": [ "# Load the data (example path, replace with your actual path)\n", "file_path= 'Dataset/train.txt'\n", "\n", "# Preprocess the data\n", "with open(file_path, 'r', encoding='utf-8') as file:\n", " lines = file.readlines()\n", "\n", "labels = []\n", "reviews = []\n", "for line in lines:\n", " split_line = line.strip().split(' ', 1)\n", " label = 1 if split_line[0] == '__label__2' else 0\n", " review = split_line[1]\n", " labels.append(label)\n", " reviews.append(review)\n", "\n", "labels = np.array(labels)\n" ] }, { "cell_type": "markdown", "id": "f2a2b587", "metadata": {}, "source": [ "## Tokenization and Sequence Padding:\n", "\n", "- Initializes a Tokenizer object, specifying a maximum vocabulary size of 10,000 words and an out-of-vocabulary token .\n", "- Fits the tokenizer on the collected reviews, creating an index of all unique words.\n", "- Converts the reviews into lists of integers based on the tokenizer's word index.\n", "- Pads these sequences to a fixed length of 250, ensuring all input data has consistent dimensions, necessary for training neural networks." ] }, { "cell_type": "code", "execution_count": 4, "id": "edfcccbf", "metadata": {}, "outputs": [], "source": [ "# Tokenizing and padding text\n", "tokenizer = Tokenizer(num_words=10000, oov_token=\"\")\n", "tokenizer.fit_on_texts(reviews)\n", "sequences = tokenizer.texts_to_sequences(reviews)\n", "padded_sequences = pad_sequences(sequences, padding='post', maxlen=250)" ] }, { "cell_type": "markdown", "id": "62aea0f6", "metadata": {}, "source": [ "## Loading Pre-trained GloVe Embeddings\n", "\n", "### Download the Glove Embedding from Kaggle\n", "\n", "#### https://www.kaggle.com/datasets/danielwillgeorge/glove6b100dtxt\n", "\n", "- Defines a function to load the GloVe (Global Vectors for Word Representation) embeddings.\n", "- Reads the GloVe file, parsing each line to extract the word and its corresponding coefficient vector.\n", "- Creates an embedding matrix that maps each word in the tokenizer's index to its GloVe vector, if available. Words not in GloVe will have a vector of zeros.\n" ] }, { "cell_type": "code", "execution_count": 5, "id": "a44dac7b", "metadata": {}, "outputs": [], "source": [ "# # Load pre-trained GloVe embeddings\n", "\n", "def load_glove_embeddings(path, word_index, embedding_dim):\n", " embeddings_index = {}\n", " # Open the file with UTF-8 encoding specified\n", " with open(path, 'r', encoding='utf-8') as f:\n", " for line in f:\n", " word, coefs = line.split(maxsplit=1)\n", " coefs = np.fromstring(coefs, 'f', sep=' ')\n", " embeddings_index[word] = coefs\n", "\n", " # Prepare embedding matrix\n", " num_words = min(len(word_index) + 1, 10000)\n", " embedding_matrix = np.zeros((num_words, embedding_dim))\n", " for word, i in word_index.items():\n", " if i >= num_words:\n", " continue\n", " embedding_vector = embeddings_index.get(word)\n", " if embedding_vector is not None: # Corrected line here\n", " # Use the embedding vector for words found in the GloVe index\n", " embedding_matrix[i] = embedding_vector # words not found will be all-zeros.\n", "\n", " return embedding_matrix\n", "\n" ] }, { "cell_type": "code", "execution_count": 6, "id": "8800fc79", "metadata": {}, "outputs": [], "source": [ "embedding_dim = 100 # Size of the GloVe vectors you're using\n", "glove_path = 'Embedding/glove.6B.100d/glove.6B.100d.txt'\n", "embedding_matrix = load_glove_embeddings(glove_path, tokenizer.word_index, embedding_dim)\n" ] }, { "cell_type": "markdown", "id": "5ed572bf", "metadata": {}, "source": [ "## Model Definition and Compilation:\n", "\n", "- Defines a sequential model for sentiment analysis.\n", "- Adds an Embedding layer to transform indices into dense vectors of fixed size.\n", "- Utilizes Bidirectional layers with LSTM units to capture patterns from both forward and backward states of the input sequence.\n", "- Adds Dense layers with ReLU activation for non-linear transformations and a dropout layer to reduce overfitting.\n", "- The final output layer uses a sigmoid activation function for binary classification.\n", "- Compiles the model with the Adam optimizer and binary cross-entropy loss function, tracking accuracy as a metric." ] }, { "cell_type": "code", "execution_count": 7, "id": "2d8b02df", "metadata": {}, "outputs": [], "source": [ "# Build the model\n", "model = Sequential([\n", " Embedding(10000, embedding_dim, embeddings_initializer=Constant(embedding_matrix), \n", " input_length=250, trainable=False),\n", " Bidirectional(LSTM(128, return_sequences=True)),\n", " Dropout(0.5),\n", " Bidirectional(LSTM(128)),\n", " Dropout(0.5),\n", " Dense(1, activation='sigmoid')\n", "])" ] }, { "cell_type": "code", "execution_count": 8, "id": "ce4fa260", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Model: \"sequential\"\n", "_________________________________________________________________\n", " Layer (type) Output Shape Param # \n", "=================================================================\n", " embedding (Embedding) (None, 250, 100) 1000000 \n", " \n", " bidirectional (Bidirectiona (None, 250, 256) 234496 \n", " l) \n", " \n", " dropout (Dropout) (None, 250, 256) 0 \n", " \n", " bidirectional_1 (Bidirectio (None, 256) 394240 \n", " nal) \n", " \n", " dropout_1 (Dropout) (None, 256) 0 \n", " \n", " dense (Dense) (None, 1) 257 \n", " \n", "=================================================================\n", "Total params: 1,628,993\n", "Trainable params: 628,993\n", "Non-trainable params: 1,000,000\n", "_________________________________________________________________\n" ] } ], "source": [ "model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])\n", "model.summary()" ] }, { "cell_type": "code", "execution_count": 9, "id": "a3c4e6a3", "metadata": {}, "outputs": [], "source": [ "# Splitting the data\n", "X_train, X_val, y_train, y_val = train_test_split(padded_sequences, labels, test_size=0.2, random_state=42)\n" ] }, { "cell_type": "markdown", "id": "ddafc948", "metadata": {}, "source": [ "## Model Training:\n", "\n", "- Trains the model on the padded text sequences and labels.\n", "- Runs for 5 epochs with 20% of the data reserved for validation to monitor performance and mitigate overfitting." ] }, { "cell_type": "code", "execution_count": 10, "id": "596a0174", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Epoch 1/5\n", "1093/1093 [==============================] - 1457s 1s/step - loss: 0.3991 - accuracy: 0.8189 - val_loss: 0.3092 - val_accuracy: 0.8722\n", "Epoch 2/5\n", "1093/1093 [==============================] - 1371s 1s/step - loss: 0.2911 - accuracy: 0.8783 - val_loss: 0.2605 - val_accuracy: 0.8944\n", "Epoch 3/5\n", "1093/1093 [==============================] - 1641s 2s/step - loss: 0.2511 - accuracy: 0.8970 - val_loss: 0.2432 - val_accuracy: 0.9026\n", "Epoch 4/5\n", "1093/1093 [==============================] - 1400s 1s/step - loss: 0.2308 - accuracy: 0.9075 - val_loss: 0.2350 - val_accuracy: 0.9058\n", "Epoch 5/5\n", "1093/1093 [==============================] - 1400s 1s/step - loss: 0.2066 - accuracy: 0.9193 - val_loss: 0.2306 - val_accuracy: 0.9133\n" ] } ], "source": [ "# Training the model\n", "history = model.fit(X_train, y_train, epochs=5, batch_size=64, validation_data=(X_val, y_val), verbose=1)\n" ] }, { "cell_type": "markdown", "id": "039f21c0", "metadata": {}, "source": [ "## Save the Train Model" ] }, { "cell_type": "code", "execution_count": 11, "id": "e4693576", "metadata": {}, "outputs": [], "source": [ "# Save the model\n", "model.save('sentiment_model.h5')" ] }, { "cell_type": "code", "execution_count": 12, "id": "2e1b099f", "metadata": {}, "outputs": [], "source": [ "# Save the tokenizer\n", "import pickle\n", "with open('tokenizer.pkl', 'wb') as handle:\n", " pickle.dump(tokenizer, handle, protocol=pickle.HIGHEST_PROTOCOL)\n" ] }, { "cell_type": "markdown", "id": "66daf062", "metadata": {}, "source": [ "## Evaluate \n", "\n", "- Evaluate the model Validation loss\n", "- Evaluate the model Validation accuracy\n" ] }, { "cell_type": "code", "execution_count": 13, "id": "b64ef635", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "547/547 [==============================] - 191s 349ms/step - loss: 0.2306 - accuracy: 0.9133\n", "Validation loss: 0.23057065904140472\n", "Validation accuracy: 0.9132547974586487\n" ] } ], "source": [ "# Evaluate the model\n", "loss, accuracy = model.evaluate(X_val, y_val)\n", "print(\"Validation loss:\", loss)\n", "print(\"Validation accuracy:\", accuracy)" ] }, { "cell_type": "code", "execution_count": 14, "id": "54a7aec7", "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkAAAAHHCAYAAABXx+fLAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAAB1jklEQVR4nO3dd3wU1frH8c+mJ5CEkkISAqGGTpAmYAFFEDCCFxWxgIj6UwFFrgURBBtguYiK/VL0KoINRekEAekIBpHeSyCNkkra7vz+WFhYEkogYVK+79drX7AzZ2afk02yT84854zFMAwDERERkXLExewARERERK41JUAiIiJS7igBEhERkXJHCZCIiIiUO0qAREREpNxRAiQiIiLljhIgERERKXeUAImIiEi5owRIREREyh0lQCJyTVksFsaMGVPo4/bv34/FYmHatGlFHpOIlD9KgETKoWnTpmGxWLBYLKxYsSLffsMwCA8Px2KxcMcdd5gQYdGYO3cuFouF0NBQbDab2eGISAmiBEikHPPy8mL69On5ti9btozDhw/j6elpQlRF55tvviEiIoKjR4+yZMkSs8MRkRJECZBIOda9e3e+//578vLynLZPnz6dli1bUq1aNZMiu3oZGRn88ssvDBs2jBYtWvDNN9+YHdIFZWRkmB2CSLmjBEikHOvbty/Hjh1j0aJFjm05OTn88MMP3H///QUek5GRwb///W/Cw8Px9PQkMjKSd999F8MwnNplZ2fz7LPPEhgYiK+vL3feeSeHDx8u8JxxcXE88sgjBAcH4+npSePGjZkyZcpV9W3WrFmcOnWKe+65h/vuu4+ffvqJrKysfO2ysrIYM2YM9evXx8vLi5CQEP71r3+xZ88eRxubzcb7779P06ZN8fLyIjAwkNtvv50///wTuHh90vk1T2PGjMFisbB161buv/9+KleuzA033ADA33//zcMPP0zt2rXx8vKiWrVqPPLIIxw7dqzAr9nAgQMJDQ3F09OTWrVq8eSTT5KTk8PevXuxWCy89957+Y5btWoVFouFb7/9trBfUpEyxc3sAETEPBEREbRr145vv/2Wbt26ATBv3jxSUlK47777+OCDD5zaG4bBnXfeye+//87AgQOJiopiwYIFPP/888TFxTl94D766KN8/fXX3H///bRv354lS5bQo0ePfDEkJCRw/fXXY7FYGDx4MIGBgcybN4+BAweSmprK0KFDr6hv33zzDZ06daJatWrcd999DB8+nF9//ZV77rnH0cZqtXLHHXcQExPDfffdxzPPPENaWhqLFi3in3/+oU6dOgAMHDiQadOm0a1bNx599FHy8vL4448/WLNmDa1atbqi+O655x7q1avH2LFjHcnjokWL2Lt3LwMGDKBatWps2bKFzz//nC1btrBmzRosFgsAR44coU2bNpw8eZLHH3+cBg0aEBcXxw8//EBmZia1a9emQ4cOfPPNNzz77LP5vi6+vr707NnziuIWKTMMESl3pk6dagDG+vXrjUmTJhm+vr5GZmamYRiGcc899xidOnUyDMMwatasafTo0cNx3M8//2wAxhtvvOF0vrvvvtuwWCzG7t27DcMwjNjYWAMwnnrqKad2999/vwEYo0ePdmwbOHCgERISYiQnJzu1ve+++wx/f39HXPv27TMAY+rUqZfsX0JCguHm5mZ88cUXjm3t27c3evbs6dRuypQpBmBMmDAh3zlsNpthGIaxZMkSAzCefvrpC7a5WGzn93f06NEGYPTt2zdf2zN9Pde3335rAMby5csd2/r162e4uLgY69evv2BMn332mQEY27Ztc+zLyckxAgICjP79++c7TqS80SUwkXLu3nvv5dSpU/z222+kpaXx22+/XfDy19y5c3F1deXpp5922v7vf/8bwzCYN2+eox2Qr935ozmGYfDjjz8SHR2NYRgkJyc7Hl27diUlJYWNGzcWuk8zZszAxcWF3r17O7b17duXefPmceLECce2H3/8kYCAAIYMGZLvHGdGW3788UcsFgujR4++YJsr8cQTT+Tb5u3t7fh/VlYWycnJXH/99QCOr4PNZuPnn38mOjq6wNGnMzHde++9eHl5OdU+LViwgOTkZB588MErjlukrFACJFLOBQYG0rlzZ6ZPn85PP/2E1Wrl7rvvLrDtgQMHCA0NxdfX12l7w4YNHfvP/Ovi4uK4hHRGZGSk0/OkpCROnjzJ559/TmBgoNNjwIABACQmJha6T19//TVt2rTh2LFj7N69m927d9OiRQtycnL4/vvvHe327NlDZGQkbm4XrgbYs2cPoaGhVKlSpdBxXEytWrXybTt+/DjPPPMMwcHBeHt7ExgY6GiXkpIC2L9mqampNGnS5KLnr1SpEtHR0U6z/L755hvCwsK45ZZbirAnIqWTaoBEhPvvv5/HHnuM+Ph4unXrRqVKla7J655Zm+fBBx+kf//+BbZp1qxZoc65a9cu1q9fD0C9evXy7f/mm294/PHHCxnpxV1oJMhqtV7wmHNHe8649957WbVqFc8//zxRUVFUrFgRm83G7bfffkXrGPXr14/vv/+eVatW0bRpU2bPns1TTz2Fi4v+9hVRAiQi3HXXXfzf//0fa9asYebMmRdsV7NmTRYvXkxaWprTKND27dsd+8/8a7PZHCMsZ+zYscPpfGdmiFmtVjp37lwkffnmm29wd3fnf//7H66urk77VqxYwQcffMDBgwepUaMGderUYe3ateTm5uLu7l7g+erUqcOCBQs4fvz4BUeBKleuDMDJkyedtp8ZEbscJ06cICYmhldffZVXXnnFsX3Xrl1O7QIDA/Hz8+Off/655Dlvv/12AgMD+eabb2jbti2ZmZk89NBDlx2TSFmmPwNEhIoVK/LJJ58wZswYoqOjL9iue/fuWK1WJk2a5LT9vffew2KxOGaSnfn3/FlkEydOdHru6upK7969+fHHHwv8QE9KSip0X7755htuvPFG+vTpw9133+30eP755wEcU8B79+5NcnJyvv4AjplZvXv3xjAMXn311Qu28fPzIyAggOXLlzvt//jjjy877jPJmnHecgLnf81cXFzo1asXv/76q2MafkExAbi5udG3b1++++47pk2bRtOmTQs9oiZSVmkESEQALngJ6lzR0dF06tSJl19+mf3799O8eXMWLlzIL7/8wtChQx01P1FRUfTt25ePP/6YlJQU2rdvT0xMDLt37853zvHjx/P777/Ttm1bHnvsMRo1asTx48fZuHEjixcv5vjx45fdh7Vr17J7924GDx5c4P6wsDCuu+46vvnmG1588UX69evHV199xbBhw1i3bh033ngjGRkZLF68mKeeeoqePXvSqVMnHnroIT744AN27drluBz1xx9/0KlTJ8drPfroo4wfP55HH32UVq1asXz5cnbu3HnZsfv5+XHTTTfx9ttvk5ubS1hYGAsXLmTfvn352o4dO5aFCxdy88038/jjj9OwYUOOHj3K999/z4oVK5wuYfbr148PPviA33//nbfeeuuy4xEp88ybgCYiZjl3GvzFnD8N3jAMIy0tzXj22WeN0NBQw93d3ahXr57xzjvvOKZfn3Hq1Cnj6aefNqpWrWpUqFDBiI6ONg4dOpRvWrhh2KetDxo0yAgPDzfc3d2NatWqGbfeeqvx+eefO9pczjT4IUOGGICxZ8+eC7YZM2aMARibNm0yDMM+9fzll182atWq5Xjtu+++2+kceXl5xjvvvGM0aNDA8PDwMAIDA41u3boZGzZscLTJzMw0Bg4caPj7+xu+vr7GvffeayQmJl5wGnxSUlK+2A4fPmzcddddRqVKlQx/f3/jnnvuMY4cOVLg1+zAgQNGv379jMDAQMPT09OoXbu2MWjQICM7OzvfeRs3bmy4uLgYhw8fvuDXRaS8sRjGeeOtIiJSprRo0YIqVaoQExNjdigiJYZqgEREyrA///yT2NhY+vXrZ3YoIiWKRoBERMqgf/75hw0bNvCf//yH5ORk9u7di5eXl9lhiZQYGgESESmDfvjhBwYMGEBubi7ffvutkh+R82gESERERModjQCJiIhIuaMESERERModLYRYAJvNxpEjR/D19b2quz2LiIjItWMYBmlpaYSGhl7ynndKgApw5MgRwsPDzQ5DRERErsChQ4eoXr36RdsoASrAmZs8Hjp0CD8/P5OjERERkcuRmppKeHi4082aL0QJUAHOXPby8/NTAiQiIlLKXE75ioqgRUREpNxRAiQiIiLljhIgERERKXdUA3QVrFYrubm5ZochUuTc3d1xdXU1OwwRkWKjBOgKGIZBfHw8J0+eNDsUkWJTqVIlqlWrprWwRKRMUgJ0Bc4kP0FBQfj4+OgDQsoUwzDIzMwkMTERgJCQEJMjEhEpekqACslqtTqSn6pVq5odjkix8Pb2BiAxMZGgoCBdDhORMqdEFEF/9NFHRERE4OXlRdu2bVm3bt0F2+bm5vLaa69Rp04dvLy8aN68OfPnz3dqM27cOFq3bo2vry9BQUH06tWLHTt2FEmsZ2p+fHx8iuR8IiXVme9x1bmJSFlkegI0c+ZMhg0bxujRo9m4cSPNmzena9eujuH3840cOZLPPvuMDz/8kK1bt/LEE09w11138ddffznaLFu2jEGDBrFmzRoWLVpEbm4uXbp0ISMjo8ji1mUvKev0PS4iZZnFMAzDzADatm1L69atmTRpEmC/EWl4eDhDhgxh+PDh+dqHhoby8ssvM2jQIMe23r174+3tzddff13gayQlJREUFMSyZcu46aabLhlTamoq/v7+pKSk5FsJOisri3379lGrVi28vLwK01WRUkXf6yJS2lzs8/t8po4A5eTksGHDBjp37uzY5uLiQufOnVm9enWBx2RnZ+f7Zezt7c2KFSsu+DopKSkAVKlSpQiiFhERkdLO1AQoOTkZq9VKcHCw0/bg4GDi4+MLPKZr165MmDCBXbt2YbPZWLRoET/99BNHjx4tsL3NZmPo0KF06NCBJk2aFNgmOzub1NRUp0dZtnr1alxdXenRo4fZoYiIiJjC9Bqgwnr//fepV68eDRo0wMPDg8GDBzNgwABcXAruyqBBg/jnn3+YMWPGBc85btw4/P39HY/w8PDiCr9EmDx5MkOGDGH58uUcOXLEtDhycnJMe20RETHP3qR0Dp/INDUGUxOggIAAXF1dSUhIcNqekJBAtWrVCjwmMDCQn3/+mYyMDA4cOMD27dupWLEitWvXztd28ODB/Pbbb/z+++9Ur179gnG89NJLpKSkOB6HDh26uo6VYOnp6cycOZMnn3ySHj16MG3aNKf9v/76K61bt8bLy4uAgADuuusux77s7GxefPFFwsPD8fT0pG7dukyePBmAadOmUalSJadz/fzzz06FtGPGjCEqKor//ve/TnUl8+fP54YbbqBSpUpUrVqVO+64gz179jid6/Dhw/Tt25cqVapQoUIFWrVqxdq1a9m/fz8uLi78+eefTu0nTpxIzZo1sdlsV/slExGRIhCfksV//9hL9IcruOU/y5i8Yp+p8Zi6DpCHhwctW7YkJiaGXr16AfZLVjExMQwePPiix3p5eREWFkZubi4//vgj9957r2OfYRgMGTKEWbNmsXTpUmrVqnXRc3l6euLp6XnF/TAMg1O51is+/kp5u7sWeqbOd999R4MGDYiMjOTBBx9k6NChvPTSS1gsFubMmcNdd93Fyy+/zFdffUVOTg5z5851HNuvXz9Wr17NBx98QPPmzdm3bx/JycmFev3du3fz448/8tNPPznWlsnIyGDYsGE0a9aM9PR0XnnlFe666y5iY2NxcXEhPT2dm2++mbCwMGbPnk21atXYuHEjNpuNiIgIOnfuzNSpU2nVqpXjdaZOncrDDz98wZFBEREpficzc5i7OZ7Zm+JYu+84Z6ZdubpYSMvKMzU20xdCHDZsGP3796dVq1a0adOGiRMnkpGRwYABAwD7h25YWBjjxo0DYO3atcTFxREVFUVcXBxjxozBZrPxwgsvOM45aNAgpk+fzi+//IKvr6+jnsjf39+xwFtROpVrpdErC4r8vJey9bWu+HgU7i2cPHkyDz74IAC33347KSkpLFu2jI4dO/Lmm29y33338eqrrzraN2/eHICdO3fy3XffsWjRIkfRekGjbpeSk5PDV199RWBgoGNb7969ndpMmTKFwMBAtm7dSpMmTZg+fTpJSUmsX7/eUchet25dR/tHH32UJ554ggkTJuDp6cnGjRvZvHkzv/zyS6HjExGRq5OZk8eirQn8uukIy3YmkWs9O9m8dURl7mweSvemIVSteOUDD0XB9ASoT58+JCUl8corrxAfH09UVBTz5893FEYfPHjQ6a/4rKwsRo4cyd69e6lYsSLdu3fnf//7n9Pll08++QSAjh07Or3WmVGB8mrHjh2sW7eOWbNmAeDm5kafPn2YPHkyHTt2JDY2lscee6zAY2NjY3F1deXmm2++qhhq1qzplPwA7Nq1i1deeYW1a9eSnJzsuGx18OBBmjRpQmxsLC1atLjgLL5evXoxaNAgZs2axX333ce0adPo1KkTERERVxWriIhcnpw8G3/sSmL2piMs3JLgdFWkYYgfdzYPJbp5CNUrl5xFhE1PgMBeq3OhS15Lly51en7zzTezdevWi57vWi9t5O3uytbXul7T1zzzuoUxefJk8vLyCA0NdWwzDANPT08mTZp00dGxS42cubi45Pu6F7SCcIUKFfJti46OpmbNmnzxxReEhoZis9lo0qSJo0j6Uq/t4eFBv379mDp1Kv/617+YPn0677///kWPERGRq2OzGazff5xfNh1h7uajnMw8+zu/RhUf7mweyp1RodQP9jUxygsrEQlQaWexWAp9Kepay8vL46uvvuI///kPXbp0cdrXq1cvvv32W5o1a0ZMTIzj8uO5mjZtis1mY9myZU7rNp0RGBhIWloaGRkZjiQnNjb2knEdO3aMHTt28MUXX3DjjTcC5FvTqVmzZvz3v//l+PHjFxwFevTRR2nSpAkff/wxeXl5/Otf/7rka4uISOEYhsGWI6nM3nSEXzcd4WhKlmNfQEVP7mgWQs+oUKLCK5X41eRL9qe2FJnffvuNEydOMHDgQPz9/Z329e7dm8mTJ/POO+9w6623UqdOHe677z7y8vKYO3cuL774IhEREfTv359HHnnEUQR94MABEhMTuffee2nb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NNzc3Ix3333X2LZtmzF69GjD3d3d2Lx5s1lduKjC9u/VV181FixYYOzZs8fYsGGDcd999xleXl7Gli1bzOrCRaWlpRl//fWX8ddffxmAMWHCBOOvv/4yDhw4YBiGYQwfPtx46KGHHO337t1r+Pj4GM8//7yxbds246OPPjJcXV2N+fPnm9WFiyps/9577z3j559/Nnbt2mVs3rzZeOaZZwwXFxdj8eLFZnXhop588knD39/fWLp0qXH06FHHIzMz09GmtP8MXkkfS9PP4fDhw41ly5YZ+/btM/7++29j+PDhhsViMRYuXGgYRul//wrbv9L03l3I+bPASsp7qASoGHz44YdGjRo1DA8PD6NNmzbGmjVrHPtuvvlmo3///k7tv/vuO6N+/fqGh4eH0bhxY2POnDnXOOLCKUz/hg4d6mgbHBxsdO/e3di4caMJUV+eM9O+z3+c6VP//v2Nm2++Od8xUVFRhoeHh1G7dm1j6tSp1zzuy1XY/r311ltGnTp1DC8vL6NKlSpGx44djSVLlpgT/GUoqG+A03tS2n8Gr6SPpenn8JFHHjFq1qxpeHh4GIGBgcatt97qSA4Mo/S/f4XtX2l67y7k/ASopLyHFsMwjOIdYxIREREpWVQDJCIiIuWOEiAREREpd5QAiYiISLmjBEhERETKHSVAIiIiUu4oARIREZFyRwmQiIiIlDtKgERELoPFYuHnn382OwwRKSJKgESkxHv44YexWCz5HrfffrvZoYlIKaW7wYtIqXD77bczdepUp22enp4mRSMipZ1GgESkVPD09KRatWpOj8qVKwP2y1OffPIJ3bp1w9vbm9q1a/PDDz84Hb9582ZuueUWvL29qVq1Ko8//jjp6elObaZMmULjxo3x9PQkJCSEwYMHO+1PTk7mrrvuwsfHh3r16jF79uzi7bSIFBslQCJSJowaNYrevXuzadMmHnjgAe677z62bdsGQEZGBl27dqVy5cqsX7+e77//nsWLFzslOJ988gmDBg3i8ccfZ/PmzcyePZu6des6vcarr77Kvffey99//0337t154IEHOH78+DXtp4gUkWK/3aqIyFXq37+/4erqalSoUMHp8eabbxqGYb9D+hNPPOF0TNu2bY0nn3zSMAzD+Pzzz43KlSsb6enpjv1z5swxXFxcjPj4eMMwDCM0NNR4+eWXLxgDYIwcOdLxPD093QCMefPmFVk/ReTaUQ2QiJQKnTp14pNPPnHaVqVKFcf/27Vr57SvXbt2xMbGArBt2zaaN29OhQoVHPs7dOiAzWZjx44dWCwWjhw5wq233nrRGJo1a+b4f4UKFfDz8yMxMfFKuyQiJlICJCKlQoUKFfJdkioq3t7el9XO3d3d6bnFYsFmsxVHSCJSzFQDJCJlwpo1a/I9b9iwIQANGzZk06ZNZGRkOPavXLkSFxcXIiMj8fX1JSIigpiYmGsas4iYRyNAIlIqZGdnEx8f77TNzc2NgIAAAL7//ntatWrFDTfcwDfffMO6deuYPHkyAA888ACjR4+mf//+jBkzhqSkJIYMGcJDDz1EcHAwAGPGjOGJJ54gKCiIbt26kZaWxsqVKxkyZMi17aiIXBNKgESkVJg/fz4hISFO2yIjI9m+fTtgn6E1Y8YMnnrqKUJCQvj2229p1KgRAD4+PixYsIBnnnmG1q1b4+PjQ+/evZkwYYLjXP379ycrK4v33nuP5557joCAAO6+++5r10ERuaYshmEYZgchInI1LBYLs2bNolevXmaHIiKlhGqAREREpNxRAiQiIiLljmqARKTU05V8ESksjQCJiIhIuaMESERERModJUAiIiJS7igBEhERkXJHCZCIiIiUO0qAREREpNxRAiQiIiLljhIgERERKXeUAImIiEi58/8U4zqe1kif2wAAAABJRU5ErkJggg==", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# plot the training history to visualize the learning over epochs:\n", "import matplotlib.pyplot as plt\n", "# Plotting training history\n", "plt.plot(history.history['accuracy'], label='Accuracy')\n", "plt.plot(history.history['val_accuracy'], label='Validation Accuracy')\n", "plt.title('Model Accuracy')\n", "plt.ylabel('Accuracy')\n", "plt.xlabel('Epoch')\n", "plt.legend()\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "f1590396", "metadata": {}, "source": [ "## Model Load\n", "\n", "- load the sentiment_model.h5 model from you dir " ] }, { "cell_type": "code", "execution_count": 15, "id": "57021426", "metadata": {}, "outputs": [], "source": [ "from tensorflow.keras.models import load_model\n", "\n", "# Load the model\n", "model = load_model('sentiment_model.h5')" ] }, { "cell_type": "code", "execution_count": 16, "id": "9acdbe1f", "metadata": {}, "outputs": [], "source": [ "# Load tokenizer\n", "with open('tokenizer.pkl', 'rb') as f:\n", " tokenizer = pickle.load(f)" ] }, { "cell_type": "code", "execution_count": 17, "id": "b44fd582", "metadata": {}, "outputs": [], "source": [ "# from tensorflow.keras.preprocessing.text import Tokenizer\n", "from tensorflow.keras.preprocessing.sequence import pad_sequences" ] }, { "cell_type": "markdown", "id": "bbf51b83", "metadata": {}, "source": [ "## Utility Functions for Text Preprocessing and Sentiment Prediction:\n", "\n", "- preprocess_text converts input texts into padded sequences suitable for model input, using the previously defined tokenizer.\n", "- predict_sentiment processes texts, makes predictions with the trained model, and interprets the results as 'Positive' or 'Negative' based on the prediction score." ] }, { "cell_type": "code", "execution_count": 18, "id": "de6eac0b", "metadata": {}, "outputs": [], "source": [ "def preprocess_text(texts, tokenizer, max_length=250):\n", " sequences = tokenizer.texts_to_sequences(texts)\n", " padded_sequences = pad_sequences(sequences, maxlen=max_length, padding='post')\n", " return padded_sequences\n", "\n", "def predict_sentiment(texts, model, tokenizer):\n", " preprocessed_texts = preprocess_text(texts, tokenizer)\n", " predictions = model.predict(preprocessed_texts)\n", " print(\"predictions\",predictions)\n", " sentiment_labels = ['Negative' if pred < 0.5 else 'Positive' for pred in predictions.flatten()]\n", " return sentiment_labels" ] }, { "cell_type": "markdown", "id": "a58a1314", "metadata": {}, "source": [ "## Sentiment Prediction on New Reviews:\n", "\n", "- Lists new review texts to test the model.\n", "- Calls predict_sentiment to determine the sentiment of each review.\n", "- Prints out each review with its predicted sentiment, providing a practical demonstration of the model in action." ] }, { "cell_type": "code", "execution_count": 20, "id": "d95588b0", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1/1 [==============================] - 0s 245ms/step\n", "predictions [[0.9961173 ]\n", " [0.00163375]\n", " [0.5040343 ]\n", " [0.9762217 ]\n", " [0.00119857]\n", " [0.15386383]\n", " [0.98825365]]\n", "Review: I absolutely loved this product, it worked wonders for me!\n", "Sentiment: Positive\n", "\n", "Review: Horrible experience, it broke down the first time I used it.\n", "Sentiment: Negative\n", "\n", "Review: Okay product, but I expected something better.\n", "Sentiment: Positive\n", "\n", "Review: Perfect, just as described! Would buy again!\n", "Sentiment: Positive\n", "\n", "Review: Not worth the money, very disappointing.\n", "Sentiment: Negative\n", "\n", "Review: If you want to listen to El Duke , then it is better if you have access to his shower,this is not him, it is a gimmick,very well orchestrated.\n", "Sentiment: Negative\n", "\n", "Review: Review of Kelly Club for Toddlers: For the price of 7.99, this PC game is WELL worth it, great graphics, colorful and lots to do! My four year old daughter is in love with the many tasks to complete in this game, including dressing and grooming wide variety of pets and decoration of numerous floats to show in your little one's very own parade.\n", "Sentiment: Positive\n", "\n" ] } ], "source": [ "# Example reviews\n", "new_reviews = [\n", " \"I absolutely loved this product, it worked wonders for me!\",\n", " \"Horrible experience, it broke down the first time I used it.\",\n", " \"Okay product, but I expected something better.\",\n", " \"Perfect, just as described! Would buy again!\",\n", " \"Not worth the money, very disappointing.\",\n", " \"If you want to listen to El Duke , then it is better if you have access to his shower,this is not him, it is a gimmick,very well orchestrated.\",\n", " \"Review of Kelly Club for Toddlers: For the price of 7.99, this PC game is WELL worth it, great graphics, colorful and lots to do! My four year old daughter is in love with the many tasks to complete in this game, including dressing and grooming wide variety of pets and decoration of numerous floats to show in your little one's very own parade.\"\n", "]\n", "\n", "# Predict sentiments\n", "sentiments = predict_sentiment(new_reviews, model, tokenizer)\n", "# print(sentiments)\n", "for review, sentiment in zip(new_reviews, sentiments):\n", " print(f\"Review: {review}\\nSentiment: {sentiment}\\n\")" ] }, { "cell_type": "code", "execution_count": 23, "id": "6a3b88ac", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1/1 [==============================] - 0s 218ms/step\n", "Prediction: [0.00290678]\n", "Review: very bad product\n", "Sentiment: Negative\n", "\n" ] } ], "source": [ "def preprocess_text(text, tokenizer, max_length=250):\n", " sequence = tokenizer.texts_to_sequences([text])\n", " padded_sequence = pad_sequences(sequence, maxlen=max_length, padding='post')\n", " return padded_sequence\n", "\n", "def predict_sentiment(text, model, tokenizer):\n", " preprocessed_text = preprocess_text(text, tokenizer)\n", " prediction = model.predict(preprocessed_text)[0]\n", " print(\"Prediction:\", prediction)\n", " sentiment_label = 'Negative' if prediction < 0.5 else 'Positive'\n", " return sentiment_label\n", "\n", "# Example review\n", "new_review = \"very bad product\"\n", "\n", "# Predict sentiment\n", "sentiment = predict_sentiment(new_review, model, tokenizer)\n", "print(f\"Review: {new_review}\\nSentiment: {sentiment}\\n\")" ] }, { "cell_type": "code", "execution_count": null, "id": "f35e0602", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.13" } }, "nbformat": 4, "nbformat_minor": 5 }