閒來無事,邊理解PR曲線和ROC曲線,邊寫了一下計算兩個指標的代碼。在python環境下,sklearn裏有現成的函數計算ROC曲線座標點,這裏爲了深刻理解這兩個指標,寫代碼的時候只用到numpy包。事實證實,實踐是檢驗真理的惟一標準,在手寫代碼的過程當中,才能真正體會到這兩個評判標準的一些小細節,代碼記錄以下。python
由於兩個曲線都是用來判斷一個分類器分類性能的,因此這裏直接用隨機數生成一組類別和對應的置信度。類別有0、1兩個類別。置信度從0到1隨機生成。app
data_len = 50
label = np.random.randint(0, 2, size=data_len)
score = np.random.choice(np.arange(0.1, 1, 0.01), data_len)
複製代碼
生成結果以下:其中第一行表明真實的類別,第二行表明分類器判斷目標是類別1的置信度。dom
label | 1 | 0 | 1 | 0 | 0 | 1 | 1 | …… |
---|---|---|---|---|---|---|---|---|
score | 0.22 | 0.31 | 0.92 | 0.34 | 0.37 | 0.18 | 0.51 | …… |
由於咱們的置信度是隨機生成的,因此獲得的結果等同於一個二分類器「瞎猜」的結果。函數
無論是PR曲線仍是ROC曲線,首先要選定一個類別,而後針對這個類別具體計算。性能
該曲線的橫座標是召回率(R),縱座標是精確度(P),故命名爲PR曲線。 舉一個簡單的例子來講明P和R的定義:假設一個二分類器須要預測100個樣本,這些樣本中有80個類別1,20個類別0。當把置信度取某一個值S時,假設此時分類器認爲有60個樣本是類別1,在預測的這60我的樣本中,有50個樣本預測正確,其他10個樣本預測錯誤。那麼,對於類別1的P、R值計算以下:spa
即有0.667的機率預測正確,對於80個類別1的樣本,分類器比如能夠召喚神獸的魔法師,養了80只神獸,只召喚回來50只。因此召回率就是62.5%,其餘的就被無情丟棄了。翻譯
對於類別0來講,既然二分類器認爲類別1的有60個,那麼反過來其他40個都認爲是類別0,經過上述能夠推出這40個只有10個是類別0,其他的是類別1,因此對於類別0的P、R值計算以下:code
根據以上說明代碼實現以下:cdn
def PR_curve(y,pred):
pos = np.sum(y == 1)
neg = np.sum(y == 0)
pred_sort = np.sort(pred)[::-1] # 從大到小排序
index = np.argsort(pred)[::-1] # 從大到小排序
y_sort = y[index]
print(y_sort)
Pre = []
Rec = []
for i, item in enumerate(pred_sort):
if i == 0:#由於計算precision的時候分母要用到i,當i爲0時會出錯,因此單獨列出
Pre.append(1)
Rec.append(0)
else:
Pre.append(np.sum((y_sort[:i] == 1)) /i)
Rec.append(np.sum((y_sort[:i] == 1)) / pos)
print(Pre)
print(Rec)
## 畫圖
plt.plot(Rec, Pre, 'k')
# plt.legend(loc='lower right')
plt.title('Receiver Operating Characteristic')
plt.plot([(0, 0), (1, 1)], 'r--')
plt.xlim([-0.01, 1.01])
plt.ylim([-0.01, 01.01])
plt.ylabel('Precision')
plt.xlabel('Recall')
plt.show()
複製代碼
畫出的PR曲線:blog
這裏有個疑惑:在西瓜書裏,PR曲線是過(1,0),(0,1)兩個點的曲線,可是(1,0)這個點總以爲不太可能,是我對PR曲線的理解有問題?ROC曲線的縱座標是TPR,橫座標是FPR(中文翻譯太亂了,我仍是習慣用英文表示)。TPR等同於PR曲線的召回率,FPR是全部被預測成正例的反例和真實反例的個數之比。
仍是以上那個例子,對於·類別1,二者的計算以下:
def ROC_curve(y,pred):
pos = np.sum(y == 1)
neg = np.sum(y == 0)
pred_sort = np.sort(pred)[::-1] #從大到小排序
index = np.argsort(pred)[::-1]#從大到小排序
y_sort = y[index]
print(y_sort)
tpr = []
fpr = []
thr = []
for i,item in enumerate(pred_sort):
tpr.append(np.sum((y_sort[:i] == 1)) / pos)
fpr.append(np.sum((y_sort[:i] == 0)) / neg)
thr.append(item)
print(fpr)
print(tpr)
print(thr)
#畫圖
plt.plot(fpr, tpr, 'k')
plt.title('Receiver Operating Characteristic')
plt.plot([(0,0),(1,1)],'r--')
plt.xlim([-0.01,1.01])
plt.ylim([-0.01,01.01])
plt.ylabel('True Positive Rate')
plt.xlabel('False Positive Rate')
plt.show()
複製代碼
結果以下:
能夠看到,隨機瞎猜的分類器獲得的ROC曲線在y=x這條直線周圍抖動。若是咱們把隨機生成的置信度只保留小數點後一位,那麼數據裏有不少相同置信度的值。這種方式每次計算出來的ROC曲線會稍微有些差別,取決於排序的結果。
這幾個值確實挺繞的,附一張公式表,便於搞混的時候查詢: