序列標註 | (8) 中文分詞評估指標

原文地址

1. 背景

NLP中一個最基本任務就是分詞,當我們分詞完成之後怎麼來評判分詞結果的好壞呢?換句話來說就是我該如何對分詞結果打分?這個分數怎麼算法,依照的標準是什麼?例如:
在這裏插入圖片描述
對於分詞一和分詞二的打分應該是多少呢?爲了搞清楚這個問題,我們先來學習(回顧)一些機器學習中的常見分類評估標準。

2. 機器學習中的分類評估

  • 準確率
    準確率(accuracy),是一個用來衡量分類器預測結果與真實結果差異的一個指標,越接近於1說明分類結果越準確。舉個例子,比如現在有一個貓狗圖片分類器對100張圖片進行分類,分類結果顯示有38張圖片是貓,62張圖片是狗,經與真實標籤對比後發現,38張貓的圖片中有20張是分類正確的,62張狗的圖片中有57張是分類正確的,那麼準確率是多少呢?顯然就應該是 (20+57)/100=0.77(20+57)/100=0.77,即分對的數量除以總的數量就這麼簡單,這也是準確率好理解的原因之一。

同時可以發現,對於這100張圖片來說,其真實標籤爲狗75張,貓25張。那麼現在問題就來了,假如某分類器使壞,對於任何輸入讓其結果都輸出爲狗,那麼從整個結果來看就是狗的照片全都分對,貓的照片全都分錯,準確率爲 75/100=0.75 。可是仔細想想這有意義嗎?若是貓狗照片數量爲10和90,你啥也不做,就這麼一搞準確率就到 0.9 了,豈不無法無天?那我們應該這麼避免這種情況呢?那就要輪到精確率和召回率登場了。

  • 精確率、召回率和F-score
    什麼精確率(precision)與召回率(recall)呢?我們先用一個矩陣來把上面貓狗分類的結果給展示出來:
    在這裏插入圖片描述
    怎麼去讀這張表呢?首先從這張表可知:數據集中有25張貓的圖片,75張狗的圖片;同時對於左圖20這個值,其含義是將貓預測爲貓的數量爲20,同理5表示將貓預測爲狗的數量,18表示將狗預測成貓的數量,57表示將狗預測爲狗的數量。衍生開就是,如右圖所示(上圖把貓作爲正類):

1)TP(True Positive):表示將正樣本預測爲正樣本(真實爲正樣本,預測爲正樣本),即預測正確。
2)FP(False Positive):表示將負樣本預測爲正樣本(真實爲負樣本,預測爲正樣本),即預測錯誤;
3)FN(False Negtive):表示將正樣本預測爲負樣本(真實爲正樣本,預測爲負樣本),即預測錯誤;
4)TN(True Negtive):表示將負樣本預測爲負樣本(真實爲負樣本,預測爲負樣本),即預測正確;

圖p0140這個矩陣就稱爲混淆矩陣(confusion matrix),同時我們可以得到如下計算公式:
在這裏插入圖片描述
可以看到,精確率計算的是所有預測爲正類的樣本中有多少真實爲正樣本,而召回率計算的是真實爲正類的樣本中有多少被預測成正類。因此一般來說,召回率越高也就意味着這個模型找尋正樣本的能力越強。但值得注意的是,通常在實際任務中,並不明確哪一類是正樣本哪一類又是負樣本,所以每個類別,都可以計算其各項指標(最後對每個類別的指標計算平均即可(macro,micro,weighted)):
在這裏插入圖片描述
對於上面的計算過程,也可以通過sklearn中的包來完成。

from sklearn.metrics import classification_report,confusion_matrix
y = [0]*25 + [1]*75
y_pre =[0]*20+[1]*5+[0]*18+[1]*57
print(confusion_matrix(y,y_pre))
print(classification_report(y,y_pre,target_names=['cat','dog']))

在這裏插入圖片描述
此時,通過這三個指標,我們再來對比下面的極端情況:
在這裏插入圖片描述
對於貓來說:精確率、召回率、F1分別爲:0,0,0;
對於狗來說:精確率、召回率、F1分別爲:0.75,1,0.86
只用準確率:準確率爲0.75

還有一個問題就是,既然有了精確率和召回率那還搞了F1幹啥?這當然也是爲了避免一些極端情況。還是以上面的數據爲例,試想一下假如某個分類器說對於貓的識別,它的召回率能做到1,那麼這算是厲害還是不厲害呢?可能厲害也可能不厲害,厲害就是當貓狗所有類別都分類正確的情況下,但這是及其困難;還有一種作弊的方式就是將所有的樣本都預測成貓,那麼這樣將會得到如下混淆矩陣:
在這裏插入圖片描述
此時對於貓來說,其精確率、召回率、F1分別爲:0.25,1,0.4
總結就是,通過引入精確率,召回率能夠明顯的解決只用準確率的不足之處,同時加入F-score能夠解決召回率和精確率的不足之處。

3. NLP中的精確率、召回率和F-score

前面說到的分類中的評估標準,但是在分詞中標準答案和分詞結果數(詞數)不一定相等,因此要做一個思維轉換。對於長爲 n 的字符串,分詞結果是一系列單詞。設每個單詞按照其在文中的起止位置可記作區間 [i,j],其中1ijn1\leq i \leq j \leq n。那麼標準答案所有區間構成集合 A 作爲正類,其它情況作爲負類。同時,分詞結果(預測)所有單詞構成的區間集合爲 B 。那麼:
在這裏插入圖片描述
因此相應的計算公式如下:
在這裏插入圖片描述
這樣說可能有點晦澀,如下圖所示:
在這裏插入圖片描述
可以發現,重合部分就是正確部分;因此,對於分詞結果1來說,精確率和召回率均爲0,因爲沒有重合部分。而對於分詞結果2來說都爲1。下面再來看個例子:
在這裏插入圖片描述

4. OOV Recall 與 IV Recall

OOV指的是“未登錄詞”(Out Of Vocabulary)的簡稱,也就是新詞,已知詞典中不存在的詞。出現OOV的原因一方面可能確實是因爲產生了有意義的新詞而詞典並沒有收錄;另一方面可能就是因爲分詞器產生的錯誤無意義的分詞結果,這當然也不會出現在字典中。IV指的是“登陸詞”(In Vocabulary),也就是已經存在字典中的詞。而OOV Recall和IV Recall 分別指的就是OOV的召回率和IV的召回率。爲了說明這兩個召回率的具體含義,請先耐心看下面的詳細例子:
在這裏插入圖片描述
前面三項指標同第3節中的一樣,不在贅述。從上面的計算過程可以看到,OOV召回率等於重複詞區間未在詞典中出現的詞除以標準分詞中未在詞典中出現的詞。需要注意的是重複詞區間未在詞典中出現的詞就意味着未在字典中出現的新詞是有意義的,只是字典沒有收錄而已;同理標準分詞中未在詞典中出現的詞就更是如此。同時也可以將兩者分別稱爲重複詞區間有意義的新詞和所有有意義的新詞。有意義的新詞越多也就表示你用來分詞的字典收錄越不全(可能也會因爲詞語的顆粒度大小造成),而OOV recall越低也就意味着詞典分詞器對有意義新詞的發現或者說查找能力越低。
在這裏插入圖片描述
同理,從IV 召回率的計算公式可以發現重複詞區間在詞典中出現的詞指的就是分詞得到的正確部分(即正樣本);標準分詞中在詞典中出現的詞指的就是所有正樣本。因此,IV 召回率就可以來衡量詞典中的詞被正確找回的概率。如果IV召回率低,就說明字典分詞器連詞典中的詞彙都無法百分之百的發現或者找回,說明其消歧能力不好。例如“商品,和服,服務”三個詞都在詞典中,詞典分詞依然可能分不對句子”商品和服務“。

import re
 ​
 def to_region(segmentation: str) -> list:
     """
     將分詞結果轉換爲區間
     :param segmentation: 商品 和 服務
     :return: [(0, 2), (2, 3), (3, 5)]
     """
     region = []
     start = 0
     for word in re.compile("\\s+").split(segmentation.strip()):
         end = start + len(word)
         region.append((start, end))
         start = end
     return region
 ​
 ​
 def prf(gold: str, pred: str, dic) -> tuple:
     """
     計算P、R、F1
     :param gold: 標準答案文件,比如“商品 和 服務”
     :param pred: 分詞結果文件,比如“商品 和服 務”
     :param dic: 詞典
     :return: (P, R, F1, OOV_R, IV_R)
     """
     A_size, B_size, A_cap_B_size, OOV, IV, OOV_R, IV_R = 0, 0, 0, 0, 0, 0, 0
     A, B = set(to_region(gold)), set(to_region(pred))
     A_size += len(A)
     B_size += len(B)
     A_cap_B_size += len(A & B)
     text = re.sub("\\s+", "", gold)for (start, end) in A:
         word = text[start: end]
         if word in dic:
             IV += 1
         else:
             OOV += 1for (start, end) in A & B:
         word = text[start: end]
         if word in dic:
             IV_R += 1
         else:
             OOV_R += 1
     p, r = A_cap_B_size / B_size * 100, A_cap_B_size / A_size * 100
     return p, r, 2 * p * r / (p + r), OOV_R / OOV * 100, IV_R / IV * 100
 ​
 ​
 if __name__ == '__main__':
     dic = ['結婚', '尚未', '的', '和', '青年', '都', '應該', '好好考慮', '自己',  '人生', '大事']
     gold = '結婚 的 和 尚未 結婚 的 都 應該 好好 考慮 一下 人生 大事'
     pred = '結婚 的 和尚 未結婚 的 都 應該 好好考慮 一下 人生大事'
     print("Precision:%.2f Recall:%.2f F1:%.2f OOV-R:%.2f IV-R:%.2f" % prf(gold, pred, dic))

5. 總結

此處一共介紹了精確率、召回率、F-score、OOV召回率、IV召回率,其中前面三種指標可以用來衡量任意一種分詞器分詞結果的好壞;而後兩種指標則是用來衡量基於詞典分詞模型好壞的一個評估指標。同時,一定需要明白的是:精確率計算的是預測對的正樣本數佔整個預測爲正樣本數的比重(所有預測爲正類的樣本中,有多少真正爲正類),而召回率計算的是預測對的正樣本佔整個真實正樣本的比重(真實爲正類的樣本中,有多少被預測爲正類),而F-score則是對兩者的一個調和平均。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章