Sentiment Classification with Word Embeddings and RNNs

Sentiment classification determines the attitude expressed in a piece of text. A model may predict whether a review is positive or negative, or assign a more detailed rating such as one to five stars.

Word embeddings are particularly helpful when the labeled sentiment dataset is limited. They transfer linguistic structure learned from much larger collections of text into the classifier.

What Is Sentiment Classification?

Given text \(x\), the model predicts a sentiment label \(y\):\[ x\longrightarrow y. \]

For example:

ReviewPossible rating
“The dessert is excellent.”4 or 5 stars
“Service was quite slow.”2 stars
“Good for a quick meal, but nothing special.”3 stars
“Completely lacking in good taste, service, and ambiance.”1 star

The precise label depends on the annotation scheme. Common formulations include:

  • Positive versus negative
  • Negative, neutral, or positive
  • One-to-five-star classification
  • A continuous sentiment score
  • Several emotion categories
  • Aspect-specific sentiment

Practical Uses

Sentiment models can help analyze:

  • Product reviews
  • Customer-support messages
  • Survey responses
  • Restaurant reviews
  • Social-media posts
  • Employee feedback
  • Public comments
  • Brand mentions

Aggregated predictions can help identify trends, but individual outputs should be interpreted carefully because language can contain sarcasm, ambiguity, cultural references, and mixed opinions.

Why Labeled Data Can Be a Bottleneck

A high-quality sentiment dataset requires text paired with reliable labels. Producing those labels may require:

  • Manual annotation
  • Clear rating criteria
  • Agreement between annotators
  • Domain expertise
  • Quality control

Unlabeled text is generally much more abundant. Word embeddings can be learned from that larger source and transferred into a sentiment model.

This is useful when the target dataset is too small to learn strong token representations from scratch.

Representing Review Tokens

Suppose a tokenized review is\[ w_1,w_2,\ldots,w_T. \]

Each token is mapped to an embedding:\[ e_t=E[w_t], \]

where\[ E\in\mathbb{R}^{V\times d} \]

is the embedding matrix, \(V\) is the vocabulary size, and \(d\) is the embedding dimension.

The resulting review representation begins as a sequence:\[ e_1,e_2,\ldots,e_T. \]

A sentiment classifier must combine these vectors into a form suitable for predicting one label.

A Simple Baseline: Average the Embeddings

The simplest model averages all token embeddings:\[ r = \frac{1}{T} \sum_{t=1}^{T}e_t. \]

The vector\[ r\in\mathbb{R}^{d} \]

has the same dimension regardless of review length.

A classifier then computes\[ z=Wr+b, \]

followed by a softmax:\[ \hat{y} = \operatorname{softmax}(z). \]

For five-star prediction,\[ \hat{y} = \begin{pmatrix} P(y=1\mid x)\\ P(y=2\mid x)\\ P(y=3\mid x)\\ P(y=4\mid x)\\ P(y=5\mid x) \end{pmatrix}. \]

The predicted class is\[ \hat{c} = \arg\max_k \hat{y}_k. \]

Sum Versus Average

The embeddings can be summed:\[ r_{\text{sum}} = \sum_{t=1}^{T}e_t, \]

or averaged:\[ r_{\text{avg}} = \frac{1}{T} \sum_{t=1}^{T}e_t. \]

A sum retains a signal related to sequence length, while an average normalizes for length.

MethodAdvantageLimitation
SumRetains magnitude related to token countLong reviews can have much larger norms
AverageMore comparable across lengthsRemoves explicit length information

Length can also be supplied separately as an additional feature.

Why the Averaging Model Can Work

The average embedding provides a rough summary of the words appearing in the review.

Positive reviews may contain representations associated with words such as:

  • excellent
  • delicious
  • friendly
  • wonderful
  • enjoyable

Negative reviews may contain:

  • terrible
  • slow
  • disappointing
  • rude
  • bland

If the embeddings capture relationships between these words, a linear classifier can learn a useful decision boundary in the averaged space.

This model is:

  • Fast
  • Compact
  • Easy to train
  • Compatible with variable-length text
  • A valuable baseline for smaller datasets

Masking Padding Tokens

Reviews in a batch are often padded to the same stored length. Padding must not contribute to the average.

Let\[ m_t = \begin{cases} 1, & \text{if position }t\text{ contains a real token},\\ 0, & \text{if it contains padding}. \end{cases} \]

Then the masked average is\[ r = \frac{ \sum_{t=1}^{T}m_te_t }{ \sum_{t=1}^{T}m_t }. \]

A numerical safeguard is needed in case an example contains no valid tokens.

The Main Limitation: Word Order Is Lost

Averaging treats the review as an unordered collection of words.

For any permutation \(\pi\),\[ \frac{1}{T}\sum_{t=1}^{T}e_t = \frac{1}{T}\sum_{t=1}^{T}e_{\pi(t)}. \]

Therefore, the model cannot distinguish sequences that contain the same tokens in different orders.

This is a major problem because sentiment often depends on composition.

Compare:

  • “The service was good.”
  • “The service was not good.”

The word “good” appears in both, but negation reverses the sentiment.

A Failure Case for Averaging

Consider:

“Completely lacking in good taste, good service, and good ambiance.”

The word “good” appears several times. An averaging model may give those positive embeddings substantial influence and miss the fact that “lacking in” changes the meaning of the entire phrase.

Other difficult patterns include:

  • “Not worth the price”
  • “Hardly impressive”
  • “I expected it to be good”
  • “It was good until the ending”
  • “The food was great, but the service was awful”
  • “Great—another two-hour delay”

These examples require sensitivity to syntax, contrast, scope, or sarcasm.

A Many-to-One RNN Classifier

An RNN preserves sequential structure by processing embeddings in order:\[ h_t = f(h_{t-1},e_t). \]

After the final token, the hidden state represents the sequence:\[ r=h_T. \]

The sentiment prediction is\[ \hat{y} = \operatorname{softmax}(Wh_T+b). \]

This is a many-to-one architecture:\[ (e_1,e_2,\ldots,e_T) \longrightarrow \hat{y}. \]

The model receives many token inputs and produces one sentiment label.

Why an RNN Handles Composition Better

The recurrent state depends on both the current word and the preceding sequence:\[ h_t=f(h_{t-1},e_t). \]

When the model reads “not,” it can update its state so that the later word “good” is interpreted differently from an isolated positive word.

Similarly, after reading “lacking in,” the model can learn that subsequent positive nouns or adjectives occur under a negative construction.

An RNN does not guarantee perfect understanding of negation or sarcasm, but it has representational access to word order that an average does not.

Using GRUs or LSTMs

A basic RNN may struggle with long-range dependencies. A GRU or LSTM is generally a stronger recurrent choice.

For a GRU:\[ h_t = \operatorname{GRU}(e_t,h_{t-1}). \]

For an LSTM:\[ (h_t,c_t) = \operatorname{LSTM} (e_t,h_{t-1},c_{t-1}). \]

The final representation may be\[ r=h_T. \]

Gated recurrent cells can better preserve information across longer reviews, although they still compress the sequence into a fixed-size final state.

Bidirectional Sequence Encoding

When the complete review is available, a bidirectional encoder can use both preceding and following context.

The forward state is\[ \overrightarrow{h}_t = f_{\rightarrow} (\overrightarrow{h}_{t-1},e_t), \]

and the backward state is\[ \overleftarrow{h}_t = f_{\leftarrow} (\overleftarrow{h}_{t+1},e_t). \]

A review-level representation can combine the terminal states:\[ r = [ \overrightarrow{h}_T; \overleftarrow{h}_1 ]. \]

Alternatively, all contextual states can be pooled.

Bidirectionality is appropriate for offline review classification because the complete text is known before the prediction is made.

Pooling Across Recurrent States

Using only the final hidden state may create a bottleneck for long reviews. Instead, define contextual states\[ h_1,h_2,\ldots,h_T \]

and pool them.

Mean pooling

\[ r = \frac{1}{T} \sum_{t=1}^{T}h_t. \]

Max pooling

For each feature dimension \(j\),\[ r_j = \max_t h_{t,j}. \]

Attention pooling

Learn a score for each position:\[ s_t = v^\top\tanh(Wh_t+b), \]

then normalize:\[ \alpha_t = \frac{\exp(s_t)} {\sum_j\exp(s_j)}. \]

The review representation becomes\[ r = \sum_{t=1}^{T}\alpha_th_t. \]

Attention pooling allows the classifier to emphasize especially informative phrases.

Training the Classifier

For \(K\) sentiment classes, let the target be a one-hot vector \(y\), and let the prediction be \(\hat{y}\).

The cross-entropy loss is\[ \mathcal{L} = -\sum_{k=1}^{K} y_k\log\hat{y}_k. \]

For a batch of \(N\) examples,\[ \mathcal{L}_{\text{batch}} = \frac{1}{N} \sum_{i=1}^{N} \mathcal{L}^{(i)}. \]

Backpropagation updates:

  • The output classifier
  • The recurrent encoder
  • Optionally, the embedding matrix

Frozen Versus Trainable Embeddings

Pretrained embeddings can be frozen or fine-tuned.

Frozen embeddings

The embedding matrix remains fixed:\[ E=E_{\text{pretrained}}. \]

This is useful when:

  • The sentiment dataset is small
  • Overfitting is a concern
  • Computation is limited
  • The source and target domains are similar

Trainable embeddings

The embedding matrix is updated:\[ E \leftarrow E-\eta\frac{\partial\mathcal{L}}{\partial E}. \]

This may help when:

  • The labeled dataset is larger
  • Domain-specific word meanings matter
  • The source corpus differs from the target domain
  • Sentiment usage is specialized

A common strategy is to train the classifier with frozen embeddings first and then unfreeze them using a smaller learning rate.

Generalizing to Rare Words

Suppose “absent” never appears in the labeled sentiment dataset, but it appears frequently in the corpus used to learn the embeddings.

If its representation is related to words such as

  • lacking
  • missing
  • without

then a sentiment classifier may transfer what it learned about those familiar terms.

This is one of the main benefits of pretrained representations:

A word does not need to appear frequently in the labeled dataset if useful information about it was learned elsewhere.

This benefit depends on vocabulary coverage. A word-level model still needs a strategy for tokens missing from the embedding vocabulary.

Subword Representations

Rare and unseen words can be represented using subword units.

For example, an uncommon word may be divided into reusable pieces. This allows its representation to share information with other words containing similar components.

Subword tokenization is useful for:

  • Misspellings
  • Names
  • Technical terminology
  • Morphologically rich languages
  • Newly created words
  • Informal online text

It generally provides better coverage than a fixed word-only vocabulary.

Contextual Representations

Static embeddings assign one vector to each word regardless of context. This creates difficulty for polysemous words.

For example, “cold” may describe:

  • Temperature
  • Illness
  • Personality
  • Color tone

A contextual encoder computes\[ e_t=f(w_t,w_1,\ldots,w_T), \]

so the representation changes with the sentence.

A modern sentiment classifier often uses:

  1. A pretrained contextual encoder
  2. A pooled sequence representation
  3. A task-specific classification head

This usually handles composition and ambiguity better than static embedding averages, although it requires more computation.

One-to-Five Stars Are Ordinal

A five-star rating is often treated as five-way classification. However, the labels have an order:\[ 1<2<3<4<5. \]

Predicting five stars when the truth is four is usually less severe than predicting one star.

Ordinary softmax cross-entropy does not encode this ordering directly. It treats all incorrect classes as categorically different.

Alternatives include:

  • Regression
  • Ordinal classification
  • Cumulative threshold models
  • Earth-mover-style losses
  • A hybrid classification and regression objective

The best formulation depends on whether calibrated class probabilities or ordinal distance matters more.

Mixed Sentiment and Aspect-Level Analysis

A review may express different opinions about different aspects:

“The food was excellent, but the service was painfully slow.”

A single label compresses this into one overall rating and may lose important detail.

Aspect-based sentiment analysis can produce outputs such as:

AspectSentiment
FoodPositive
ServiceNegative
AmbianceNot mentioned

This requires detecting both the aspect and the sentiment associated with it.

Class Imbalance

Sentiment datasets may contain many more positive reviews than negative ones, or few neutral examples.

Accuracy can be misleading in this setting. A model can achieve high accuracy by favoring the majority class.

Possible responses include:

  • Class-weighted loss
  • Stratified sampling
  • Balanced minibatches
  • Macro-averaged metrics
  • Per-class recall analysis
  • Careful threshold selection

For class \(k\), a weighted loss can be written as\[ \mathcal{L} = -\sum_{k=1}^{K} \lambda_k y_k\log\hat{y}_k. \]

Minority classes receive larger weights when appropriate.

Evaluation Metrics

Useful metrics include:

  • Accuracy
  • Macro precision
  • Macro recall
  • Macro F1
  • Confusion matrix
  • Mean absolute error for ratings
  • Calibration error
  • Per-domain performance
  • Robustness to negation and spelling variation

For ordered ratings, mean absolute error is\[ \operatorname{MAE} = \frac{1}{N} \sum_{i=1}^{N} |\hat{y}^{(i)}-y^{(i)}|. \]

A confusion matrix reveals whether errors occur mostly between neighboring ratings or across opposite sentiments.

A Mean-Embedding Baseline

import torch
import torch.nn as nn

class MeanEmbeddingClassifier(nn.Module):
    def __init__(
        self,
        vocabulary_size,
        embedding_size,
        number_of_classes,
        padding_index
    ):
        super().__init__()

        self.padding_index = padding_index

        self.embedding = nn.Embedding(
            vocabulary_size,
            embedding_size,
            padding_idx=padding_index
        )

        self.classifier = nn.Linear(
            embedding_size,
            number_of_classes
        )

    def forward(self, token_ids):
        vectors = self.embedding(token_ids)

        mask = (
            token_ids != self.padding_index
        ).unsqueeze(-1)

        masked_vectors = vectors * mask

        lengths = mask.sum(dim=1).clamp(min=1)

        pooled = (
            masked_vectors.sum(dim=1)
            / lengths
        )

        return self.classifier(pooled)

This provides a fast and interpretable baseline. More complex models should be compared against it to confirm that their added cost produces meaningful gains.

A Bidirectional LSTM Classifier

import torch
import torch.nn as nn

class BiLSTMSentimentClassifier(nn.Module):
    def __init__(
        self,
        vocabulary_size,
        embedding_size,
        hidden_size,
        number_of_classes,
        padding_index
    ):
        super().__init__()

        self.embedding = nn.Embedding(
            vocabulary_size,
            embedding_size,
            padding_idx=padding_index
        )

        self.encoder = nn.LSTM(
            input_size=embedding_size,
            hidden_size=hidden_size,
            batch_first=True,
            bidirectional=True
        )

        self.classifier = nn.Linear(
            2 * hidden_size,
            number_of_classes
        )

    def forward(self, token_ids):
        vectors = self.embedding(token_ids)
        _, (hidden, _) = self.encoder(vectors)

        forward_final = hidden[-2]
        backward_final = hidden[-1]

        representation = torch.cat(
            [forward_final, backward_final],
            dim=1
        )

        return self.classifier(representation)

For padded batches, a production implementation should use actual sequence lengths or packed sequences so padding does not distort terminal states.

Common Failure Modes

Negation

“The meal was not good.”

The model must understand the scope of “not.”

Contrast

“The food was great, but I would never return.”

Later clauses may dominate the overall judgment.

Sarcasm

“Wonderful—another hour waiting for cold food.”

The literal positive word conflicts with the intended sentiment.

Domain-specific polarity

“Unpredictable” may be positive for a thriller but negative for a vehicle.

Mixed sentiment

A review may praise one feature and criticize another.

Rating inconsistency

Two people may express similar opinions but choose different star ratings.

Distribution shift

A model trained on restaurant reviews may perform poorly on financial commentary or medical feedback.

Common Mistakes

Evaluating only the training loss

A sentiment model must be measured on held-out data from a realistic target distribution.

Ignoring padding in average pooling

Padding vectors can distort the review representation.

Assuming pretrained embeddings understand negation

Static embeddings represent individual words. Composition must be learned by the sequence model.

Fine-tuning embeddings too aggressively

A small dataset can damage useful pretrained structure.

Using accuracy alone on imbalanced data

Macro metrics and per-class errors may reveal poor minority-class performance.

Treating star ratings as unrelated categories

Their ordinal structure may matter to the application.

Assuming one global sentiment captures every opinion

Aspect-level analysis may be necessary for mixed reviews.

Key Takeaway

Sentiment classification maps a variable-length text sequence to a sentiment label. Pretrained word embeddings improve data efficiency by transferring linguistic relationships learned from much larger text collections.

A simple model averages token embeddings:\[ r = \frac{1}{T} \sum_{t=1}^{T}e_t, \]

and passes the result to a classifier. This is fast and useful as a baseline, but it ignores word order and can fail on negation or contrast.

A many-to-one RNN, GRU, LSTM, or bidirectional encoder processes embeddings sequentially:\[ h_t=f(h_{t-1},e_t), \qquad \hat{y}=g(h_T), \]

allowing the model to represent phrases whose meaning depends on composition. Contextual encoders extend this idea further by computing token representations from the complete sentence.

Similar Posts

Questions, corrections, or additional insights?