Statistical NLP
Module Completed: January 2022
🎯 Module Overview
Natural Language Processing (NLP) enables machines to read, understand, and generate human language. This module covers the statistical foundations of NLP — from raw text cleaning through to vector representations and classical ML classifiers for text. The project tackles a real multi-label blog authorship classification problem spanning 600,000+ posts.
📖 Topics Covered
Text Preprocessing Pipeline
Before any model can consume text, it must be cleaned and normalised. Standard steps:
- Language Detection — filter to target language only;
fasttextlibrary is faster and more accurate thanlangdetectfor large corpora - Special character & number removal — strip punctuation, digits, URLs using regex
- Lowercasing — normalise case so "The" and "the" are treated identically
- Stop word removal — remove high-frequency, low-information words (the, is, at, which);
nltk.corpus.stopwords - Tokenisation — split text into individual tokens (words or subwords)
- Stemming — reduce words to root form by rule-based suffix stripping (e.g., "running" → "run"); fast but crude;
nltk.PorterStemmer - Lemmatisation — reduce words to dictionary base form using vocabulary lookup (e.g., "better" → "good"); slower but more accurate;
nltk.WordNetLemmatizer - N-grams — sequences of N consecutive tokens; bigrams (N=2) capture "New York" as a single feature
Bag of Words (BoW)
Represents text as a vector of word frequency counts — ignores word order, purely frequency-based:
- Each unique word = one dimension in the vocabulary
- Each document = a sparse vector of counts
CountVectorizer(ngram_range=(1,2), analyzer='word')— includes unigrams and bigrams- Fast, interpretable, but ignores context and treats all words as equally important
TF-IDF (Term Frequency — Inverse Document Frequency)
Improves on BoW by weighting terms by their importance:
TF-IDF(t, d) = TF(t, d) × IDF(t)
- TF = frequency of term t in document d
- IDF = log(N / df(t)) where N = total docs, df(t) = docs containing t
- Common words that appear in many documents get lower IDF (lower weight)
- Rare, discriminative words get higher IDF (higher weight)
- Result: more meaningful feature representation than raw counts
TfidfVectorizer(ngram_range=(1,1), analyzer='word')
HashingVectorizer
Applies a hash function to map tokens to feature indices — does not build a vocabulary dictionary:
- Fixed-size output regardless of vocabulary size → memory efficient for very large corpora
- Cannot invert (no
inverse_transform) — useful for streaming/online learning - May have hash collisions (different words mapped to same index)
HashingVectorizer(ngram_range=(1,1))
Multi-Label Classification
A sample can belong to multiple classes simultaneously (e.g., a blog post has gender + age + topic + sign labels). Key differences from multi-class:
MultiLabelBinarizer— encodes multiple labels per instance into a binary matrixOneVsRestClassifier— trains one binary classifier per label; each independently predicts if the label applies- Evaluation metrics: accuracy alone is misleading; use
f1_score,average_precision_score,average_recall_score— these account for partial matches across labels
Classifiers for Text
Text features (after vectorisation) fed into classical ML models:
| Model | Strength for Text |
|---|---|
| Logistic Regression | Fast, interpretable baseline; struggles at very high dimensions |
| SVC | Effective for high-dimensional sparse features; slow on large corpora |
| Random Forest | Ensemble; feature importance available; handles non-linearity |
| XGBoost | Best performer on large multi-feature text tasks; gradient boosting captures complex patterns; feature importance built-in |
Key insight: For large corpora with many features, ensemble methods (Random Forest, XGBoost) outperform linear models. XGBoost's gradient boosting in feature space gives it an edge over Random Forest's hyperparameter-based optimisation.
Vectoriser Comparison
- CountVectorizer — simple frequency; fast; can be dominated by high-frequency words
- TfidfVectorizer — downweights common words; generally performs better in practice
- HashingVectorizer — memory efficient; no vocabulary; collision risk
Word Embeddings — Word2Vec
Dense vector representations where semantically similar words are close in vector space:
- CBOW (Continuous Bag of Words) — predict centre word from context window
- Skip-gram — predict context words from centre word; better for rare words
- Captures semantic relationships:
king - man + woman ≈ queen - Vocabulary: fixed; unknown words (OOV) cannot be represented
gensim.models.Word2Vec
Regular Expressions for NLP
Pattern matching for text cleaning and extraction:
.(any char),*(0+),+(1+),?(0 or 1),^(start),$(end)\d(digit),\w(word char),\s(whitespace),\b(word boundary)- Character classes:
[a-z],[^aeiou](negation) - Capture groups:
(pattern)— extract matched substrings re.sub(pattern, replacement, text)— most common for text cleaning
🏆 Project — Statistical NLP: Blog Authorship Classification (NLP-1)
Submitted: 30 January 2022 Domain: Digital Content Management
Problem Statement
Multi-label classification of blog posts to predict the combined author attributes (gender + age + topic + astrological sign) from blog text.
Dataset
- Blog Authorship Corpus — 681,288 posts from 19,320 bloggers (blogger.com, August 2004)
- Features:
text(raw blog post),gender,age,topic,sign - Age groups: teens (13–17), twenties (23–27), thirties (33–47); equal male/female per group
- 40 unique topics, 12 unique astrological signs
Data Preparation
- Language detection with
fasttext— keep only English posts - Text cleaning: remove special chars, numbers, lowercase, stop words, extra whitespace →
clean_datacolumn - Combined label:
gender + age + topic + signconcatenated →labelstarget column MultiLabelBinarizeron labels +OneVsRestClassifierfor multi-label setup
Experiments — Vectoriser × Classifier Grid
All combinations of 3 vectorisers × 4 classifiers evaluated:
| Vectoriser | Classifiers Tested |
|---|---|
| CountVectorizer (ngram 1–2) | Random Forest, SVC, Logistic Regression, XGBoost |
| TfidfVectorizer (ngram 1–1) | Random Forest, SVC, Logistic Regression, XGBoost |
| HashingVectorizer (ngram 1–1) | Random Forest, SVC, Logistic Regression, XGBoost |
Results:
- XGBClassifier performed best across all vectorisers — highest accuracy and F1-score
- TF-IDF performed marginally better than CountVectorizer overall
- HashingVectorizer — comparable performance, better memory profile
Hyperparameter Tuning
GridSearchCV applied to XGBClassifier with CountVectorizer and TfidfVectorizer — marginal improvement; model already close to performance ceiling for the given data size/sample.
Key Conclusions
- Best Model: XGBClassifier + TF-IDF
- Best Metrics: F1-score and average precision/recall (not raw accuracy — multi-label problem)
- Why XGBoost: Ensemble gradient boosting in feature space; automatic feature importance; handles large sparse text features better than SVC or LogisticRegression
🔑 Key Learnings
- TF-IDF > BoW in practice — downweighting common words gives more discriminative features; but the gap narrows with n-grams
- Multi-label ≠ multi-class — one sample can have multiple labels simultaneously; requires
MultiLabelBinarizer+OneVsRestClassifier; accuracy alone is misleading - fasttext for language detection — significantly more accurate and faster than rule-based or other library alternatives for large corpora
- Stop word removal is a default step — but for some tasks (sentiment analysis), stop words like "not" carry critical meaning; remove carefully
- Stemming vs Lemmatisation trade-off — stemming is faster (rule-based), lemmatisation is more accurate (vocabulary-based); use lemmatisation when precision matters
- XGBoost for large text features — ensemble methods scale better to high-dimensional sparse feature spaces than linear classifiers
- HashingVectorizer for streaming — when vocabulary is too large for memory, hashing maps tokens to fixed-size space without building a dictionary
🛠️ Tools & Libraries
import re
import nltk
import fasttext
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from wordcloud import WordCloud
from sklearn.feature_extraction.text import (CountVectorizer,
TfidfVectorizer,
HashingVectorizer)
from sklearn.preprocessing import MultiLabelBinarizer
from sklearn.multiclass import OneVsRestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.metrics import (f1_score, average_precision_score,
classification_report)
from xgboost import XGBClassifier
import gensim
from gensim.models import Word2Vec