基于Keras构建自定义NER模型时如何处理未登录未知词问题
你当前方案的核心问题
你盲目扩充词表的做法逻辑上就走不通:新增的10万词根本没有在你的NER标注训练语料里出现过,训练过程中这些词对应的Embedding权重不会得到有效更新,完全是随机值,对模型效果没有任何提升,反而徒增了Embedding层的参数量,自然会导致训练速度大幅下降。
可落地的解决方案按优先级排列:
第一优先级:增加未知词兜底机制,先解决预测报错
首先给词表加特殊未知词标记,从根源上避免KeyError:- 构建词表时加入
[UNK]标记:word2idx["[UNK]"] = len(word2idx) - 预测时用字典的get方法兜底:
text = [word2idx.get(w, word2idx["[UNK]"]) for w in text]
遇到未登录词时会统一映射到[UNK]的ID,不会直接报错,Embedding层记得把input_dim调整为len(word2idx),给[UNK]留好位置。
- 构建词表时加入
第二优先级:加入字粒度特征提升未登录词识别效果
NER场景下的未登录词大多是人名、地名、企业名这类专有名词,本身的字符特征有很强的规律性(比如首字母大写、带Ltd/Inc后缀等),你可以新增一个字级别特征的输入分支:把每个词拆成单个字符,过字符Embedding+CNN/LSTM得到字向量,再和原来的词向量拼接后输入后续的BiLSTM层。就算词没有在训练语料里出现过,模型也可以通过字符特征判断实体类型,完全不需要扩充词表。第三优先级:用预训练词向量初始化Embedding层
你可以用GloVe、Word2Vec这类公开的预训练词向量来初始化你的Embedding层,训练时可以选择冻住权重或者微调。这些预训练词向量是在海量通用文本上训练得到的,覆盖的词汇量远大于你自己爬的词表,而且每个词的向量都包含通用语义,就算是你的训练语料里没出现过的词,只要在预训练词表里有,就能拿到有效的语义向量,比你自己随机初始化Embedding效果好得多,冻住权重的话还不会增加训练成本。第四优先级:换用子词粒度的预训练模型(工业界上线首选)
现在工业界做NER基本都用BERT、DistilBERT这类基于子词分词的预训练模型,这类模型用WordPiece/BPE分词策略,天然不存在未登录词的问题,会把陌生词拆成多个子词处理,而且预训练阶段已经学习了海量文本的语义特征,微调后的效果远好于自己搭的BiLSTM模型,部署也更省心,非常适合生产环境使用。
内容的提问来源于stack exchange,提问作者taga

