如果我们得到了标注序列,直接比对结果非常不直观。而 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) |
正文完