引言
最近工作偶尔会跟 NER 模型(命名实体识别)打交道,简单介绍一下背景,NER 模型的输出是一个结构化数据,比如:
// 万达广场沙县小吃
{
"slot": "沙县小吃",
"start": "4",
"end": "8",
"entity": "商铺"
}
在交付给业务方之前,需要使用 BIO 方案将原字符串序列转换成一个带命名实体标签的字符串序列:
万达广场沙县小吃
=>
万_O 达_O 广_O 场_O 沙_B-商铺 县_I-商铺 小_I-商铺 吃_I-商铺
如果事情到这里就结束了,那简直皆大欢喜,卷完下班。然而,在实践中,业务方们那儿反馈了两个问题,直接「被迫」加班。
问题一:模型预测的结果里,为啥会有
[UNK]_O这样的字符?没法看好不啦…
一个业务方要的是一个古文领域的 NER 模型,不难想象,总是存在一部分生僻的汉字字符。熟悉 BERT 的同学都知道,无法识别的汉字字符,在数据预处理阶段,我们习惯上会直接拿一个特殊的 [UNK] 替代它。这也就意味着,模型预测结果,自然而然地存在如问题描述中所示的字符:[UNK]_O。
问题二:原来的文本序列包含空格的,为啥预测的序列内,空格被吞了?这跟标注的序列一样又不一样,脑壳疼…
你可能会问,那如果数据集不是古文,而是常用的现代汉语数据集,那应该不会出现问题一了吧?当然,然而故事永远不会这么简单。另一个业务方反馈了问题二,经过一番排查,发现是模型在预测输入文本之前,会对待预测文本作分词处理,再以空格为分隔符重新合并成一个字符串,方便后续处理,比如:
重庆冰淇淋 红豆冰粉
=>
重庆 冰淇淋 红豆 冰粉
如此一来,空格就被吞掉了…不管是问题一还是问题二,如果只在乎被识别的命名实体,那其实无伤大雅。不过业务方的态度出奇得一致,纷纷表示这样不行,预测序列除开标签,必须与预测文本完全一致。
基本思路
看到问题一,我们很自然地想到了通过遍历的方式对两个序列进行比对,遇到同一位置不一致的字符(特指其中一个字符是 [UNK])时,替代即可。于是我们闭着眼睛,一气呵成:
def replace_unk(text: str, sequence: str) -> str:
characters = [word for word in text]
tokens, tags = (
list(map(lambda item: item.split("_")[0], sequence.split())),
list(map(lambda item: item.split("_")[1], sequence.split())),
)
for i in range(len(characters)):
if characters[i] == tokens[i]:
continue
tokens[i] = characters[i]
retur

博客围绕NER模型预测结果出现的问题展开。业务方反馈模型预测结果存在特殊字符、空格被吞的问题,原解决思路因字符编码和空格问题难以实现。最终借助Python原生difflib库,利用其比较序列对、找最长公共子序列的功能,快速优雅地解决了问题。
最低0.47元/天 解锁文章
633

被折叠的 条评论
为什么被折叠?



