1. 程式人生 > 其它 >doccano標註後的序列標註任務資料轉錄為BIO形式

doccano標註後的序列標註任務資料轉錄為BIO形式

技術標籤:自然語言處理python自然語言處理深度學習

在這裡插入圖片描述
掃碼關注公眾號“自然語言處理與演算法”,帶你搞NLP~
今兒是2020年12月31日,本年度最後一更,盆友們,2021再見!繼續努力鴨~

doccano是一個開源的語料標註工具,其可以用來標註實體識別訓練語料。但是標註之後的資料是不能直接作為訓練資料的,還需要將其轉錄一下,下面以轉錄為BIO為例。

1 doccano標註之後的資料格式(json)

{"id": 4, "text": "?生益科技主要從事覆銅板製造與銷售業務,銷售收入佔到公司總收入的81.52%。", "meta"
: {}, "annotation_approver": null, "labels": [[1, 5, "ORG"], [32, 38, "NUM"]]} {"id": 6, "text": "目前,益科技,已是覆銅板的龍頭企業。早在公司2018 年年報,生益科技硬質覆銅板銷售總額全球排名第二。", "meta": {}, "annotation_approver": null, "labels"
: [[31, 35, "ORG"], [22, 27, "NUM"], [3, 6, "ORG"], [20, 22, "ORG"]]} {"id": 9, "text": "專注高頻覆銅板的南通專案一期產能為100w平米/年,現逐步投產。", "meta": {}, "annotation_approver": null, "labels": [[17, 21, "NUM"]]} {
"id": 10, "text": "?獲得NOKIA、華為、中興、浪潮、格力、國星光電等客戶的認可。", "meta": {}, "annotation_approver": null, "labels": [[3, 8, "ORG"], [9, 11, "ORG"], [12, 14, "ORG"], [15, 17, "ORG"], [18, 20, "ORG"], [21, 25, "ORG"]]} {"id": 11, "text": "③業績符合預期,產品未來看點足", "meta": {}, "annotation_approver": null, "labels": [[1, 3, "PRO"], [5, 7, "TAP"]]}

需要注意的是以上並不是標準的json格式,所以直接從doccano系統裡下載下來的json是不能用python直接載入的。
標準的json檔案格式如下:

[
{''''},
{'''''},
{'''''}
]

轉換程式碼如下:


def generate_json():
    '''將標註系統下載下來的檔案轉換為標準json格式'''
    f1 = open('out.json', 'w', encoding='utf-8')
    f1.write("[")
    with open('in.json', 'r', encoding='utf-8')as f2:
        lines = f2.readlines()
        k = len(lines)
        i = 0
        while i < k-2:
            f1.write(lines[i].strip() + ',\n')
            i += 1
        f1.write(lines[i].strip() + '\n')
    f1.write(']')
    f1.close()

轉換為標準格式後可以直接載入,然後將其中的資料轉錄為BIO形式。

2.轉錄為BIO

轉錄程式碼如下:

import json

def tranfer2bio():
    '''
    將json檔案中的資料轉錄為BIO形式,儲存規則可以在43行修改
    :return:
    '''
    f1 = open('./train.txt', 'w', encoding='utf-8')
    with open("./1.json", 'r', encoding='utf-8') as inf:
        load = json.load(inf)
        for i in range(len(load)):
            labels = load[i]['labels']
            text = load[i]['text']
            tags = ['O'] * len(text)
            for j in range(len(labels)):
                label = labels[j]
                #print(label)
                tags[label[0]] = 'B-' + str(label[2])
                k = label[0]+1
                while k < label[1]:
                    tags[k] = 'I-' + str(label[2])
                    k += 1
            print(tags)
            for word, tag in zip(text, tags):
                f1.write(word + '\t' + tag + '\n')
            f1.write("\n")

#tranfer2bio()

3.根據BIO序列提取實體

模型預測出對應文字的BIO序列後,如何根據序列提取實體呢?這個問題有很多解決辦法。博主這裡給出一個開源通用的解決方法[1]。
思路1:遇到B則前面存在的實體,進行一次儲存。多個i粘連一塊兒也可能被認為是一個實體。錯誤的情況是B識別成i了。對於類別判斷失誤,粘連的實體取眾數。

#標籤轉錄BIO格式
string="我是李明,我愛中國,我來自呼和浩特"
predict=["o","o","i-per","i-per","o","o","o","b-loc","i-loc","o","o","o","o","b-per","i-loc","i-loc","i-loc"]
item = {"string": string, "entities": []}
entity_name = ""
flag=[]
visit=False
for char, tag in zip(string, predict):
    if tag[0] == "b":
        if entity_name!="":
            x=dict((a,flag.count(a)) for a in flag)
            y=[k for k,v in x.items() if max(x.values())==v]
            item["entities"].append({"word": entity_name,"type": y[0]})
            flag.clear()
            entity_name=""
        entity_name += char
        flag.append(tag[2:])
    elif tag[0]=="i":
        entity_name += char
        flag.append(tag[2:])
    else:
        if entity_name!="":
            x=dict((a,flag.count(a)) for a in flag)
            y=[k for k,v in x.items() if max(x.values())==v]
            item["entities"].append({"word": entity_name,"type": y[0]})
            flag.clear()
        flag.clear()
        entity_name=""
 
if entity_name!="":
    x=dict((a,flag.count(a)) for a in flag)
    y=[k for k,v in x.items() if max(x.values())==v]
    item["entities"].append({"word": entity_name,"type": y[0]})
print(item)
{'string': '我是李明,我愛中國,我來自呼和浩特', 'entities': [{'word': '李明', 'type': 'per'}, {'word': '中國', 'type': 'loc'}, {'word': '呼和浩特', 'type': 'loc'}]}

思路2:只取B開頭的實體,其它的不要。同樣類別也是取眾數。

#標籤轉錄BIO格式
string="我是李明,我愛中國,我來自呼和浩特"
predict=["o","o","i-per","i-per","o","o","o","b-loc","i-loc","o","o","o","o","b-per","i-loc","i-loc","i-loc"]
item = {"string": string, "entities": []}
entity_name = ""
flag=[]
visit=False
for char, tag in zip(string, tags):
    if tag[0] == "b":
        if entity_name!="":
            x=dict((a,flag.count(a)) for a in flag)
            y=[k for k,v in x.items() if max(x.values())==v]
            item["entities"].append({"word": entity_name,"type": y[0]})
            flag.clear()
            entity_name=""
        visit=True
        entity_name += char
        flag.append(tag[2:])
    elif tag[0]=="i" and visit:
        entity_name += char
        flag.append(tag[2:])
    else:
        if entity_name!="":
            x=dict((a,flag.count(a)) for a in flag)
            y=[k for k,v in x.items() if max(x.values())==v]
            item["entities"].append({"word": entity_name,"type": y[0]})
            flag.clear()
        flag.clear()
        visit=False
        entity_name=""
 
if entity_name!="":
    x=dict((a,flag.count(a)) for a in flag)
    y=[k for k,v in x.items() if max(x.values())==v]
    item["entities"].append({"word": entity_name,"type": y[0]})
print(item)
{'string': '我是李明,我愛中國,我來自呼和浩特', 'entities': [{'word': '中國', 'type': 'loc'}, {'word': '呼和浩特', 'type': 'loc'}]}

2020年12月31日 於上海

參考文獻
[1]BIO序列提取實體(NER命名實體識別).https://blog.csdn.net/hqh131360239/article/details/107764716