如果我们得到了标注序列,直接比对结果非常不直观。而 Spacy 可以轻松解决这个问题。比如有文本和标注序列,如果你的得到的标注是 span 和 label 就更轻松了。
import spacy
import numpy as np
text = "故障现象: 一辆 99 年出厂的奥迪 2001.8T 电控发动机,带涡轮增压系统和 ABS 防抱死,不带车辆防盗系统。" \
"故障原因: 装完新车,第一次通电时产生的,当时担忧线路有装错的坟,造成线路和电器的烧坏,在翻开点火开关后," \
"用电瓶线在电瓶桩头上来回碰了几次,感觉没有问题后才将电瓶线接好。这样电脑在通电的情况下,突然断电,反复几次," \
"造成电脑对节气门位置传感器的根本设置数值丧失,使原来正常的系统产生了故障。解决措施: 用群众专用检测仪 V1551 对节气门位置传感器进行根本设置"
tags = ['O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O',
'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O',
'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O',
'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O',
'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'I- 故障设备', 'I- 故障设备',
'O', 'I- 故障原因', 'I- 故障原因', 'O', 'O', 'O', 'O', 'O', 'I- 故障设备', 'I- 故障设备',
'O', 'I- 故障设备', 'I- 故障设备', 'O', 'I- 故障原因', 'I- 故障原因', 'O', 'O', 'O', 'O',
'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O',
'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O',
'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'I- 故障设备', 'O', 'O', 'O', 'O', 'O', 'O',
'O', 'O', 'O', 'O', 'O', 'I- 故障原因', 'I- 故障原因', 'O', 'O', 'O', 'O', 'O', 'O', 'O',
'O', 'I- 故障设备', 'I- 故障设备', 'O', 'I- 故障设备', 'I- 故障设备', 'I- 故障设备', 'I- 故障设备',
'I- 故障设备', 'I- 故障设备', 'I- 故障设备', 'I- 故障设备', 'O', 'O', 'O', 'I- 故障原因', 'I- 故障原因',
'I- 故障原因', 'I- 故障原因', 'I- 故障原因', 'I- 故障原因', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O',
'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O',
'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O',
'O', 'O', 'O', 'O', 'O']
1. 获取标注范围和 label
def search_entity_spans(tags):
#返回范围和 label 的元组列表
spans = []
start = None
current_label = None
for i, tag in enumerate(tags):
if tag.startswith("B-"):
if start is not None:
spans.append((start, i , current_label))
start = i
current_label = tag.split("-", 1)[1]
elif tag.startswith("I-"):
if start is None:
start = i
current_label = tag.split("-", 1)[1]
elif current_label != tag.split("-", 1)[1]:
spans.append((start, i, current_label))
start = i
current_label = tag.split("-", 1)[1]
else:
if start is not None:
spans.append((start, i, current_label))
start = None
current_label = None
if start is not None:
spans.append((start, len(tags), current_label))
return spans
spans = search_entity_spans(tags)
print(spans)
##[(76, 78, '故障设备'), (79, 81, '故障原因')...]
2. spacy.displacy.render 渲染标注样式
通过将 ents
参数设置为包含需要标注的实体类型列表来指定自定义样式。然后,我们将 options
作为参数传递给 spacy.displacy.render
函数,并将 manual
设置为 True
以启用手动模式。
def plot_annotation(text, spans):
colors = {'故障设备': 'linear-gradient(90deg, #aa9cfc, #fc9ce7)',
'故障原因': 'linear-gradient(90deg, #a9ffc9, #fffea9)'}
options = {"ents": ["故障设备", "故障原因"], "colors": colors}
ents = []
for s in spans:
ents.append({"start": int(s[0]), "end": int(s[1]), "label": s[2]})
doc = {"text": text, "ents": sorted(ents, key=lambda i: i["start"])}
spacy.displacy.render(doc, style="ent", options=options, manual=True, jupyter=True)
plot_annotation(text, spans)
补充:下面是只搜索实体范围函数, 这里没有使用,只是记录。
def search_entity_spans1(tags):
"""根据 tags 返回 span 范围元组列表"""
spans = []
start = None
for i, tag in enumerate(tags):
if tag.startswith("B-"):
if start is not None:
spans.append((start, i))
start = i
elif tag.startswith("I-"):
if start is None:
start = i
else:
if start is not None:
spans.append((start, i))
start = None
if start is not None:
spans.append((start, len(tags)))
return spans
spans = search_entity_spans1(tags)
print(spans)
print(tags)
正文完