Model selection and evaluation
查準率、查全率與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)