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.
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.
Unlike formal programming languages (C++, Python) which have rigid, unambiguous context-free grammars, human language is inherently noisy, flexible, and ambiguous across multiple levels:
Search engines (Google, Bing) indexing billions of documents, semantic vector search, question answering, and automated document summarization.
Autonomous customer support agents, personal assistants (Siri, Alexa), enterprise knowledge copilots, and interactive tutoring systems.
Real-time multilingual translation (Google Translate, DeepL), speech-to-speech cross-lingual communication, and localized content generation.
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.
The fundamental process of segmenting a continuous stream of characters into discrete meaningful units called Tokens.
"Don't go!" → ["Do", "n't", "go", "!"])."unaffordability" → ["un", "afford", "ability"]), eliminating Out-Of-Vocabulary (OOV) errors completely!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.
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.
| 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. |
Assigning grammatical categories (Noun, Verb, Adjective, Adverb, Preposition) to each token based on its definition and syntactic context:
"Time flies like an arrow"Locating and classifying key informational entities mentioned in unstructured text into predefined categories:
"Sundar Pichai visited Tokyo on June 15 for Google."Machine learning and neural networks cannot process raw strings; text must be mapped into numerical vectors in a mathematical vector space.
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!
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.
TF(t, d) = Count of term t in document d / Total terms in document dIDF(t) = ln( Total number of documents N / Number of documents containing term 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.
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."
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.
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.
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)
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.
(Instruction, Response) demonstration pairs to teach it to act as an obedient conversational assistant.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:
"Classify the sentiment of: 'The battery died in an hour.'"
Instructing the model to output intermediate reasoning steps before arriving at a final answer:
Forces the model to allocate additional computational tokens to decompose multi-step logic, math, and planning problems, dramatically reducing hallucinations.
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.
"""
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")
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.
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.
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.
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.
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.
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.