2019-03 NER命令實體識別歸納

1. 基本概念

? 概念:一般來說,NER的標注列表為['O' ,'B-MISC', 'I-MISC', 'B-ORG' ,'I-ORG', 'B-PER' ,'I-PER', 'B-LOC' ,'I-LOC']。其中,一般一共分為四大類:PER(人名),LOC(位置),ORG(組織)以及MISC,而且B表示開始,I表示中間,O表示單字詞。
? 評估指標:一般看Acc、Precision、Recall、F1值和ROC、AUC。但是由于在很多場景中,'O'的這個label出現(xiàn)次數(shù)是最多的,所以光看Acc肯定是沒用的咯!
? CRF學到的constrains:
? ? 1. 句子首個單詞接下來的不是“B-“ 就是 “O”, 而非 “I-“
? ? 2. “B-label1 I-label2 I-label3 I-…”中的label1, label2, label3 … 應該是相同的實體標簽。
? ? 3. 等等很多標簽的搭配問題。

2. 數(shù)據(jù)清洗

? 1. 控制句子長度,100左右吧。
? 2. 符號清理。
? 3. 舍去全是‘O’的標注句子。
? 4. 如果對數(shù)字識別不做要求,干脆轉換成0進行識別,做成統(tǒng)一符號。

3. 模型

? 當前來講,主流的有兩種:一種是BiLSTM+CRF的;另一種是BERT直接預測的。當然,對于BERT模型來說,在其上層也是可以再加CRF進行限制一下的。不過本人還沒進行實戰(zhàn)的試驗,后續(xù)把結果整理整理。

4. 評估指標代碼

? 對于評估的指標,上面已經(jīng)說到是Acc、Precision、Recall和F1值。但是對于多個分類時,理論上要一個類別一個類別地去計算對應的Acc、Precision、Recall和F1值。其中需要了解的理論是混淆矩陣(confusion matrix)的概念,如下所示:


confusion matrix.png

如有150個樣本數(shù)據(jù),這些數(shù)據(jù)分成3類,每類50個。分類結束后得到的混淆矩陣為上圖所示。第一行第一列中的43表示有43個實際歸屬第一類的實例被預測為第一類,同理,第二行第一列的2表示有2個實際歸屬為第二類的實例被錯誤預測為第一類。
? 所以通過混淆矩陣來得到各個評估指標,拿precision這個指標來舉例,其他的同理:

from tensorflow.python.ops.metrics_impl import _streaming_confusion_matrix

def precision(labels,
              predictions, 
              num_classes, 
              pos_indices=None,    ## 這里表示要計算指標的label對應的index
              weights=None, 
              average='micro'):
    cm, op = _streaming_confusion_matrix(labels, predictions, num_classes, weights)
    pr, _, _ = metrics_from_confusion_matrix(cm, pos_indices, average=average)
    op, _, _ = metrics_from_confusion_matrix(op, pos_indices, average=average)
    return (pr, op) 

def metrics_from_confusion_matrix(cm, pos_indices=None, average='micro', beta=1):
    num_classes = cm.shape[0]
    if pos_indices is None:
        pos_indices = [i for i in range(num_classes)]

    if average == 'micro':
        return pr_re_fbeta(cm, pos_indices, beta)

    elif average in {'macro', 'weighted'}:
        precisions, recalls, fbetas, n_golds = [], [], [], []
        for idx in pos_indices:
            pr, re, fbeta = pr_re_fbeta(cm, [idx], beta)
            precisions.append(pr)
            recalls.append(re)
            fbetas.append(fbeta)
            cm_mask = np.zeros([num_classes, num_classes])
            cm_mask[idx, :] = 1
            n_golds.append(tf.to_float(tf.reduce_sum(cm * cm_mask)))

        if average == 'macro':
            pr = tf.reduce_mean(precisions)
            re = tf.reduce_mean(recalls)
            fbeta = tf.reduce_mean(fbetas)
            return pr, re, fbeta

        if average == 'weighted':
            n_gold = tf.reduce_sum(n_golds)
            pr_sum = sum(p * n for p, n in zip(precisions, n_golds))
            pr = safe_div(pr_sum, n_gold)
            re_sum = sum(r * n for r, n in zip(recalls, n_golds))
            re = safe_div(re_sum, n_gold)
            fbeta_sum = sum(f * n for f, n in zip(fbetas, n_golds))
            fbeta = safe_div(fbeta_sum, n_gold)
            return pr, re, fbeta

    else:
        raise NotImplementedError()

##TODO: 這里是對單個類別計算指標的核心代碼
def pr_re_fbeta(cm, pos_indices, beta=1):  # for example: pos_indices= [2]
    """Uses a confusion matrix to compute precision, recall and fbeta"""
    num_classes = cm.shape[0]
    neg_indices = [i for i in range(num_classes) if i not in pos_indices]
    cm_mask = np.ones([num_classes, num_classes])
    cm_mask[neg_indices, neg_indices] = 0
    diag_sum = tf.reduce_sum(tf.diag_part(cm * cm_mask))

    cm_mask = np.ones([num_classes, num_classes])
    cm_mask[:, neg_indices] = 0
    tot_pred = tf.reduce_sum(cm * cm_mask)

    cm_mask = np.ones([num_classes, num_classes])
    cm_mask[neg_indices, :] = 0
    tot_gold = tf.reduce_sum(cm * cm_mask)

    pr = safe_div(diag_sum, tot_pred)
    re = safe_div(diag_sum, tot_gold)
    fbeta = safe_div((1. + beta ** 2) * pr * re, beta ** 2 * pr + re)

    return pr, re, fbeta

def safe_div(numerator, denominator):
    """Safe division, return 0 if denominator is 0"""
    numerator, denominator = tf.to_float(numerator), tf.to_float(denominator)
    zeros = tf.zeros_like(numerator, dtype=numerator.dtype)
    denominator_is_zero = tf.equal(denominator, zeros)
    return tf.where(denominator_is_zero, zeros, numerator / denominator)

5. 對ROC的理解

? 先結合下圖二分類的混淆矩陣,闡述一下含義:


混淆矩陣.png

?這里的T表示真實和預測完全匹配了;而F表示真實和預測的不匹配了。而且,第一列用P表示預測為正,第二列用N表示預測為負。
?對于ROC曲線,是通過TPR和FPR這兩個指標來衡量綜合得到的:
??????TPR=TP/(TP+FN):表示預測為正實際為正的占總正樣本的比例。
??????FPR=FP/(FP+TN):表示預測為正實際為負的占總負樣本的比例。
?則在ROC曲線中,橫坐標是FPR,縱坐標是TPR


ROC.png

?理解以上圖,需要結合二分類中的閾值來闡述:當分類閾值變大時候,預測為正類的樣本會減少,那么帶來TP、FP這兩個都是預測為正的指標也變小,所以最后TPR=0,F(xiàn)PR=0;當分類閾值變小時候,預測為正類的樣本會增加,那么帶來TP、FP這兩個都是預測為正的指標也變大,所以最后TPR=1,F(xiàn)PR=1。
?所以最終會有圖上兩個極端的點(0,0)和(1,1),那么對于(0,1)這個點來說,表示的是TP=1、FP=0的情況,這是分類模型想要達到的最為理想的情況。所以如果ROC曲線上的某一點距離理想點(0,1)最近,那么就是設定分類模型中閾值最合適的點。

? ROC的作用很大,不僅以上說的可以調(diào)節(jié)出最優(yōu)的分類閾值,還可以解決類別不平衡現(xiàn)象。一個類別的樣本數(shù)量增加很多倍時,ROC曲線基本是保持不變的。

5. 對AUC的理解

? AUC (Area Under Curve) 被定義為ROC曲線下的面積,顯然這個面積的數(shù)值不會大于1。而它的作用是:使用AUC值作為評價標準是因為很多時候ROC曲線并不能清晰的說明哪個分類器的效果更好,而作為一個數(shù)值,對應AUC更大的分類器效果更好。

參考文獻:
通俗理解BiLSTM-CRF命名實體識別模型中的CRF
機器學習之分類性能度量指標 : ROC曲線、AUC值、正確率、召回率

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容