利用Python中的numpy包實現PR曲線和ROC曲線的計算

閒來無事,邊理解PR曲線和ROC曲線,邊寫了一下計算兩個指標的代碼。在python環境下,sklearn裏有現成的函數計算ROC曲線座標點,這裏爲了深刻理解這兩個指標,寫代碼的時候只用到numpy包。事實證實,實踐是檢驗真理的惟一標準,在手寫代碼的過程當中,才能真正體會到這兩個評判標準的一些小細節,代碼記錄以下。python

1、模擬一個預測結果

由於兩個曲線都是用來判斷一個分類器分類性能的,因此這裏直接用隨機數生成一組類別和對應的置信度。類別有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 ……

由於咱們的置信度是隨機生成的,因此獲得的結果等同於一個二分類器「瞎猜」的結果。函數

2、PR曲線

無論是PR曲線仍是ROC曲線,首先要選定一個類別,而後針對這個類別具體計算。性能

該曲線的橫座標是召回率(R),縱座標是精確度(P),故命名爲PR曲線。 舉一個簡單的例子來講明P和R的定義:假設一個二分類器須要預測100個樣本,這些樣本中有80個類別1,20個類別0。當把置信度取某一個值S時,假設此時分類器認爲有60個樣本是類別1,在預測的這60我的樣本中,有50個樣本預測正確,其他10個樣本預測錯誤。那麼,對於類別1的P、R值計算以下:spa

P =\frac{50}{60} = 0.833,\\ R = \frac{50}{80} = 0.625

即有0.667的機率預測正確,對於80個類別1的樣本,分類器比如能夠召喚神獸的魔法師,養了80只神獸,只召喚回來50只。因此召回率就是62.5%,其餘的就被無情丟棄了。翻譯

對於類別0來講,既然二分類器認爲類別1的有60個,那麼反過來其他40個都認爲是類別0,經過上述能夠推出這40個只有10個是類別0,其他的是類別1,因此對於類別0的P、R值計算以下:code

P =\frac{10}{40} = 0.25
R = \frac{10}{20} = 0.5

根據以上說明代碼實現以下: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曲線的理解有問題?

3、ROC曲線

ROC曲線的縱座標是TPR,橫座標是FPR(中文翻譯太亂了,我仍是習慣用英文表示)。TPR等同於PR曲線的召回率,FPR是全部被預測成正例的反例和真實反例的個數之比。

仍是以上那個例子,對於·類別1,二者的計算以下:

TPR = \frac{10}{20} = 0.5
FPR = \frac{10}{20} = 0.5
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曲線會稍微有些差別,取決於排序的結果。

附:

這幾個值確實挺繞的,附一張公式表,便於搞混的時候查詢:

1)分類結果混淆矩陣

在這裏插入圖片描述

2)P-R公式
P= \frac{TP}{TP+FP}
R = \frac{TP}{TP+FN}
3)TPR-FPR公式
TPR= \frac{TP}{TP+FN}
R = \frac{FP}{TN+FP}
相關文章
相關標籤/搜索