Skip to content
SDB
Natural Language Processing

Chapter 13 · intermediate · 28 min

NLP Evaluation & Deployment

Intrinsic and extrinsic evaluation, NLP benchmarks, production deployment, and model monitoring

Subhendu Datta BhowmikAI Tutorials

NLP Evaluation: Intrinsic vs Extrinsic

Intrinsic Evaluation

Intrinsic evaluation measures model quality in isolation, independent of any downstream application. It evaluates properties of the model itself:

TaskIntrinsic MetricWhat It Measures
Language ModelingPerplexityHow well the LM predicts held-out text
Word EmbeddingsAnalogy accuracy (king-man+woman=?)Geometric properties of the embedding space
Word EmbeddingsWord similarity correlationCorrelation with human similarity judgments
MTBLEU, chrF, TERN-gram overlap with reference translations
SummarizationROUGE-1/2/LN-gram recall against reference summaries
ASRWER, CERWord/character error rate vs transcript
ParsingUAS, LASUnlabeled/labeled attachment score for dependencies
NERSpan-level F1Strict entity boundary and type matching

Perplexity for language models: PP(W)=P(w1,w2,,wN)1/N=exp(1Ni=1NlogP(wiw<i))PP(W) = P(w_1, w_2, \ldots, w_N)^{-1/N} = \exp\left(-\frac{1}{N} \sum_{i=1}^N \log P(w_i | w_{<i})\right)

Lower perplexity = better model. A perplexity of kk means the model is as confused as if choosing uniformly among kk alternatives at each step.

Extrinsic Evaluation

Extrinsic evaluation measures how well the model contributes to a downstream task. It is the true test of real-world utility:

Embedding ModelIntrinsicExtrinsic (downstream)
Word2Vec-Google-300dWord analogy: 78%Sentiment classification: 88%
GloVe-840B-300dWord analogy: 82%Sentiment classification: 89%
BERT-baseSentiment classification: 93%

A model may perform better intrinsically but worse extrinsically (or vice versa). Always prefer extrinsic evaluation for production decisions.

NLP Benchmarks

GLUE and SuperGLUE

GLUE (General Language Understanding Evaluation, Wang et al. 2018) aggregates 9 NLU tasks:

TaskTypeDescription
SST-2SentimentBinary sentiment on movie reviews
MNLINLIMulti-genre natural language inference (3 classes)
QQPParaphraseQuora question pair similarity
QNLIQA-NLIWhether context contains answer to question
RTENLIRecognizing textual entailment (2 classes)
WNLICoreferenceWinograd schema NLI
CoLAGrammarAcceptability of English sentences
MRPCParaphraseMicrosoft Research Paraphrase Corpus
STS-BSimilaritySemantic textual similarity (regression)

SuperGLUE (harder tasks) includes BoolQ, CB, COPA, MultiRC, ReCoRD, RTE, WiC, WSC.

Human performance on GLUE ≈ 87; BERT-large ≈ 80; DeBERTa-v3-large ≈ 90 (superhuman on many sub-tasks).

Task-Specific Metrics Reference

NLP TaskPrimary MetricSecondary Metrics
Text ClassificationAccuracy, F1AUC-ROC, Matthews Correlation
Multi-label ClassificationMicro-F1Macro-F1, Hamming Loss
NERSpan F1Precision, Recall by entity type
Dependency ParsingLASUAS, EM
Machine TranslationBLEU, COMETchrF, TER
SummarizationROUGE-LBERTScore, FactCC (faithfulness)
ASRWERCER, RTF (real-time factor)
QA (Extractive)EM, F1Has-Answer F1 (SQuAD 2.0)
Language ModelingPerplexityBPC (bits per character)
DialogueBLEU, METEORHuman evaluation (coherence, engagement)

Human Evaluation

For open-ended generation, automated metrics correlate poorly with human preferences. Human evaluation assesses:

  • Fluency: Is the output grammatically correct and natural?
  • Coherence: Does it make logical sense as a whole?
  • Faithfulness: Does it accurately reflect the source/input?
  • Relevance: Does it address the prompt/question?

Platforms: Amazon Mechanical Turk, Scale AI, Surge AI. Use inter-annotator agreement (Krippendorff's α, Fleiss's κ) to measure annotation quality.

Deploying NLP Models

Serving Architecture

Client Request (text)
  ↓
Load Balancer / API Gateway
  ↓
NLP Inference Server (FastAPI / TorchServe / Triton)
  ├─ Tokenization
  ├─ Model Inference (ONNX / TensorRT / PyTorch)
  └─ Post-processing (decode labels, threshold)
  ↓
Response (labels, scores, spans)

Model Optimization for Production

Quantization: reduce weight precision from FP32 → INT8 or FP16:

  • 2–4× memory reduction
  • 2–3× speedup on CPU
  • 0.5–2% accuracy drop (acceptable in most cases)
  • Tools: ONNX Runtime, Hugging Face Optimum, BitsAndBytes

Distillation: train a small student model to mimic a large teacher:

  • DistilBERT: 40% smaller, 60% faster, 97% of BERT performance
  • TinyBERT, MobileBERT: further compressed versions

ONNX Export: convert PyTorch models to ONNX for cross-platform deployment:

torch.onnx.export(model, dummy_input, "model.onnx", opset_version=14)

Batching and caching: batch multiple requests together for higher GPU utilization; cache frequent queries (cosine similarity for semantic cache).

Deployment Options

OptionProsConsBest For
HuggingFace Inference APIZero-ops, instantCost, rate limitsPrototypes
Replicate / ModalServerless, auto-scaleCold startsBursty workloads
AWS SageMakerManaged, scalableComplex setupEnterprise
Self-hosted (k8s + Triton)Full control, cheap at scaleOperational complexityHigh volume
OpenAI/Anthropic APISOTA quality, simpleCost, data privacyLLM features

A/B Testing

Roll out new models gradually:

  1. Shadow mode: run new model in parallel, log results, compare offline
  2. Canary deployment: send 5–10% of traffic to new model, monitor metrics
  3. Full rollout: increase to 100% if metrics are stable
NLP Evaluation, Serving API, and Monitoringpython
# ─── 1. Comprehensive NLP Evaluation ─────────────────────────
from sklearn.metrics import (classification_report, confusion_matrix,
                              roc_auc_score, average_precision_score)
import numpy as np

# Multi-class classification evaluation
y_true = [0, 1, 2, 0, 1, 2, 0, 1, 2, 1]
y_pred = [0, 1, 2, 0, 2, 2, 1, 1, 0, 1]
y_prob = np.array([
    [0.8, 0.1, 0.1], [0.1, 0.8, 0.1], [0.1, 0.1, 0.8],
    [0.7, 0.2, 0.1], [0.2, 0.3, 0.5], [0.1, 0.1, 0.8],
    [0.3, 0.5, 0.2], [0.1, 0.8, 0.1], [0.4, 0.2, 0.4],
    [0.1, 0.7, 0.2],
])

print("Classification Report:")
print(classification_report(y_true, y_pred,
      target_names=["positive", "negative", "neutral"]))

# AUC-ROC (one-vs-rest)
auc_ovr = roc_auc_score(y_true, y_prob, multi_class='ovr', average='weighted')
print(f"Weighted AUC-ROC (OvR): {auc_ovr:.4f}")

# ─── 2. NER Span-Level F1 ─────────────────────────────────────
# seqeval is the standard library for NER evaluation
# pip install seqeval
from seqeval.metrics import f1_score, precision_score, recall_score, classification_report as seq_report

y_true_ner = [
    ["B-PER", "I-PER", "O", "B-ORG", "O"],
    ["O", "B-GPE", "O", "O"],
]
y_pred_ner = [
    ["B-PER", "I-PER", "O", "B-ORG", "O"],  # perfect
    ["O", "B-GPE", "I-GPE", "O"],             # wrong: extra I-GPE
]

print("\nNER Span-level Evaluation:")
print(seq_report(y_true_ner, y_pred_ner))
print(f"  F1:        {f1_score(y_true_ner, y_pred_ner):.4f}")
print(f"  Precision: {precision_score(y_true_ner, y_pred_ner):.4f}")
print(f"  Recall:    {recall_score(y_true_ner, y_pred_ner):.4f}")

# ─── 3. BLEU, ROUGE, and BERTScore ───────────────────────────
import sacrebleu
from rouge_score import rouge_scorer

# Machine Translation
mt_refs = [["The cat sat on the mat.", "A cat was sitting on the mat."]]
mt_hyps = ["The cat is sitting on the mat."]
bleu = sacrebleu.corpus_bleu(mt_hyps, mt_refs)
chrf = sacrebleu.corpus_chrf(mt_hyps, mt_refs)
print(f"\nMT Evaluation:")
print(f"  BLEU: {bleu.score:.2f}, chrF: {chrf.score:.2f}")

# Summarization
rouge = rouge_scorer.RougeScorer(['rouge1', 'rouge2', 'rougeL'], use_stemmer=True)
ref_summary = "AI has transformed many industries including healthcare and finance."
hyp_summary = "Artificial intelligence has changed healthcare, finance, and other sectors."
scores = rouge.score(ref_summary, hyp_summary)
print(f"\nSummarization ROUGE:")
for k, v in scores.items():
    print(f"  {k}: P={v.precision:.3f} R={v.recall:.3f} F={v.fmeasure:.3f}")

# BERTScore (semantic similarity, better human correlation)
# pip install bert-score
from bert_score import score as bert_score
P, R, F1 = bert_score([hyp_summary], [ref_summary], lang="en", verbose=False)
print(f"  BERTScore F1: {F1.mean():.4f}")

# ─── 4. FastAPI NLP Serving ───────────────────────────────────
# Save as: nlp_api.py
# pip install fastapi uvicorn transformers torch
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from transformers import pipeline
import time

app = FastAPI(title="NLP Inference API", version="1.0.0")

# Load models once at startup
sentiment_pipe = pipeline("sentiment-analysis",
                           model="distilbert-base-uncased-finetuned-sst-2-english")
ner_pipe = pipeline("ner", model="dbmdz/bert-large-cased-finetuned-conll03-english",
                    aggregation_strategy="simple")

class TextRequest(BaseModel):
    text: str
    model: str = "sentiment"

class PredictionResponse(BaseModel):
    text: str
    predictions: list
    latency_ms: float
    model_version: str = "v1.0"

@app.post("/predict", response_model=PredictionResponse)
async def predict(request: TextRequest):
    start = time.time()
    if len(request.text) > 10000:
        raise HTTPException(400, "Text exceeds maximum length of 10000 characters")

    if request.model == "sentiment":
        result = sentiment_pipe(request.text[:512])
    elif request.model == "ner":
        result = ner_pipe(request.text[:512])
    else:
        raise HTTPException(400, f"Unknown model: {request.model}")

    latency = (time.time() - start) * 1000
    return PredictionResponse(
        text=request.text[:100],
        predictions=result,
        latency_ms=round(latency, 2),
    )

@app.get("/health")
async def health():
    return {"status": "ok", "models": ["sentiment", "ner"]}

# Run: uvicorn nlp_api:app --host 0.0.0.0 --port 8000

# ─── 5. Monitoring: Data Drift Detection ──────────────────────
from scipy.stats import ks_2samp
from transformers import AutoTokenizer

def compute_text_stats(texts):
    """Compute distribution features for drift detection."""
    lengths = [len(t.split()) for t in texts]
    return {
        "mean_length": np.mean(lengths),
        "std_length": np.std(lengths),
        "max_length": np.max(lengths),
        "lengths": lengths,
    }

# Reference distribution (training data)
train_texts = ["This is great!", "Terrible product.", "Average quality.",
               "Works as expected.", "Would recommend.", "Disappointed."] * 100

# Production distribution (potentially drifted)
prod_texts  = ["Excelente producto!!!", "Sehr gut.", "Fantastique!", "훌륭해요",
               "Amazing 10/10 best ever", "Complete garbage waste of money"] * 100

train_stats = compute_text_stats(train_texts)
prod_stats  = compute_text_stats(prod_texts)

# Kolmogorov-Smirnov test for distribution shift
ks_stat, p_value = ks_2samp(train_stats["lengths"], prod_stats["lengths"])
print(f"\nDrift Detection (text length distribution):")
print(f"  KS statistic: {ks_stat:.4f}, p-value: {p_value:.4f}")
print(f"  Drift detected: {p_value < 0.05}")

Production Monitoring

What to Monitor

SignalWhatAlert When
Latencyp50/p95/p99 response timep95 > SLA threshold
ThroughputRequests per secondSudden drops or spikes
Error rateHTTP 4xx/5xx, exceptionsError rate > 1%
Confidence distributionModel output score histogramMean confidence drops significantly
Input driftLength, vocabulary, language distributionKS-test p < 0.05
Output driftLabel distribution shiftProduction labels diverge from training
Feedback signalUser corrections, thumbs up/downAccuracy drops on labeled feedback

Concept Drift vs Data Drift

  • Data drift (covariate shift): input distribution P(X)P(X) changes — e.g., new product categories appear in customer reviews
  • Concept drift: relationship P(YX)P(Y|X) changes — e.g., sentiment about a term shifts due to a news event (a brand name becomes associated with controversy)

Detection methods: statistical tests (KS-test, PSI - Population Stability Index), embedding drift (cosine distance between rolling mean embeddings), performance-based monitoring (accuracy on labeled samples).

Responsible AI for NLP

Bias and Fairness:

  • Sentiment models may perform differently across demographic groups
  • NER models may miss entities from non-Western cultures
  • MT quality varies dramatically across language pairs and domains
  • Use CheckList (Ribeiro et al.) for behavioral testing: minimum functionality, invariance, directional

Toxic Content:

  • Train or fine-tune toxicity classifiers (Perspective API, Detoxify)
  • Implement input/output content filtering in the serving pipeline

Privacy:

  • PII detection and redaction before logging: names, emails, phone numbers
  • Differential privacy in training (DP-SGD) for sensitive data
  • Comply with GDPR/CCPA — allow users to request deletion of their data

Knowledge check

What is the key difference between intrinsic and extrinsic NLP evaluation?

NLP Module Summary

Congratulations on completing the Natural Language Processing module! Here is the full picture:

ChapterCore Concepts
1. PreprocessingTokenization, normalization, stemming, lemmatization
2. PhonologyPhonemes, MFCC, ASR (Whisper), CTC, TTS, WER
3. MorphologyMorphemes, POS tagging, chunking, WordNet
4. SyntaxCFG, CYK, PCFG, dependency parsing, Universal Dependencies
5. SemanticsLexical relations, WSD (Lesk/BERT), SRL (PropBank)
6. Discourse & PragmaticsCoreference (SpanBERT), RST, speech acts, sarcasm
7. RepresentationsBoW, TF-IDF, Word2Vec, GloVe, FastText, BERT, SBERT
8. Text ClassificationNaive Bayes, BERT fine-tuning, zero-shot (BART-MNLI), VADER
9. IE & NERIOB tagging, CRF, BERT NER, relation extraction, OpenIE
10. Machine TranslationSMT, seq2seq+attention, Transformer, BLEU, NLLB-200
11. QA & SummarizationSQuAD/BERT, RAG (DPR+FAISS), BART/T5, ROUGE
12. NLP ModelsN-grams, LSTM, Transformer, BERT/GPT/T5, LoRA, scaling laws
13. Evaluation & DeploymentGLUE, perplexity, FastAPI serving, drift monitoring, responsible AI

The modern NLP stack:

  • Foundation: Pre-trained Transformer (BERT, LLaMA, GPT-4)
  • Adaptation: Fine-tuning or PEFT (LoRA) on task-specific data
  • Retrieval: RAG for knowledge-intensive tasks
  • Serving: ONNX/TensorRT + FastAPI + batching
  • Monitoring: drift detection, confidence tracking, human feedback

Natural Language Processing