Building a classifier end to end shows the core supervised NLP workflow: features, train/test split, evaluation.
NLTK classifiers work on featuresets: each example is a dict of feature names to values, paired with a label. You write a feature extractor function that turns a document into that dict, for example {"contains(great)": True, "contains(awful)": False, ...} over a chosen vocabulary.
The workflow:
- build
(features, label)pairs for every document, - shuffle and split into training and test sets (never evaluate on training data),
clf = nltk.NaiveBayesClassifier.train(train_set),nltk.classify.accuracy(clf, test_set),clf.classify(features)for one prediction,clf.prob_classify(...)for probabilities,clf.show_most_informative_features(10)to see which features drive decisions.
Naive Bayes assumes features are independent given the label. That's false for language, but it trains instantly, works well with many sparse features and is a strong baseline for sentiment and topic tasks.
The vocabulary is a design choice: the 2000 most frequent words is a common start. With True/False features for every vocabulary word, inputs should resemble the training documents: a five-word snippet is mostly "absent" features and gets classified poorly. Restrict it to training data only; picking features using the test set leaks information.
nltk.classify.SklearnClassifier wraps any scikit-learn estimator (such as LogisticRegression or LinearSVC) so it accepts the same featuresets, which usually beats Naive Bayes. For real projects with more data, scikit-learn's vectorizers or transformer models are the usual next step.
Example
import random
import nltk
from nltk.corpus import movie_reviews as mr
docs = [(list(mr.words(f)), cat) for cat in mr.categories() for f in mr.fileids(cat)]
random.seed(0)
random.shuffle(docs)
train_docs, test_docs = docs[:1600], docs[1600:]
fd = nltk.FreqDist(w.lower() for words, _ in train_docs for w in words if w.isalpha())
vocab = [w for w, _ in fd.most_common(2000)] # from training data only
def features(words):
present = set(w.lower() for w in words)
return {f"has({w})": (w in present) for w in vocab}
train_set = [(features(d), c) for d, c in train_docs]
test_set = [(features(d), c) for d, c in test_docs]
clf = nltk.NaiveBayesClassifier.train(train_set)
print(round(nltk.classify.accuracy(clf, test_set), 2)) # about 0.8
print(clf.classify(test_set[0][0]), test_set[0][1]) # predicted vs gold label
clf.show_most_informative_features(3)
# has(outstanding) = True pos : neg = 22.2 : 1.0 ...Remember
- Features are dicts produced by your own extractor function
- Train on one split, measure accuracy on a held-out split
- NaiveBayesClassifier is fast and interpretable via most_informative_features
- SklearnClassifier plugs scikit-learn models into the same workflow
Watch out for
- Building the vocabulary from all documents, including the test set, which inflates accuracy.