UNIT 10 • COMPUTATIONAL LINGUISTICS & GENERATIVE LANGUAGE MODELS

Natural Language Processing

An engineering deep-dive into computational linguistics and generative language models: tokenization, morphology, POS tagging, Named Entity Recognition (NER), TF-IDF vectors, Word2Vec distributed representations, sequence-to-sequence translation, Large Language Models (LLMs), and prompt engineering architectures.

📖 Standard: Jurafsky & Martin (SLP 3rd Ed) & Russell-Norvig
⏱️ Study Depth: 7 Hours • Theory & Python Implementation
💻 Implementation: Python 3.12 (TF-IDF Vectorizer & Sentiment Classifier)
🎯 Verification: 100% Pure Natural English Language
01

What is Natural Language Processing? Challenges & Scope

Natural Language Processing (NLP) is the multidisciplinary subfield of Artificial Intelligence and Computational Linguistics dedicated to empowering computers to comprehend, interpret, manipulate, and generate human language in a meaningful, context-sensitive way.

🧩 The Core Challenge: Linguistic Ambiguity

Unlike formal programming languages (C++, Python) which have rigid, unambiguous context-free grammars, human language is inherently noisy, flexible, and ambiguous across multiple levels:

  • Lexical / Polysemy Ambiguity: A single word possesses multiple meanings depending on context (e.g., "bank" as a financial institution vs. a river edge; "apple" as a fruit vs. a technology company).
  • Syntactic / Structural Ambiguity: Sentences can have multiple valid grammatical parse trees (e.g., "I saw the man with the telescope": did I use the telescope to see him, or did the man possess a telescope?).
  • Semantic & Pragmatic Ambiguity: Real meaning depends on shared world knowledge, sarcasm, cultural context, and intent (e.g., "Could you pass the salt?" is a behavioral request, not a yes/no inquiry about physical arm ability).
🌐 Key Industrial Applications of Modern NLP
Information Retrieval

Search engines (Google, Bing) indexing billions of documents, semantic vector search, question answering, and automated document summarization.

Conversational AI

Autonomous customer support agents, personal assistants (Siri, Alexa), enterprise knowledge copilots, and interactive tutoring systems.

Language Translation

Real-time multilingual translation (Google Translate, DeepL), speech-to-speech cross-lingual communication, and localized content generation.

02

The Text Preprocessing Pipeline

Raw unstructured text (from tweets, medical notes, customer reviews) is filled with punctuation, capitalization noise, spelling errors, and HTML artifacts. A standardized Text Preprocessing Pipeline converts raw strings into clean, standardized linguistic tokens.

1. Tokenization

The fundamental process of segmenting a continuous stream of characters into discrete meaningful units called Tokens.

  • Word Tokenization: Splitting sentences by whitespace and punctuation (e.g., "Don't go!" → ["Do", "n't", "go", "!"]).
  • Subword Tokenization (BPE & WordPiece): Used in modern LLMs (GPT, BERT). Decomposes rare words into frequent character chunks (e.g., "unaffordability" → ["un", "afford", "ability"]), eliminating Out-Of-Vocabulary (OOV) errors completely!
2. Stop-Word Removal

Filtering out highly ubiquitous, low-information function words (e.g., "the", "is", "at", "which", "and") that dominate word count distributions without carrying distinct topical meaning.

⚠️ When NOT to Remove Stop Words!

In Sentiment Analysis, removing "not" reverses meaning ("not good" → "good"!). In modern Transformers, stop words are never removed because self-attention mechanisms require syntactic connector words to resolve relational context.

✂️ Normalization: Stemming vs. Lemmatization
Criterion / Feature Stemming (e.g., Porter Stemmer) Lemmatization (e.g., WordNet Lemmatizer)
Underlying Mechanism Heuristic rule-based chopping of prefixes and suffixes (e.g., cutting off "-ing", "-ed", "-s"). Full morphological analysis consulting a grammatical dictionary and vocabulary lookup table.
Part of Speech Awareness No. Operates on the word string blindly in isolation. Yes. Context-aware (determines whether a word acts as a Noun or Verb before reducing).
Dictionary Validity Output is often a non-existent word root (e.g., "studies" → "studi", "universe" → "univers"). Guaranteed to return a valid canonical dictionary base form (the Lemma): "better" → "good".
Execution Speed Blazing fast; simple string pattern matching. Slower; requires linguistic database lookups.
3. Part-of-Speech (POS) Tagging

Assigning grammatical categories (Noun, Verb, Adjective, Adverb, Preposition) to each token based on its definition and syntactic context:

"Time flies like an arrow"
Time (Noun) | flies (Verb) | like (Preposition) | an (Determiner) | arrow (Noun)
4. Named Entity Recognition (NER)

Locating and classifying key informational entities mentioned in unstructured text into predefined categories:

"Sundar Pichai visited Tokyo on June 15 for Google."
- Person: Sundar Pichai
- Location: Tokyo
- Date: June 15
- Organization: Google
03

Vector Representations: Bag of Words, TF-IDF & Word2Vec

Machine learning and neural networks cannot process raw strings; text must be mapped into numerical vectors in a mathematical vector space.

1. Bag of Words (BoW) Model

Represents a document as a numerical vector of token counts across a fixed vocabulary. The document is treated as an unordered "bag", completely disregarding word order and grammar.

Severe Limitations: Vectors are massive and extremely sparse (99% zeros). Crucially, BoW cannot distinguish between "not happy, very sad" and "not sad, very happy" because both have identical bag counts!

2. TF-IDF (Term Frequency - Inverse Document Frequency)

Rather than raw counts, TF-IDF evaluates how important a word is to a specific document within a collection (corpus). Words that appear frequently in one document but rarely across the entire corpus receive the highest importance weights.

1. Term Frequency TF(t, d):
TF(t, d) = Count of term t in document d / Total terms in document d

2. Inverse Document Frequency IDF(t):
IDF(t) = ln( Total number of documents N / Number of documents containing term t )

3. Final Composite Weight:
TF-IDF(t, d) = TF(t, d) * IDF(t)

If a word appears in every single document in the corpus (e.g., "company"), N / DF = 1, so IDF = ln(1) = 0! Its weight is penalized to zero, automatically neutralizing uninformative common words.

🧬 3. Word2Vec & Distributed Word Embeddings (Mikolov et al., 2013)

BoW and TF-IDF vectors treat words as orthogonal, independent dimensions: the vector for "cat" has zero mathematical similarity to "kitten". Word Embeddings map words into dense, continuous low-dimensional vector spaces (e.g., 300 dimensions) where semantically similar words cluster close together, adhering to the Distributional Hypothesis (J.R. Firth, 1957): "You shall know a word by the company it keeps."

Continuous Bag of Words (CBOW)

Predicts a single target word given its surrounding context words (e.g., given "the cat sat on the [?]" → predicts "mat"). Fast to train; excels on frequent words.

Skip-Gram Model

Inverts the task: predicts the surrounding context words given a single central target word (e.g., given "mat" → predicts "cat", "sat", "on"). Superior performance on rare words and specialized vocabularies.

Semantic Vector Algebra in Embedding Space
   Vector("King") - Vector("Man") + Vector("Woman") ≈ Vector("Queen")
   Vector("Paris") - Vector("France") + Vector("Japan") ≈ Vector("Tokyo")

   Geometric Cosine Similarity:
   Cosine_Similarity(A, B) = (A . B) / ( ||A|| * ||B|| )
   (Yields 1.0 for identical semantic direction, 0.0 for unrelated orthogonal concepts)
04

Machine Translation & Chatbot Architectures

🌐 Evolution of Machine Translation
  1. Rule-Based MT (1960s-1980s): Hand-written bilingual dictionaries and complex grammatical transformation rules. Fragile and incapable of scaling.
  2. Statistical MT (1990s-2015): Analyzed massive parallel bilingual corpora using phrase translation tables and statistical language models (e.g., IBM Models, Moses).
  3. Neural Machine Translation (NMT, 2016-Present): End-to-end deep learning. Seq2Seq models with Attention and Transformers map the entire source sentence into a continuous semantic representation and decode fluently into the target language.
🤖 Chatbot Architectural Generations
  • Rule-Based (Pattern Matching): Pattern matching on user input keywords using regex and hard-coded state trees (e.g., ELIZA 1966, AIML). Breaks on unforeseen inputs.
  • Retrieval-Based (FAQ Matchers): Uses semantic embeddings and cosine similarity to match user queries against an indexed database of verified questions and answers. High factual precision; zero generative capability.
  • Generative LLM Agents: Deep autoregressive language models that synthesize contextually grounded, creative responses dynamically from multi-turn conversational history.
05

Large Language Models (LLMs) & Prompt Engineering

Modern NLP is dominated by Large Language Models (LLMs): transformer-based architectures with tens or hundreds of billions of parameters (e.g., GPT-4, Gemini, Claude, LLaMA) trained on massive multi-trillion-token web corpora.

🏗️ The 3-Stage Training Pipeline of Modern LLMs
  1. Self-Supervised Pretraining: The model ingests raw, uncurated web text (trillions of tokens) using the Causal Language Modeling objective: predicting the single next token given all preceding context tokens. This stage builds foundational world knowledge, grammar, reasoning, and coding syntax.
  2. Supervised Fine-Tuning (SFT / Instruction Tuning): Pretrained base models are wild document completers. SFT trains the model on thousands of curated (Instruction, Response) demonstration pairs to teach it to act as an obedient conversational assistant.
  3. Reinforcement Learning from Human Feedback (RLHF / DPO): Human evaluators rank model responses. A reward model is trained on these preferences, and Reinforcement Learning (PPO or Direct Preference Optimization / DPO) aligns the LLM to be Helpful, Honest, and Harmless (the 3 H's).
🎯 Prompt Engineering Paradigms

Prompt Engineering is the strategic craft of designing input instructions to guide an LLM toward accurate, high-fidelity outputs without modifying the underlying model weights:

Zero-Shot vs. Few-Shot Prompting
  • Zero-Shot: Asking the model to perform a task with direct instructions and zero input-output demonstrations:
    "Classify the sentiment of: 'The battery died in an hour.'"
  • Few-Shot (In-Context Learning): Providing 2 to 5 exemplary input-output pairs inside the prompt before the target query to establish pattern and formatting standards.
Chain-of-Thought (CoT) Prompting

Instructing the model to output intermediate reasoning steps before arriving at a final answer:

"Let's think step by step..."

Forces the model to allocate additional computational tokens to decompose multi-step logic, math, and planning problems, dramatically reducing hallucinations.

06

Complete Python Implementation: NLP Engine from Scratch

Below is a clean, self-contained Python script implementing an End-to-End NLP Engine from first principles: custom regex tokenization, stop-word removal, a custom TF-IDF Vectorizer, a Cosine Similarity Matcher, and a Multinomial Naive Bayes Sentiment Classifier with Laplace smoothing.

nlp_pipeline_engine.py — TF-IDF, Vector Cosine Search & Sentiment Classifier
"""
AI Track Unit 10: Natural Language Processing Foundations
Complete NLP Pipeline: Tokenizer, TF-IDF Vectorizer, Cosine Search & Sentiment Classifier
Author: DevAscent Engineering Team
"""

import math
import re
from collections import Counter
from typing import List, Dict, Tuple, Set

# =====================================================================
# 1. TEXT PREPROCESSING & TOKENIZER
# =====================================================================
STOP_WORDS: Set[str] = {
    'the', 'is', 'at', 'which', 'on', 'a', 'an', 'and', 'or', 'in',
    'this', 'to', 'of', 'for', 'with', 'it', 'as', 'was', 'by'
}

def clean_and_tokenize(text: str, remove_stopwords: bool = True) -> List[str]:
    """Lowercase, strip non-alphanumeric punctuation, and extract word tokens."""
    cleaned = re.sub(r'[^a-zA-Z0-9\s]', ' ', text.lower())
    tokens = cleaned.split()
    if remove_stopwords:
        tokens = [t for t in tokens if t not in STOP_WORDS and len(t) > 1]
    return tokens

# =====================================================================
# 2. CUSTOM TF-IDF VECTORIZER
# =====================================================================
class CustomTFIDFVectorizer:
    def __init__(self):
        self.vocabulary: Dict[str, int] = {}
        self.idf: Dict[str, float] = {}

    def fit(self, corpus: List[str]):
        tokenized_docs = [clean_and_tokenize(doc) for doc in corpus]
        N = len(corpus)

        # Build vocabulary
        unique_words = sorted(list(set(word for doc in tokenized_docs for word in doc)))
        self.vocabulary = {word: idx for idx, word in enumerate(unique_words)}

        # Compute Document Frequency (DF)
        df = Counter()
        for doc in tokenized_docs:
            for word in set(doc):
                df[word] += 1

        # Compute Inverse Document Frequency (IDF) with smoothing (+1)
        for word, count in df.items():
            self.idf[word] = math.log((N + 1) / (count + 1)) + 1.0

    def transform(self, corpus: List[str]) -> List[List[float]]:
        vectors = []
        vocab_size = len(self.vocabulary)

        for doc in corpus:
            tokens = clean_and_tokenize(doc)
            tf_counter = Counter(tokens)
            doc_len = len(tokens) if len(tokens) > 0 else 1.0

            vec = [0.0] * vocab_size
            for word, count in tf_counter.items():
                if word in self.vocabulary:
                    idx = self.vocabulary[word]
                    tf = count / doc_len
                    vec[idx] = tf * self.idf[word]

            # L2 Normalization (Cosine scale)
            norm = math.sqrt(sum(v ** 2 for v in vec))
            if norm > 0:
                vec = [v / norm for v in vec]
            vectors.append(vec)

        return vectors

    def fit_transform(self, corpus: List[str]) -> List[List[float]]:
        self.fit(corpus)
        return self.transform(corpus)

# =====================================================================
# 3. SEMANTIC SEARCH (COSINE SIMILARITY)
# =====================================================================
def cosine_similarity(v1: List[float], v2: List[float]) -> float:
    return sum(a * b for a, b in zip(v1, v2))

# =====================================================================
# 4. NAIVE BAYES SENTIMENT CLASSIFIER WITH LAPLACE SMOOTHING
# =====================================================================
class NaiveBayesSentimentClassifier:
    def __init__(self):
        self.class_priors: Dict[str, float] = {}
        self.word_counts: Dict[str, Counter] = {'pos': Counter(), 'neg': Counter()}
        self.total_words: Dict[str, int] = {'pos': 0, 'neg': 0}
        self.vocab: Set[str] = set()

    def fit(self, documents: List[str], labels: List[str]):
        n = len(documents)
        label_counts = Counter(labels)
        for label in ['pos', 'neg']:
            self.class_priors[label] = label_counts[label] / n

        for doc, label in zip(documents, labels):
            tokens = clean_and_tokenize(doc, remove_stopwords=False)
            for token in tokens:
                self.word_counts[label][token] += 1
                self.total_words[label] += 1
                self.vocab.add(token)

    def predict(self, text: str) -> Tuple[str, Dict[str, float]]:
        tokens = clean_and_tokenize(text, remove_stopwords=False)
        vocab_size = len(self.vocab)

        # Log-probabilities to prevent underflow
        log_probs = {}
        for label in ['pos', 'neg']:
            log_prob = math.log(self.class_priors[label])
            for token in tokens:
                # Laplace Smoothing (+1)
                count = self.word_counts[label][token]
                prob = (count + 1.0) / (self.total_words[label] + vocab_size)
                log_prob += math.log(prob)
            log_probs[label] = log_prob

        best_class = 'pos' if log_probs['pos'] > log_probs['neg'] else 'neg'
        return best_class, log_probs

# =====================================================================
# DEMONSTRATION HARNESS
# =====================================================================
if __name__ == "__main__":
    print("=" * 68)
    print("DEMO 1: TF-IDF VECTORIZATION & SEMANTIC RETRIEVAL SEARCH")
    print("=" * 68)

    docs = [
        "Artificial Intelligence and Machine Learning are transforming modern technology.",
        "Deep Learning utilizes neural networks with multiple layers for computer vision.",
        "Natural language processing powers smart voice assistants and language translation.",
        "Planetary rovers explore the red dusty surface of Mars using autonomous robotics."
    ]

    tfidf = CustomTFIDFVectorizer()
    doc_vectors = tfidf.fit_transform(docs)

    query = "computer neural networks"
    query_vector = tfidf.transform([query])[0]

    print(f"User Query: '{query}'\n")
    print("Semantic Similarity Match Rankings:")
    scores = []
    for idx, doc_vec in enumerate(doc_vectors):
        sim = cosine_similarity(query_vector, doc_vec)
        scores.append((sim, docs[idx]))

    scores.sort(key=lambda item: item[0], reverse=True)
    for rank, (score, doc) in enumerate(scores, 1):
        print(f"  Rank {rank} [Score: {score:.4f}]: {doc}")

    print("\n" + "=" * 68)
    print("DEMO 2: NAIVE BAYES SENTIMENT CLASSIFICATION (WITH LAPLACE SMOOTHING)")
    print("=" * 68)

    training_reviews = [
        "This product is amazing exceptional quality and works beautifully",
        "Outstanding performance absolutely love this device great design",
        "Terrible waste of money completely broken and defective hardware",
        "Horrible customer service arrived late damaged and unusable",
        "Fantastic battery life brilliant screen highly recommended"
    ]
    training_labels = ["pos", "pos", "neg", "neg", "pos"]

    clf = NaiveBayesSentimentClassifier()
    clf.fit(training_reviews, training_labels)

    test_reviews = [
        "This device is fantastic with brilliant performance",
        "Defective waste of money completely unusable and broken"
    ]

    for review in test_reviews:
        predicted_sentiment, _ = clf.predict(review)
        print(f"Review: '{review}'")
        print(f"  --> Predicted Sentiment: {predicted_sentiment.upper()}\n")
07

Viva Voce & Engineering Interview Questions

Q1: Contrast Stemming and Lemmatization. When should an NLP engineer choose one over the other?

Answer:
Stemming is a crude, rule-based heuristic that chops character affixes (e.g., cutting "-ing" or "-ed") without linguistic awareness. It is fast, but frequently produces non-words (e.g., "universe" → "univers") and causes over-stemming (conflating distinct words).
Lemmatization conducts full morphological analysis consulting dictionary lemma tables and Part-of-Speech context (e.g., identifying whether "meeting" is a verb or a noun). It returns a valid base dictionary word (e.g., "better" → "good"). Choose stemming for high-throughput search indexing where speed is paramount; choose lemmatization for grammar parsing, conversational chatbots, and sentiment tasks requiring linguistic accuracy.

Q2: Why does the TF-IDF formula use the logarithm of (N / DF) instead of raw ratios?

Answer: If raw ratios (N / DF) were used, words that appear in only 1 out of 1,000,000 documents would receive a weight of 1,000,000, while words appearing in 100 documents would receive 10,000. This raw multiplier introduces exponential extremes that dwarf Term Frequency. Applying the natural logarithm dampens the scale smoothly: ln(1,000,000) ≈ 13.8 and ln(10,000) ≈ 9.2. Crucially, if a word appears in all N documents (e.g., "the"), N / DF = 1, yielding ln(1) = 0, mathematically canceling out uninformative words.

Q3: How does Word2Vec capture semantic relationships like "King - Man + Woman = Queen"?

Answer: Word2Vec maps words into a continuous vector space where dimensions encode latent linguistic and conceptual attributes. Because words sharing similar contexts are optimized to have high dot products, the spatial offset (direction vector) connecting "Man" to "King" represents the concept of "Royalty". When this exact same directional vector is added to "Woman", the vector arithmetic navigates through embedding space to land in the closest geometric neighborhood of "Queen", demonstrating distributed representation learning.

Q4: What is the purpose of Laplace Smoothing in Naive Bayes, and what happens if it is omitted?

Answer: Naive Bayes multiplies conditional probabilities across all words in a document: Product over words of P(word | class). If a test document contains a single word that never appeared in the training set for that class, the frequency count is 0, making P(word | class) = 0. Multiplying by zero zeroes out the entire product, blinding the model to all other strong signals in the sentence. Laplace Smoothing (+1) adds 1 to the numerator and the vocabulary size |V| to the denominator, ensuring every word retains a tiny non-zero probability.

Q5: Explain the 3 stages in the modern Large Language Model (LLM) training pipeline.

Answer:
1. Pretraining (Self-Supervised): Predicts the next token across trillions of web text tokens. Builds general grammar, world knowledge, and reasoning capabilities.
2. Supervised Fine-Tuning (SFT): Trains on curated question-answer and instruction-following demonstrations to transform the wild document completer into a helpful assistant.
3. Reinforcement Learning from Human Feedback (RLHF / DPO): Uses human preference comparisons to train a reward model and optimize model behavior via RL (PPO/DPO), aligning the model to be Helpful, Honest, and Harmless.

Q6: What is Chain-of-Thought (CoT) prompting, and why does it improve LLM reasoning?

Answer: Chain-of-Thought (CoT) prompting instructs the LLM to output explicit intermediate reasoning steps before providing the final answer (e.g., "Let's think step by step"). Because transformer models compute token generation autoregressively with a constant amount of compute per token, generating intermediate reasoning tokens allows the model to "think on paper", breaking down complex multi-step deductions and dramatically reducing calculation errors and factual hallucinations.