Binary sentiment classification using NLP and Machine Learning.
This project implements a complete machine learning pipeline for binary sentiment classification of movie reviews. The solution includes both Data Science (DS) and Machine Learning Engineering (MLE) components, with full Docker support for reproducibility.
Dataset: 50,000 movie reviews (25,000 positive, 25,000 negative)
Task: Binary classification (positive/negative sentiment)
Target Metric: Accuracy >= 0.85
Key Findings:
- Dataset is perfectly balanced (50% positive, 50% negative)
- No missing values in the dataset
- Review lengths vary significantly (10-2500 words)
- Common positive words: "great", "excellent", "amazing", "wonderful"
- Common negative words: "bad", "terrible", "worst", "awful"
Notebook: notebooks/eda-modelling.ipynb
I compared multiple preprocessing and vectorization approaches:
- Stemming: Reduces words to their root form (e.g., "running" → "run")
- Lemmatization: Reduces words to their dictionary form (e.g., "better" → "good")
Result: Lemmatization performed better, preserving semantic meaning while reducing dimensionality.
- TF-IDF: Term Frequency-Inverse Document Frequency
- Count Vectorization: Simple word count vectors
Result: TF-IDF performed better, effectively weighting important words while reducing the impact of common terms.
Final Configuration: Lemmatization + TF-IDF with bigrams (1,2)
I trained and compared 5 different models:
| Model | Accuracy | Precision | Recall | F1-Score |
|---|---|---|---|---|
| Logistic Regression | 0.8878 | 0.8881 | 0.8878 | 0.8877 |
| Linear SVC | 0.8804 | 0.8805 | 0.8804 | 0.8804 |
| Multinomial Naive Bayes | 0.8606 | 0.8612 | 0.8606 | 0.8606 |
| Random Forest | 0.8430 | 0.8430 | 0.8430 | 0.8430 |
| Gradient Boosting | 0.8094 | 0.8138 | 0.8094 | 0.8087 |
Selected Model: Logistic Regression (max_iter=1000)
Training Configuration:
- Training samples: 32,000
- Test samples: 8,000
- Features: 5,000 (TF-IDF with lemmatization)
- Training time: ~0.07 seconds
- Total pipeline time: ~42 seconds
Model Performance:
| Metric | Score |
|---|---|
| Accuracy | 0.8878 |
| Precision | 0.8881 |
| Recall | 0.8878 |
| F1-Score | 0.8877 |
Per-Class Performance:
| Class | Precision | Recall | F1-Score | Support |
|---|---|---|---|---|
| Negative | 0.90 | 0.87 | 0.89 | 4,000 |
| Positive | 0.88 | 0.90 | 0.89 | 4,000 |
Reasoning:
- Highest accuracy (0.8878) among all models, exceeding the 0.85 requirement ✓
- Fast training and inference (0.07s training time)
- Good interpretability (feature weights)
- Balanced performance across both classes
- Lower computational requirements compared to ensemble methods
Test Set Results (8,000 samples):
- Accuracy: 0.8878 (88.78%)
- Precision: 0.8881 (88.81%)
- Recall: 0.8878 (88.78%)
- F1-Score: 0.8877 (88.77%)
Confusion Matrix Analysis:
- True Negatives: 3,480 (87% of negative samples correctly identified)
- True Positives: 3,620 (90% of positive samples correctly identified)
- False Positives: 520 (13% of negatives misclassified as positive)
- False Negatives: 380 (10% of positives misclassified as negative)
The model performs well on both classes with slightly better recall on positive sentiments.
Potential Applications:
- Movie Review Aggregation: Automatically classify user reviews on platforms like IMDb, Rotten Tomatoes
- Customer Feedback Analysis: Analyze product reviews for e-commerce platforms
- Social Media Monitoring: Track brand sentiment on Twitter, Facebook, etc.
- Content Moderation: Flag negative content for manual review
- Market Research: Understand customer sentiment towards products/services
Business Value:
- Cost Reduction: Automate manual review classification (saves 100+ hours/month)
- Real-time Insights: Instant sentiment analysis vs. manual processing
- Scalability: Process millions of reviews automatically
- Data-Driven Decisions: Quantify customer sentiment for product improvements
ROI Estimate: For a platform with 10,000 reviews/day, automation saves ~$50,000/year in manual labor costs while providing instant insights.
Follow this step-by-step roadmap to run the complete sentiment analysis pipeline.
- Python 3.9+ (for local execution)
- Docker (for containerized execution)
- Conda (recommended for environment management)
# Create and activate conda environment
conda create -n myenv python=3.9
conda activate myenv
# Install dependencies
pip install -r requirements.txt
# Download NLTK data
python -c "import nltk; nltk.download('punkt'); nltk.download('punkt_tab'); nltk.download('stopwords'); nltk.download('wordnet'); nltk.download('omw-1.4')"# Create virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
# Download NLTK data
python -c "import nltk; nltk.download('punkt'); nltk.download('punkt_tab'); nltk.download('stopwords'); nltk.download('wordnet'); nltk.download('omw-1.4')"Ensure your data is in the correct location:
data/
├── raw/
│ ├── train.csv # Training data (required)
│ └── inference.csv # Inference data (required)
Data Format: CSV files with columns text and sentiment (0=negative, 1=positive)
# Basic training
python src/train/train.py
# With custom parameters
python src/train/train.py \
--preprocessing lemmatization \
--vectorization tfidf \
--model logistic \
--test-size 0.2# Build and run training container
docker compose --profile training up --buildExpected Output:
- Model saved to
outputs/models/best_model.pkl - Vectorizer saved to
outputs/models/vectorizer.pkl - Preprocessor saved to
outputs/models/preprocessor.pkl - Metrics saved to
outputs/predictions/ - Confusion matrix saved to
outputs/figures/ - Processed data saved to
data/processed/
Training Time: ~1-2 minutes (depending on hardware)
# Basic inference
python src/inference/run_inference.py
# With custom parameters
python src/inference/run_inference.py \
--model-dir outputs/models \
--data-file data/raw/inference.csv \
--output-dir outputs# Build and run inference container
docker compose --profile inference up --buildExpected Output:
- Predictions saved to
outputs/predictions/predictions.csv - Inference metrics saved to
outputs/predictions/inference_metrics.json - Processed inference data saved to
data/processed/
Inference Time: ~30 seconds for 10,000 samples
# View training metrics
cat outputs/predictions/test_metrics.txt
# View inference metrics
cat outputs/predictions/inference_metrics.txt# View first 10 predictions with confidence levels
head -n 11 outputs/predictions/predictions.csv# Open confusion matrix visualization
open outputs/figures/confusion_matrix.png # macOS
xdg-open outputs/figures/confusion_matrix.png # Linuxfinal-project/
├── data/ # Git ignored
│ ├── raw/
│ │ ├── train.csv # Training data
│ │ └── inference.csv # Inference data
│ └── processed/ # Preprocessed data (auto-generated)
├── notebooks/
│ ├── eda.ipynb # Exploratory data analysis
│ └── modeling.ipynb # Feature engineering & modeling
├── src/
│ ├── __init__.py
│ ├── data_loader.py # Data loading utilities
│ ├── preprocessing.py # Text preprocessing with humanization
│ ├── feature_engineering.py # Feature engineering experiments
│ ├── utils.py # Evaluation & visualization
│ ├── train/
│ │ ├── train.py # Training script
│ │ └── Dockerfile # Training Docker config
│ └── inference/
│ ├── run_inference.py # Inference script
│ └── Dockerfile # Inference Docker config
├── outputs/ # Git ignored (auto-generated)
│ ├── models/ # Trained models
│ ├── predictions/ # Predictions and metrics
│ └── figures/ # Visualizations
├── requirements.txt # Python dependencies
├── docker-compose.yml # Docker orchestration
├── .gitignore
└── README.md
- Model: Logistic Regression
- Training Samples: 32,000
- Test Samples: 8,000
- Features: 5,000 (TF-IDF + Lemmatization)
- Accuracy: 88.78%
- Precision: 88.81%
- Recall: 88.78%
- F1-Score: 88.77%
- Training Time: 0.07 seconds
- Total Pipeline Time: 42 seconds
- Negative Class: Precision 90%, Recall 87%, F1 89%
- Positive Class: Precision 88%, Recall 90%, F1 89%
- Expected Accuracy: ~88%
- Expected Average Confidence: ~90%
# Re-download NLTK data
python -c "import nltk; nltk.download('punkt'); nltk.download('punkt_tab'); nltk.download('stopwords'); nltk.download('wordnet'); nltk.download('omw-1.4')"# Ensure you're in the correct environment
conda activate myenv # or source venv/bin/activate
# Reinstall dependencies
pip install -r requirements.txt# Clean Docker cache and rebuild
docker system prune -a
docker compose --profile training build --no-cache# Reduce batch size or use smaller dataset
# Or increase Docker memory limit in Docker Desktop settingsTo verify the project runs completely from scratch:
# 1. Clean all outputs
rm -rf outputs data/processed & rm -rf outputs data/raw
mkdir -p outputs data/raw & mkdir data/processed
# 2. Run training
conda run -n myenv python src/train/train.py
# 3. Run inference
conda run -n myenv python src/inference/run_inference.py
# 4. Verify outputs
ls -la outputs/models/
ls -la outputs/predictions/
ls -la data/processed/