这里有两个问题。
注释标记分类
一种常见的顺序标记,尤其是在命名实体识别中,遵循这样的方案:开头带有标记X 的标记序列得到B-X,并且在重置标签时得到I-X。
问题是大多数带注释的数据集都是用空间标记的!例如:
[CSL] O
Damien B-ARTIST
Hirst I-ARTIST
oil B-MEDIUM
in I-MEDIUM
canvas I-MEDIUM
[SEP] O
其中O 表示它不是一个命名实体,B-ARTIST 是标记为ARTIST 的标记序列的开始,I-ARTIST 在序列内部——MEDIUM 的模式类似。
在我发布这个答案的那一刻,这里的拥抱脸文档中有一个 NER 的例子:
https://huggingface.co/transformers/usage.html#named-entity-recognition
该示例并未完全回答此处的问题,但可以添加一些说明。该示例中命名实体标签的类似样式如下:
label_list = [
"O", # not a named entity
"B-ARTIST", # beginning of an artist name
"I-ARTIST", # an artist name
"B-MEDIUM", # beginning of a medium name
"I-MEDIUM", # a medium name
]
适应标记化
关于注解模式,BERT 和其他几个模型有不同的标记化模型。因此,我们必须调整这两种标记化。
在这种bert-base-uncased 的情况下,预期的结果是这样的:
damien B-ARTIST
hi I-ARTIST
##rst I-ARTIST
oil B-MEDIUM
in I-MEDIUM
canvas I-MEDIUM
为了完成这项工作,您可以遍历原始注释中的每个标记,然后对其进行标记并再次添加其标签:
tokens_old = ['Damien', 'Hirst', 'oil', 'in', 'canvas']
labels_old = ["B-ARTIST", "I-ARTIST", "B-MEDIUM", "I-MEDIUM", "I-MEDIUM"]
label2id = {label: idx for idx, label in enumerate(label_list)}
tokens, labels = zip(*[
(token, label)
for token_old, label in zip(tokens_old, labels_old)
for token in tokenizer.tokenize(token_old)
])
当您在tokens 中添加[CLS] 和[SEP] 时,它们的标签"O" 必须添加到labels。
使用上面的代码,可能会遇到这样的情况:当开头的单词分裂成片段时,像B-ARTIST 这样的开头标记会重复。根据 huggingface 文档中的描述,您可以使用 -100 对这些标签进行编码以被忽略:
https://huggingface.co/transformers/custom_datasets.html#token-classification-with-w-nut-emerging-entities
这样的事情应该可以工作:
tokens, labels = zip(*[
(token, label2id[label] if (label[:2] != "B-" or i == 0) else -100)
for token_old, label in zip(tokens_old, labels_old)
for i, token in enumerate(tokenizer.tokenize(token_old))
])