【火爐煉AI】機器學習011-分類模型的評估:準確率,精確率,召回率,F1值

煉丹老頑童發表於2018-08-06

【火爐煉AI】機器學習011-分類模型的評估:準確率,精確率,召回率,F1值

(本文所使用的Python庫和版本號: Python 3.5, Numpy 1.14, scikit-learn 0.19, matplotlib 2.2 )

在前面的(【火爐煉AI】機器學習004-嶺迴歸器的構建和模型評估)中,講解了迴歸模型的評估方法,主要有均方誤差MSE, 解釋方差分,R方得分等指標。

同樣的,對於分類模型,也有很多評估指標來判斷該分類模型是否達到我們的要求,這幾個評估指標主要是指:準確率(accuracy),精確率(precision),召回率(recall),F1值(F1 measure)。


1. 指標的基本概念和計算方法

1.1 準確率(Accuracy)

準確率的定義是:對於給定的測試集,分類模型正確分類的樣本數與總樣本數之比。舉個例子來講,有一個簡單的二分類模型(暫時叫做Classifier_A),專門用於分類蘋果和梨,在某個測試集中,有30個蘋果+70個梨,這個二分類模型在對這個測試集進行分類的時候,得出該資料集有40個蘋果(包括正確分類的25個蘋果和錯誤分類的15個梨)和60個梨(包括正確分類的55個梨和錯誤分類的5個蘋果)。畫成矩陣圖為:

【火爐煉AI】機器學習011-分類模型的評估:準確率,精確率,召回率,F1值

從圖中可以看出,行表示該測試集中實際的類別,比如蘋果類一共有25+5=30個,梨類有15+55=70個。其中被分類模型正確分類的是該表格的對角線所在的數字。在sklearn中,這樣一個表格被命名為混淆矩陣(Confusion Matrix),所以,按照準確率的定義,可以計算出該分類模型在測試集上的準確率為:

【火爐煉AI】機器學習011-分類模型的評估:準確率,精確率,召回率,F1值

即,該分類模型在測試集上的準確率為80%。

但是,準確率指標並不總是能夠評估一個模型的好壞,比如對於下面的情況,假如有一個資料集,含有98個蘋果,2個梨,而分類器(暫時叫做Classifier_B)是一個很差勁的分類器,它把資料集的所有樣本都劃分為蘋果,也就是不管輸入什麼樣的樣本,該模型都認為該樣本是蘋果。那麼這個表格會是什麼樣的了?

【火爐煉AI】機器學習011-分類模型的評估:準確率,精確率,召回率,F1值

則該模型的準確率為98%,因為它正確地識別出來了測試集中的98個蘋果,只是錯誤的把2個梨也當做蘋果,所以按照準確率的計算公式,該模型有高達98%的準確率。

可是,這樣的模型有意義嗎?一個把所有樣本都預測為蘋果的模型,反而得到了非常高的準確率,那麼問題出在哪兒了?只能說準確率不可信。特別是對於這種樣品數量偏差比較大的問題,準確率的“準確度”會極大的下降。所以,這時就需要引入其他評估指標評價模型的好壞了。

1.2 精確率(Precision)

精確率的定義是:對於給定測試集的某一個類別,分類模型預測正確的比例,或者說:分類模型預測的正樣本中有多少是真正的正樣本,其計算公式是:

【火爐煉AI】機器學習011-分類模型的評估:準確率,精確率,召回率,F1值

所以,根據定義,精確率要區分不同的類別,比如上面我們討論有兩個類別,所以要分類來計算各自的精確率。對於上面提到的Classifier_A和Classifier_B分類模型,我們可以分別計算出其精確率:

【火爐煉AI】機器學習011-分類模型的評估:準確率,精確率,召回率,F1值

在很多文章中,都講到把某一個類別當做正類(Positive),把其他類別當做負類(Negative),然後只關注正類的精確率。但是,有的時候我們不知道哪一個類作為正類更合適,比如此處的二分類問題,我們可以把蘋果當做正類,也可以把梨當做正類,兩者計算出來的精確率明顯是不一樣的。

1.3 召回率(Recall)

召回率的定義為:對於給定測試集的某一個類別,樣本中的正類有多少被分類模型預測正確,其計算公式為:

【火爐煉AI】機器學習011-分類模型的評估:準確率,精確率,召回率,F1值

同樣的,召回率也要考慮某一個類別,比如,下面我們將蘋果作為正類,在Classifier_A模型下得到的表格為:

【火爐煉AI】機器學習011-分類模型的評估:準確率,精確率,召回率,F1值

如果我們將梨作為正類,在Classifier_A模型下得到的表格為:

【火爐煉AI】機器學習011-分類模型的評估:準確率,精確率,召回率,F1值

同樣的,可以計算出上面兩個模型對給定測試集的召回率,如下表所示:

【火爐煉AI】機器學習011-分類模型的評估:準確率,精確率,召回率,F1值

1.4 F1值(F1-Measure)

在理想情況下,我們希望模型的精確率越高越好,同時召回率也越高越高,但是,現實情況往往事與願違,在現實情況下,精確率和召回率像是坐在蹺蹺板上一樣,往往出現一個值升高,另一個值降低,那麼,有沒有一個指標來綜合考慮精確率和召回率了,這個指標就是F值。F值的計算公式為:

【火爐煉AI】機器學習011-分類模型的評估:準確率,精確率,召回率,F1值

式中:P: Precision, R: Recall, a:權重因子。

當a=1時,F值便是F1值,代表精確率和召回率的權重是一樣的,是最常用的一種評價指標。F1的計算公式為:

【火爐煉AI】機器學習011-分類模型的評估:準確率,精確率,召回率,F1值

所以根據上面的精確率和召回率,可以輕鬆的計算出這兩個模型的F1值:

【火爐煉AI】機器學習011-分類模型的評估:準確率,精確率,召回率,F1值


2. 用sklearn計算精確率,召回率,F1值

上面第一部分,我們都是手動計算出每種分類模型的各種評價指標,但是上帝告訴我們,他已經幫我們造好了輪子,我們只需要直接呼叫即可。廢話少說,直接把計算這些評價指標的程式碼貼過來:

2.1 用sklearn計算分類結果的混淆矩陣

# 假如有一個模型在測試集上得到的預測結果為:
y_true = [1, 0, 0, 2, 1, 0, 3, 3, 3] # 實際的類別
y_pred = [1, 1, 0, 2, 1, 0, 1, 3, 3] # 模型預測的類別

# 使用sklearn 模組計算混淆矩陣
from sklearn.metrics import confusion_matrix
confusion_mat = confusion_matrix(y_true, y_pred)
print(confusion_mat) #看看混淆矩陣長啥樣
複製程式碼

-------------------------------------輸---------出--------------------------------

[[2 1 0 0] [0 2 0 0] [0 0 1 0] [0 1 0 2]]

--------------------------------------------完-------------------------------------

其實這個矩陣的產生過程很簡單,比如實際類別是3(y_true=3)一共有三個樣本,其中兩個預測正確,一個預測錯誤成1,結果如表格所示:

【火爐煉AI】機器學習011-分類模型的評估:準確率,精確率,召回率,F1值

2.2 混淆矩陣視覺化

上面雖然把混淆矩陣列印出來了,但是結果很難直接觀察對比,下面將混淆矩陣視覺化為圖表,便於觀察。

def plot_confusion_matrix(confusion_mat):
    '''將混淆矩陣畫圖並顯示出來'''
    plt.imshow(confusion_mat, interpolation='nearest', cmap=plt.cm.gray)
    plt.title('Confusion matrix')
    plt.colorbar()
    tick_marks = np.arange(confusion_mat.shape[0])
    plt.xticks(tick_marks, tick_marks)
    plt.yticks(tick_marks, tick_marks)
    plt.ylabel('True label')
    plt.xlabel('Predicted label')
    plt.show()

plot_confusion_matrix(confusion_mat)
複製程式碼

混淆矩陣結果圖

2.3 列印模型的效能報告

Sklearn模組中已經有成熟的對於模型評估的效能報告,裡面已經整合了分類模型的精確率,召回率和F1值資訊,可以說,是我們分析模型的必備良器。

# 列印該模型的效能報告
# 直接使用sklearn列印精度,召回率和F1值
from sklearn.metrics import classification_report
target_names = ['Class-0', 'Class-1', 'Class-2', 'Class-3']
print(classification_report(y_true, y_pred, target_names=target_names))
複製程式碼

-------------------------------------輸---------出--------------------------------

precision recall f1-score support

Class-0 1.00 0.67 0.80 3 Class-1 0.50 1.00 0.67 2 Class-2 1.00 1.00 1.00 1 Class-3 1.00 0.67 0.80 3 avg / total 0.89 0.78 0.79 9

--------------------------------------------完-------------------------------------

這個效能報告表中的評估指標的計算方法如前面第一節講述的,比如對於Precision,對於Class_1, Precision=TP/(TP+FP)=2/(2+1+0+1)=0.50,而對於Class_2: Precision=1/(1+0+0+0)=1.0.

對於Recall的計算方法,比如,對於Class_0=TP/(TP+FN)=2/(2+1+0+0)=0.67.所以可以看出,Precision的演算法是在混淆矩陣的縱向上運算,而Recall的演算法是在混淆矩陣的橫向上運算。

F1-score的計算便是直接套用公式即可。後面的support是真實的各個類別的樣本數量。

########################小**********結###############################

1,sklearn中已經有成熟的方法可以直接計算混淆矩陣和列印效能報告,這些函式對於模型的評估非常有幫助。

2,精確率,召回率,F1值的計算其實很簡單,只要弄明白各自對應的公式,弄明白公式所代表的具體含義即可。

#################################################################


3. 評估樸素貝葉斯多分類模型

在文章【火爐煉AI】機器學習010-用樸素貝葉斯分類器解決多分類問題中我們使用了高斯樸素貝葉斯分類器構建了一個多分類模型,將該資料整合功分成了四個類別,貌似結果很不錯,可是該文章沒有提高用可以量化的效能指標來評估該分類模型的好壞。此處我們可以用上面講到的各種評價指標來評估該分類模型。

3.1 用交叉驗證檢驗模型的準確性

先載入資料集,然後劃分為train set 和testset。構建樸素貝葉斯分類模型,用trainset進行訓練,然後用sklearn中的cross_val_score來輸出該分類模型在test set上的整體表現,程式碼如下:

# 評估樸素貝葉斯多分類模型
# 1, 準備資料集
data_path='D:\PyProjects\DataSet/NaiveBayers/data_multivar.txt'
df=pd.read_csv(data_path,header=None)
dataset_X,dataset_y=df.iloc[:,:-1],df.iloc[:,-1]
dataset_X=dataset_X.values
dataset_y=dataset_y.values
# 將整個資料集劃分為train set和test set
from sklearn.cross_validation import train_test_split
train_X, test_X, train_y, test_y=train_test_split(dataset_X,dataset_y,test_size=0.25,random_state=42)

# 構建樸素貝葉斯分類模型
from sklearn.naive_bayes import GaussianNB
gaussianNB_new=GaussianNB()
gaussianNB_new.fit(train_X,train_y)

# 2 用交叉驗證來檢驗模型的準確性,只是在test set上驗證準確性
from sklearn.cross_validation import cross_val_score
num_validations=5
accuracy=cross_val_score(gaussianNB_new,test_X,test_y,
                         scoring='accuracy',cv=num_validations)
print('準確率:{:.2f}%'.format(accuracy.mean()*100))
precision=cross_val_score(gaussianNB_new,test_X,test_y,
                         scoring='precision_weighted',cv=num_validations)
print('精確度:{:.2f}%'.format(precision.mean()*100))
recall=cross_val_score(gaussianNB_new,test_X,test_y,
                         scoring='recall_weighted',cv=num_validations)
print('召回率:{:.2f}%'.format(recall.mean()*100))
f1=cross_val_score(gaussianNB_new,test_X,test_y,
                         scoring='f1_weighted',cv=num_validations)
print('F1  值:{:.2f}%'.format(f1.mean()*100))
複製程式碼

-------------------------------------輸---------出--------------------------------

準確率:99.00% 精確度:99.17% 召回率:99.00% F1 值:98.97%

--------------------------------------------完-------------------------------------

3.2 檢視該模型在測試集上的混淆矩陣和效能報告

直接上程式碼,沒什麼好講的。

# 使用sklearn 模組計算混淆矩陣
y_pred=gaussianNB_new.predict(test_X)
confusion_mat = confusion_matrix(test_y, y_pred)
print(confusion_mat) #看看混淆矩陣長啥樣

print('*'*50)
# 列印該模型的效能報告
# 直接使用sklearn列印精度,召回率和F1值
target_names = ['Class-0', 'Class-1', 'Class-2', 'Class-3']
print(classification_report(test_y, y_pred, target_names=target_names))
複製程式碼

-------------------------------------輸---------出--------------------------------

[[27 0 0 0] [ 0 18 0 0] [ 0 0 33 0] [ 0 0 0 22]]

precision recall f1-score support

Class-0 1.00 1.00 1.00 27 Class-1 1.00 1.00 1.00 18 Class-2 1.00 1.00 1.00 33 Class-3 1.00 1.00 1.00 22

avg / total 1.00 1.00 1.00 100

--------------------------------------------完-------------------------------------

########################小**********結###############################

1,對於樸素貝葉斯解決這裡的多分類問題,從模型檢測結果可以看出,該分類器可以很好地將這個資料集的四個類別區分開來,得到的準確率,精確率,召回率,F1值等都比較好。

2,該分類器在測試集上的混淆矩陣都只有TP,沒有FP或FN,表明該分類器可以100%的將這測試集中的四個類別區分開來。

3,在交叉驗證時,得到的結果並不是100%,而是99%以上,原因很有可能是交叉驗證是採用多步驗證來計算這些指標,評估得到的結果可能更可靠一些。

#################################################################


注:本部分程式碼已經全部上傳到(我的github)上,歡迎下載。

參考資料:

1, Python機器學習經典例項,Prateek Joshi著,陶俊傑,陳小莉譯

相關文章