scikit-learn/examples/document_classification_20n...

271 lines
8.5 KiB
Python
Raw Normal View History

"""
======================================================
Classification of text documents using sparse features
======================================================
2012-08-21 14:17:44 +08:00
This is an example showing how scikit-learn can be used to classify documents
by topics using a bag-of-words approach. This example uses a scipy.sparse
matrix to store the features and demonstrates various classifiers that can
efficiently handle sparse matrices.
2012-08-21 14:17:44 +08:00
The dataset used in this example is the 20 newsgroups dataset. It will be
automatically downloaded, then cached.
2012-08-21 14:17:44 +08:00
The bar plot indicates the accuracy, training time (normalized) and test time
(normalized) of each classifier.
"""
# Author: Peter Prettenhofer <peter.prettenhofer@gmail.com>
# Olivier Grisel <olivier.grisel@ensta.org>
# Mathieu Blondel <mathieu@mblondel.org>
# Lars Buitinck <L.J.Buitinck@uva.nl>
# License: Simplified BSD
import logging
import numpy as np
from optparse import OptionParser
import sys
from time import time
2012-03-04 00:52:11 +08:00
import pylab as pl
2010-11-12 14:35:00 +08:00
from sklearn.datasets import fetch_20newsgroups
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.feature_selection import SelectKBest, chi2
from sklearn.linear_model import RidgeClassifier
from sklearn.svm import LinearSVC
2012-01-14 21:40:29 +08:00
from sklearn.linear_model import SGDClassifier
from sklearn.linear_model import Perceptron
from sklearn.naive_bayes import BernoulliNB, MultinomialNB
from sklearn.neighbors import KNeighborsClassifier
from sklearn.neighbors import NearestCentroid
from sklearn.utils.extmath import density
from sklearn import metrics
# Display progress logs on stdout
logging.basicConfig(level=logging.INFO,
format='%(asctime)s %(levelname)s %(message)s')
# parse commandline arguments
op = OptionParser()
op.add_option("--report",
action="store_true", dest="print_report",
help="Print a detailed classification report.")
op.add_option("--chi2_select",
action="store", type="int", dest="select_chi2",
help="Select some number of features using a chi-squared test")
op.add_option("--confusion_matrix",
action="store_true", dest="print_cm",
help="Print the confusion matrix.")
op.add_option("--top10",
action="store_true", dest="print_top10",
help="Print ten most discriminative terms per class"
" for every classifier.")
2012-08-21 13:42:25 +08:00
op.add_option("--all_categories",
action="store_true", dest="all_categories",
help="Whether to use all categories or not.")
(opts, args) = op.parse_args()
if len(args) > 0:
op.error("this script takes no arguments.")
sys.exit(1)
print __doc__
op.print_help()
print
###############################################################################
# Load some categories from the training set
2012-08-21 13:42:25 +08:00
if opts.all_categories:
categories = None
else:
categories = [
'alt.atheism',
'talk.religion.misc',
'comp.graphics',
'sci.space',
]
print "Loading 20 newsgroups dataset for categories:"
print categories if categories else "all"
data_train = fetch_20newsgroups(subset='train', categories=categories,
2011-05-04 15:45:16 +08:00
shuffle=True, random_state=42)
data_test = fetch_20newsgroups(subset='test', categories=categories,
2011-05-04 15:45:16 +08:00
shuffle=True, random_state=42)
print 'data loaded'
categories = data_train.target_names # for case categories == None
print "%d documents (training set)" % len(data_train.data)
print "%d documents (testing set)" % len(data_test.data)
print "%d categories" % len(categories)
print
# split a training set and a test set
y_train, y_test = data_train.target, data_test.target
print "Extracting features from the training dataset using a sparse vectorizer"
t0 = time()
vectorizer = TfidfVectorizer(sublinear_tf=True, max_df=0.5,
stop_words='english')
X_train = vectorizer.fit_transform(data_train.data)
print "done in %fs" % (time() - t0)
print "n_samples: %d, n_features: %d" % X_train.shape
print
print "Extracting features from the test dataset using the same vectorizer"
t0 = time()
X_test = vectorizer.transform(data_test.data)
print "done in %fs" % (time() - t0)
print "n_samples: %d, n_features: %d" % X_test.shape
2010-11-14 00:48:02 +08:00
print
if opts.select_chi2:
print ("Extracting %d best features by a chi-squared test" %
opts.select_chi2)
t0 = time()
2011-07-22 22:21:21 +08:00
ch2 = SelectKBest(chi2, k=opts.select_chi2)
X_train = ch2.fit_transform(X_train, y_train)
X_test = ch2.transform(X_test)
print "done in %fs" % (time() - t0)
print
def trim(s):
"""Trim string to fit on terminal (assuming 80-column display)"""
return s if len(s) <= 80 else s[:77] + "..."
2012-03-04 02:19:33 +08:00
# mapping from integer feature name to original token string
feature_names = np.asarray(vectorizer.get_feature_names())
2012-03-04 02:19:33 +08:00
###############################################################################
# Benchmark classifiers
def benchmark(clf):
2010-11-14 00:48:02 +08:00
print 80 * '_'
print "Training: "
print clf
t0 = time()
clf.fit(X_train, y_train)
train_time = time() - t0
print "train time: %0.3fs" % train_time
t0 = time()
pred = clf.predict(X_test)
test_time = time() - t0
print "test time: %0.3fs" % test_time
score = metrics.f1_score(y_test, pred)
print "f1-score: %0.3f" % score
if hasattr(clf, 'coef_'):
print "dimensionality: %d" % clf.coef_.shape[1]
print "density: %f" % density(clf.coef_)
if opts.print_top10:
print "top 10 keywords per class:"
for i, category in enumerate(categories):
2011-12-20 01:16:51 +08:00
top10 = np.argsort(clf.coef_[i])[-10:]
2012-03-04 02:19:33 +08:00
print trim("%s: %s" % (
category, " ".join(feature_names[top10])))
print
if opts.print_report:
print "classification report:"
print metrics.classification_report(y_test, pred,
target_names=categories)
if opts.print_cm:
print "confusion matrix:"
print metrics.confusion_matrix(y_test, pred)
print
2012-03-04 00:52:11 +08:00
clf_descr = str(clf).split('(')[0]
return clf_descr, score, train_time, test_time
2012-03-04 00:52:11 +08:00
results = []
for clf, name in ((RidgeClassifier(tol=1e-2, solver="lsqr"), "Ridge Classifier"),
(Perceptron(n_iter=50), "Perceptron"),
(KNeighborsClassifier(n_neighbors=10), "kNN")):
print 80 * '='
print name
2012-03-04 00:52:11 +08:00
results.append(benchmark(clf))
for penalty in ["l2", "l1"]:
2011-02-27 09:10:26 +08:00
print 80 * '='
print "%s penalty" % penalty.upper()
# Train Liblinear model
2012-05-06 03:02:28 +08:00
results.append(benchmark(LinearSVC(loss='l2', penalty=penalty,
2012-03-04 00:52:11 +08:00
dual=False, tol=1e-3)))
# Train SGD model
2012-03-04 00:52:11 +08:00
results.append(benchmark(SGDClassifier(alpha=.0001, n_iter=50,
penalty=penalty)))
# Train SGD with Elastic Net penalty
2011-02-27 09:19:14 +08:00
print 80 * '='
print "Elastic-Net penalty"
2012-03-04 00:52:11 +08:00
results.append(benchmark(SGDClassifier(alpha=.0001, n_iter=50,
penalty="elasticnet")))
# Train NearestCentroid without threshold
print 80 * '='
print "NearestCentroid (aka Rocchio classifier)"
results.append(benchmark(NearestCentroid()))
# Train sparse Naive Bayes classifiers
print 80 * '='
print "Naive Bayes"
2012-03-04 00:52:11 +08:00
results.append(benchmark(MultinomialNB(alpha=.01)))
results.append(benchmark(BernoulliNB(alpha=.01)))
2011-12-20 01:16:51 +08:00
class L1LinearSVC(LinearSVC):
def fit(self, X, y):
# The smaller C, the stronger the regularization.
# The more regularization, the more sparsity.
2012-05-06 03:02:28 +08:00
self.transformer_ = LinearSVC(penalty="l1",
dual=False, tol=1e-3)
X = self.transformer_.fit_transform(X, y)
return LinearSVC.fit(self, X, y)
def predict(self, X):
X = self.transformer_.transform(X)
return LinearSVC.predict(self, X)
print 80 * '='
print "LinearSVC with L1-based feature selection"
2012-05-06 03:02:28 +08:00
results.append(benchmark(L1LinearSVC()))
2012-03-04 00:52:11 +08:00
# make some plots
indices = np.arange(len(results))
results = [[x[i] for x in results] for i in xrange(4)]
clf_names, score, training_time, test_time = results
2012-08-21 14:17:44 +08:00
training_time = np.array(training_time) / np.max(training_time)
test_time = np.array(test_time) / np.max(test_time)
2012-03-04 00:52:11 +08:00
pl.title("Score")
pl.barh(indices, score, .2, label="score", color='r')
pl.barh(indices + .3, training_time, .2, label="training time", color='g')
pl.barh(indices + .6, test_time, .2, label="test time", color='b')
pl.yticks(())
pl.legend(loc='best')
pl.subplots_adjust(left=.25)
for i, c in zip(indices, clf_names):
pl.text(-.3, i, c)
pl.show()