diff --git a/beginner/README.md b/beginner/README.md index 63a866f..e8c226e 100644 --- a/beginner/README.md +++ b/beginner/README.md @@ -24,6 +24,12 @@ - **Prerequisites:** Python and basic statistics. - **Interactive Lab:** [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/AIMLCLUBOCT/Workshops/blob/main/beginner/notebooks/03_intro_machine_learning.ipynb) [`03_intro_machine_learning.ipynb`](./notebooks/03_intro_machine_learning.ipynb) +### 4. NLP & Sentiment Analysis: Text Classification with Scikit-Learn +- **Topics:** Text cleaning, tokenization, TF-IDF feature extraction, Multinomial Naive Bayes, and interactive model inference. +- **Duration:** 2.5 Hours +- **Prerequisites:** Python and introductory classification concepts. +- **Interactive Lab:** [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/AIMLCLUBOCT/Workshops/blob/main/beginner/notebooks/04_nlp_sentiment_analysis.ipynb) [`04_nlp_sentiment_analysis.ipynb`](./notebooks/04_nlp_sentiment_analysis.ipynb) + --- ## 🚀 How to Run diff --git a/beginner/notebooks/04_nlp_sentiment_analysis.ipynb b/beginner/notebooks/04_nlp_sentiment_analysis.ipynb new file mode 100644 index 0000000..b92ca54 --- /dev/null +++ b/beginner/notebooks/04_nlp_sentiment_analysis.ipynb @@ -0,0 +1,217 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# \ud83d\udcdd Workshop 4: Natural Language Processing & Sentiment Analysis Starter\n", + "\n", + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/AIMLCLUBOCT/Workshops/blob/main/beginner/notebooks/04_nlp_sentiment_analysis.ipynb)\n", + "\n", + "**AI & Machine Learning Club \u2013 Oriental College of Technology, Bhopal**\n", + "\n", + "Welcome to this hands-on NLP tutorial! In this lab, you will learn:\n", + "1. Text preprocessing: cleaning, normalizing, and tokenizing natural language.\n", + "2. How **TF-IDF (Term Frequency-Inverse Document Frequency)** turns words into mathematical feature vectors.\n", + "3. Training a **Multinomial Naive Bayes** classifier for sentiment prediction.\n", + "4. Evaluating model performance using Confusion Matrix & Classification Report.\n", + "5. Live interactive inference on custom sentences." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1. Setup & Environment Dependencies\n", + "Let's import the necessary libraries (`scikit-learn`, `pandas`, `numpy`, and `matplotlib`)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import re\n", + "import numpy as np\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "from sklearn.model_selection import train_test_split\n", + "from sklearn.feature_extraction.text import TfidfVectorizer\n", + "from sklearn.naive_bayes import MultinomialNB\n", + "from sklearn.metrics import classification_report, confusion_matrix, ConfusionMatrixDisplay\n", + "\n", + "print('Libraries imported successfully!')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2. Load Sample Sentiment Dataset\n", + "We use a dataset containing balanced positive and negative reviews." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "data = [\n", + " # Positive reviews (1)\n", + " ('This AI workshop was extremely informative and well-structured!', 1),\n", + " ('The practical coding demonstrations helped clarify the core concepts.', 1),\n", + " ('Brilliant hands-on lab! I learned so much about neural networks.', 1),\n", + " ('The mentors were patient, encouraging, and answered all questions.', 1),\n", + " ('Loved the step-by-step approach to machine learning fundamentals.', 1),\n", + " ('The project starter code was clean, reproducible, and easy to run.', 1),\n", + " ('Great session! Really looking forward to the next hackathon.', 1),\n", + " ('Outstanding explanations of linear algebra and loss functions.', 1),\n", + " ('I highly recommend this club workshop to all engineering students.', 1),\n", + " ('Awesome experience building my first sentiment classification model!', 1),\n", + " # Negative reviews (0)\n", + " ('The pacing was way too fast and hard to follow for beginners.', 0),\n", + " ('Disappointed with the sound quality in the seminar hall.', 0),\n", + " ('Too much theory on slides and not enough interactive coding time.', 0),\n", + " ('The prerequisites were not clearly stated and I felt completely lost.', 0),\n", + " ('The example code had errors and failed to run on my local machine.', 0),\n", + " ('The workshop started late and ended before covering all topics.', 0),\n", + " ('Unhelpful explanations, it felt rushed and disorganized.', 0),\n", + " ('The presentation was boring and repetitive.', 0),\n", + " ('Could not get the environment setup properly in time.', 0),\n", + " ('Poor communication regarding the schedule and lab location.', 0)\n", + "]\n", + "\n", + "df = pd.DataFrame(data, columns=['review', 'sentiment'])\n", + "print(f'Total reviews: {len(df)}')\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3. Text Preprocessing Function\n", + "Raw text data contains punctuation, irregular casing, and noise. We normalize text into clean tokens." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def clean_text(text: str) -> str:\n", + " text = text.lower() # Lowercase\n", + " text = re.sub(r'[^a-zA-Z0-9\\s]', ' ', text) # Strip punctuation\n", + " text = re.sub(r'\\s+', ' ', text).strip() # Normalize whitespace\n", + " return text\n", + "\n", + "df['cleaned_review'] = df['review'].apply(clean_text)\n", + "df[['review', 'cleaned_review']].head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Train / Test Split & TF-IDF Vectorization\n", + "We separate training data (75%) from holdout test data (25%) and vectorize using unigrams and bigrams." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "X_train, X_test, y_train, y_test = train_test_split(\n", + " df['cleaned_review'], df['sentiment'], test_size=0.25, random_state=42, stratify=df['sentiment']\n", + ")\n", + "\n", + "vectorizer = TfidfVectorizer(ngram_range=(1, 2), stop_words='english')\n", + "X_train_vec = vectorizer.fit_transform(X_train)\n", + "X_test_vec = vectorizer.transform(X_test)\n", + "\n", + "print(f'Training shape: {X_train_vec.shape}')\n", + "print(f'Testing shape: {X_test_vec.shape}')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5. Model Training & Evaluation\n", + "We fit a **Multinomial Naive Bayes** classifier and evaluate precision, recall, and the confusion matrix." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model = MultinomialNB(alpha=1.0)\n", + "model.fit(X_train_vec, y_train)\n", + "\n", + "preds = model.predict(X_test_vec)\n", + "print('Classification Report:')\n", + "print(classification_report(y_test, preds, target_names=['Negative', 'Positive']))\n", + "\n", + "# Display Confusion Matrix\n", + "cm = confusion_matrix(y_test, preds)\n", + "disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=['Negative', 'Positive'])\n", + "disp.plot(cmap='Blues')\n", + "plt.title('Sentiment Confusion Matrix')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 6. Interactive Live Inference\n", + "Test the classifier with your own review sentences!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def predict_sentiment(text: str):\n", + " cleaned = clean_text(text)\n", + " vec = vectorizer.transform([cleaned])\n", + " pred = model.predict(vec)[0]\n", + " proba = model.predict_proba(vec)[0]\n", + " sentiment = '\ud83d\ude0a Positive' if pred == 1 else '\ud83d\ude1e Negative'\n", + " confidence = proba[pred] * 100\n", + " print(f'Input: \"{text}\"')\n", + " print(f'Sentiment: {sentiment} (Confidence: {confidence:.2f}%)\\n')\n", + "\n", + "predict_sentiment('The instructors explained every concept with fantastic examples!')\n", + "predict_sentiment('I had a terrible experience with the code errors and confusing slides.')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 7. \ud83d\ude80 Student Extension Challenge\n", + "Now it's your turn to experiment!\n", + "- **Challenge 1:** Try comparing `MultinomialNB` with `LogisticRegression` or `LinearSVC`.\n", + "- **Challenge 2:** Inspect `vectorizer.get_feature_names_out()` and discover which words have the strongest TF-IDF weights.\n", + "- **Challenge 3:** Connect this classifier to a simple Streamlit interface in `beginner/`!" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file