2010-05-26 20:40:29 +08:00
|
|
|
"""
|
|
|
|
|
================
|
|
|
|
|
Precision-Recall
|
|
|
|
|
================
|
|
|
|
|
|
2013-07-26 19:50:34 +08:00
|
|
|
Example of Precision-Recall metric to evaluate classifier output quality.
|
2013-07-26 01:12:57 +08:00
|
|
|
|
2013-08-01 09:13:22 +08:00
|
|
|
In information retrieval, precision is a measure of result relevancy, while
|
|
|
|
|
recall is a measure of how many truly relevant results are returned. A high
|
|
|
|
|
area under the curve represents both high recall and high precision, where high
|
|
|
|
|
precision relates to a low false positive rate, and high recall relates to a
|
|
|
|
|
low false negative rate. High scores for both show that the classifier is
|
|
|
|
|
returning accurate results (high precision), as well as returning a majority of
|
|
|
|
|
all positive results (high recall).
|
|
|
|
|
|
|
|
|
|
A system with high recall but low precision returns many results, but most of
|
|
|
|
|
its predicted labels are incorrect when compared to the training labels. A
|
|
|
|
|
system with high precision but low recall is just the opposite, returning very
|
2013-08-20 10:20:11 +08:00
|
|
|
few results, but most of its predicted labels are correct when compared to the
|
2013-08-01 09:13:22 +08:00
|
|
|
training labels. An ideal system with high precision and high recall will
|
|
|
|
|
return many results, with all results labeled correctly.
|
|
|
|
|
|
|
|
|
|
Precision (:math:`P`) is defined as the number of true positives (:math:`T_p`)
|
|
|
|
|
over the number of true positives plus the number of false positives
|
|
|
|
|
(:math:`F_p`).
|
2013-07-26 01:12:57 +08:00
|
|
|
|
|
|
|
|
:math:`P = \\frac{T_p}{T_p+F_p}`
|
|
|
|
|
|
2013-08-01 09:13:22 +08:00
|
|
|
Recall (:math:`R`) is defined as the number of true positives (:math:`T_p`)
|
|
|
|
|
over the number of true positives plus the number of false negatives
|
|
|
|
|
(:math:`F_n`).
|
2013-07-26 01:12:57 +08:00
|
|
|
|
|
|
|
|
:math:`R = \\frac{T_p}{T_p + F_n}`
|
|
|
|
|
|
2013-08-01 09:13:22 +08:00
|
|
|
These quantities are also related to the (:math:`F_1`) score, which is defined
|
|
|
|
|
as the harmonic mean of precision and recall.
|
2013-07-26 01:12:57 +08:00
|
|
|
|
|
|
|
|
:math:`F1 = 2\\frac{P \\times R}{P+R}`
|
|
|
|
|
|
2013-08-01 09:13:22 +08:00
|
|
|
It is important to note that the precision may not decrease with recall. The
|
|
|
|
|
definition of precision (:math:`\\frac{T_p}{T_p + F_p}`) shows that lowering
|
|
|
|
|
the threshold of a classifier may increase the denominator, by increasing the
|
|
|
|
|
number of results returned. If the threshold was previously set too high, the
|
|
|
|
|
new results may all be true positives, which will increase precision. If the
|
|
|
|
|
previous threshold was about right or too low, further lowering the threshold
|
|
|
|
|
will introduce false positives, decreasing precision.
|
|
|
|
|
|
|
|
|
|
Recall is defined as :math:`\\frac{T_p}{T_p+F_n}`, where :math:`T_p+F_n` does
|
|
|
|
|
not depend on the classifier threshold. This means that lowering the classifier
|
|
|
|
|
threshold may increase recall, by increasing the number of true positive
|
|
|
|
|
results. It is also possible that lowering the threshold may leave recall
|
|
|
|
|
unchanged, while the precision fluctuates.
|
|
|
|
|
|
|
|
|
|
The relationship between recall and precision can be observed in the
|
|
|
|
|
stairstep area of the plot - at the edges of these steps a small change
|
|
|
|
|
in the threshold considerably reduces precision, with only a minor gain in
|
2013-08-01 09:20:48 +08:00
|
|
|
recall. See the corner at recall = .59, precision = .8 for an example of this
|
2013-08-01 09:13:22 +08:00
|
|
|
phenomenon.
|
2013-07-26 19:33:10 +08:00
|
|
|
|
2013-09-20 21:56:56 +08:00
|
|
|
Precision-recall curves are typically used in binary classification to study
|
2013-09-20 19:42:04 +08:00
|
|
|
the output of a classifier. In order to extend Precision-recall curve and
|
|
|
|
|
average precision to multi-class or multi-label classification, it is necessary
|
|
|
|
|
to binarize the output. One curve can be drawn per label, but one can also draw
|
|
|
|
|
a precision-recall curve by considering each element of the label indicator
|
|
|
|
|
matrix as a binary prediction (micro-averaging).
|
|
|
|
|
|
2013-07-26 01:12:57 +08:00
|
|
|
.. note::
|
|
|
|
|
|
2013-08-01 21:02:15 +08:00
|
|
|
See also :func:`sklearn.metrics.average_precision_score`,
|
2013-08-01 21:37:22 +08:00
|
|
|
:func:`sklearn.metrics.recall_score`,
|
2013-09-20 19:42:04 +08:00
|
|
|
:func:`sklearn.metrics.precision_score`,
|
|
|
|
|
:func:`sklearn.metrics.f1_score`
|
2010-05-26 20:40:29 +08:00
|
|
|
"""
|
2013-02-01 22:04:03 +08:00
|
|
|
print(__doc__)
|
2010-05-26 20:40:29 +08:00
|
|
|
|
2014-05-15 04:31:03 +08:00
|
|
|
import matplotlib.pyplot as plt
|
2010-05-26 20:40:29 +08:00
|
|
|
import numpy as np
|
2015-10-24 00:40:11 +08:00
|
|
|
from itertools import cycle
|
|
|
|
|
|
2011-09-02 17:00:02 +08:00
|
|
|
from sklearn import svm, datasets
|
|
|
|
|
from sklearn.metrics import precision_recall_curve
|
2013-09-20 19:42:04 +08:00
|
|
|
from sklearn.metrics import average_precision_score
|
2015-09-11 02:26:39 +08:00
|
|
|
from sklearn.model_selection import train_test_split
|
2013-09-20 19:42:04 +08:00
|
|
|
from sklearn.preprocessing import label_binarize
|
|
|
|
|
from sklearn.multiclass import OneVsRestClassifier
|
2010-05-26 20:40:29 +08:00
|
|
|
|
|
|
|
|
# import some data to play with
|
|
|
|
|
iris = datasets.load_iris()
|
|
|
|
|
X = iris.data
|
|
|
|
|
y = iris.target
|
2013-09-20 19:42:04 +08:00
|
|
|
|
2015-10-24 00:40:11 +08:00
|
|
|
# setup plot details
|
|
|
|
|
colors = cycle(['navy', 'turquoise', 'darkorange', 'cornflowerblue', 'teal'])
|
|
|
|
|
lw = 2
|
|
|
|
|
|
2013-09-20 19:42:04 +08:00
|
|
|
# Binarize the output
|
|
|
|
|
y = label_binarize(y, classes=[0, 1, 2])
|
|
|
|
|
n_classes = y.shape[1]
|
2010-05-26 20:40:29 +08:00
|
|
|
|
|
|
|
|
# Add noisy features
|
2013-08-01 09:13:22 +08:00
|
|
|
random_state = np.random.RandomState(0)
|
2013-07-26 01:12:57 +08:00
|
|
|
n_samples, n_features = X.shape
|
2013-08-01 09:13:22 +08:00
|
|
|
X = np.c_[X, random_state.randn(n_samples, 200 * n_features)]
|
2010-05-26 20:40:29 +08:00
|
|
|
|
2013-07-26 01:12:57 +08:00
|
|
|
# Split into training and test
|
2013-08-01 09:13:22 +08:00
|
|
|
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.5,
|
|
|
|
|
random_state=random_state)
|
2013-07-26 01:12:57 +08:00
|
|
|
|
2010-05-26 20:40:29 +08:00
|
|
|
# Run classifier
|
2013-09-20 19:42:04 +08:00
|
|
|
classifier = OneVsRestClassifier(svm.SVC(kernel='linear', probability=True,
|
|
|
|
|
random_state=random_state))
|
|
|
|
|
y_score = classifier.fit(X_train, y_train).decision_function(X_test)
|
2010-05-26 20:40:29 +08:00
|
|
|
|
|
|
|
|
# Compute Precision-Recall and plot curve
|
2013-09-20 19:42:04 +08:00
|
|
|
precision = dict()
|
|
|
|
|
recall = dict()
|
|
|
|
|
average_precision = dict()
|
|
|
|
|
for i in range(n_classes):
|
|
|
|
|
precision[i], recall[i], _ = precision_recall_curve(y_test[:, i],
|
|
|
|
|
y_score[:, i])
|
|
|
|
|
average_precision[i] = average_precision_score(y_test[:, i], y_score[:, i])
|
|
|
|
|
|
|
|
|
|
# Compute micro-average ROC curve and ROC area
|
|
|
|
|
precision["micro"], recall["micro"], _ = precision_recall_curve(y_test.ravel(),
|
|
|
|
|
y_score.ravel())
|
|
|
|
|
average_precision["micro"] = average_precision_score(y_test, y_score,
|
|
|
|
|
average="micro")
|
|
|
|
|
|
2015-10-24 00:40:11 +08:00
|
|
|
|
2013-09-20 19:42:04 +08:00
|
|
|
# Plot Precision-Recall curve
|
2014-05-15 04:31:03 +08:00
|
|
|
plt.clf()
|
2015-10-24 00:40:11 +08:00
|
|
|
plt.plot(recall[0], precision[0], lw=lw, color='navy',
|
|
|
|
|
label='Precision-Recall curve')
|
2014-05-15 04:31:03 +08:00
|
|
|
plt.xlabel('Recall')
|
|
|
|
|
plt.ylabel('Precision')
|
|
|
|
|
plt.ylim([0.0, 1.05])
|
|
|
|
|
plt.xlim([0.0, 1.0])
|
|
|
|
|
plt.title('Precision-Recall example: AUC={0:0.2f}'.format(average_precision[0]))
|
|
|
|
|
plt.legend(loc="lower left")
|
|
|
|
|
plt.show()
|
2013-09-20 19:42:04 +08:00
|
|
|
|
|
|
|
|
# Plot Precision-Recall curve for each class
|
2014-05-15 04:31:03 +08:00
|
|
|
plt.clf()
|
2015-10-24 00:40:11 +08:00
|
|
|
plt.plot(recall["micro"], precision["micro"], color='gold', lw=lw,
|
2014-05-15 10:35:13 +08:00
|
|
|
label='micro-average Precision-recall curve (area = {0:0.2f})'
|
|
|
|
|
''.format(average_precision["micro"]))
|
2015-10-24 00:40:11 +08:00
|
|
|
for i, color in zip(range(n_classes), colors):
|
|
|
|
|
plt.plot(recall[i], precision[i], color=color, lw=lw,
|
2014-05-15 10:35:13 +08:00
|
|
|
label='Precision-recall curve of class {0} (area = {1:0.2f})'
|
|
|
|
|
''.format(i, average_precision[i]))
|
2013-09-20 19:42:04 +08:00
|
|
|
|
2014-05-15 04:31:03 +08:00
|
|
|
plt.xlim([0.0, 1.0])
|
|
|
|
|
plt.ylim([0.0, 1.05])
|
|
|
|
|
plt.xlabel('Recall')
|
|
|
|
|
plt.ylabel('Precision')
|
|
|
|
|
plt.title('Extension of Precision-Recall curve to multi-class')
|
|
|
|
|
plt.legend(loc="lower right")
|
|
|
|
|
plt.show()
|