Capstone Project 2: Real-Time NLP Sentiment Analyzer
Build a real-time NLP Sentiment Analysis project. Learn to process text reviews, vectorize inputs with TF-IDF, train a classifier, and serve it.
Introduction
In Module 8, we explored how text is preprocessed and vectorized. In this second capstone project, we will apply these techniques to build a Real-Time NLP Sentiment Analyzer.
You will compile a pipeline that takes raw customer reviews (e.g., product feedback or app ratings), preprocesses the strings (removing punctuation, applying lowercase), vectorizes them using a TF-IDF template, trains a Logistic Regression classifier to categorize sentiments (Positive vs. Negative), and exposes the pipeline as a live web server endpoint.
What You Will Learn
- Processing text streams for model training.
- Combining a TF-IDF Vectorizer and a Classifier into a single serializable Pipeline.
- Handling string payloads in web APIs.
- Serving sentiment scores with positive/negative probabilities.
Project Structure
Set up your repository folders:
sentiment-analyzer/
│
├── models/
│ └── sentiment_pipeline.joblib # Contains vectorizer + model weights
├── src/
│ ├── train.py
│ └── app.py
├── requirements.txt
└── Dockerfile
Step 1: Pipeline Training (src/train.py)
Instead of saving the vectorizer and the model as two separate files, Scikit-Learn provides a Pipeline class. A Pipeline binds the vectorization stage (TfidfVectorizer) and the classification stage (LogisticRegression) together. When you call joblib.dump(), the entire pipeline is saved in a single file, guaranteeing that inputs are vectorized identically during inference.
# src/train.py
import os
import joblib
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
def train_sentiment_model():
print("Step 1: Preparing training text samples...")
# Synthetic corpus of product reviews
reviews = [
"This product is amazing! I love the quality.",
"Absolutely garbage. Broke on the first day.",
"Highly recommended. Extremely fast shipping.",
"Terrible customer service. Not worth the money.",
"Pretty good purchase, met my expectations.",
"Worst experience ever. Do not buy this.",
"Great value for money, fits perfectly.",
"Disappointed with the size and color.",
"Fabulous design, works perfectly.",
"Defective item, returning it immediately."
]
# 1 represents Positive, 0 represents Negative
labels = [1, 0, 1, 0, 1, 0, 1, 0, 1, 0]
# 2. Build Pipeline
# TfidfVectorizer filters out standard English stop words
pipeline = Pipeline([
('vectorizer', TfidfVectorizer(stop_words='english')),
('classifier', LogisticRegression())
])
# 3. Train
print("Step 2: Training Pipeline...")
pipeline.fit(reviews, labels)
# 4. Evaluate (on same training data due to small sample size)
preds = pipeline.predict(reviews)
print("\nTraining Metrics Summary:")
print(classification_report(labels, preds, target_names=["Negative", "Positive"]))
# 5. Serialize Pipeline
os.makedirs("models", exist_ok=True)
joblib.dump(pipeline, "models/sentiment_pipeline.joblib")
print("Pipeline successfully saved to: models/sentiment_pipeline.joblib")
if __name__ == "__main__":
train_sentiment_model()
Step 2: FastAPI Sentiment Server (src/app.py)
We write the FastAPI server that loads the pipeline, receives raw text strings, and returns predictions with probability scores.
# src/app.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
import joblib
import os
app = FastAPI(title="Real-Time Sentiment Analyzer API", version="1.0")
# Load pipeline
pipeline_path = "models/sentiment_pipeline.joblib"
if os.path.exists(pipeline_path):
pipeline = joblib.load(pipeline_path)
else:
# Fallback mock pipeline
class MockPipeline:
def predict(self, texts): return [1]
def predict_proba(self, texts): return [[0.1, 0.9]]
pipeline = MockPipeline()
class TextInput(BaseModel):
text: str = Field(..., min_length=3, max_length=1000, description="Customer review text")
@app.post("/analyze")
def analyze_sentiment(payload: TextInput):
try:
# Pass raw text list directly to the loaded pipeline
prediction = pipeline.predict([payload.text])[0]
probabilities = pipeline.predict_proba([payload.text])[0]
sentiment = "Positive" if prediction == 1 else "Negative"
confidence = float(probabilities[1] if prediction == 1 else probabilities[0])
return {
"status": "success",
"review": payload.text,
"sentiment": sentiment,
"confidence_score": round(confidence, 4),
"probabilities": {
"positive": round(float(probabilities[1]), 4),
"negative": round(float(probabilities[0]), 4)
}
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
Execution & Verification
To verify your sentiment analysis project:
- Install requirements:
Ensure
fastapi,pydantic,uvicorn,scikit-learn, andjoblibare installed. - Train the Pipeline:
python src/train.py - Start the API Web Server:
uvicorn src.app:app --reload --port 8000 - Test with Curl (Negative Sentiment Example):
Expected Response:curl -X POST "http://localhost:8000/analyze" \ -H "Content-Type: application/json" \ -d '{"text": "Broke on day one, terrible service!"}'{ "status": "success", "review": "Broke on day one, terrible service!", "sentiment": "Negative", "confidence_score": 0.6542, "probabilities": { "positive": 0.3458, "negative": 0.6542 } }
Summary
In this second capstone project, we combined text vectorization and classification into a unified, serializable Scikit-Learn Pipeline. By exporting this configuration to disk, we deployed a FastAPI server that automatically transforms raw input sentences, queries the Logistic Regression boundaries, and exposes sentiment categories alongside probability scores.
Next Topic
How do we build a project that processes unstructured image binary uploads using deep neural networks and PyTorch? Let's check: Capstone Project 3: Image Classifier using Transfer Learning.