Quality Filtering › Classifier-Based Filtering

Michael BrenndoerferJanuary 10, 20269 min read
Language AI HandbookAll chapters
Part 20 of 67Data Curation
Chapter 144Quality Filtering › Classifier-Based Filtering
Reading section 5 of 11 · Classifier-Based Filtering · 9 minSection 5 of 11 · 9 min

Book contents

Section 5 of 11 · 9 min read

Quality Filtering › Classifier-Based FilteringOne section · Sections in this chapter5 / 11 ↓
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

Classifier-based filtering takes a more direct approach: train a binary classifier to predict whether a given document is high-quality or not. Rather than encoding rules or using indirect signals like perplexity, the classifier learns from examples of good and bad text. This makes it more flexible and often more accurate than heuristic or perplexity-based approaches, at the cost of requiring labeled training data and higher inference cost.

The classic implementation from OpenAI (used for the WebText dataset, which GPT-2 trained on) trains a logistic regression classifier on n-gram features extracted from two classes of text:

  • Positive examples: Text from outbound links on Reddit that received at least 3 upvotes. The assumption is that Reddit users curate quality content by voting, so pages linked from Reddit and upvoted represent human-approved text across a wide range of topics and styles.
  • Negative examples: Random Common Crawl pages.

The classifier then scores every Common Crawl document by its predicted probability of being a positive example. Documents below a threshold are discarded. This approach uses the crowdsourced curation signal embedded in Reddit voting without requiring any manual labeling by the research team.

The Reddit upvote signal is not perfect. Reddit's user base skews toward certain demographics, interests, and languages. Content that Reddit users find interesting may not be the same as content that makes the best language model training data. Despite this, the resulting WebText dataset and models trained on it showed that the Reddit-curated filtering dramatically improved quality over unfiltered Common Crawl.

The Logistic Regression Approach

The classifier trained on TF-IDF features over character n-grams is surprisingly effective for this task. Character n-grams capture low-level patterns (spelling, punctuation, character repetition) that distinguish boilerplate and spam from natural prose without requiring linguistic analysis. The character-level representation is language-agnostic and robust to vocabulary differences between the training examples and the documents being scored.

The TF-IDF weighting (term frequency times inverse document frequency) ensures that features that appear uniformly across all documents (common characters, punctuation) get down-weighted, while features that are distinctive to certain document types get up-weighted. The logistic regression then learns to combine these features into a scalar quality score.

In[10]:
Code
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report
from sklearn.model_selection import train_test_split

# Positive examples: high-quality web text
positive_examples = [
    "The study found that regular exercise reduces the risk of cardiovascular disease by 30 percent.",
    "Scientists have discovered a new species of deep-sea fish in the Mariana Trench.",
    "The Federal Reserve raised interest rates by 25 basis points in response to inflation data.",
    "Python 3.12 introduces several performance improvements including faster startup time.",
    "The novel explores themes of identity and belonging through the lens of immigrant experience.",
    "Researchers at Stanford developed an algorithm that predicts protein structures with high accuracy.",
    "Climate models suggest global temperatures could rise by 2 degrees by 2100 under current policies.",
    "The Supreme Court issued a ruling on Section 230 liability for online platforms.",
    "Astronomers detected gravitational waves from the merger of two neutron stars.",
    "The company reported quarterly earnings that exceeded analyst expectations by 15 percent.",
    "New research suggests that sleep deprivation impairs cognitive function significantly.",
    "The documentary examines the impact of social media on adolescent mental health.",
]

# Negative examples: low-quality/spam web text
negative_examples = [
    "Buy cheap insurance online get insurance quotes best rates insurance deals today.",
    "Click here now!!!! FREE MONEY LIMITED OFFER ACT NOW BEFORE ITS TOO LATE!!!",
    "Home About Contact Privacy Terms Copyright 2023 All Rights Reserved Footer Nav.",
    "var i=0;for(i=0;i<arr.length;i++){document.getElementById('div'+i).style.display='none';}",
    "best seo services cheap seo packages buy backlinks google rank first page guaranteed",
    "Lorem ipsum dolor sit amet consectetur adipiscing elit sed do eiusmod tempor incididunt",
    "!!! AMAZING DEAL !!! buy now pay later no credit check free shipping returns accepted !!!",
    "Copyright reserved terms privacy home about contact us sitemap footer links header nav",
    "casino online poker slots gambling win money jackpot bonus free spins play now sign up",
    "download free mp3 songs download movies free download software crack serial key keygen",
    "online pharmacy drugs buy prescription without prescription cheap medications fast delivery",
    "make money online work from home earn 500 daily passive income guaranteed no experience",
]

texts = positive_examples + negative_examples
labels = [1] * len(positive_examples) + [0] * len(negative_examples)

# TF-IDF on character n-grams (2-6 grams)
vectorizer = TfidfVectorizer(
    analyzer="char_wb",
    ngram_range=(2, 6),
    max_features=50000,
    sublinear_tf=True,
)

X = vectorizer.fit_transform(texts)
X_train, X_test, y_train, y_test = train_test_split(
    X, labels, test_size=0.3, random_state=42, stratify=labels
)

clf = LogisticRegression(C=1.0, max_iter=1000, random_state=42)
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
Out[11]:
Console
Classification Report:
              precision    recall  f1-score   support

 Low quality       0.75      0.75      0.75         4
High quality       0.75      0.75      0.75         4

    accuracy                           0.75         8
   macro avg       0.75      0.75      0.75         8
weighted avg       0.75      0.75      0.75         8
In[12]:
Code
# Score new documents using classifier probabilities
new_docs = {
    "Research finding": "A study published in Nature found that mRNA vaccines produce durable immune responses.",
    "Boilerplate nav": "Home Products Services Blog About Contact Privacy Terms Footer Copyright",
    "Spam": "earn money fast online 1000 per day guaranteed no experience needed click now",
    "Coherent opinion": "The debate over AI regulation centers on balancing innovation with safety concerns.",
    "Social post": "just got the new iphone and honestly its pretty good but the camera app is confusing",
}

new_texts = list(new_docs.values())
X_new = vectorizer.transform(new_texts)
quality_scores = clf.predict_proba(X_new)[:, 1]
Out[13]:
Console
Document                   Quality Score
------------------------------------------
Research finding                 0.539  (KEEP)
Boilerplate nav                  0.430  (FILTER)
Spam                             0.435  (FILTER)
Coherent opinion                 0.537  (KEEP)
Social post                      0.505  (KEEP)

The quality scores reflect the classifier's learned pattern: text that resembles curated web content scores high, while boilerplate, spam, and keyword-stuffed text scores low. The casual social media post sits near the boundary, where its classification is ambiguous. Whether to include informal social text depends on your training objectives: if you want the model to handle conversational queries, discarding all informal text is counterproductive.

This example uses a small training set for illustration. In production, these classifiers are trained on millions of positive and negative examples, which substantially improves their accuracy and generalization. The WebText classifier reportedly used millions of Reddit-linked pages as positives and millions of random crawl pages as negatives.

What Character N-Grams Capture

It is worth understanding why character-level n-gram features are so effective for quality classification. Character n-grams at the 2-6 gram range capture patterns like:

  • Repetition of exclamation marks ("!!!!") that signal spam
  • Camel-case identifiers ("getElementById") that signal code
  • Copyright boilerplate sequences ("rights reserved", "© 20")
  • Structural patterns of URLs (".html", "href=")
  • Distinctive spacing and punctuation patterns of natural prose

These patterns are robust because they occur at a finer granularity than words. A new spam campaign can use completely different vocabulary but will still use the same exclamation-mark repetition patterns. A new programming language will still produce code with distinctive symbol combinations. The character-level representation generalizes better than word-level features across the long tail of web content.

The char_wb analyzer in scikit-learn adds word-boundary tokens to the character n-grams, which captures the shapes of individual words (short words, long words, words with unusual character combinations) without building an explicit vocabulary. This makes it much harder to game: an adversarial spam author cannot simply replace flagged words with synonyms, because the character-level patterns of their writing style remain detectable.

Fasttext-Based Filtering

For production pipelines that process billions of documents, even logistic regression with sparse features can become a bottleneck. Facebook's fastText classifier offers a faster alternative. Trained on subword n-gram embeddings, fastText achieves near-linear-time inference and can be trained on large datasets in minutes.

The CCNet pipeline uses fastText for language identification, assigning each document to a language with a confidence score, and the same architecture works for quality classification. FastText learns a shallow embedding for each subword n-gram and sums them to produce document representations, which are then used for classification. The model is shallow (no deep neural layers) but captures rich character-level patterns through the n-gram vocabulary.

The RefinedWeb dataset (used to train Falcon) extends this approach with a quality classifier that incorporates multiple signals: URL patterns, document length distribution, and language model scores, combined with a neural classifier that learns to weight these features. This ensemble approach is more robust than any single signal because different signals catch different failure modes, and the neural combination learns which signal is most reliable for which type of document.

Self-Supervised Quality Signals

A more recent approach avoids the need for manual labeling entirely. Instead of asking "does this look like Reddit-approved content?", you can train on implicit quality signals extracted automatically:

  • Educational content detection: The Phi-1 paper introduced the idea of training classifiers to detect "textbook quality" text. A GPT-4 model rated a sample of documents on educational richness, and those ratings were used to train a smaller fastText classifier that then scored the full corpus. This transfers expensive LLM judgment to cheap classifier inference.
  • Model-as-teacher at scale: Use a strong language model (like GPT-4) to rate a sample of documents, then train a smaller classifier on those ratings. This allows you to scale expensive LLM ratings across billions of documents that would be too costly to rate directly.
  • Consistency with known facts: Score documents by how many verifiable factual claims they contain versus unsupported assertions. Pages with many specific, checkable claims (dates, measurements, named entities) tend to be more informative than vague or opinion-heavy content.
  • Citation and evidence quality: Academic-style text that cites sources and qualifies claims with evidence tends to be more reliable training data than unsupported assertions. Some pipelines use structural markers (citation patterns, hedging language like "according to" or "researchers found") as lightweight quality signals.

The "textbook quality" approach from the Phi-1 paper showed that filtering for educationally rich text and synthetically generating high-quality examples could produce capable small models from surprisingly little data. A 1.3B parameter model trained on 6B tokens of textbook-quality data matched models trained on 300B tokens of conventional web data on several coding benchmarks. This finding reinforced that the marginal value of data quality exceeds the marginal value of data quantity in many training regimes.