转换NER SpaCy格式为IOB格式

时间:2020-01-14 09:35:41

标签: nlp spacy named-entity-recognition ner

我有已经以SpaCy格式标记的数据。例如:

("Who is Shaka Khan?", {"entities": [(7, 17, "PERSON")]}),
("I like London and Berlin.", {"entities": [(7, 13, "LOC"), (18, 24, "LOC")]})

但是我想尝试使用任何其他NER模型来训练它,例如BERT-NER,它需要IOB标记。从SpaCy数据格式到IOB有转换代码吗?

谢谢!

3 个答案:

答案 0 :(得分:2)

这与https://stackoverflow.com/a/59209377/461847密切相关,并且大多是从Bullet 1复制而来,也请参见注释中的注释:

import spacy
from spacy.gold import biluo_tags_from_offsets

TRAIN_DATA = [
    ("Who is Shaka Khan?", {"entities": [(7, 17, "PERSON")]}),
    ("I like London and Berlin.", {"entities": [(7, 13, "LOC"), (18, 24, "LOC")]}),
]

nlp = spacy.load('en_core_web_sm')
docs = []
for text, annot in TRAIN_DATA:
    doc = nlp(text)
    tags = biluo_tags_from_offsets(doc, annot['entities'])
    # then convert L->I and U->B to have IOB tags for the tokens in the doc

答案 1 :(得分:1)

恐怕,您将不得不编写自己的转换,因为IOB编码取决于预训练的表示模型(BERT,RoBERTa或您选择的任何预训练的模型)将使用哪种令牌化。

SpaCy格式指定实体的字符跨度,即

@Override
public ResponseEntity<List<His>> getPagosByFechaPago(String dateTo, String dateFrom) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");

        Date dateHasta = null, dateDesde = null;
        try {
            dateHasta = formatter.parse(dateTo);
            dateDesde = formatter.parse(dateFrom);
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        Period diff = Period.between(LocalDate.parse(dateFrom), LocalDate.parse(dateTo));
        if(diff.getYears() == 0 && diff.getMonths() <= 2 && diff.getMonths() >= -2) {
            Query query = new Query();
            query.addCriteria(Criteria.where("date").gte(dateHasta).lt(dateDesde));
            List<His> response = mongoTemplate.find(query, His.class);
            log.info(response.toString());
            return new ResponseEntity<>(response, HttpStatus.OK);
        } else {
            return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
        }
}

将返回"Who is Shaka Khan?"[7:17] 。您需要将其与预训练模型使用的令牌相匹配。

以下是使用Huggingface's Transformers时不同模型如何标记例句的示例。

  • BERT:"Shaka Khan"
  • RoBERTa:['Who', 'is', 'S', '##hak', '##a', 'Khan', '?']
  • XLNet:['Who', '_is', '_Sh', 'aka', '_Khan', '?']

了解令牌生成器的工作原理后,可以实现转换。像这样的事情可以对BERT令牌化起作用。

['▁Who', '▁is', '▁Shak', 'a', '▁Khan', '?']

请注意,如果一个BERT令牌包含一个实体的末尾和下一实体的开始,则该代码段将中断。分词器也没有区分原始字符串中有多少空格(或其他空格),这也是潜在的错误来源。

答案 2 :(得分:0)

首先您需要将带注释的 json 文件转换为 csv
然后你可以运行下面的代码转换成spaCy V2 Binary格式

df = pd.read_csv('SC_CSV.csv')
l1 = []
l2 = []

for i in range(0, len(df['ner'])):
    l1.append(df['ner'][i])
    l2.append({"entities":[(0,len(df['ner'][i]),df['label'][i])]})
    
TRAIN_DATA = list(zip(l1, l2))
TRAIN_DATA 

现在是 TRAIN_DATA 格式的 spaCy V2

这有助于将文件从您的 Spacy v2 格式转换为全新的 Spacy v3 格式。

import pandas as pd
from tqdm import tqdm
import spacy
from spacy.tokens import DocBin

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)
    doc.ents = ents # label the text with the ents
    db.add(doc)

db.to_disk("./train.spacy") # save the docbin object