如何导出 "Document with entities from spaCy" 以在 doccano 中使用

How to export "Document with entities from spaCy" for use in doccano

我想用 doccano 或其他“开源文本注释工具”训练我的模型并不断改进我的模型。

我的理解是,我可以按照此处描述的格式将带注释的数据导入 doccano:

因此,第一步我加载了一个模型并创建了一个文档:

text = "Test text that should be annotated for Michael Schumacher" 
nlp = spacy.load('en_core_news_sm')
doc = nlp(text)

我知道我可以从 doccano 导出 jsonl 格式(带有文本和带注释的标签)并用它训练模型,但我想知道如何从 python 中的 spaCy 文档导出该数据,以便我可以将它导入 doccano。

提前致谢。

Spacy 不支持这种开箱即用的确切格式,但您应该能够相当轻松地编写自定义函数。看看 spacy.gold.docs_to_json(),它显示了与 JSON.

类似的转换

我最近有一个类似的任务,这是我的做法:

import spacy
nlp = spacy.load('en_core_news_sm')

def text_to_doccano(text):
    """
    :text (str): source text
    Returns (list (dict)): deccano format json
    """
    djson = list()
    doc = nlp(text)
    for sent in doc.sents:
        labels = list()
        for e in sent.ents:
            labels.append([e.start_char, e.end_char, e.label_])
        djson.append({'text': sent.text, "labels": labels})
    return djson

基于你的例子...

text = "Test text that should be annotated for Michael Schumacher."
djson = text_to_doccano(text)
print(djson)

... 会打印出:

[{'text': 'Test text that should be annotated for Michael Schumacher.', 'labels': [[39, 57, 'PERSON']]}]

在相关说明中,当您将结果保存到文件时,标准 json.dump 保存 JSON 的方法将不起作用,因为它会将其写入以逗号分隔的条目列表。 AFAIK,doccano 期望每行一个条目并且没有尾随逗号。在解决这个问题时,以下代码片段很有魅力:

import json

open(filepath, 'w').write("\n".join([json.dumps(e) for e in djson]))

/干杯

Doccano and/or spaCy 似乎已经改变了一些东西,现在接受的答案中有一些缺陷。截至 2021 年 8 月 1 日,此修订版应该更适用于 spaCy 3.1 和 Doccano...

def text_to_doccano(text):
    """
    :text (str): source text
    Returns (list (dict)): deccano format json
    """
    djson = list()
    doc = nlp(text)
    for sent in doc.sents:
        labels = list()
        for e in sent.ents:
            labels.append([e.start_char - sent.start_char, e.end_char - sent.start_char, e.label_])
        djson.append({'text': sent.text, "label": labels})
    return djson

不同点:

  1. labels 在 JSON 中变为单数 label (?!?)
  2. e.start_chare.end_char 实际上(现在?)文档中的开始和结束,而不是句子中...所以你必须通过句子中的位置来抵消它们文档。

我用过Doccano标注工具,生成标注, 我已经从 Doccano 导出了 .jsonl 文件 使用以下自定义代码转换为 .spaCy 训练格式。

要遵循的步骤:

第一步:使用doccano工具标注数据。

第 2 步: 从 Doccano 导出注释文件,格式为 .jsonl。

第 3 步: 将该 .jsonl 文件传递​​给下面代码中的 fillterDoccanoData("./root.jsonl") 函数,在我的例子中,我有 root.jsonl 对我来说,你可以用你自己的。

第 4 步: 使用以下代码将您的 .jsonl 文件转换为 .spacy 训练文件。

第 5 步: 最后,您可以在工作目录中找到 train.spacy。

谢谢

import spacy
from spacy.tokens import DocBin
from tqdm import tqdm
import logging
import json

#filtter data to convert in spacy format
def fillterDoccanoData(doccano_JSONL_FilePath):
    try:
        training_data = []
        lines=[]
        with open(doccano_JSONL_FilePath, 'r') as f:
            lines = f.readlines()

        for line in lines:
            data = json.loads(line)
            text = data['data']
            entities = data['label']
            if len(entities)>0:
                training_data.append((text, {"entities" : entities}))
        return training_data
    except Exception as e:
        logging.exception("Unable to process " + doccano_JSONL_FilePath + "\n" + "error = " + str(e))
        return None

#read Doccano Annotation file .jsonl
TRAIN_DATA=fillterDoccanoData("./root.jsonl") #root.jsonl is annotation file name file name 

nlp = spacy.blank("en") # load a new spacy model
db = DocBin() # create a DocBin object
for text, annot in tqdm(TRAIN_DATA): # data in previous format
    doc = nlp.make_doc(text) # create doc object from text
    ents = []
    for start, end, label in annot["entities"]: # add character indexes
        span = doc.char_span(start, end, label=label, alignment_mode="contract")
        if span is None:
            print("Skipping entity")
        else:
            ents.append(span)
    try:
        doc.ents = ents # label the text with the ents
        db.add(doc)
    except:
        print(text, annot)
db.to_disk("./train.spacy") # save the docbin object