Step-by-step builds

Sentiment Analysis Project in Python: From TF-IDF Baseline to Transformers

Satvik ParamkushamUpdated October 5, 202611 min read

Quick answer

A sentiment analysis project trains a model to label text as positive or negative. The standard build loads the IMDB movie review dataset, trains a TF-IDF plus Logistic Regression baseline, measures accuracy and F1, then compares it with a pretrained transformer from Hugging Face. A small Streamlit app lets anyone type a review and see both predictions.

Sentiment analysis is the classic first NLP project, and it is still one of the best. The dataset is free and clean, the task is easy to explain in one line, and you can go from a simple machine learning baseline to a modern transformer in the same weekend.

What separates a strong sentiment analysis project from the hundred copies on GitHub is not the model. It is the comparison. This guide builds two models on the same data, measures both with the same metrics, looks at where each one fails, and wraps them in a demo app. That structure is exactly what a project report, a viva panel or an interviewer wants to see.

All code runs on a normal laptop. The transformer step is faster with a GPU, and the free tier of Google Colab is enough if your laptop struggles.

What is a sentiment analysis project?

A sentiment analysis project builds a model that reads a piece of text and predicts the opinion it expresses, usually positive or negative, sometimes with a neutral class. Companies use it to track product reviews, support tickets, app store ratings and social media mentions at a scale no human team could read.

It is a text classification problem. You feed the model labelled examples (this review is positive, this one is negative), it learns which patterns of words go with which label, and then it predicts labels for text it has never seen.

The two approaches you will compare:

  • Classical machine learning: convert text to TF-IDF word weights, then train Logistic Regression. Fast, cheap, explainable.
  • Pretrained transformer: a model such as DistilBERT that already understands language from pretraining and was fine-tuned for sentiment. Better with context and negation, slower and heavier.

What do you need for a sentiment analysis project in Python?

You need Python basics, about 4 GB of free disk space for the libraries and model download, and no GPU for the baseline.

PartWhat we useWhy
DataIMDB reviews via Hugging Face datasets (stanfordnlp/imdb)50,000 labelled reviews, one line to load
Baselinescikit-learn: TfidfVectorizer plus LogisticRegressionTrains in a minute or two on a laptop
Transformertransformers pipeline with a DistilBERT sentiment modelPretrained, no training needed
Metricsaccuracy, F1, confusion matrix from scikit-learnStandard numbers for your report
DemoStreamlitA web app in about 20 lines

If NLP terms are new, read about tokens and the transformer model before Step 6. They explain why the transformer reads text differently from TF-IDF.

How does a sentiment analysis model work? (workflow)

Every sentiment analysis project follows the same five-stage workflow:

  1. Load labelled data: text plus a label for each example.
  2. Clean the text: remove HTML tags and other noise that carries no opinion.
  3. Turn text into numbers: TF-IDF counts weighted words; a transformer turns tokens into contextual vectors.
  4. Train and predict: the classifier learns from the training split and predicts on the test split.
  5. Evaluate: accuracy, precision, recall and F1 on data the model never saw, plus a look at the mistakes.

The IMDB dataset comes pre-split into 25,000 training and 25,000 test reviews, evenly balanced between positive and negative. Balanced classes mean accuracy is a fair headline number here, but you should still report F1, because real-world review data is rarely balanced.

How to build a sentiment analysis project in Python, step by step

  1. Step 1. Set up the environment

    Create a project folder and virtual environment, then install the libraries.

    mkdir sentiment-project && cd sentiment-project
    python -m venv .venv
    # Windows: .venv\Scripts\activate    macOS/Linux: source .venv/bin/activate
    pip install datasets scikit-learn transformers torch joblib streamlit

    On Google Colab, torch, transformers and scikit-learn are usually preinstalled; pip install anything that is missing, then continue from Step 2 in a notebook.

  2. Step 2. Load the IMDB dataset from Hugging Face

    The Hugging Face datasets library downloads and caches the data on first use.

    # data.py
    import re
    from datasets import load_dataset
    
    def clean(text):
        text = re.sub(r"<br\s*/?>", " ", text)  # IMDB reviews contain <br /> tags
        return re.sub(r"\s+", " ", text).strip()
    
    def load_imdb():
        imdb = load_dataset("stanfordnlp/imdb")
        train, test = imdb["train"], imdb["test"]
        X_train = [clean(t) for t in train["text"]]
        X_test = [clean(t) for t in test["text"]]
        return X_train, train["label"], X_test, test["label"]
    
    if __name__ == "__main__":
        X_train, y_train, X_test, y_test = load_imdb()
        print(len(X_train), "train /", len(X_test), "test")
        print(X_train[0][:300], "| label:", y_train[0])  # 0 = negative, 1 = positive

    Always print a few raw examples before training. You will spot problems like HTML tags, very long reviews or label mix-ups in seconds.

  3. Step 3. Train the TF-IDF and Logistic Regression baseline

    The baseline is your reference point. Every fancier model has to beat it to be worth its cost.

    # baseline.py
    import joblib
    from sklearn.pipeline import make_pipeline
    from sklearn.feature_extraction.text import TfidfVectorizer
    from sklearn.linear_model import LogisticRegression
    from sklearn.metrics import accuracy_score, f1_score, classification_report
    from data import load_imdb
    
    X_train, y_train, X_test, y_test = load_imdb()
    
    model = make_pipeline(
        TfidfVectorizer(ngram_range=(1, 2), min_df=2, max_features=100_000, sublinear_tf=True),
        LogisticRegression(max_iter=1000),
    )
    model.fit(X_train, y_train)
    pred = model.predict(X_test)
    
    print("Accuracy:", round(accuracy_score(y_test, pred), 4))
    print("F1:", round(f1_score(y_test, pred), 4))
    print(classification_report(y_test, pred, target_names=["negative", "positive"]))
    joblib.dump(model, "baseline.joblib")

    Using word pairs (ngram_range=(1, 2)) lets the model learn phrases like "not good" and "waste of", which single words miss. Write down both numbers; they go in your comparison table.

  4. Step 4. Look inside the baseline

    A big advantage of Logistic Regression is that you can see what it learned. Each word or phrase gets a weight: large positive weights push towards positive, large negative weights towards negative.

    # inspect_baseline.py
    import joblib
    
    model = joblib.load("baseline.joblib")
    tfidf, clf = model.named_steps["tfidfvectorizer"], model.named_steps["logisticregression"]
    words = tfidf.get_feature_names_out()
    weights = clf.coef_[0]
    
    order = weights.argsort()
    print("Most negative:", [words[i] for i in order[:15]])
    print("Most positive:", [words[i] for i in order[-15:][::-1]])

    Put these two lists in your report. They make the model explainable in a way a transformer is not, and examiners almost always ask "how does your model decide?"

  5. Step 5. Run a pretrained transformer with the pipeline API

    The transformers pipeline loads a model already fine-tuned for sentiment, so there is no training step. We run it on a 1,000-review sample, because running all 25,000 on a CPU takes a long time.

    # transformer_eval.py
    import random
    from transformers import pipeline
    from sklearn.metrics import accuracy_score, f1_score
    from data import load_imdb
    
    _, _, X_test, y_test = load_imdb()
    random.seed(42)
    idx = random.sample(range(len(X_test)), 1000)
    texts = [X_test[i] for i in idx]
    labels = [y_test[i] for i in idx]
    
    clf = pipeline("sentiment-analysis",
                   model="distilbert/distilbert-base-uncased-finetuned-sst-2-english")
    out = clf(texts, truncation=True, batch_size=16)
    pred = [1 if o["label"] == "POSITIVE" else 0 for o in out]
    
    print("Transformer accuracy:", round(accuracy_score(labels, pred), 4))
    print("Transformer F1:", round(f1_score(labels, pred), 4))

    truncation=True cuts reviews longer than the model's 512-token limit instead of crashing. For a fair comparison, also score the baseline on the same 1,000 reviews with joblib.load("baseline.joblib").predict(texts).

  6. Step 6. Compare the models and study the mistakes

    Build one table for your report with both models on the same 1,000 reviews:

    ModelAccuracyF1Time for 1,000 reviewsSize
    TF-IDF + Logistic Regressionyour numberyour numberyour numbera few MB
    DistilBERT (SST-2)your numberyour numberyour numberabout 270 MB

    Then read 20 reviews each model got wrong. You will usually find patterns: sarcasm, reviews that praise the actors but hate the plot, negation far from the opinion word, or very long reviews where truncation cut the verdict. Writing two paragraphs about these error patterns shows more understanding than a one-point accuracy gain.

    Note that the DistilBERT model was fine-tuned on short movie review sentences (SST-2), not on IMDB itself. That mismatch is a good discussion point, and fine-tuning it on IMDB training data is a natural upgrade. See what fine-tuning is before you try it.

  7. Step 7. Build a Streamlit demo

    A live demo where the examiner types their own review is far more convincing than screenshots.

    # app.py
    import joblib
    import streamlit as st
    from transformers import pipeline
    
    @st.cache_resource
    def load_models():
        baseline = joblib.load("baseline.joblib")
        bert = pipeline("sentiment-analysis",
                        model="distilbert/distilbert-base-uncased-finetuned-sst-2-english")
        return baseline, bert
    
    baseline, bert = load_models()
    st.title("Sentiment Analyser")
    text = st.text_area("Type a review", "The phone looks great but the battery dies by lunch.")
    
    if st.button("Analyse") and text.strip():
        p = baseline.predict_proba([text])[0][1]
        b = bert(text, truncation=True)[0]
        col1, col2 = st.columns(2)
        col1.metric("TF-IDF + LR", "Positive" if p >= 0.5 else "Negative", f"{p:.2f} positive")
        col2.metric("DistilBERT", b["label"].title(), f"{b['score']:.2f} confidence")

    Run it with streamlit run app.py. Mixed reviews like the default example are where the two models disagree, which makes for a good live demonstration.

What are good sentiment analysis project ideas for Indian students?

The same pipeline works on any labelled text. Ideas that feel more local and more original than IMDB:

  • E-commerce product reviews: Kaggle hosts several Flipkart and Amazon India review datasets. Map star ratings to labels (4 to 5 positive, 1 to 2 negative) and drop 3-star reviews or keep them as neutral.
  • Hinglish tweets: the SemEval-2020 Task 9 (SentiMix) shared task released Hindi-English code-mixed tweets labelled positive, negative or neutral. Comparing a TF-IDF model trained on that data with an English-only transformer that never saw romanised Hindi makes a strong experiment.
  • Multilingual sentiment: try a multilingual model such as cardiffnlp/twitter-xlm-roberta-base-sentiment, whose model card lists Hindi among its languages.
  • App store reviews: collect reviews of Indian apps you use and track sentiment across versions.
  • Aspect-based sentiment: go beyond one label per review and detect opinions about battery, camera and price separately. A good final-year upgrade.

Other upgrades: fine-tune DistilBERT on your dataset, add a neutral class, or plot sentiment over time for a brand. For more NLP ideas at final-year depth, see our NLP final year projects.

How do you explain a sentiment analysis project in a viva?

Open with one sentence: "I compared a TF-IDF and Logistic Regression baseline with a pretrained DistilBERT model for movie review sentiment, measured accuracy and F1 on the same test set, and analysed where each one fails."

Expect these follow-ups:

  • What is TF-IDF, and why use word pairs?
  • Why report F1 and not just accuracy?
  • Why might the transformer handle "not bad at all" better than TF-IDF?
  • What does truncation do, and when does it hurt?
  • Is your model biased? (Discuss how training data from one domain, like movies, transfers poorly to another, like product reviews.)
  • How would you detect sarcasm?

Our machine learning interview questions cover precision, recall, F1 and the confusion matrix with model answers.

Common mistakes in sentiment analysis projects

These mistakes appear in a large share of student submissions:

  1. Fitting TF-IDF on the full dataset. Fit the vectorizer only on training data. The pipeline in Step 3 does this for you.
  2. Testing on training data. Any score from data the model trained on is meaningless.
  3. Comparing models on different test sets. The baseline on 25,000 reviews and the transformer on 1,000 is not a fair fight.
  4. Ignoring the domain gap. A movie-review model will misread phrases like "the battery is unpredictable" in product reviews.
  5. Reporting accuracy alone on imbalanced data. If 90 percent of your reviews are positive, a model that always says positive scores 90 percent.

Avoid these and your results section will hold up to questioning.

Frequently asked questions

Which dataset is best for a sentiment analysis project?

For a first project, the IMDB movie review dataset (stanfordnlp/imdb on Hugging Face) is the standard choice: 50,000 balanced, labelled reviews. For a more original project, use Indian product review datasets from Kaggle or the SemEval-2020 Hinglish tweet data, and compare results with IMDB.

Is sentiment analysis a good final-year project?

A basic positive or negative classifier is better suited to a mini project. For final year, add depth: compare classical and transformer models, fine-tune on Indian data, handle Hinglish, or do aspect-based sentiment. Measured results and error analysis matter more than the model name.

Which algorithm is best for sentiment analysis in Python?

Logistic Regression or Linear SVM on TF-IDF features is the strongest simple baseline: fast, explainable and accurate on clean data. Pretrained transformers such as DistilBERT or RoBERTa usually handle context and negation better. The best choice depends on your data, speed needs and whether you must explain decisions.

Can I do a sentiment analysis project without deep learning?

Yes. The TF-IDF and Logistic Regression model in this guide is classical machine learning and trains on any laptop in a few minutes. Many real systems still use it because it is fast and explainable. Adding a transformer comparison is optional, but it strengthens the project.

What should a sentiment analysis project report include?

Include the problem statement, dataset description with class balance, preprocessing steps, the models compared, a results table with accuracy and F1 on the same test set, a confusion matrix, an error analysis of misclassified examples, limitations such as sarcasm and domain gap, and future work.

References