查准率、查全率与F1
对于二分类问题,可将样例根据其真实类别与学习器预测类别的组合划分为
- 真正例(true positive) :真的正样本,本来就是正样本
- 假正例(false positive) :假的正样本,其实是负样本
- 真反倒(true negative) :真的负样本,本来就是负样本
- 假反例(false negative) 假的负样本,其实是正样本
显然有TP+FP+TN+FN=样例总数.上述4种情况可以理解为: 以预测结果的角度对每个样本的判决产生看法
- 查准率/正确率P(确保预测为正的样本中有更多样本是正确的) :分类正确的正样本/所有判断为正的样本总数
- 查全率/召回率R(确保所有正样本中有更多样本被正确分类):,分类正确的正样本/数据集中的正样本
查准率和查全率是一对矛盾的度量.一般来说,查准率高时,查全率往往偏低;而查全率高时,查准率往往偏低.
例如,若希望将好瓜尽可能多地选出来,则可通过增加选瓜的数量来实现,如果将所有西瓜都选上,那么所有的好瓜也必然都被选上了,但这样查准率就会较低;若希望选出的瓜中好瓜比例尽可能高,则可只挑选最有把握的瓜, 但这样就难免会漏掉不少好瓜,使得查全率较低.通常只有在一些简单任务中才可能使查全率和查准率都很高.
P-R 曲线
在很多情形下我们可根据学习器的预测结果对样例进行排序,排在前面的是学习器认为"最可能"是正例的样本,排在最后的则是学习器认为"最不可能"是正例的样本.通过置信度就可以对所有样本进行排序,再逐个样本的选择阈值,在该样本之前的都属于正例,该样本之后的都属于负例。每一个样本作为划分阈值时,都可以计算对应的P和R.
- 若一个学习器的P-R 曲线被另一个学习器的曲线完全"包住" , 则可断言后者的性能优于前者,中学习器A 的性能优于学习器C;
- 两个学习器的P-R 曲线发生了交叉,
- "平衡点"一个度量,它是" 查准率=查全率"时的取值,越大性能越好,所以学习器A 优于B
- F1度量:,N为样例总数,
在一些应用中,对查准率和查全率的重视程度有所不同.例如在商品推荐系统中,为了尽可能少打扰用户,更希望推荐内容确是用户感兴趣的,此时查准率更重要;而在逃犯信息检索系统中,更希望尽可能少漏掉逃犯,此时查全率更重要. F1 度量的一般形式能让我们表达出对查准率/查全率的不同偏好,
退化为标准的F1; 时查全率有更大影响; 时查准率有更大影响.
ROC 与AUC
根据上一小节,在不同的应用任务中,我们可根据任务需求来采用不同的截断点,例如若我们更重视"查准率",则可选择排序中靠前的位置进行截断;若更重视"查全率",则可选择靠后的位置进行截断.因此,排序本身的质量好坏,体现了综合考虑学习器在不同任务下的"期望泛化性能"的好坏,或者说"一般情况下"泛化性能的好坏. ROC 曲线则是从这个角度出发来研究学习器泛化性能的有力工具.
- 纵轴是"真正例率" (True Positive Rate,TPR) .所有正样本中被正确分类的比例
- 横轴是"假正例率" (False PositiveRate,FPR).所有负样本中被错误分类的比例
- 若一个学习器的ROC曲线被另一个学习器的曲线完全"包住" , 则可断言后者的性能优于前者.
- 两个学习器的P-R 曲线发生了交叉, AUC度量,
import numpy as np
import matplotlib.pylab as plt
from sklearn.datasets import make_hastie_10_2
from sklearn.ensemble import AdaBoostClassifier
def plotROC(predStrengths, classLabels):
"""
每遇到一个+1标签,沿着y轴下降一个步长,降低真正例率;
每遇到一个其他标签,沿着x轴倒退一个步长,降低假正例率;
:param predStrengths:
:param classLabels:
:return:
"""
cursor = (1.0, 1.0) # 游标位置
ySum = 0.0 # 计算AUC的变量
numPositiveClass = sum(np.array(classLabels) == 1.0)
yStep = 1 / float(numPositiveClass) # 确定了y轴步长
xStep = 1 / float(len(classLabels) - numPositiveClass)# 确定了y轴步长
#数组值从小到大的索引值
sortedIndicies = predStrengths.argsort() #从小到大顺序排列,从(1.0,1.0)开始画一直到(0,0)
fig = plt.figure()
fig.clf()
ax = plt.subplot(111)
# loop through all the values, drawing a line segment at each point
for index in sortedIndicies.tolist():
if classLabels[index] == 1.0:
delX = 0
delY = yStep
else:
delX = xStep
delY = 0
ySum += cursor[1]
# draw line from cursor to (cursor[0]-delX,cursor[1]-delY)
ax.plot([cursor[0], cursor[0] - delX], [cursor[1], cursor[1] - delY], c='b')
cursor = (cursor[0] - delX, cursor[1] - delY)
ax.plot([0, 1], [0, 1], 'b--')
plt.xlabel('False positive rate');
plt.ylabel('True positive rate')
plt.title('ROC cursorve for AdaBoost horse colic detection system')
ax.axis([0, 1, 0, 1])
plt.show()
# 每个小矩形相加,矩形的宽度为xStep,因此对矩形的高度进行相加得到ySum
print("the Area Under the cursorve is: ", ySum * xStep)
if __name__ == "__main__":
X, y = make_hastie_10_2(n_samples=4000, random_state=1)
X_test, y_test = X[2000:], y[2000:]
X_train, y_train = X[:2000], y[:2000]
clf = AdaBoostClassifier(n_estimators=100)
clf.fit(X_train,y_train)
preds = clf.predict_proba(X_test)
plotROC(preds[:,1],y_test)