Transformers 获得单词而不是标记的命名实体预测
Transformers get named entity prediction for words instead of tokens
这是一个非常基本的问题,但我花了好几个小时才找到答案。我使用 Hugginface 变压器构建了 NER。
说我输入了句子
input = "Damien Hirst oil in canvas"
我把它标记化得到
tokenizer = transformers.BertTokenizer.from_pretrained('bert-base-uncased')
tokenized = tokenizer.encode(input) #[101, 12587, 7632, 12096, 3514, 1999, 10683, 102]
将标记化的句子输入模型以获得标记的预测标签
['B-ARTIST' 'B-ARTIST' 'I-ARTIST' 'I-ARTIST' 'B-MEDIUM' 'I-MEDIUM'
'I-MEDIUM' 'B-ARTIST']
prediction
作为模型的输出。它将标签分配给不同的令牌。
如何重新组合这些数据以获得单词标签而不是标记?所以我会知道
"Damien Hirst" = ARTIST
"Oil in canvas" = MEDIUM
这里有两个问题。
注释令牌分类
一种常见的顺序标记,尤其是在命名实体识别中,遵循以下方案:开头带有标记 X
的标记序列得到 B-X
并且在标签重置时它得到 I-X
。
问题是大多数带注释的数据集都是用 space 标记的!例如:
[CSL] O
Damien B-ARTIST
Hirst I-ARTIST
oil B-MEDIUM
in I-MEDIUM
canvas I-MEDIUM
[SEP] O
其中 O
表示它不是 named-entity,B-ARTIST
是标记为 ARTIST
的标记序列的开头,而 I-ARTIST
是在序列内部 - MEDIUM
.
的类似模式
在我发布这个答案的那一刻,这里的 huggingface 文档中有一个 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))
])
这是一个非常基本的问题,但我花了好几个小时才找到答案。我使用 Hugginface 变压器构建了 NER。
说我输入了句子
input = "Damien Hirst oil in canvas"
我把它标记化得到
tokenizer = transformers.BertTokenizer.from_pretrained('bert-base-uncased')
tokenized = tokenizer.encode(input) #[101, 12587, 7632, 12096, 3514, 1999, 10683, 102]
将标记化的句子输入模型以获得标记的预测标签
['B-ARTIST' 'B-ARTIST' 'I-ARTIST' 'I-ARTIST' 'B-MEDIUM' 'I-MEDIUM'
'I-MEDIUM' 'B-ARTIST']
prediction
作为模型的输出。它将标签分配给不同的令牌。
如何重新组合这些数据以获得单词标签而不是标记?所以我会知道
"Damien Hirst" = ARTIST
"Oil in canvas" = MEDIUM
这里有两个问题。
注释令牌分类
一种常见的顺序标记,尤其是在命名实体识别中,遵循以下方案:开头带有标记 X
的标记序列得到 B-X
并且在标签重置时它得到 I-X
。
问题是大多数带注释的数据集都是用 space 标记的!例如:
[CSL] O
Damien B-ARTIST
Hirst I-ARTIST
oil B-MEDIUM
in I-MEDIUM
canvas I-MEDIUM
[SEP] O
其中 O
表示它不是 named-entity,B-ARTIST
是标记为 ARTIST
的标记序列的开头,而 I-ARTIST
是在序列内部 - MEDIUM
.
在我发布这个答案的那一刻,这里的 huggingface 文档中有一个 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))
])